Browse Source

DebugTools: CompareImage now supports only generic pixel formats.

pull/233/head
Vladimír Vondruš 8 years ago
parent
commit
4d565a8f73
  1. 4
      doc/changelog.dox
  2. 4
      doc/snippets/debugtools-compareimage.cpp
  3. 64
      src/Magnum/DebugTools/CMakeLists.txt
  4. 413
      src/Magnum/DebugTools/CompareImage.cpp
  5. 23
      src/Magnum/DebugTools/CompareImage.h
  6. 2
      src/Magnum/DebugTools/Test/CMakeLists.txt
  7. 131
      src/Magnum/DebugTools/Test/CompareImageTest.cpp
  8. 2
      src/Magnum/DebugTools/visibility.h

4
doc/changelog.dox

@ -128,6 +128,8 @@ See also:
- @ref DebugTools::textureSubImage() now accepts both GL-specific
@ref GL::PixelFormat / @ref GL::PixelType combination and the generic
@ref PixelFormat enum
- @ref DebugTools::CompareImage now accepts generic @ref PixelFormat on input
instead of GL-specific @ref GL::PixelFormat / @ref GL::PixelType
@subsubsection changelog-latest-changes-gl GL library
@ -328,6 +330,8 @@ See also:
- Configuration value reader/writers are now for only
@ref Magnum::MeshPrimitive and @ref Magnum::MeshIndexType, not for
@ref GL::MeshPrimitive or @ref GL::MeshIndexType
- @ref DebugTools::CompareImage no longer accepts GL-specific pixel formats,
only the non-deprecated values from the generic @ref PixelFormat enum
- The @ref Trade::TgaImageConverter "TgaImageConverter" plugin no longer
accepts GL-specific pixel formats, only the non-deprecated values from the
generic @ref PixelFormat enum

4
doc/snippets/debugtools-compareimage.cpp

@ -44,7 +44,7 @@ Image2D doProcessing() {
importer->openFile(Utility::Directory::join(SNIPPETS_DIR, "image2.tga"));
auto image = importer->image2D(0);
CORRADE_INTERNAL_ASSERT(image);
return Image2D{image->storage(), image->format(), image->type(), image->size(), image->release()};
return Image2D{image->storage(), image->format(), image->size(), image->release()};
}
Image2D loadExpectedImage() {
@ -53,7 +53,7 @@ Image2D loadExpectedImage() {
importer->openFile(Utility::Directory::join(SNIPPETS_DIR, "image1.tga"));
auto image = importer->image2D(0);
CORRADE_INTERNAL_ASSERT(image);
return Image2D{image->storage(), image->format(), image->type(), image->size(), image->release()};
return Image2D{image->storage(), image->format(), image->size(), image->release()};
}
}

64
src/Magnum/DebugTools/CMakeLists.txt

@ -26,6 +26,8 @@
set(MagnumDebugTools_SRCS
Profiler.cpp)
set(MagnumDebugTools_GracefulAssert_SRCS )
set(MagnumDebugTools_HEADERS
DebugTools.h
Profiler.h)
@ -105,18 +107,34 @@ endif()
# Build the TestSuite-related functionality only if it is present
find_package(Corrade COMPONENTS TestSuite)
if(Corrade_TestSuite_FOUND)
list(APPEND MagnumDebugTools_SRCS
list(APPEND MagnumDebugTools_GracefulAssert_SRCS
CompareImage.cpp)
list(APPEND MagnumDebugTools_HEADERS
CompareImage.h)
endif()
# DebugTools library
add_library(MagnumDebugTools ${SHARED_OR_STATIC}
# Objects shared between main and test library
add_library(MagnumDebugToolsObjects OBJECT
${MagnumDebugTools_SRCS}
${MagnumDebugTools_HEADERS}
${MagnumDebugTools_PRIVATE_HEADERS})
target_include_directories(MagnumDebugToolsObjects PUBLIC $<TARGET_PROPERTY:Magnum,INTERFACE_INCLUDE_DIRECTORIES>)
if(NOT BUILD_STATIC)
target_compile_definitions(MagnumDebugToolsObjects PRIVATE "MagnumDebugToolsObjects_EXPORTS")
endif()
if(NOT BUILD_STATIC OR BUILD_STATIC_PIC)
set_target_properties(MagnumDebugToolsObjects PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
set_target_properties(MagnumDebugToolsObjects PROPERTIES FOLDER "Magnum/DebugTools")
if(TARGET_GL)
target_include_directories(MagnumDebugToolsObjects PUBLIC $<TARGET_PROPERTY:MagnumGL,INTERFACE_INCLUDE_DIRECTORIES>)
endif()
# DebugTools library
add_library(MagnumDebugTools ${SHARED_OR_STATIC}
$<TARGET_OBJECTS:MagnumDebugToolsObjects>
${MagnumDebugTools_GracefulAssert_SRCS})
set_target_properties(MagnumDebugTools PROPERTIES
DEBUG_POSTFIX "-d"
FOLDER "Magnum/DebugTools")
@ -151,6 +169,46 @@ install(TARGETS MagnumDebugTools
install(FILES ${MagnumDebugTools_HEADERS} DESTINATION ${MAGNUM_INCLUDE_INSTALL_DIR}/DebugTools)
if(BUILD_TESTS)
# Library with graceful assert for testing
add_library(MagnumDebugToolsTestLib ${SHARED_OR_STATIC}
$<TARGET_OBJECTS:MagnumDebugToolsObjects>
${MagnumDebugTools_GracefulAssert_SRCS})
set_target_properties(MagnumDebugToolsTestLib PROPERTIES
DEBUG_POSTFIX "-d"
FOLDER "Magnum/DebugTools")
target_compile_definitions(MagnumDebugToolsTestLib PRIVATE
"CORRADE_GRACEFUL_ASSERT" "MagnumDebugTools_EXPORTS")
if(BUILD_STATIC_PIC)
set_target_properties(MagnumDebugToolsTestLib PROPERTIES POSITION_INDEPENDENT_CODE ON)
endif()
target_link_libraries(MagnumDebugToolsTestLib PUBLIC Magnum)
if(Corrade_TestSuite_FOUND)
target_link_libraries(MagnumDebugToolsTestLib PUBLIC Corrade::TestSuite)
endif()
if(TARGET_GL)
target_link_libraries(MagnumDebugToolsTestLib PUBLIC MagnumGL)
if(WITH_SCENEGRAPH)
target_link_libraries(MagnumDebugToolsTestLib PUBLIC MagnumSceneGraph)
endif()
if(WITH_SHAPES)
target_link_libraries(MagnumDebugToolsTestLib PUBLIC MagnumShapes MagnumPrimitives)
endif()
if(WITH_SCENEGRAPH OR WITH_SHAPES)
target_link_libraries(MagnumDebugToolsTestLib PUBLIC
MagnumMeshTools
MagnumShaders)
endif()
endif()
# On Windows we need to install first and then run the tests to avoid "DLL
# not found" hell, thus we need to install this too
if(CORRADE_TARGET_WINDOWS AND NOT CMAKE_CROSSCOMPILING AND NOT BUILD_STATIC)
install(TARGETS MagnumDebugToolsTestLib
RUNTIME DESTINATION ${MAGNUM_BINARY_INSTALL_DIR}
LIBRARY DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR}
ARCHIVE DESTINATION ${MAGNUM_LIBRARY_INSTALL_DIR})
endif()
add_subdirectory(Test)
endif()

413
src/Magnum/DebugTools/CompareImage.cpp

@ -73,108 +73,122 @@ template<std::size_t size, class T> Float calculateImageDelta(const ImageView2D&
return max;
}
template<class T> Float calculateIntegerImageDelta(const ImageView2D& actual, const ImageView2D& expected, std::vector<Float>& output) {
if(
}
std::tuple<std::vector<Float>, Float, Float> calculateImageDelta(const ImageView2D& actual, const ImageView2D& expected) {
/* Calculate a delta image */
std::vector<Float> delta(expected.size().product());
CORRADE_ASSERT(!isPixelFormatImplementationSpecific(expected.format()),
"DebugTools::CompareImage: can't compare implementation-specific pixel formats", {});
Float max{Constants::nan()};
switch(expected.format()) {
#define _c(format, size, T) \
case PixelFormat::format: \
max = calculateImageDelta<size, T>(actual, expected, delta); \
break;
#define _d(first, second, size, T) \
case PixelFormat::first: \
case PixelFormat::second: \
max = calculateImageDelta<size, T>(actual, expected, delta); \
break;
_d(R8Unorm, R8UI, 1, UnsignedByte)
_d(RG8Unorm, RG8UI, 2, UnsignedByte)
_d(RGB8Unorm, RGB8UI, 3, UnsignedByte)
_d(RGBA8Unorm, RGBA8UI, 4, UnsignedByte)
_d(R8Snorm, R8I, 1, Byte)
_d(RG8Snorm, RG8I, 2, Byte)
_d(RGB8Snorm, RGB8I, 3, Byte)
_d(RGBA8Snorm, RGBA8I, 4, Byte)
_d(R16Unorm, R16UI, 1, UnsignedShort)
_d(RG16Unorm, RG16UI, 2, UnsignedShort)
_d(RGB16Unorm, RGB16UI, 3, UnsignedShort)
_d(RGBA16Unorm, RGBA16UI, 4, UnsignedShort)
_d(R16Snorm, R16I, 1, Short)
_d(RG16Snorm, RG16I, 2, Short)
_d(RGB16Snorm, RGB16I, 3, Short)
_d(RGBA16Snorm, RGBA16I, 4, Short)
_c(R32UI, 1, UnsignedInt)
_c(RG32UI, 2, UnsignedInt)
_c(RGB32UI, 3, UnsignedInt)
_c(RGBA32UI, 4, UnsignedInt)
_c(R32I, 1, Int)
_c(RG32I, 2, Int)
_c(RGB32I, 3, Int)
_c(RGBA32I, 4, Int)
_c(R32F, 1, Float)
_c(RG32F, 2, Float)
_c(RGB32F, 3, Float)
_c(RGBA32F, 4, Float)
#undef _d
#undef _c
case PixelFormat::R16F:
case PixelFormat::RG16F:
case PixelFormat::RGB16F:
case PixelFormat::RGBA16F:
CORRADE_ASSERT(false,
"DebugTools::CompareImage: half-float formats are not supported yet", {});
#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL)
CORRADE_IGNORE_DEPRECATED_PUSH
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
expected.format() == PixelFormat::Red
case PixelFormat::Red:
#endif
#ifndef MAGNUM_TARGET_GLES2
|| expected.format() == PixelFormat::RedInteger
#else
#ifndef MAGNUM_TARGET_WEBGL
||
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::Green:
case PixelFormat::Blue:
#endif
expected.format() == PixelFormat::Luminance
#ifdef MAGNUM_TARGET_GLES2
case PixelFormat::Luminance:
#endif
)
return calculateImageDelta<1, T>(actual, expected, output);
else if(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
expected.format() == PixelFormat::RG
#endif
#ifndef MAGNUM_TARGET_GLES2
|| expected.format() == PixelFormat::RGInteger
#else
#ifndef MAGNUM_TARGET_WEBGL
||
case PixelFormat::RG:
#endif
expected.format() == PixelFormat::LuminanceAlpha
#ifdef MAGNUM_TARGET_GLES2
case PixelFormat::LuminanceAlpha:
#endif
)
return calculateImageDelta<2, T>(actual, expected, output);
else if(expected.format() == PixelFormat::RGB
#ifndef MAGNUM_TARGET_GLES2
|| expected.format() == PixelFormat::RGBInteger
case PixelFormat::RGB:
case PixelFormat::RGBA:
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::BGR:
#endif
)
return calculateImageDelta<3, T>(actual, expected, output);
else if(expected.format() == PixelFormat::RGBA
#ifndef MAGNUM_TARGET_GLES2
|| expected.format() == PixelFormat::RGBAInteger
#endif
)
return calculateImageDelta<4, T>(actual, expected, output);
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
template<class T> Float calculateImageDelta(const ImageView2D& actual, const ImageView2D& expected, std::vector<Float>& output) {
if(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
expected.format() == PixelFormat::Red
#ifndef MAGNUM_TARGET_WEBGL
case PixelFormat::BGRA:
#endif
#ifdef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_WEBGL
||
case PixelFormat::SRGB:
case PixelFormat::SRGBAlpha:
#endif
expected.format() == PixelFormat::Luminance
#ifndef MAGNUM_TARGET_GLES2
case PixelFormat::RedInteger:
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::GreenInteger:
case PixelFormat::BlueInteger:
#endif
)
return calculateImageDelta<1, T>(actual, expected, output);
else if(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
expected.format() == PixelFormat::RG
case PixelFormat::RGInteger:
case PixelFormat::RGBInteger:
case PixelFormat::RGBAInteger:
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::BGRInteger:
case PixelFormat::BGRAInteger:
#endif
#ifdef MAGNUM_TARGET_GLES2
#endif
case PixelFormat::DepthComponent:
#ifndef MAGNUM_TARGET_WEBGL
||
case PixelFormat::StencilIndex:
#endif
expected.format() == PixelFormat::LuminanceAlpha
case PixelFormat::DepthStencil:
/** @todo CORRADE_ASSERT_UNREACHABLE() with message here */
CORRADE_ASSERT(false,
"DebugTools::CompareImage: deprecated GL-specific formats are not supported", {});
CORRADE_IGNORE_DEPRECATED_POP
#endif
)
return calculateImageDelta<2, T>(actual, expected, output);
else if(expected.format() == PixelFormat::RGB)
return calculateImageDelta<3, T>(actual, expected, output);
else if(expected.format() == PixelFormat::RGBA)
return calculateImageDelta<4, T>(actual, expected, output);
CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
}
std::tuple<std::vector<Float>, Float, Float> calculateImageDelta(const ImageView2D& actual, const ImageView2D& expected) {
/* Calculate a delta image */
std::vector<Float> delta(expected.size().product());
}
Float max;
if(expected.type() == PixelType::UnsignedByte)
max = calculateIntegerImageDelta<UnsignedByte>(actual, expected, delta);
else if(expected.type() == PixelType::UnsignedShort)
max = calculateIntegerImageDelta<UnsignedShort>(actual, expected, delta);
else if(expected.type() == PixelType::UnsignedInt)
max = calculateIntegerImageDelta<UnsignedInt>(actual, expected, delta);
#ifndef MAGNUM_TARGET_GLES2
else if(expected.type() == PixelType::Byte)
max = calculateIntegerImageDelta<Byte>(actual, expected, delta);
else if(expected.type() == PixelType::Short)
max = calculateIntegerImageDelta<Short>(actual, expected, delta);
else if(expected.type() == PixelType::Int)
max = calculateIntegerImageDelta<Int>(actual, expected, delta);
#endif
else if(expected.type() == PixelType::Float)
max = calculateImageDelta<Float>(actual, expected, delta);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
CORRADE_ASSERT(max == max,
"DebugTools::CompareImage: unknown format" << expected.format(), {});
/* Calculate mean delta. Do it the special way so we don't lose
precision -- that would result in having false negatives! */
@ -225,101 +239,115 @@ void printDeltaImage(Debug& out, const std::vector<Float>& deltas, const Vector2
namespace {
template<class T> void printIntegerPixelAt(Debug& out, const char* const pixels, const std::size_t stride, const Vector2i& pos, const PixelFormat format) {
if(
void printPixelAt(Debug& out, const char* const pixels, const std::size_t stride, const Vector2i& pos, const PixelFormat format) {
switch(format) {
#define _c(format, size, T) \
case PixelFormat::format: \
out << pixelAt<size, T>(pixels, stride, pos); \
break;
#define _d(first, second, size, T) \
case PixelFormat::first: \
case PixelFormat::second: \
out << pixelAt<size, T>(pixels, stride, pos); \
break;
_d(R8Unorm, R8UI, 1, UnsignedByte)
_d(RG8Unorm, RG8UI, 2, UnsignedByte)
_c(RGB8UI, 3, UnsignedByte)
_c(RGBA8UI, 4, UnsignedByte)
/* RGB8Unorm, RGBA8Unorm handled below */
_d(R8Snorm, R8I, 1, Byte)
_d(RG8Snorm, RG8I, 2, Byte)
_d(RGB8Snorm, RGB8I, 3, Byte)
_d(RGBA8Snorm, RGBA8I, 4, Byte)
_d(R16Unorm, R16UI, 1, UnsignedShort)
_d(RG16Unorm, RG16UI, 2, UnsignedShort)
_d(RGB16Unorm, RGB16UI, 3, UnsignedShort)
_d(RGBA16Unorm, RGBA16UI, 4, UnsignedShort)
_d(R16Snorm, R16I, 1, Short)
_d(RG16Snorm, RG16I, 2, Short)
_d(RGB16Snorm, RGB16I, 3, Short)
_d(RGBA16Snorm, RGBA16I, 4, Short)
_c(R32UI, 1, UnsignedInt)
_c(RG32UI, 2, UnsignedInt)
_c(RGB32UI, 3, UnsignedInt)
_c(RGBA32UI, 4, UnsignedInt)
_c(R32I, 1, Int)
_c(RG32I, 2, Int)
_c(RGB32I, 3, Int)
_c(RGBA32I, 4, Int)
_c(R32F, 1, Float)
_c(RG32F, 2, Float)
_c(RGB32F, 3, Float)
_c(RGBA32F, 4, Float)
#undef _d
#undef _c
/* Take the opportunity and print 8-bit colors in hex */
case PixelFormat::RGB8Unorm:
out << Color3ub{pixelAt<3, UnsignedByte>(pixels, stride, pos)};
break;
case PixelFormat::RGBA8Unorm:
out << Color4ub{pixelAt<4, UnsignedByte>(pixels, stride, pos)};
break;
case PixelFormat::R16F:
case PixelFormat::RG16F:
case PixelFormat::RGB16F:
case PixelFormat::RGBA16F:
#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL)
CORRADE_IGNORE_DEPRECATED_PUSH
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
format == PixelFormat::Red
case PixelFormat::Red:
#endif
#ifndef MAGNUM_TARGET_GLES2
|| format == PixelFormat::RedInteger
#else
#ifndef MAGNUM_TARGET_WEBGL
||
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::Green:
case PixelFormat::Blue:
#endif
format == PixelFormat::Luminance
#ifdef MAGNUM_TARGET_GLES2
case PixelFormat::Luminance:
#endif
)
out << pixelAt<1, T>(pixels, stride, pos);
else if(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
format == PixelFormat::RG
#endif
#ifndef MAGNUM_TARGET_GLES2
|| format == PixelFormat::RGInteger
#else
#ifndef MAGNUM_TARGET_WEBGL
||
case PixelFormat::RG:
#endif
format == PixelFormat::LuminanceAlpha
#endif
)
out << pixelAt<2, T>(pixels, stride, pos);
/* Take the opportunity and print 8-bit colors in hex */
else if(format == PixelFormat::RGB
#ifndef MAGNUM_TARGET_GLES2
|| format == PixelFormat::RGBInteger
#ifdef MAGNUM_TARGET_GLES2
case PixelFormat::LuminanceAlpha:
#endif
)
out << Math::Color3<T>{pixelAt<3, T>(pixels, stride, pos)};
else if(format == PixelFormat::RGBA
#ifndef MAGNUM_TARGET_GLES2
|| format == PixelFormat::RGBAInteger
case PixelFormat::RGB:
case PixelFormat::RGBA:
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::BGR:
#endif
)
out << Math::Color4<T>{pixelAt<4, T>(pixels, stride, pos)};
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
template<class T> void printPixelAt(Debug& out, const char* const pixels, const std::size_t stride, const Vector2i& pos, const PixelFormat format) {
if(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
format == PixelFormat::Red
#ifndef MAGNUM_TARGET_WEBGL
case PixelFormat::BGRA:
#endif
#ifdef MAGNUM_TARGET_GLES2
#ifndef MAGNUM_TARGET_WEBGL
||
case PixelFormat::SRGB:
case PixelFormat::SRGBAlpha:
#endif
#ifndef MAGNUM_TARGET_GLES2
case PixelFormat::RedInteger:
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::GreenInteger:
case PixelFormat::BlueInteger:
#endif
format == PixelFormat::Luminance
case PixelFormat::RGInteger:
case PixelFormat::RGBInteger:
case PixelFormat::RGBAInteger:
#ifndef MAGNUM_TARGET_GLES
case PixelFormat::BGRInteger:
case PixelFormat::BGRAInteger:
#endif
)
out << pixelAt<1, T>(pixels, stride, pos);
else if(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
format == PixelFormat::RG
#endif
#ifdef MAGNUM_TARGET_GLES2
case PixelFormat::DepthComponent:
#ifndef MAGNUM_TARGET_WEBGL
||
case PixelFormat::StencilIndex:
#endif
format == PixelFormat::LuminanceAlpha
case PixelFormat::DepthStencil:
CORRADE_IGNORE_DEPRECATED_POP
#endif
)
out << pixelAt<2, T>(pixels, stride, pos);
else if(format == PixelFormat::RGB)
out << pixelAt<3, T>(pixels, stride, pos);
else if(format == PixelFormat::RGBA)
out << pixelAt<4, T>(pixels, stride, pos);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
}
void printPixelAt(Debug& out, const char* const pixels, const std::size_t stride, const Vector2i& pos, const PixelFormat format, const PixelType type) {
if(type == PixelType::UnsignedByte)
printIntegerPixelAt<UnsignedByte>(out, pixels, stride, pos, format);
else if(type == PixelType::UnsignedShort)
printIntegerPixelAt<UnsignedShort>(out, pixels, stride, pos, format);
else if(type == PixelType::UnsignedInt)
printIntegerPixelAt<UnsignedInt>(out, pixels, stride, pos, format);
#ifndef MAGNUM_TARGET_GLES2
else if(type == PixelType::Byte)
printIntegerPixelAt<Byte>(out, pixels, stride, pos, format);
else if(type == PixelType::Short)
printIntegerPixelAt<Short>(out, pixels, stride, pos, format);
else if(type == PixelType::Int)
printIntegerPixelAt<Int>(out, pixels, stride, pos, format);
#endif
else if(type == PixelType::Float)
printPixelAt<Float>(out, pixels, stride, pos, format);
else CORRADE_ASSERT_UNREACHABLE(); /* LCOV_EXCL_LINE */
/* Already handled by a printing assert before */
CORRADE_ASSERT_UNREACHABLE();
}
}
}
@ -363,11 +391,11 @@ void printPixelDeltas(Debug& out, const std::vector<Float>& delta, const ImageVi
<< Debug::nospace << "," << Debug::nospace << pos.y()
<< Debug::nospace << "]";
printPixelAt(out, actualPixels, actualStride, pos, expected.format(), expected.type());
printPixelAt(out, actualPixels, actualStride, pos, expected.format());
out << Debug::nospace << ", expected";
printPixelAt(out, expectedPixels, expectedStride, pos, expected.format(), expected.type());
printPixelAt(out, expectedPixels, expectedStride, pos, expected.format());
out << "(Δ =" << Debug::boldColor(delta[it->second] > maxThreshold ?
Debug::Color::Red : Debug::Color::Yellow) << delta[it->second]
@ -398,56 +426,11 @@ bool Comparator<DebugTools::CompareImage>::operator()(const ImageView2D& actual,
_state = State::DifferentSize;
return false;
}
if(actual.format() != expected.format() || actual.type() != expected.type()) {
if(actual.format() != expected.format()) {
_state = State::DifferentFormat;
return false;
}
/* Assert on unsupported format/storage */
#ifndef CORRADE_NO_DEBUG
const bool formatSupported = (
(
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
expected.format() == PixelFormat::Red ||
expected.format() == PixelFormat::RG ||
#endif
#ifndef MAGNUM_TARGET_GLES2
expected.format() == PixelFormat::RedInteger ||
expected.format() == PixelFormat::RGInteger ||
expected.format() == PixelFormat::RGBInteger ||
expected.format() == PixelFormat::RGBAInteger ||
#else
expected.format() == PixelFormat::Luminance ||
expected.format() == PixelFormat::LuminanceAlpha ||
#endif
expected.format() == PixelFormat::RGB ||
expected.format() == PixelFormat::RGBA
) && (
#ifndef MAGNUM_TARGET_GLES2
expected.type() == PixelType::Byte ||
expected.type() == PixelType::Short ||
expected.type() == PixelType::Int ||
#endif
expected.type() == PixelType::UnsignedByte ||
expected.type() == PixelType::UnsignedShort ||
expected.type() == PixelType::UnsignedInt
)) || ((
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
expected.format() == PixelFormat::Red ||
expected.format() == PixelFormat::RG ||
#endif
#ifdef MAGNUM_TARGET_GLES2
expected.format() == PixelFormat::Luminance ||
expected.format() == PixelFormat::LuminanceAlpha ||
#endif
expected.format() == PixelFormat::RGB ||
expected.format() == PixelFormat::RGBA
) && expected.type() == PixelType::Float);
CORRADE_ASSERT(
formatSupported,
"DebugTools::CompareImage: format" << expected.format() << Debug::nospace << "/" << expected.type() << "is not supported", {});
#endif
std::vector<Float> delta;
std::tie(delta, _max, _mean) = DebugTools::Implementation::calculateImageDelta(actual, expected);
@ -471,10 +454,8 @@ void Comparator<DebugTools::CompareImage>::printErrorMessage(Debug& out, const s
out << "different size, actual" << _actualImage->size() << "but"
<< _expectedImage->size() << "expected.";
else if(_state == State::DifferentFormat)
out << "different format, actual" << _actualImage->format()
<< Debug::nospace << "/" << Debug::nospace << _actualImage->type()
<< "but" << _expectedImage->format() << Debug::nospace << "/"
<< Debug::nospace << _expectedImage->type() << "expected.";
out << "different format, actual" << _actualImage->format() << "but"
<< _expectedImage->format() << "expected.";
else {
if(_state == State::AboveThresholds)
out << "both max and mean delta above threshold, actual"

23
src/Magnum/DebugTools/CompareImage.h

@ -102,17 +102,18 @@ give for example the following result:
Supports the following formats:
- @ref PixelFormat::Red, @ref PixelFormat::RedInteger, @ref PixelFormat::RG,
@ref PixelFormat::RGInteger, @ref PixelFormat::RGB, @ref PixelFormat::RGBInteger,
@ref PixelFormat::RGBA and @ref PixelFormat::RGBAInteger with
@ref PixelType::UnsignedByte, @ref PixelType::Byte, @ref PixelType::UnsignedShort,
@ref PixelType::Short, @ref PixelType::UnsignedInt and @ref PixelType::Int
- @ref PixelFormat::Red, @ref PixelFormat::RG, @ref PixelFormat::RGB and
@ref PixelFormat::RGBA with @ref PixelType::Float
In OpenGL ES 2.0 and WebGL 1.0, @ref PixelFormat::Luminance and
@ref PixelFormat::LuminanceAlpha are also accepted in place of
@ref PixelFormat::Red and @ref PixelFormat::RG.
- @ref PixelFormat::RGBA8Unorm, @ref PixelFormat::RGBA16Unorm and their
one-/two-/three-component versions
- @ref PixelFormat::RGBA8Snorm, @ref PixelFormat::RGBA16Snorm and their
one-/two-/three-component versions
- @ref PixelFormat::RGBA8UI, @ref PixelFormat::RGBA16UI,
@ref PixelFormat::RGBA32UI and their one-/two-/three-component versions
- @ref PixelFormat::RGBA8I, @ref PixelFormat::RGBA16I,
@ref PixelFormat::RGBA32I and their one-/two-/three-component versions
- @ref PixelFormat::RGBA32F and its one-/two-/three-component versions
@ref PixelFormat::RGBA16F and other half-float formats are not supported at the
moment. Implementation-specific pixel formats can't be supported.
Supports all @ref PixelStorage parameters. The images don't need to have the
same pixel storage parameters, meaning you are able to compare different

2
src/Magnum/DebugTools/Test/CMakeLists.txt

@ -24,7 +24,7 @@
#
if(Corrade_TestSuite_FOUND)
corrade_add_test(DebugToolsCompareImageTest CompareImageTest.cpp LIBRARIES MagnumDebugTools)
corrade_add_test(DebugToolsCompareImageTest CompareImageTest.cpp LIBRARIES MagnumDebugToolsTestLib)
set_target_properties(DebugToolsCompareImageTest PROPERTIES FOLDER "Magnum/DebugTools/Test")
endif()

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

@ -29,8 +29,8 @@
#include <Corrade/TestSuite/Compare/Container.h>
#include "Magnum/ImageView.h"
#include "Magnum/PixelFormat.h"
#include "Magnum/DebugTools/CompareImage.h"
#include "Magnum/GL/PixelFormat.h"
#include "Magnum/Math/Functions.h"
#include "Magnum/Math/Color.h"
@ -39,6 +39,13 @@ namespace Magnum { namespace DebugTools { namespace Test {
struct CompareImageTest: TestSuite::Tester {
explicit CompareImageTest();
void formatUnknown();
void formatHalf();
void formatImplementationSpecific();
#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL)
void formatDeprecated();
#endif
void calculateDelta();
void calculateDeltaStorage();
@ -51,7 +58,6 @@ struct CompareImageTest: TestSuite::Tester {
void compareDifferentSize();
void compareDifferentFormat();
void compareDifferentType();
void compareSameZeroThreshold();
void compareAboveThresholds();
void compareAboveMaxThreshold();
@ -59,7 +65,14 @@ struct CompareImageTest: TestSuite::Tester {
};
CompareImageTest::CompareImageTest() {
addTests({&CompareImageTest::calculateDelta,
addTests({&CompareImageTest::formatUnknown,
&CompareImageTest::formatHalf,
&CompareImageTest::formatImplementationSpecific,
#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL)
&CompareImageTest::formatDeprecated,
#endif
&CompareImageTest::calculateDelta,
&CompareImageTest::calculateDeltaStorage,
&CompareImageTest::deltaImage,
@ -71,7 +84,6 @@ CompareImageTest::CompareImageTest() {
&CompareImageTest::compareDifferentSize,
&CompareImageTest::compareDifferentFormat,
&CompareImageTest::compareDifferentType,
&CompareImageTest::compareSameZeroThreshold,
&CompareImageTest::compareAboveThresholds,
&CompareImageTest::compareAboveMaxThreshold,
@ -96,22 +108,54 @@ namespace {
0.01f, 0.0f, 0.1f,
0.12f, 1.0f, 0.0f};
const ImageView2D ActualRed{
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
PixelFormat::Red
#else
PixelFormat::Luminance
#endif
, PixelType::Float, {3, 3}, ActualRedData};
const ImageView2D ExpectedRed{
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
PixelFormat::Red
#else
PixelFormat::Luminance
#endif
, PixelType::Float, {3, 3}, ExpectedRedData};
const ImageView2D ActualRed{PixelFormat::R32F, {3, 3}, ActualRedData};
const ImageView2D ExpectedRed{PixelFormat::R32F, {3, 3}, ExpectedRedData};
}
void CompareImageTest::formatUnknown() {
std::ostringstream out;
Error redirectError{&out};
ImageView2D image{PixelStorage{}, PixelFormat(0xdead), 0, 0, {}};
Implementation::calculateImageDelta(image, image);
CORRADE_COMPARE(out.str(), "DebugTools::CompareImage: unknown format PixelFormat(0xdead)\n");
}
void CompareImageTest::formatHalf() {
std::ostringstream out;
Error redirectError{&out};
ImageView2D image{PixelFormat::RG16F, {}};
Implementation::calculateImageDelta(image, image);
CORRADE_COMPARE(out.str(), "DebugTools::CompareImage: half-float formats are not supported yet\n");
}
void CompareImageTest::formatImplementationSpecific() {
std::ostringstream out;
Error redirectError{&out};
ImageView2D image{PixelStorage{}, pixelFormatWrap(0xdead), 0, 0, {}};
Implementation::calculateImageDelta(image, image);
CORRADE_COMPARE(out.str(), "DebugTools::CompareImage: can't compare implementation-specific pixel formats\n");
}
#if defined(MAGNUM_BUILD_DEPRECATED) && defined(MAGNUM_TARGET_GL)
void CompareImageTest::formatDeprecated() {
std::ostringstream out;
Error redirectError{&out};
CORRADE_IGNORE_DEPRECATED_PUSH
ImageView2D image{PixelFormat::RGB, PixelType::UnsignedByte, {}};
CORRADE_IGNORE_DEPRECATED_POP
Implementation::calculateImageDelta(image, image);
CORRADE_COMPARE(out.str(), "DebugTools::CompareImage: deprecated GL-specific formats are not supported\n");
}
#endif
void CompareImageTest::calculateDelta() {
std::vector<Float> delta;
Float max, mean;
@ -131,22 +175,15 @@ namespace {
};
const UnsignedByte ExpectedRgbData[] = {
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
0, 0, 0, 0x55, 0xf8, 0x3a, 0x56, 0x10, 0xed, 0, 0, 0,
0, 0, 0, 0x23, 0x27, 0x10, 0xab, 0xcd, 0xfa, 0, 0, 0
#else
0x55, 0xf8, 0x3a, 0x56, 0x10, 0xed, 0, 0,
0x23, 0x27, 0x10, 0xab, 0xcd, 0xfa, 0, 0,
#endif
};
const ImageView2D ActualRgb{PixelStorage{}.setSkip({0, 1, 0}),
PixelFormat::RGB, PixelType::UnsignedByte, {2, 2}, ActualRgbData};
PixelFormat::RGB8Unorm, {2, 2}, ActualRgbData};
const ImageView2D ExpectedRgb{
#if !(defined(MAGNUM_TARGET_WEBGL) && defined(MAGNUM_TARGET_GLES2))
PixelStorage{}.setSkip({1, 0, 0}).setRowLength(3),
#endif
PixelFormat::RGB, PixelType::UnsignedByte, {2, 2}, ExpectedRgbData};
PixelFormat::RGB8Unorm, {2, 2}, ExpectedRgbData};
}
void CompareImageTest::calculateDeltaStorage() {
@ -267,20 +304,8 @@ void CompareImageTest::pixelDeltaOverflow() {
void CompareImageTest::compareDifferentSize() {
std::stringstream out;
ImageView2D a{
#ifndef MAGNUM_TARGET_GLES2
PixelFormat::RGInteger,
#else
PixelFormat::LuminanceAlpha,
#endif
PixelType::UnsignedByte, {3, 4}, nullptr};
ImageView2D b{
#ifndef MAGNUM_TARGET_GLES2
PixelFormat::RGInteger,
#else
PixelFormat::LuminanceAlpha,
#endif
PixelType::UnsignedByte, {3, 5}, nullptr};
ImageView2D a{PixelFormat::RG8UI, {3, 4}, nullptr};
ImageView2D b{PixelFormat::RG8UI, {3, 5}, nullptr};
{
Error e(&out);
@ -295,24 +320,8 @@ void CompareImageTest::compareDifferentSize() {
void CompareImageTest::compareDifferentFormat() {
std::stringstream out;
ImageView2D a{PixelFormat::RGBA, PixelType::Float, {3, 4}, nullptr};
ImageView2D b{PixelFormat::RGB, PixelType::Float, {3, 4}, nullptr};
{
Error e(&out);
TestSuite::Comparator<CompareImage> compare;
CORRADE_VERIFY(!compare(a, b));
compare.printErrorMessage(e, "a", "b");
}
CORRADE_COMPARE(out.str(), "Images a and b have different format, actual GL::PixelFormat::RGBA/GL::PixelType::Float but GL::PixelFormat::RGB/GL::PixelType::Float expected.\n");
}
void CompareImageTest::compareDifferentType() {
std::stringstream out;
ImageView2D a{PixelFormat::RGB, PixelType::UnsignedByte, {3, 4}, nullptr};
ImageView2D b{PixelFormat::RGB, PixelType::UnsignedShort, {3, 4}, nullptr};
ImageView2D a{PixelFormat::RGBA32F, {3, 4}, nullptr};
ImageView2D b{PixelFormat::RGB32F, {3, 4}, nullptr};
{
Error e(&out);
@ -321,7 +330,7 @@ void CompareImageTest::compareDifferentType() {
compare.printErrorMessage(e, "a", "b");
}
CORRADE_COMPARE(out.str(), "Images a and b have different format, actual GL::PixelFormat::RGB/GL::PixelType::UnsignedByte but GL::PixelFormat::RGB/GL::PixelType::UnsignedShort expected.\n");
CORRADE_COMPARE(out.str(), "Images a and b have different format, actual PixelFormat::RGBA32F but PixelFormat::RGB32F expected.\n");
}
void CompareImageTest::compareSameZeroThreshold() {
@ -332,7 +341,7 @@ void CompareImageTest::compareSameZeroThreshold() {
0xbadc0d_rgbf, 0xbeefe0_rgbf
};
const ImageView2D image{PixelFormat::RGB, PixelType::Float, {2, 2}, data};
const ImageView2D image{PixelFormat::RGB32F, {2, 2}, data};
CORRADE_VERIFY((TestSuite::Comparator<CompareImage>{0.0f, 0.0f}(image, image)));
}

2
src/Magnum/DebugTools/visibility.h

@ -31,7 +31,7 @@
#ifndef DOXYGEN_GENERATING_OUTPUT
#ifndef MAGNUM_BUILD_STATIC
#ifdef MagnumDebugTools_EXPORTS
#if defined(MagnumDebugTools_EXPORTS) || defined(MagnumDebugToolsObjects_EXPORTS)
#define MAGNUM_DEBUGTOOLS_EXPORT CORRADE_VISIBILITY_EXPORT
#else
#define MAGNUM_DEBUGTOOLS_EXPORT CORRADE_VISIBILITY_IMPORT

Loading…
Cancel
Save