| /* | |
| * Copyright 2025 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "tests/Test.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkColorSpace.h" | |
| #include "include/core/SkImageInfo.h" | |
| #include "include/core/SkPaint.h" | |
| #include "include/core/SkPixmap.h" | |
| #include "include/core/SkSurface.h" | |
| #include "src/core/SkBlitter.h" | |
| #include "src/core/SkCoreBlitters.h" | |
| #include "src/core/SkMask.h" | |
| #include <memory> | |
| static bool all_pixels_same_color(uint32_t* buffer, size_t len) { | |
| for (size_t i = 1; i < len; ++i) { | |
| if (buffer[0] != buffer[i]) { | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| using BlitterFactory = std::unique_ptr<SkBlitter> (*)(const SkPixmap&, const SkPaint&); | |
| static void compare_mask_and_antiH(skiatest::Reporter* reporter, | |
| SkColor backgroundColor, | |
| SkColor paintColor, | |
| BlitterFactory makeBlitter) { | |
| // 19 is big enough to exercise any multi-lane code (e.g. SIMD/NEON) | |
| // and have some remainder to exercise single-pixel code. | |
| constexpr size_t kPixelsToBlit = 19; | |
| static_assert(kPixelsToBlit % 4 != 0); | |
| static_assert(kPixelsToBlit % 8 != 0); | |
| static_assert(kPixelsToBlit % 16 != 0); | |
| // Space for a kPixelsToBlit by 1 pixel image of 8888 color | |
| SkColor buffer1[kPixelsToBlit * 1]; | |
| SkColor buffer2[kPixelsToBlit * 1]; | |
| auto ii = SkImageInfo::Make( | |
| {kPixelsToBlit, 1}, | |
| SkColorInfo(kN32_SkColorType, kPremul_SkAlphaType, SkColorSpace::MakeSRGB())); | |
| SkPixmap device1(ii, buffer1, ii.minRowBytes()); | |
| auto surface1 = SkSurfaces::WrapPixels(device1); | |
| SkASSERT(surface1); | |
| SkPixmap device2(ii, buffer2, ii.minRowBytes()); | |
| auto surface2 = SkSurfaces::WrapPixels(device2); | |
| SkASSERT(surface2); | |
| SkPaint paint; | |
| paint.setColor(paintColor); | |
| auto blitter1 = makeBlitter(device1, paint); | |
| SkASSERT(blitter1); | |
| auto blitter2 = makeBlitter(device2, paint); | |
| SkASSERT(blitter2); | |
| SkAlpha antiAlias[1]; | |
| // The blitAntiH needs a buffer where the first value is the number of pixels | |
| // to blit and then at least that many 0s so we don't read off the end of the | |
| // buffer to figure out we need to stop. | |
| int16_t runs[kPixelsToBlit+1] = {kPixelsToBlit}; | |
| uint8_t maskImage[kPixelsToBlit]; | |
| auto maskBounds = SkIRect::MakeXYWH(0, 0, kPixelsToBlit, 1); | |
| for (int alpha = 0; alpha <= 255; ++alpha) { | |
| antiAlias[0] = static_cast<SkAlpha>(alpha); | |
| std::fill_n(maskImage, kPixelsToBlit, static_cast<SkAlpha>(alpha)); | |
| SkMask mask(maskImage, maskBounds, kPixelsToBlit, SkMask::kA8_Format); | |
| surface1->getCanvas()->clear(backgroundColor); | |
| blitter1->blitAntiH(0, 0, antiAlias, runs); | |
| surface2->getCanvas()->clear(backgroundColor); | |
| blitter2->blitMask(mask, mask.fBounds); | |
| if (!all_pixels_same_color(buffer1, std::size(buffer1))) { | |
| REPORT_FAILURE(reporter, | |
| "blitAntiH was not the same for all pixels", | |
| SkStringPrintf("background=%08x, paint=%08x, alpha=%d", | |
| unsigned(backgroundColor), | |
| unsigned(paintColor), | |
| alpha)); | |
| return; | |
| } | |
| if (!all_pixels_same_color(buffer2, std::size(buffer2))) { | |
| REPORT_FAILURE(reporter, | |
| "blitMask was not the same for all pixels", | |
| SkStringPrintf("background=%08x, paint=%08x, alpha=%d", | |
| unsigned(backgroundColor), | |
| unsigned(paintColor), | |
| alpha)); | |
| return; | |
| } | |
| SkColor antiHColor = buffer1[0]; | |
| SkColor maskColor = buffer2[0]; | |
| REPORTER_ASSERT(reporter, | |
| antiHColor == maskColor, | |
| "background=%08x, paint=%08x, alpha=%d, " | |
| "blitAntiH=%08x, blitMask=%08x, " | |
| "diff=%02x %02x %02x %02x", | |
| unsigned(backgroundColor), unsigned(paintColor), alpha, | |
| unsigned(antiHColor), unsigned(maskColor), | |
| unsigned(abs((int)(SkColorGetA(antiHColor) - SkColorGetA(maskColor)))), | |
| unsigned(abs((int)(SkColorGetR(antiHColor) - SkColorGetR(maskColor)))), | |
| unsigned(abs((int)(SkColorGetG(antiHColor) - SkColorGetG(maskColor)))), | |
| unsigned(abs((int)(SkColorGetB(antiHColor) - SkColorGetB(maskColor))))); | |
| } | |
| } | |
| DEF_TEST(SkARGB32OpaqueBlitter_MaskAndAntiHDrawTheSame, r) { | |
| SkColor backgroundColors[] = { | |
| SK_ColorWHITE, SK_ColorBLACK, SK_ColorGRAY, | |
| SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, | |
| SK_ColorYELLOW, SK_ColorCYAN, SK_ColorMAGENTA, | |
| // arbitrary opaque color with uneven channels | |
| SkColorSetARGB(255, 255 / 4, 255 / 3, 255 / 2), | |
| }; | |
| SkColor paintColors[] = { | |
| SK_ColorWHITE, SK_ColorBLACK, SK_ColorGRAY, | |
| SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, | |
| SK_ColorYELLOW, SK_ColorCYAN, SK_ColorMAGENTA, | |
| SkColorSetARGB(255, 255 / 4, 255 / 3, 255 / 2), | |
| }; | |
| for (SkColor backgroundColor : backgroundColors) { | |
| for (SkColor paintColor : paintColors) { | |
| compare_mask_and_antiH(r, | |
| backgroundColor, | |
| paintColor, | |
| [](const SkPixmap& device, const SkPaint& paint) -> std::unique_ptr<SkBlitter> { | |
| return std::make_unique<SkARGB32_Opaque_Blitter>(device, paint); | |
| }); | |
| } | |
| } | |
| } | |
| DEF_TEST(SkARGB32BlackBlitter_MaskAndAntiHDrawTheSame, r) { | |
| SkColor backgroundColors[] = { | |
| SK_ColorWHITE, SK_ColorBLACK, SK_ColorGRAY, | |
| SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, | |
| SK_ColorYELLOW, SK_ColorCYAN, SK_ColorMAGENTA, | |
| // arbitrary opaque color with uneven channels | |
| SkColorSetARGB(255, 255 / 4, 255 / 3, 255 / 2), | |
| }; | |
| for (SkColor backgroundColor : backgroundColors) { | |
| compare_mask_and_antiH(r, | |
| backgroundColor, | |
| SK_ColorBLACK, | |
| [](const SkPixmap& device, const SkPaint& paint) -> std::unique_ptr<SkBlitter> { | |
| return std::make_unique<SkARGB32_Black_Blitter>(device, paint); | |
| }); | |
| } | |
| } | |
| DEF_TEST(SkARGB32Blitter_MaskAndAntiHDrawTheSame, r) { | |
| SkColor backgroundColors[] = { | |
| SK_ColorWHITE, SK_ColorBLACK, SK_ColorGRAY, | |
| SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, | |
| SK_ColorYELLOW, SK_ColorCYAN, SK_ColorMAGENTA, | |
| // arbitrary opaque color with uneven channels | |
| SkColorSetARGB(255, 255 / 4, 255 / 3, 255 / 2), | |
| }; | |
| SkColor paintColors[] = { | |
| SK_ColorWHITE, SK_ColorBLACK, SK_ColorGRAY, | |
| SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, | |
| SK_ColorYELLOW, SK_ColorCYAN, SK_ColorMAGENTA, | |
| SkColorSetARGB(255, 255 / 4, 255 / 3, 255 / 2), | |
| }; | |
| SkAlpha alphaValues[] = { | |
| 0, 10, 100, 200, 245 /*SkARGB32_Opaque_Blitter is used when alpha is 255*/ | |
| }; | |
| for (SkColor backgroundColor : backgroundColors) { | |
| for (SkColor paintColor : paintColors) { | |
| for (SkAlpha alpha : alphaValues) { | |
| SkColor newColor = SkColorSetA(paintColor, alpha); | |
| compare_mask_and_antiH(r, | |
| backgroundColor, | |
| newColor, | |
| [](const SkPixmap& device, const SkPaint& paint) -> std::unique_ptr<SkBlitter> { | |
| return std::make_unique<SkARGB32_Blitter>(device, paint); | |
| }); | |
| } | |
| } | |
| } | |
| } |