diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp index 062654f349..58ef465674 100644 --- a/tests/ReadPixelsTest.cpp +++ b/tests/ReadPixelsTest.cpp @@ -19,6 +19,7 @@ #include "src/gpu/GrProxyProvider.h" #include "src/gpu/SkGr.h" #include "tests/Test.h" +#include "tests/TestUtils.h" #include "tools/gpu/GrContextFactory.h" #include "tools/gpu/ProxyUtils.h" @@ -619,45 +620,6 @@ DEF_TEST(ReadPixels_ValidConversion, reporter) { } } -namespace { -using ComparePixmapsErrorReporter = void(int x, int y, const float diffs[4]); -} // anonymous namespace - -static void compare_pixmaps(const SkPixmap& a, const SkPixmap& b, const float tol[4], - std::function& error) { - if (a.width() != b.width() || a.height() != b.height()) { - static constexpr float kDummyDiffs[4] = {}; - error(-1, -1, kDummyDiffs); - return; - } - SkAutoPixmapStorage afloat; - SkAutoPixmapStorage bfloat; - afloat.alloc(a.info().makeColorType(kRGBA_F32_SkColorType)); - bfloat.alloc(b.info().makeColorType(kRGBA_F32_SkColorType)); - SkConvertPixels(afloat.info(), afloat.writable_addr(), afloat.rowBytes(), a.info(), a.addr(), - a.rowBytes()); - SkConvertPixels(bfloat.info(), bfloat.writable_addr(), bfloat.rowBytes(), b.info(), b.addr(), - b.rowBytes()); - for (int y = 0; y < a.height(); ++y) { - for (int x = 0; x < a.width(); ++x) { - const float* rgbaA = static_cast(afloat.addr(x, y)); - const float* rgbaB = static_cast(bfloat.addr(x, y)); - float diffs[4]; - bool bad = false; - for (int i = 0; i < 4; ++i) { - diffs[i] = rgbaB[i] - rgbaA[i]; - if (std::abs(diffs[i]) > tol[i]) { - bad = true; - } - } - if (bad) { - error(x, y, diffs); - return; - } - } - } -} - static int min_rgb_channel_bits(SkColorType ct) { switch (ct) { case kUnknown_SkColorType: return 0; @@ -781,6 +743,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) { const float tols[4] = {tol, tol, tol, 0}; auto error = std::function( [&](int x, int y, const float diffs[4]) { + SkASSERT(x >= 0 && y >= 0); ERRORF(reporter, "Surf Color Type: %d, Read CT: %d, Rect [%d, %d, %d, %d]" ", origin: %d, CS conversion: %d\n" @@ -789,7 +752,7 @@ DEF_GPUTEST_FOR_RENDERING_CONTEXTS(AsyncReadPixels, reporter, ctxInfo) { rect.fBottom, origin, (bool)readCS, x, y, diffs[0], diffs[1], diffs[2], diffs[3]); }); - compare_pixmaps(ref, result, tols, error); + compare_pixels(ref, result, tols, error); } } } diff --git a/tests/TestUtils.cpp b/tests/TestUtils.cpp index 2715bdbcfa..dfe6ec3366 100644 --- a/tests/TestUtils.cpp +++ b/tests/TestUtils.cpp @@ -172,6 +172,70 @@ bool bitmap_to_base64_data_uri(const SkBitmap& bitmap, SkString* dst) { return true; } +bool compare_pixels(const GrPixelInfo& infoA, const char* a, size_t rowBytesA, + const GrPixelInfo& infoB, const char* b, size_t rowBytesB, + const float tolRGBA[4], std::function& error) { + if (infoA.width() != infoB.width() || infoA.height() != infoB.height()) { + static constexpr float kDummyDiffs[4] = {}; + error(-1, -1, kDummyDiffs); + return false; + } + + SkAlphaType floatAlphaType = infoA.alphaType(); + // If one is premul and the other is unpremul we do the comparison in premul space. + if ((infoA.alphaType() == kPremul_SkAlphaType || + infoB.alphaType() == kPremul_SkAlphaType) && + (infoA.alphaType() == kUnpremul_SkAlphaType || + infoB.alphaType() == kUnpremul_SkAlphaType)) { + floatAlphaType = kPremul_SkAlphaType; + } + sk_sp floatCS; + if (SkColorSpace::Equals(infoA.colorSpace(), infoB.colorSpace())) { + floatCS = infoA.refColorSpace(); + } else { + floatCS = SkColorSpace::MakeSRGBLinear(); + } + GrPixelInfo floatInfo(GrColorType::kRGBA_F32, floatAlphaType, std::move(floatCS), + infoA.width(), infoA.height()); + + size_t floatBpp = GrColorTypeBytesPerPixel(GrColorType::kRGBA_F32); + size_t floatRowBytes = floatBpp * infoA.width(); + std::unique_ptr floatA(new char[floatRowBytes * infoA.height()]); + std::unique_ptr floatB(new char[floatRowBytes * infoA.height()]); + SkAssertResult(GrConvertPixels(floatInfo, floatA.get(), floatRowBytes, infoA, a, rowBytesA)); + SkAssertResult(GrConvertPixels(floatInfo, floatB.get(), floatRowBytes, infoB, b, rowBytesB)); + + auto at = [floatBpp, floatRowBytes](const char* floatBuffer, int x, int y) { + return reinterpret_cast(floatBuffer + y * floatRowBytes + x * floatBpp); + }; + for (int y = 0; y < infoA.height(); ++y) { + for (int x = 0; x < infoA.width(); ++x) { + const float* rgbaA = at(floatA.get(), x, y); + const float* rgbaB = at(floatB.get(), x, y); + float diffs[4]; + bool bad = false; + for (int i = 0; i < 4; ++i) { + diffs[i] = rgbaB[i] - rgbaA[i]; + if (std::abs(diffs[i]) > std::abs(tolRGBA[i])) { + bad = true; + } + } + if (bad) { + error(x, y, diffs); + return false; + } + } + } + return true; +} + +bool compare_pixels(const SkPixmap& a, const SkPixmap& b, const float tolRGBA[4], + std::function& error) { + return compare_pixels(a.info(), static_cast(a.addr()), a.rowBytes(), + b.info(), static_cast(b.addr()), b.rowBytes(), + tolRGBA, error); +} + #include "src/utils/SkCharToGlyphCache.h" static SkGlyphID hash_to_glyph(uint32_t value) { diff --git a/tests/TestUtils.h b/tests/TestUtils.h index 33a638700d..64f0f9b75c 100644 --- a/tests/TestUtils.h +++ b/tests/TestUtils.h @@ -6,6 +6,7 @@ */ #include "include/core/SkBitmap.h" +#include "src/gpu/GrDataUtils.h" #include "tests/Test.h" class GrSurfaceContext; @@ -44,3 +45,33 @@ bool does_full_buffer_contain_correct_color(const GrColor* srcBuffer, const GrCo // Encodes the bitmap into a data:/image/png;base64,... url suitable to view in a browser after // printing to a log. If false is returned, dst holds an error message instead of a URI. bool bitmap_to_base64_data_uri(const SkBitmap& bitmap, SkString* dst); + +/** Used by compare_pixels. */ +using ComparePixmapsErrorReporter = void(int x, int y, const float diffs[4]); + +/** + * Compares pixels pointed to by 'a' with 'infoA' and rowBytesA to pixels pointed to by 'b' with + * 'infoB' and 'rowBytesB'. + * + * If the infos have different dimensions error is called with negative coordinate values and + * zero diffs and no comparisons are made. + * + * Before comparison pixels are converted to a common color type, alpha type, and color space. + * The color type is always 32 bit float. The alpha type is premul if one of 'infoA' and 'infoB' is + * premul and the other is unpremul. The color space is linear sRGB if 'infoA' and 'infoB' have + * different colorspaces, otherwise their common color space is used. + * + * 'tolRGBA' expresses the allowed difference between pixels in the comparison space per channel. If + * pixel components differ more than by 'tolRGBA' in absolute value in any channel then 'error' is + * called with the coordinate and difference in the comparison space (B - A). + * + * The function quits after a single error is reported and returns false if 'error' was called and + * true otherwise. + */ +bool compare_pixels(const GrPixelInfo& infoA, const char* a, size_t rowBytesA, + const GrPixelInfo& infoB, const char* b, size_t rowBytesB, + const float tolRGBA[4], std::function& error); + +/** Convenience version of above that takes SkPixmap inputs. */ +bool compare_pixels(const SkPixmap& a, const SkPixmap& b, const float tolRGBA[4], + std::function& error);