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 relying on memory-mapping, and is now available on WebGL 2 as well (see
[mosra/magnum#560](https://github.com/mosra/magnum/pull/560)) [mosra/magnum#560](https://github.com/mosra/magnum/pull/560))
- @ref DebugTools::CompareImage now supports comparing half-float pixel - @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 @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; 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) { TestSuite::ComparisonStatusFlags ImageComparatorBase::operator()(const ImageView2D& actual, const ImageView2D& expected) {
return compare(actual.format(), actual.pixels(), expected); return compare(actual.format(), actual.pixels(), expected);
} }
@ -608,7 +616,7 @@ TestSuite::ComparisonStatusFlags ImageComparatorBase::operator()(const Container
return flags; 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; _state->expectedFilename = expected;
Containers::Pointer<Trade::AbstractImporter> importer; Containers::Pointer<Trade::AbstractImporter> importer;
@ -644,6 +652,11 @@ TestSuite::ComparisonStatusFlags ImageComparatorBase::compare(const PixelFormat
return TestSuite::ComparisonStatusFlag::Failed|TestSuite::ComparisonStatusFlag::Diagnostic; 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 /* Save a view on the expected image data and proxy to the actual data
comparison. If comparison failed, offer to save a diagnostic. */ comparison. If comparison failed, offer to save a diagnostic. */
_state->expectedImage.emplace(*_state->expectedImageData); _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) { 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) { 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 { 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 { class MAGNUM_DEBUGTOOLS_EXPORT ImageComparatorBase {
public: public:
@ -80,11 +82,15 @@ class MAGNUM_DEBUGTOOLS_EXPORT ImageComparatorBase {
TestSuite::ComparisonStatusFlags operator()(const ImageView2D& actual, Containers::StringView expected); 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); 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() */ /* Used in templated CompareImageToFile::operator(). If actualFormatFor
TestSuite::ComparisonStatusFlags compare(PixelFormat actualFormat, const Containers::StridedArrayView3D<const char>& actualPixels, Containers::StringView expected); 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; 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) { 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( return Magnum::DebugTools::Implementation::ImageComparatorBase::compare(
Magnum::DebugTools::Implementation::pixelFormatFor<T>(), Magnum::DebugTools::Implementation::pixelFormatFor<T>,
Containers::arrayCast<3, const char>(actualPixels), expected); 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 having to include it -- it's not needed when comparing just images
alone, not files */ alone, not files */
template<class T> TestSuite::ComparisonStatusFlags operator()(const Containers::StridedArrayView2D<const T>& actualPixels, const Containers::StringView& expected) { 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( 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); 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 @snippet MagnumDebugTools-gl.cpp CompareImage-pixels-rgb
Currently, comparing against pixel views has a few inherent limitations --- it The pixel views are expected to be cast to one of Magnum scalar or vector
has to be cast to one of Magnum scalar or vector types and the format is types. The format is then autodetected from the passed type. For types that map
then autodetected from the passed type, with normalized formats preferred. In to more than one @ref PixelFormat, such as @relativeref{Magnum,Vector3ub} that
practice this means e.g. @ref Math::Vector2 "Math::Vector2<UnsignedByte>" will can be @ref PixelFormat::RGB8Unorm, @relativeref{PixelFormat,RGB8Srgb} or
be understood as @ref PixelFormat::RG8Unorm and there's currently no way to @relativeref{PixelFormat,RGB8UI}, an attempt is made to match the pixel format
interpret it as @ref PixelFormat::RG8UI, for example. 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 @see @ref CompareMaterial
*/ */
@ -605,84 +614,273 @@ class CompareFileToImage {
namespace Implementation { namespace Implementation {
/* LCOV_EXCL_START */ /* One-component types. The Vector<1, T> types aren't used that often so they
/* One-component types */ delegate to the T variants, the tests use Vector<1, T> to cover all
template<> constexpr PixelFormat pixelFormatFor<UnsignedByte>() { return PixelFormat::R8Unorm; } variants. */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedByte>>() { return PixelFormat::R8Unorm; } template<> constexpr PixelFormat pixelFormatFor<UnsignedByte>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Byte>() { return PixelFormat::R8Snorm; } /* Attempt to match Srgb or Unorm if the expected image has it */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Byte>>() { return PixelFormat::R8Snorm; } return expected == PixelFormat::R8Srgb ? PixelFormat::R8Srgb :
template<> constexpr PixelFormat pixelFormatFor<UnsignedShort>() { return PixelFormat::R16Unorm; } expected == PixelFormat::R8Unorm ? PixelFormat::R8Unorm :
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedShort>>() { return PixelFormat::R16Unorm; } PixelFormat::R8UI;
template<> constexpr PixelFormat pixelFormatFor<Short>() { return PixelFormat::R16Snorm; } }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Short>>() { return PixelFormat::R16Snorm; } template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedByte>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<UnsignedInt>() { return PixelFormat::R32UI; } return pixelFormatFor<UnsignedByte>(expected);
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, UnsignedInt>>() { return PixelFormat::R32UI; } }
template<> constexpr PixelFormat pixelFormatFor<Int>() { return PixelFormat::R32I; } template<> constexpr PixelFormat pixelFormatFor<Byte>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Int>>() { return PixelFormat::R32I; } /* Attempt to match Snorm if the expected image has it */
template<> constexpr PixelFormat pixelFormatFor<Float>() { return PixelFormat::R32F; } return expected == PixelFormat::R8Snorm ? PixelFormat::R8Snorm :
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Float>>() { return PixelFormat::R32F; } PixelFormat::R8I;
}
/* Two-component types */ template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Byte>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedByte>>() { return PixelFormat::RG8Unorm; } return pixelFormatFor<Byte>(expected);
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<UnsignedShort>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Byte>>() { return PixelFormat::RG8Snorm; } /* Attempt to match Unorm if the expected image has it */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedShort>>() { return PixelFormat::RG16Unorm; } return expected == PixelFormat::R16Unorm ? PixelFormat::R16Unorm :
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedShort>>() { return PixelFormat::RG16Unorm; } PixelFormat::R16UI;
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<1, UnsignedShort>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, UnsignedInt>>() { return PixelFormat::RG32UI; } return pixelFormatFor<UnsignedShort>(expected);
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<Short>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Int>>() { return PixelFormat::RG32I; } /* Attempt to match Snorm if the expected image has it */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Float>>() { return PixelFormat::RG32F; } return expected == PixelFormat::R16Snorm ? PixelFormat::R16Snorm :
template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<Float>>() { return PixelFormat::RG32F; } PixelFormat::R16I;
}
/* Three-component types */ template<> constexpr PixelFormat pixelFormatFor<Math::Vector<1, Short>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedByte>>() { return PixelFormat::RGB8Unorm; } return pixelFormatFor<Short>(expected);
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedByte>>() { return PixelFormat::RGB8Unorm; } }
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<UnsignedByte>>() { return PixelFormat::RGB8Unorm; } template<> constexpr PixelFormat pixelFormatFor<UnsignedInt>(PixelFormat) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Byte>>() { return PixelFormat::RGB8Snorm; } return PixelFormat::R32UI;
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<1, UnsignedInt>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedShort>>() { return PixelFormat::RGB16Unorm; } return pixelFormatFor<UnsignedInt>(expected);
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedShort>>() { return PixelFormat::RGB16Unorm; } }
template<> constexpr PixelFormat pixelFormatFor<Math::Color3<UnsignedShort>>() { return PixelFormat::RGB16Unorm; } template<> constexpr PixelFormat pixelFormatFor<Int>(PixelFormat) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Short>>() { return PixelFormat::RGB16Snorm; } return PixelFormat::R32I;
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<1, Int>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, UnsignedInt>>() { return PixelFormat::RGB32UI; } return pixelFormatFor<Int>(expected);
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<UnsignedInt>>() { return PixelFormat::RGB32UI; } }
/* Skipping Math::Color3<UnsignedInt>, as that isn't much used */ template<> constexpr PixelFormat pixelFormatFor<Float>(PixelFormat) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Int>>() { return PixelFormat::RGB32I; } return PixelFormat::R32F;
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<1, Float>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<3, Float>>() { return PixelFormat::RGB32F; } return pixelFormatFor<Float>(expected);
template<> constexpr PixelFormat pixelFormatFor<Math::Vector3<Float>>() { return PixelFormat::RGB32F; } }
/* Four-component types */ /* Two-component types. The Vector<2, T> types aren't used that often so they
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedByte>>() { return PixelFormat::RGBA8Unorm; } delegate to the Vector2<T> variants, the tests use Vector<2, T> to cover all
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedByte>>() { return PixelFormat::RGBA8Unorm; } variants. */
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<UnsignedByte>>() { return PixelFormat::RGBA8Unorm; } template<> constexpr PixelFormat pixelFormatFor<Math::Vector2<UnsignedByte>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Byte>>() { return PixelFormat::RGBA8Snorm; } /* Attempt to match Srgb or Unorm if the expected image has it */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Byte>>() { return PixelFormat::RGBA8Snorm; } return expected == PixelFormat::RG8Srgb ? PixelFormat::RG8Srgb :
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Byte>>() { return PixelFormat::RGBA8Snorm; } expected == PixelFormat::RG8Unorm ? PixelFormat::RG8Unorm :
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedShort>>() { return PixelFormat::RGBA16Unorm; } PixelFormat::RG8UI;
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<2, UnsignedByte>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Short>>() { return PixelFormat::RGBA16Snorm; } return pixelFormatFor<Math::Vector2<UnsignedByte>>(expected);
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::Vector2<Byte>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, UnsignedInt>>() { return PixelFormat::RGBA32UI; } /* Attempt to match Snorm if the expected image has it */
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<UnsignedInt>>() { return PixelFormat::RGBA32UI; } return expected == PixelFormat::RG8Snorm ? PixelFormat::RG8Snorm :
/* Skipping Math::Color4<UnsignedInt>, as that isn't much used */ PixelFormat::RG8I;
template<> constexpr PixelFormat pixelFormatFor<Math::Vector<4, Int>>() { return PixelFormat::RGBA32I; } }
template<> constexpr PixelFormat pixelFormatFor<Math::Vector4<Int>>() { return PixelFormat::RGBA32I; } template<> constexpr PixelFormat pixelFormatFor<Math::Vector<2, Byte>>(PixelFormat expected) {
/* Skipping Math::Color4<Int>, as that isn't much used */ return pixelFormatFor<Math::Vector2<Byte>>(expected);
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::Vector2<UnsignedShort>>(PixelFormat expected) {
template<> constexpr PixelFormat pixelFormatFor<Math::Color4<Float>>() { return PixelFormat::RGBA32F; } /* Attempt to match Unorm if the expected image has it */
/* LCOV_EXCL_STOP */ 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 fileToImageActualLoadFailed();
void fileToImageActualIsCompressed(); void fileToImageActualIsCompressed();
void pixelsToImageZeroDelta(); template<unsigned dimensions> void pixelFormatFor();
void pixelFormatForColor();
template<class T> void pixelsToImageZeroDelta();
void pixelsToImageNonZeroDelta(); void pixelsToImageNonZeroDelta();
void pixelsToImageDifferentFormat();
void pixelsToImageError(); void pixelsToImageError();
void pixelsToFileZeroDelta(); template<class T> void pixelsToFileZeroDelta();
void pixelsToFileNonZeroDelta(); void pixelsToFileNonZeroDelta();
void pixelsToFileDifferentFormat();
void pixelsToFileError(); void pixelsToFileError();
void pixelsToFileExpectedLoadFailed();
private: private:
Containers::Optional<PluginManager::Manager<Trade::AbstractImporter>> _importerManager; Containers::Optional<PluginManager::Manager<Trade::AbstractImporter>> _importerManager;
Containers::Optional<PluginManager::Manager<Trade::AbstractImageConverter>> _converterManager; Containers::Optional<PluginManager::Manager<Trade::AbstractImageConverter>> _converterManager;
}; };
const struct {
const char* name;
bool srgb;
} PixelsToImageData[]{
{"", false},
{"sRGB", true}
};
CompareImageTest::CompareImageTest() { CompareImageTest::CompareImageTest() {
addTests({&CompareImageTest::formatUnknown, addTests({&CompareImageTest::formatUnknown,
&CompareImageTest::formatPackedDepthStencil, &CompareImageTest::formatPackedDepthStencil,
@ -209,13 +223,32 @@ CompareImageTest::CompareImageTest() {
addTests({&CompareImageTest::fileToImageActualIsCompressed}); addTests({&CompareImageTest::fileToImageActualIsCompressed});
addTests({&CompareImageTest::pixelsToImageZeroDelta, addTests({&CompareImageTest::pixelFormatFor<1>,
&CompareImageTest::pixelsToImageNonZeroDelta, &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}); &CompareImageTest::pixelsToImageError});
addTests({&CompareImageTest::pixelsToFileZeroDelta, addInstancedTests<CompareImageTest>({
&CompareImageTest::pixelsToFileNonZeroDelta, &CompareImageTest::pixelsToFileZeroDelta<Color3ub>,
&CompareImageTest::pixelsToFileError}, &CompareImageTest::pixelsToFileZeroDelta<Vector3ub>},
Containers::arraySize(PixelsToImageData),
&CompareImageTest::setupExternalPluginManager,
&CompareImageTest::teardownExternalPluginManager);
addTests({&CompareImageTest::pixelsToFileNonZeroDelta,
&CompareImageTest::pixelsToFileDifferentFormat,
&CompareImageTest::pixelsToFileError,
&CompareImageTest::pixelsToFileExpectedLoadFailed},
&CompareImageTest::setupExternalPluginManager, &CompareImageTest::setupExternalPluginManager,
&CompareImageTest::teardownExternalPluginManager); &CompareImageTest::teardownExternalPluginManager);
@ -1654,15 +1687,132 @@ void CompareImageTest::fileToImageActualIsCompressed() {
"Actual image a (.../CompareImageCompressed.dds) is compressed, comparison not possible.\n"); "Actual image a (.../CompareImageCompressed.dds) is compressed, comparison not possible.\n");
} }
void CompareImageTest::pixelsToImageZeroDelta() { template<UnsignedInt dimensions> void CompareImageTest::pixelFormatFor() {
/* Same as image(), but taking pixels instead */ 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>(), void CompareImageTest::pixelFormatForColor() {
ExpectedRgb, (CompareImage{40.0f, 20.0f})); /* 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 */ /* No diagnostic as there's no error */
TestSuite::Comparator<CompareImage> compare{40.0f, 20.0f}; 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() { void CompareImageTest::pixelsToImageNonZeroDelta() {
@ -1685,6 +1835,22 @@ void CompareImageTest::pixelsToImageNonZeroDelta() {
CORRADE_COMPARE(out.str(), ImageCompareVerbose); 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() { void CompareImageTest::pixelsToImageError() {
/* Same as imageError(), but taking pixels instead */ /* Same as imageError(), but taking pixels instead */
@ -1703,8 +1869,15 @@ void CompareImageTest::pixelsToImageError() {
CORRADE_COMPARE(out.str(), ImageCompareError); CORRADE_COMPARE(out.str(), ImageCompareError);
} }
void CompareImageTest::pixelsToFileZeroDelta() { template<class T> void CompareImageTest::pixelsToFileZeroDelta() {
/* Same as imageToFile(), but taking pixels instead */ 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) || if(!(_importerManager->loadState("AnyImageImporter") & PluginManager::LoadState::Loaded) ||
!(_importerManager->loadState("TgaImporter") & PluginManager::LoadState::Loaded)) !(_importerManager->loadState("TgaImporter") & PluginManager::LoadState::Loaded))
@ -1716,12 +1889,18 @@ void CompareImageTest::pixelsToFileZeroDelta() {
views. */ views. */
Containers::String expectedFilename = Utility::Path::join(DEBUGTOOLS_TEST_DIR, "CompareImageExpected.tga"); 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})); (CompareImageToFile{*_importerManager, 40.0f, 20.0f}));
/* No diagnostic as there's no error */ /* No diagnostic as there's no error */
TestSuite::Comparator<CompareImageToFile> compare{&*_importerManager, nullptr, 40.0f, 20.0f}; 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{}); TestSuite::ComparisonStatusFlags{});
} }
@ -1756,6 +1935,33 @@ void CompareImageTest::pixelsToFileNonZeroDelta() {
CORRADE_COMPARE(out.str(), ImageCompareVerbose); 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() { void CompareImageTest::pixelsToFileError() {
/* Same as imageToFileError(), but taking pixels instead */ /* Same as imageToFileError(), but taking pixels instead */
@ -1772,7 +1978,10 @@ void CompareImageTest::pixelsToFileError() {
std::stringstream out; std::stringstream out;
TestSuite::Comparator<CompareImageToFile> compare{&*_importerManager, &*_converterManager, 20.0f, 10.0f}; 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 /* The diagnostic flag should be slapped on the failure coming from the
operator() comparing two ImageViews */ operator() comparing two ImageViews */
CORRADE_COMPARE(flags, TestSuite::ComparisonStatusFlag::Failed|TestSuite::ComparisonStatusFlag::Diagnostic); 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); 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) CORRADE_TEST_MAIN(Magnum::DebugTools::Test::CompareImageTest)

Loading…
Cancel
Save