Browse Source

DebugTools: improve pixel view format autodetection in CompareImage.

Originally it was just assuming that any Vector3ub or Color3ub is a
normalized format. That was kinda enough for many cases, but it started
to get annoying with sRGB image comparisons, as those had to be
manually reinterpret with a sRGB-less format in order to pass.

Now the pixel format detection looks at the expected image format as
well, and if the underlying type and component count matches, it
inherits the sRGB and normalized property from it as well. If not, it
falls back to an integer format for vectors, and normalized format for
colors. For vectors this is different from previous behavior but
shouldn't cause any problem in practice -- the only result will be that
the image comparison fails with a different message for pixel format
mismatch than before.

This now also properly and fully tests the pixelFormatFor() helper, and
adds a missing Color3 specialization of it.
pull/620/head
Vladimír Vondruš 3 years ago
parent
commit
f589aa1a5d
  1. 3
      doc/changelog.dox
  2. 17
      src/Magnum/DebugTools/CompareImage.cpp
  3. 382
      src/Magnum/DebugTools/CompareImage.h
  4. 289
      src/Magnum/DebugTools/Test/CompareImageTest.cpp

3
doc/changelog.dox

@ -478,7 +478,8 @@ See also:
relying on memory-mapping, and is now available on WebGL 2 as well (see
[mosra/magnum#560](https://github.com/mosra/magnum/pull/560))
- @ref DebugTools::CompareImage now supports comparing half-float pixel
formats as well
formats as well, pixel format autodetection for pixel views tries to
match sRGB and normalization properties of the expected image format
@subsubsection changelog-latest-changes-gl GL library

17
src/Magnum/DebugTools/CompareImage.cpp

@ -545,6 +545,14 @@ TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(const PixelFormat
return flags;
}
TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(PixelFormat(*const actualFormatFor)(PixelFormat), const Containers::StridedArrayView3D<const char>& actualPixels, const ImageView2D& expected) {
/* Figure out the actual format for the pixel view, attempting to match it
with what's in the expected image (such as making it sRGB if the
expected format is also sRGB and has the same component count and
underlying type as the view) */
return compare(actualFormatFor(expected.format()), actualPixels, expected);
}
TestSuite::ComparisonStatusFlags ImageComparatorBase::operator()(const ImageView2D& actual, const ImageView2D& expected) {
return compare(actual.format(), actual.pixels(), expected);
}
@ -608,7 +616,7 @@ TestSuite::ComparisonStatusFlags ImageComparatorBase::operator()(const Container
return flags;
}
TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(const PixelFormat actualFormat, const Containers::StridedArrayView3D<const char>& actualPixels, const Containers::StringView expected) {
TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(const PixelFormat actualFormat, PixelFormat(*const actualFormatFor)(PixelFormat), const Containers::StridedArrayView3D<const char>& actualPixels, const Containers::StringView expected) {
_state->expectedFilename = expected;
Containers::Pointer<Trade::AbstractImporter> importer;
@ -644,6 +652,11 @@ TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(const PixelFormat
return TestSuite::ComparisonStatusFlag::Failed|TestSuite::ComparisonStatusFlag::Diagnostic;
}
/* Now that the image is loaded, try to match the actual format to it if
requested */
if(actualFormatFor)
_state->actualFormat = actualFormatFor(_state->expectedImageData->format());
/* Save a view on the expected image data and proxy to the actual data
comparison. If comparison failed, offer to save a diagnostic. */
_state->expectedImage.emplace(*_state->expectedImageData);
@ -654,7 +667,7 @@ TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(const PixelFormat
}
TestSuite::ComparisonStatusFlags ImageComparatorBase::operator()(const ImageView2D& actual, const Containers::StringView expected) {
return compare(actual.format(), actual.pixels(), expected);
return compare(actual.format(), nullptr, actual.pixels(), expected);
}
TestSuite::ComparisonStatusFlags ImageComparatorBase::operator()(const Containers::StringView actual, const ImageView2D& expected) {

382
src/Magnum/DebugTools/CompareImage.h

@ -62,7 +62,9 @@ class CompareFileToImage;
namespace Implementation {
template<class> constexpr PixelFormat pixelFormatFor();
/* Used by the pixel view comparators to try to guess a format matching the
expected image */
template<class> constexpr PixelFormat pixelFormatFor(PixelFormat);
class MAGNUM_DEBUGTOOLS_EXPORT ImageComparatorBase {
public:
@ -80,11 +82,15 @@ class MAGNUM_DEBUGTOOLS_EXPORT ImageComparatorBase {
TestSuite::ComparisonStatusFlags operator()(const ImageView2D& actual, Containers::StringView expected);
/* Used in templated CompareImage::operator() */
TestSuite::ComparisonStatusFlags compare(PixelFormat actualFormat, const Containers::StridedArrayView3D<const char>& actualPixels, const ImageView2D& expected);
/* Used in templated CompareImage::operator(), the actual format gets
matched to the format in the expected image */
TestSuite::ComparisonStatusFlags compare(PixelFormat(*actualFormatFor)(PixelFormat), const Containers::StridedArrayView3D<const char>& actualPixels, const ImageView2D& expected);
/* Used in templated CompareImageToFile::operator() */
TestSuite::ComparisonStatusFlags compare(PixelFormat actualFormat, const Containers::StridedArrayView3D<const char>& actualPixels, Containers::StringView expected);
/* Used in templated CompareImageToFile::operator(). If actualFormatFor
isn't nullptr, the actualFormat gets overriden by it once the
expected image is loaded. */
TestSuite::ComparisonStatusFlags compare(PixelFormat actualFormat, PixelFormat(*actualFormatFor)(PixelFormat), const Containers::StridedArrayView3D<const char>& actualPixels, Containers::StringView expected);
void printMessage(TestSuite::ComparisonStatusFlags flags, Debug& out, Containers::StringView actual, Containers::StringView expected) const;
@ -121,9 +127,8 @@ template<> class MAGNUM_DEBUGTOOLS_EXPORT Comparator<Magnum::DebugTools::Compare
}
template<class T> TestSuite::ComparisonStatusFlags operator()(const Containers::StridedArrayView2D<const T>& actualPixels, const Magnum::ImageView2D& expected) {
/** @todo do some tryFindCompatibleFormat() here */
return Magnum::DebugTools::Implementation::ImageComparatorBase::compare(
Magnum::DebugTools::Implementation::pixelFormatFor<T>(),
Magnum::DebugTools::Implementation::pixelFormatFor<T>,
Containers::arrayCast<3, const char>(actualPixels), expected);
}
};
@ -149,9 +154,9 @@ template<> class MAGNUM_DEBUGTOOLS_EXPORT Comparator<Magnum::DebugTools::Compare
having to include it -- it's not needed when comparing just images
alone, not files */
template<class T> TestSuite::ComparisonStatusFlags operator()(const Containers::StridedArrayView2D<const T>& actualPixels, const Containers::StringView& expected) {
/** @todo do some tryFindCompatibleFormat() here */
return Magnum::DebugTools::Implementation::ImageComparatorBase::compare(
Magnum::DebugTools::Implementation::pixelFormatFor<T>(),
Magnum::DebugTools::Implementation::pixelFormatFor<T>(Magnum::PixelFormat{}),
Magnum::DebugTools::Implementation::pixelFormatFor<T>,
Containers::arrayCast<3, const char>(actualPixels), expected);
}
};
@ -323,12 +328,16 @@ three-component you can cast the pixel data to just a three-component type:
@snippet MagnumDebugTools-gl.cpp CompareImage-pixels-rgb
Currently, comparing against pixel views has a few inherent limitations --- it
has to be cast to one of Magnum scalar or vector types and the format is
then autodetected from the passed type, with normalized formats preferred. In
practice this means e.g. @ref Math::Vector2 "Math::Vector2<UnsignedByte>" will
be understood as @ref PixelFormat::RG8Unorm and there's currently no way to
interpret it as @ref PixelFormat::RG8UI, for example.
The pixel views are expected to be cast to one of Magnum scalar or vector
types. The format is then autodetected from the passed type. For types that map
to more than one @ref PixelFormat, such as @relativeref{Magnum,Vector3ub} that
can be @ref PixelFormat::RGB8Unorm, @relativeref{PixelFormat,RGB8Srgb} or
@relativeref{PixelFormat,RGB8UI}, an attempt is made to match the pixel format
of the expected image if possible. If not possible, such as comparing a
@relativeref{Magnum,Vector3ub} view to a @ref PixelFormat::RGB8Snorm, the
comparison fails the same way as if comparing two views of different pixel
formats. Byte and short color types always autodetect to normalized (sRGB)
pixel formats, never to integer formats.
@see @ref CompareMaterial
*/
@ -605,84 +614,273 @@ class CompareFileToImage {
namespace Implementation {
/* LCOV_EXCL_START */
/* One-component types */
template<> constexpr PixelFormat pixelFormatFor<UnsignedByte>() { return PixelFormat::R8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedByte>>() { return PixelFormat::R8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Byte>() { return PixelFormat::R8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Byte>>() { return PixelFormat::R8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<UnsignedShort>() { return PixelFormat::R16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedShort>>() { return PixelFormat::R16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Short>() { return PixelFormat::R16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Short>>() { return PixelFormat::R16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<UnsignedInt>() { return PixelFormat::R32UI; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedInt>>() { return PixelFormat::R32UI; }
template<> constexpr PixelFormat pixelFormatFor<Int>() { return PixelFormat::R32I; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Int>>() { return PixelFormat::R32I; }
template<> constexpr PixelFormat pixelFormatFor<Float>() { return PixelFormat::R32F; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Float>>() { return PixelFormat::R32F; }
/* Two-component types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedByte>>() { return PixelFormat::RG8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedByte>>() { return PixelFormat::RG8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Byte>>() { return PixelFormat::RG8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Byte>>() { return PixelFormat::RG8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedShort>>() { return PixelFormat::RG16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedShort>>() { return PixelFormat::RG16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Short>>() { return PixelFormat::RG16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Short>>() { return PixelFormat::RG16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedInt>>() { return PixelFormat::RG32UI; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedInt>>() { return PixelFormat::RG32UI; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Int>>() { return PixelFormat::RG32I; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Int>>() { return PixelFormat::RG32I; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Float>>() { return PixelFormat::RG32F; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Float>>() { return PixelFormat::RG32F; }
/* Three-component types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedByte>>() { return PixelFormat::RGB8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedByte>>() { return PixelFormat::RGB8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<UnsignedByte>>() { return PixelFormat::RGB8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Byte>>() { return PixelFormat::RGB8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Byte>>() { return PixelFormat::RGB8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<Byte>>() { return PixelFormat::RGB8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedShort>>() { return PixelFormat::RGB16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedShort>>() { return PixelFormat::RGB16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<UnsignedShort>>() { return PixelFormat::RGB16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Short>>() { return PixelFormat::RGB16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Short>>() { return PixelFormat::RGB16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<Short>>() { return PixelFormat::RGB16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedInt>>() { return PixelFormat::RGB32UI; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedInt>>() { return PixelFormat::RGB32UI; }
/* Skipping Math::Color3<UnsignedInt>, as that isn't much used */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Int>>() { return PixelFormat::RGB32I; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Int>>() { return PixelFormat::RGB32I; }
/* Skipping Math::Color3<Int>, as that isn't much used */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Float>>() { return PixelFormat::RGB32F; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Float>>() { return PixelFormat::RGB32F; }
/* Four-component types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedByte>>() { return PixelFormat::RGBA8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedByte>>() { return PixelFormat::RGBA8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<UnsignedByte>>() { return PixelFormat::RGBA8Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Byte>>() { return PixelFormat::RGBA8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Byte>>() { return PixelFormat::RGBA8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Byte>>() { return PixelFormat::RGBA8Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedShort>>() { return PixelFormat::RGBA16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedShort>>() { return PixelFormat::RGBA16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<UnsignedShort>>() { return PixelFormat::RGBA16Unorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Short>>() { return PixelFormat::RGBA16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Short>>() { return PixelFormat::RGBA16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Short>>() { return PixelFormat::RGBA16Snorm; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedInt>>() { return PixelFormat::RGBA32UI; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedInt>>() { return PixelFormat::RGBA32UI; }
/* Skipping Math::Color4<UnsignedInt>, as that isn't much used */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Int>>() { return PixelFormat::RGBA32I; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Int>>() { return PixelFormat::RGBA32I; }
/* Skipping Math::Color4<Int>, as that isn't much used */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Float>>() { return PixelFormat::RGBA32F; }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Float>>() { return PixelFormat::RGBA32F; }
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Float>>() { return PixelFormat::RGBA32F; }
/* LCOV_EXCL_STOP */
/* One-component types. The Vector<1, T> types aren't used that often so they
delegate to the T variants, the tests use Vector<1, T> to cover all
variants. */
template<> constexpr PixelFormat pixelFormatFor<UnsignedByte>(PixelFormat expected) {
/* Attempt to match Srgb or Unorm if the expected image has it */
return expected == PixelFormat::R8Srgb ? PixelFormat::R8Srgb :
expected == PixelFormat::R8Unorm ? PixelFormat::R8Unorm :
PixelFormat::R8UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedByte>>(PixelFormat expected) {
return pixelFormatFor<UnsignedByte>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Byte>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::R8Snorm ? PixelFormat::R8Snorm :
PixelFormat::R8I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Byte>>(PixelFormat expected) {
return pixelFormatFor<Byte>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<UnsignedShort>(PixelFormat expected) {
/* Attempt to match Unorm if the expected image has it */
return expected == PixelFormat::R16Unorm ? PixelFormat::R16Unorm :
PixelFormat::R16UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedShort>>(PixelFormat expected) {
return pixelFormatFor<UnsignedShort>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Short>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::R16Snorm ? PixelFormat::R16Snorm :
PixelFormat::R16I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Short>>(PixelFormat expected) {
return pixelFormatFor<Short>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<UnsignedInt>(PixelFormat) {
return PixelFormat::R32UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedInt>>(PixelFormat expected) {
return pixelFormatFor<UnsignedInt>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Int>(PixelFormat) {
return PixelFormat::R32I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Int>>(PixelFormat expected) {
return pixelFormatFor<Int>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Float>(PixelFormat) {
return PixelFormat::R32F;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Float>>(PixelFormat expected) {
return pixelFormatFor<Float>(expected);
}
/* Two-component types. The Vector<2, T> types aren't used that often so they
delegate to the Vector2<T> variants, the tests use Vector<2, T> to cover all
variants. */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedByte>>(PixelFormat expected) {
/* Attempt to match Srgb or Unorm if the expected image has it */
return expected == PixelFormat::RG8Srgb ? PixelFormat::RG8Srgb :
expected == PixelFormat::RG8Unorm ? PixelFormat::RG8Unorm :
PixelFormat::RG8UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedByte>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<UnsignedByte>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Byte>>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::RG8Snorm ? PixelFormat::RG8Snorm :
PixelFormat::RG8I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Byte>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<Byte>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedShort>>(PixelFormat expected) {
/* Attempt to match Unorm if the expected image has it */
return expected == PixelFormat::RG16Unorm ? PixelFormat::RG16Unorm :
PixelFormat::RG16UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedShort>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<UnsignedShort>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Short>>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::RG16Snorm ? PixelFormat::RG16Snorm :
PixelFormat::RG16I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Short>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<Short>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedInt>>(PixelFormat) {
return PixelFormat::RG32UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedInt>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<UnsignedInt>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Int>>(PixelFormat) {
return PixelFormat::RG32I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Int>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<Int>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Float>>(PixelFormat) {
return PixelFormat::RG32F;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Float>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector2<Float>>(expected);
}
/* Three-component types. The Vector<3, T> types aren't used that often so they
delegate to the Vector3<T> variants, the tests use Vector<3, T> to cover all
variants. */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedByte>>(PixelFormat expected) {
/* Attempt to match Srgb or Unorm if the expected image has it */
return expected == PixelFormat::RGB8Srgb ? PixelFormat::RGB8Srgb :
expected == PixelFormat::RGB8Unorm ? PixelFormat::RGB8Unorm :
PixelFormat::RGB8UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedByte>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<UnsignedByte>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<UnsignedByte>>(PixelFormat expected) {
/* Attempt to match Srgb if the expected image has it. No integer fallback
for color types. */
return expected == PixelFormat::RGB8Srgb ? PixelFormat::RGB8Srgb :
PixelFormat::RGB8Unorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Byte>>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::RGB8Snorm ? PixelFormat::RGB8Snorm :
PixelFormat::RGB8I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Byte>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<Byte>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<Byte>>(PixelFormat) {
/* No integer fallback for colors */
return PixelFormat::RGB8Snorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedShort>>(PixelFormat expected) {
/* Attempt to match Unorm if the expected image has it */
return expected == PixelFormat::RGB16Unorm ? PixelFormat::RGB16Unorm :
PixelFormat::RGB16UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedShort>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<UnsignedShort>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<UnsignedShort>>(PixelFormat) {
return PixelFormat::RGB16Unorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Short>>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::RGB16Snorm ? PixelFormat::RGB16Snorm :
PixelFormat::RGB16I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Short>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<Short>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<Short>>(PixelFormat) {
return PixelFormat::RGB16Snorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedInt>>(PixelFormat) {
return PixelFormat::RGB32UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedInt>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<UnsignedInt>>(expected);
}
/* Skipping Math::Color3<UnsignedInt>, as integer colors should always match
normalized types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Int>>(PixelFormat) {
return PixelFormat::RGB32I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Int>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<Int>>(expected);
}
/* Skipping Math::Color3<Int>, as integer colors should always match normalized
types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Float>>(PixelFormat) {
return PixelFormat::RGB32F;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Float>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector3<Float>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<Float>>(PixelFormat) {
return PixelFormat::RGB32F;
}
/* Four-component types. The Vector<4, T> types aren't used that often so they
delegate to the Vector4<T> variants, the tests use Vector<4, T> to cover all
variants. */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedByte>>(PixelFormat expected) {
/* Attempt to match Srgb or Unorm if the expected image has it */
return expected == PixelFormat::RGBA8Srgb ? PixelFormat::RGBA8Srgb :
expected == PixelFormat::RGBA8Unorm ? PixelFormat::RGBA8Unorm :
PixelFormat::RGBA8UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedByte>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<UnsignedByte>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<UnsignedByte>>(PixelFormat expected) {
/* Attempt to match Srgb if the expected image has it. No integer fallback
for color types. */
return expected == PixelFormat::RGBA8Srgb ? PixelFormat::RGBA8Srgb :
PixelFormat::RGBA8Unorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Byte>>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::RGBA8Snorm ? PixelFormat::RGBA8Snorm :
PixelFormat::RGBA8I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Byte>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<Byte>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Byte>>(PixelFormat) {
/* No integer fallback for colors */
return PixelFormat::RGBA8Snorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedShort>>(PixelFormat expected) {
/* Attempt to match Unorm if the expected image has it */
return expected == PixelFormat::RGBA16Unorm ? PixelFormat::RGBA16Unorm :
PixelFormat::RGBA16UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedShort>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<UnsignedShort>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<UnsignedShort>>(PixelFormat) {
/* No integer fallback for colors */
return PixelFormat::RGBA16Unorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Short>>(PixelFormat expected) {
/* Attempt to match Snorm if the expected image has it */
return expected == PixelFormat::RGBA16Snorm ? PixelFormat::RGBA16Snorm :
PixelFormat::RGBA16I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Short>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<Short>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Short>>(PixelFormat) {
/* No integer fallback for colors */
return PixelFormat::RGBA16Snorm;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedInt>>(PixelFormat) {
return PixelFormat::RGBA32UI;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedInt>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<UnsignedInt>>(expected);
}
/* Skipping Math::Color4<UnsignedInt>, as integer colors should always match
normalized types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Int>>(PixelFormat) {
return PixelFormat::RGBA32I;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Int>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<Int>>(expected);
}
/* Skipping Math::Color4<Int>, as integer colors should always match normalized
types */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Float>>(PixelFormat) {
return PixelFormat::RGBA32F;
}
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Float>>(PixelFormat expected) {
return pixelFormatFor<Math::Vector4<Float>>(expected);
}
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Float>>(PixelFormat) {
return PixelFormat::RGBA32F;
}
}

289
src/Magnum/DebugTools/Test/CompareImageTest.cpp

@ -115,18 +115,32 @@ struct CompareImageTest: TestSuite::Tester {
void fileToImageActualLoadFailed();
void fileToImageActualIsCompressed();
void pixelsToImageZeroDelta();
template<unsigned dimensions> void pixelFormatFor();
void pixelFormatForColor();
template<class T> void pixelsToImageZeroDelta();
void pixelsToImageNonZeroDelta();
void pixelsToImageDifferentFormat();
void pixelsToImageError();
void pixelsToFileZeroDelta();
template<class T> void pixelsToFileZeroDelta();
void pixelsToFileNonZeroDelta();
void pixelsToFileDifferentFormat();
void pixelsToFileError();
void pixelsToFileExpectedLoadFailed();
private:
Containers::Optional<PluginManager::Manager<Trade::AbstractImporter>> _importerManager;
Containers::Optional<PluginManager::Manager<Trade::AbstractImageConverter>> _converterManager;
};
const struct {
const char* name;
bool srgb;
} PixelsToImageData[]{
{"", false},
{"sRGB", true}
};
CompareImageTest::CompareImageTest() {
addTests({&CompareImageTest::formatUnknown,
&CompareImageTest::formatPackedDepthStencil,
@ -209,13 +223,32 @@ CompareImageTest::CompareImageTest() {
addTests({&CompareImageTest::fileToImageActualIsCompressed});
addTests({&CompareImageTest::pixelsToImageZeroDelta,
&CompareImageTest::pixelsToImageNonZeroDelta,
addTests({&CompareImageTest::pixelFormatFor<1>,
&CompareImageTest::pixelFormatFor<2>,
&CompareImageTest::pixelFormatFor<3>,
&CompareImageTest::pixelFormatFor<4>,
&CompareImageTest::pixelFormatForColor});
addInstancedTests<CompareImageTest>({
&CompareImageTest::pixelsToImageZeroDelta<Color3ub>,
&CompareImageTest::pixelsToImageZeroDelta<Vector3ub>},
Containers::arraySize(PixelsToImageData));
addTests({&CompareImageTest::pixelsToImageNonZeroDelta,
&CompareImageTest::pixelsToImageDifferentFormat,
&CompareImageTest::pixelsToImageError});
addTests({&CompareImageTest::pixelsToFileZeroDelta,
&CompareImageTest::pixelsToFileNonZeroDelta,
&CompareImageTest::pixelsToFileError},
addInstancedTests<CompareImageTest>({
&CompareImageTest::pixelsToFileZeroDelta<Color3ub>,
&CompareImageTest::pixelsToFileZeroDelta<Vector3ub>},
Containers::arraySize(PixelsToImageData),
&CompareImageTest::setupExternalPluginManager,
&CompareImageTest::teardownExternalPluginManager);
addTests({&CompareImageTest::pixelsToFileNonZeroDelta,
&CompareImageTest::pixelsToFileDifferentFormat,
&CompareImageTest::pixelsToFileError,
&CompareImageTest::pixelsToFileExpectedLoadFailed},
&CompareImageTest::setupExternalPluginManager,
&CompareImageTest::teardownExternalPluginManager);
@ -1654,15 +1687,132 @@ void CompareImageTest::fileToImageActualIsCompressed() {
"Actual image a (.../CompareImageCompressed.dds) is compressed, comparison not possible.\n");
}
void CompareImageTest::pixelsToImageZeroDelta() {
/* Same as image(), but taking pixels instead */
template<UnsignedInt dimensions> void CompareImageTest::pixelFormatFor() {
setTestCaseTemplateName(Utility::format("{}", dimensions));
/* Defaults to an integer / float format if no match */
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedByte>>(PixelFormat{})),
pixelFormat(PixelFormat::R8UI, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Byte>>(PixelFormat{})),
pixelFormat(PixelFormat::R8I, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedShort>>(PixelFormat{})),
pixelFormat(PixelFormat::R16UI, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Short>>(PixelFormat{})),
pixelFormat(PixelFormat::R16I, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedInt>>(PixelFormat{})),
pixelFormat(PixelFormat::R32UI, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Int>>(PixelFormat{})),
pixelFormat(PixelFormat::R32I, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Float>>(PixelFormat{})),
pixelFormat(PixelFormat::R32F, dimensions, false));
/* Matching normalized type if the image has it */
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedByte>>(pixelFormat(PixelFormat::R8Unorm, dimensions, false))),
pixelFormat(PixelFormat::R8Unorm, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Byte>>(pixelFormat(PixelFormat::R8Snorm, dimensions, false))),
pixelFormat(PixelFormat::R8Snorm, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedShort>>(pixelFormat(PixelFormat::R16Unorm, dimensions, false))),
pixelFormat(PixelFormat::R16Unorm, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Short>>(pixelFormat(PixelFormat::R16Snorm, dimensions, false))),
pixelFormat(PixelFormat::R16Snorm, dimensions, false));
/* Matching sRGB type if the image has it */
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedByte>>(pixelFormat(PixelFormat::R8Srgb, dimensions, true))),
pixelFormat(PixelFormat::R8Srgb, dimensions, true));
/* But not if it has different underlying type */
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, Short>>(pixelFormat(PixelFormat::R8Snorm, dimensions, false))),
pixelFormat(PixelFormat::R16I, dimensions, false));
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Vector<dimensions, UnsignedShort>>(pixelFormat(PixelFormat::R8Srgb, dimensions, false))),
pixelFormat(PixelFormat::R16UI, dimensions, false));
}
CORRADE_COMPARE_WITH(ExpectedRgb.pixels<Color3ub>(),
ExpectedRgb, (CompareImage{40.0f, 20.0f}));
void CompareImageTest::pixelFormatForColor() {
/* Defaults to a normalized format if no match */
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Color3ub>(PixelFormat{})),
PixelFormat::RGB8Unorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Color4ub>(PixelFormat{})),
PixelFormat::RGBA8Unorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Color3<Byte>>(PixelFormat{})),
PixelFormat::RGB8Snorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Color4<Byte>>(PixelFormat{})),
PixelFormat::RGBA8Snorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Color3us>(PixelFormat{})),
PixelFormat::RGB16Unorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Color4us>(PixelFormat{})),
PixelFormat::RGBA16Unorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Color3<Short>>(PixelFormat{})),
PixelFormat::RGB16Snorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Math::Color4<Short>>(PixelFormat{})),
PixelFormat::RGBA16Snorm);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Color3>(PixelFormat{})),
PixelFormat::RGB32F);
CORRADE_COMPARE(
(Implementation::pixelFormatFor<Color4>(PixelFormat{})),
PixelFormat::RGBA32F);
/* Matching sRGB type if the image has it */
CORRADE_COMPARE(
Implementation::pixelFormatFor<Color3ub>(PixelFormat::RGB8Srgb),
PixelFormat::RGB8Srgb);
CORRADE_COMPARE(
Implementation::pixelFormatFor<Color4ub>(PixelFormat::RGBA8Srgb),
PixelFormat::RGBA8Srgb);
/* But not if it has different underlying type or dimension count */
CORRADE_COMPARE(
Implementation::pixelFormatFor<Color3us>(PixelFormat::RGB8Srgb),
PixelFormat::RGB16Unorm);
CORRADE_COMPARE(
Implementation::pixelFormatFor<Color4ub>(PixelFormat::RGB8Srgb),
PixelFormat::RGBA8Unorm);
}
template<class T> void CompareImageTest::pixelsToImageZeroDelta() {
auto&& data = PixelsToImageData[testCaseInstanceId()];
setTestCaseTemplateName(std::is_same<T, Color3ub>::value ? "Color3ub" : "Vector3ub");
setTestCaseDescription(data.name);
/* Same as image(), but taking pixels instead. For T being Color3ub, the
autodetected PixelFormat is RGB8Unorm, for Vector3ub it's RGB8UI. It
should get matched to either RGB8Unorm or RGB8Srgb based on the format
in the expected image. */
/* Same as ExpectedRGB but with pixel format being different */
const ImageView2D expected{
PixelStorage{}.setSkip({1, 0, 0}).setRowLength(3),
pixelFormat(PixelFormat::RGB8Unorm, 3, data.srgb),
{2, 2}, ExpectedRgbData};
CORRADE_COMPARE_WITH(expected.pixels<T>(),
expected, (CompareImage{40.0f, 20.0f}));
/* No diagnostic as there's no error */
TestSuite::Comparator<CompareImage> compare{40.0f, 20.0f};
CORRADE_COMPARE(compare(ExpectedRgb.pixels<Color3ub>(), ExpectedRgb), TestSuite::ComparisonStatusFlags{});
CORRADE_COMPARE(compare(expected.pixels<T>(), expected), TestSuite::ComparisonStatusFlags{});
}
void CompareImageTest::pixelsToImageNonZeroDelta() {
@ -1685,6 +1835,22 @@ void CompareImageTest::pixelsToImageNonZeroDelta() {
CORRADE_COMPARE(out.str(), ImageCompareVerbose);
}
void CompareImageTest::pixelsToImageDifferentFormat() {
std::stringstream out;
{
TestSuite::Comparator<CompareImage> compare{{}, {}};
TestSuite::ComparisonStatusFlags flags =
compare(ExpectedRgb.pixels<Vector3b>(), ExpectedRgb);
/* No diagnostic as we don't have any expected filename */
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed);
Debug d{&out, Debug::Flag::DisableColors};
compare.printMessage(flags, d, "a", "b");
}
CORRADE_COMPARE(out.str(), "Images a and b have different format, actual PixelFormat::RGB8I but PixelFormat::RGB8Unorm expected.\n");
}
void CompareImageTest::pixelsToImageError() {
/* Same as imageError(), but taking pixels instead */
@ -1703,8 +1869,15 @@ void CompareImageTest::pixelsToImageError() {
CORRADE_COMPARE(out.str(), ImageCompareError);
}
void CompareImageTest::pixelsToFileZeroDelta() {
/* Same as imageToFile(), but taking pixels instead */
template<class T> void CompareImageTest::pixelsToFileZeroDelta() {
auto&& data = PixelsToImageData[testCaseInstanceId()];
setTestCaseTemplateName(std::is_same<T, Color3ub>::value ? "Color3ub" : "Vector3ub");
setTestCaseDescription(data.name);
/* Same as imageToFile(), but taking pixels instead. For T being Color3ub,
the autodetected PixelFormat is RGB8Unorm, for Vector3ub it's RGB8UI. It
should get matched to either RGB8Unorm or RGB8Srgb based on the format
in the expected image. */
if(!(_importerManager->loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager->loadState("TgaImporter") & PluginManager::LoadState::Loaded))
@ -1716,12 +1889,18 @@ void CompareImageTest::pixelsToFileZeroDelta() {
views. */
Containers::String expectedFilename = Utility::Path::join(DEBUGTOOLS_TEST_DIR, "CompareImageExpected.tga");
CORRADE_COMPARE_WITH(ExpectedRgb.pixels<Color3ub>(), expectedFilename,
/* Same as ExpectedRGB but with pixel format being different */
const ImageView2D expected{
PixelStorage{}.setSkip({1, 0, 0}).setRowLength(3),
pixelFormat(PixelFormat::RGB8Unorm, 3, data.srgb),
{2, 2}, ExpectedRgbData};
CORRADE_COMPARE_WITH(expected.pixels<T>(), expectedFilename,
(CompareImageToFile{*_importerManager, 40.0f, 20.0f}));
/* No diagnostic as there's no error */
TestSuite::Comparator<CompareImageToFile> compare{&*_importerManager, nullptr, 40.0f, 20.0f};
CORRADE_COMPARE(compare(ExpectedRgb.pixels<Color3ub>(), expectedFilename),
CORRADE_COMPARE(compare(expected.pixels<T>(), expectedFilename),
TestSuite::ComparisonStatusFlags{});
}
@ -1756,6 +1935,33 @@ void CompareImageTest::pixelsToFileNonZeroDelta() {
CORRADE_COMPARE(out.str(), ImageCompareVerbose);
}
void CompareImageTest::pixelsToFileDifferentFormat() {
if(!(_importerManager->loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager->loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
/* The filenames are referenced as string views as the assumption is that
the whole comparison and diagnostic printing gets done in a single
expression. Thus don't pass them as temporaries to avoid dangling
views. */
Containers::String expectedFilename = Utility::Path::join(DEBUGTOOLS_TEST_DIR, "CompareImageExpected.tga");
std::stringstream out;
{
TestSuite::Comparator<CompareImageToFile> compare{&*_importerManager, nullptr, {}, {}};
TestSuite::ComparisonStatusFlags flags =
compare(ExpectedRgb.pixels<Math::Color3<Byte>>(), expectedFilename);
/* Diagnostic but we're not checking it, here we just want to make sure
the right format gets detected */
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed|TestSuite::ComparisonStatusFlag::Diagnostic);
Debug d{&out, Debug::Flag::DisableColors};
compare.printMessage(flags, d, "a", "b");
}
CORRADE_COMPARE(out.str(), "Images a and b have different format, actual PixelFormat::RGB8Snorm but PixelFormat::RGB8Unorm expected.\n");
}
void CompareImageTest::pixelsToFileError() {
/* Same as imageToFileError(), but taking pixels instead */
@ -1772,7 +1978,10 @@ void CompareImageTest::pixelsToFileError() {
std::stringstream out;
TestSuite::Comparator<CompareImageToFile> compare{&*_importerManager, &*_converterManager, 20.0f, 10.0f};
TestSuite::ComparisonStatusFlags flags = compare(ActualRgb.pixels<Color3ub>(), expectedFilename);
/* Vector3ub gets matched to PixelFormat::R8UI initially, but once the
expected image is loaded it gets updated to PixelFormat::R8Unorm to
match it */
TestSuite::ComparisonStatusFlags flags = compare(ActualRgb.pixels<Vector3ub>(), expectedFilename);
/* The diagnostic flag should be slapped on the failure coming from the
operator() comparing two ImageViews */
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed|TestSuite::ComparisonStatusFlag::Diagnostic);
@ -1809,6 +2018,52 @@ void CompareImageTest::pixelsToFileError() {
Utility::Path::join(DEBUGTOOLS_TEST_DIR, "CompareImageActual.tga"), TestSuite::Compare::File);
}
void CompareImageTest::pixelsToFileExpectedLoadFailed() {
/* Same as imageToFileExpectedLoadFailed(), but taking pixels instead */
if(!(_importerManager->loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager->loadState("TgaImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter / TgaImporter plugins not found.");
std::stringstream out;
TestSuite::Comparator<CompareImageToFile> compare{&*_importerManager, &*_converterManager, 20.0f, 10.0f};
TestSuite::ComparisonStatusFlags flags = compare(ActualRgb.pixels<Color3ub>(), "nonexistent.tga");
/* Actual file *could* be loaded, so save it! */
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed|TestSuite::ComparisonStatusFlag::Diagnostic);
{
Debug d{&out, Debug::Flag::DisableColors};
compare.printMessage(flags, d, "a", "b");
}
CORRADE_COMPARE(out.str(), "Expected image b (nonexistent.tga) could not be loaded.\n");
/* Create the output dir if it doesn't exist, but avoid stale files making
false positives */
CORRADE_VERIFY(Utility::Path::make(COMPAREIMAGETEST_SAVE_DIR));
Containers::String filename = Utility::Path::join(COMPAREIMAGETEST_SAVE_DIR, "nonexistent.tga");
if(Utility::Path::exists(filename))
CORRADE_VERIFY(Utility::Path::remove(filename));
if(!(_converterManager->loadState("AnyImageConverter") & PluginManager::LoadState::Loaded) ||
!(_converterManager->loadState("TgaImageConverter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageConverter / TgaImageConverter plugins not found.");
{
out.str({});
Debug redirectOutput(&out);
compare.saveDiagnostic(flags, redirectOutput, COMPAREIMAGETEST_SAVE_DIR);
}
/* We expect the *actual* contents, but under the *expected* filename.
Comparing file contents, expecting the converter makes exactly the same
file. */
CORRADE_COMPARE(out.str(), Utility::formatString("-> {}\n", filename));
CORRADE_COMPARE_AS(filename,
Utility::Path::join(DEBUGTOOLS_TEST_DIR, "CompareImageActual.tga"), TestSuite::Compare::File);
}
}}}}
CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CompareImageTest)

Loading…
Cancel
Save