xref: /aosp_15_r20/external/skia/tests/BlendTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2015 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkBitmap.h"
10 #include "include/core/SkBlendMode.h"
11 #include "include/core/SkCanvas.h"
12 #include "include/core/SkColor.h"
13 #include "include/core/SkColorType.h"
14 #include "include/core/SkImageInfo.h"
15 #include "include/core/SkPaint.h"
16 #include "include/core/SkPoint.h"
17 #include "include/core/SkRect.h"
18 #include "include/core/SkRefCnt.h"
19 #include "include/core/SkSize.h"
20 #include "include/core/SkSurface.h"
21 #include "include/core/SkTypes.h"
22 #include "include/gpu/GpuTypes.h"
23 #include "include/gpu/ganesh/GrDirectContext.h"
24 #include "include/gpu/ganesh/GrTypes.h"
25 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
26 #include "include/private/base/SkTemplates.h"
27 #include "tests/CtsEnforcement.h"
28 #include "tests/Test.h"
29 #include "tools/gpu/BackendSurfaceFactory.h"
30 
31 #include <initializer_list>
32 #include <vector>
33 
34 struct GrContextOptions;
35 struct Results { int diffs, diffs_0x00, diffs_0xff, diffs_by_1; };
36 
acceptable(const Results & r)37 static bool acceptable(const Results& r) {
38 #if 0
39     SkDebugf("%d diffs, %d at 0x00, %d at 0xff, %d off by 1, all out of 65536\n",
40              r.diffs, r.diffs_0x00, r.diffs_0xff, r.diffs_by_1);
41 #endif
42     return r.diffs_by_1 == r.diffs   // never off by more than 1
43         && r.diffs_0x00 == 0         // transparent must stay transparent
44         && r.diffs_0xff == 0;        // opaque must stay opaque
45 }
46 
47 template <typename Fn>
test(Fn && multiply)48 static Results test(Fn&& multiply) {
49     Results r = { 0,0,0,0 };
50     for (int x = 0; x < 256; x++) {
51     for (int y = 0; y < 256; y++) {
52         int p = multiply(x, y),
53             ideal = (x*y+127)/255;
54         if (p != ideal) {
55             r.diffs++;
56             if (x == 0x00 || y == 0x00) { r.diffs_0x00++; }
57             if (x == 0xff || y == 0xff) { r.diffs_0xff++; }
58             if (SkTAbs(ideal - p) == 1) { r.diffs_by_1++; }
59         }
60     }}
61     return r;
62 }
63 
DEF_TEST(Blend_byte_multiply,r)64 DEF_TEST(Blend_byte_multiply, r) {
65     // These are all temptingly close but fundamentally broken.
66     int (*broken[])(int, int) = {
67         [](int x, int y) { return (x*y)>>8; },
68         [](int x, int y) { return (x*y+128)>>8; },
69         [](int x, int y) { y += y>>7; return (x*y)>>8; },
70     };
71     for (auto multiply : broken) { REPORTER_ASSERT(r, !acceptable(test(multiply))); }
72 
73     // These are fine to use, but not perfect.
74     int (*fine[])(int, int) = {
75         [](int x, int y) { return (x*y+x)>>8; },
76         [](int x, int y) { return (x*y+y)>>8; },
77         [](int x, int y) { return (x*y+255)>>8; },
78         [](int x, int y) { y += y>>7; return (x*y+128)>>8; },
79     };
80     for (auto multiply : fine) { REPORTER_ASSERT(r, acceptable(test(multiply))); }
81 
82     // These are pefect.
83     int (*perfect[])(int, int) = {
84         [](int x, int y) { return (x*y+127)/255; },  // Duh.
85         [](int x, int y) { int p = (x*y+128); return (p+(p>>8))>>8; },
86         [](int x, int y) { return ((x*y+128)*257)>>16; },
87     };
88     for (auto multiply : perfect) { REPORTER_ASSERT(r, test(multiply).diffs == 0); }
89 }
90 
91 // Tests blending to a surface with no texture available.
DEF_GANESH_TEST_FOR_GL_CONTEXT(ES2BlendWithNoTexture,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)92 DEF_GANESH_TEST_FOR_GL_CONTEXT(ES2BlendWithNoTexture,
93                                reporter,
94                                ctxInfo,
95                                CtsEnforcement::kApiLevel_T) {
96     auto context = ctxInfo.directContext();
97     static constexpr SkISize kDimensions{10, 10};
98     const SkColorType kColorType = kRGBA_8888_SkColorType;
99 
100     // Build our test cases:
101     struct RectAndSamplePoint {
102         SkRect rect;
103         SkIPoint outPoint;
104         SkIPoint inPoint;
105     } allRectsAndPoints[3] = {
106             {SkRect::MakeXYWH(0, 0, 5, 5), SkIPoint::Make(7, 7), SkIPoint::Make(2, 2)},
107             {SkRect::MakeXYWH(2, 2, 5, 5), SkIPoint::Make(1, 1), SkIPoint::Make(4, 4)},
108             {SkRect::MakeXYWH(5, 5, 5, 5), SkIPoint::Make(2, 2), SkIPoint::Make(7, 7)},
109     };
110 
111     struct TestCase {
112         RectAndSamplePoint fRectAndPoints;
113         SkRect             fClip;
114         int                fSampleCnt;
115         GrSurfaceOrigin    fOrigin;
116     };
117     std::vector<TestCase> testCases;
118 
119     for (auto origin : { kTopLeft_GrSurfaceOrigin, kBottomLeft_GrSurfaceOrigin}) {
120         for (int sampleCnt : {1, 4}) {
121             for (auto rectAndPoints : allRectsAndPoints) {
122                 for (auto clip : {SkRect::MakeXYWH(0, 0, 10, 10), SkRect::MakeXYWH(1, 1, 8, 8)}) {
123                     testCases.push_back({rectAndPoints, clip, sampleCnt, origin});
124                 }
125             }
126         }
127     }
128 
129     // Run each test case:
130     for (auto testCase : testCases) {
131         int sampleCnt = testCase.fSampleCnt;
132         SkRect paintRect = testCase.fRectAndPoints.rect;
133         SkIPoint outPoint = testCase.fRectAndPoints.outPoint;
134         SkIPoint inPoint = testCase.fRectAndPoints.inPoint;
135         GrSurfaceOrigin origin = testCase.fOrigin;
136 
137         // BGRA forces a framebuffer blit on ES2.
138         sk_sp<SkSurface> surface = sk_gpu_test::MakeBackendRenderTargetSurface(context,
139                                                                                kDimensions,
140                                                                                origin,
141                                                                                sampleCnt,
142                                                                                kColorType);
143 
144         if (!surface && sampleCnt > 1) {
145             // Some platforms don't support MSAA.
146             continue;
147         }
148         REPORTER_ASSERT(reporter, !!surface);
149 
150         // Fill our canvas with 0xFFFF80
151         SkCanvas* canvas = surface->getCanvas();
152         canvas->clipRect(testCase.fClip, false);
153         SkPaint black_paint;
154         black_paint.setColor(SkColorSetRGB(0xFF, 0xFF, 0x80));
155         canvas->drawRect(SkRect::Make(kDimensions), black_paint);
156 
157         // Blend 2x2 pixels at 5,5 with 0x80FFFF. Use multiply blend mode as this will trigger
158         // a copy of the destination.
159         SkPaint white_paint;
160         white_paint.setColor(SkColorSetRGB(0x80, 0xFF, 0xFF));
161         white_paint.setBlendMode(SkBlendMode::kMultiply);
162         canvas->drawRect(paintRect, white_paint);
163 
164         // Read the result into a bitmap.
165         SkBitmap bitmap;
166         REPORTER_ASSERT(reporter, bitmap.tryAllocPixels(SkImageInfo::Make(kDimensions, kColorType,
167                                                                           kPremul_SkAlphaType)));
168         REPORTER_ASSERT(
169                 reporter,
170                 surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 0, 0));
171 
172         // Check the in/out pixels.
173         SkColor color = bitmap.getColor(outPoint.x(), outPoint.y());
174         REPORTER_ASSERT(reporter, color == SkColorSetRGB(0xFF, 0xFF, 0x80),
175                         "Expected: A=FF R=FF G=FF B=80. Actual: A=%02X R=%02X G=%02X B=%02X",
176                         SkColorGetA(color),
177                         SkColorGetR(color),
178                         SkColorGetG(color),
179                         SkColorGetB(color));
180 
181         color = bitmap.getColor(inPoint.x(), inPoint.y());
182         REPORTER_ASSERT(reporter, color == SkColorSetRGB(0x80, 0xFF, 0x80),
183                         "Expected: A=FF R=80 G=FF B=80. Actual: A=%02X R=%02X G=%02X B=%02X",
184                         SkColorGetA(color),
185                         SkColorGetR(color),
186                         SkColorGetG(color),
187                         SkColorGetB(color));
188     }
189 }
190 
191 // Test that dst reads when large coordinates read the correct pixels.
192 // When we use half-width floats for dst read coordinates, we can end up reading the wrong pixel
193 // from dst and consequently writing the wrong blended color (skbug.com/14347).
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(BlendRequiringDstReadWithLargeCoordinates,reporter,contextInfo,CtsEnforcement::kApiLevel_V)194 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(BlendRequiringDstReadWithLargeCoordinates,
195                                        reporter,
196                                        contextInfo,
197                                        CtsEnforcement::kApiLevel_V) {
198     static constexpr SkColorType kColorType = kRGBA_8888_SkColorType;
199 
200     GrDirectContext* context = contextInfo.directContext();
201     SkImageInfo imageInfo =
202             SkImageInfo::Make(SkISize::Make(1200, 1), kColorType, kPremul_SkAlphaType);
203     sk_sp<SkSurface> surface = SkSurfaces::RenderTarget(context, skgpu::Budgeted::kYes, imageInfo);
204     SkCanvas* canvas = surface->getCanvas();
205     canvas->clear(SK_ColorBLACK);
206 
207     // Draw a red rectangle at x=1100.
208     SkPaint paint;
209     paint.setColor(SK_ColorRED);
210     canvas->drawIRect(SkIRect::MakeXYWH(1100, 0, 5, 1), paint);
211 
212     // Draw a rectangle with a blend mode that requires a dst read, over the drawn red rectangle.
213     SkPaint blendPaint;
214     blendPaint.setBlendMode(SkBlendMode::kSoftLight);
215     canvas->drawIRect(SkIRect::MakeXYWH(1090, 0, 20, 1), blendPaint);
216 
217     // Check the pixels at the edge of the left intersection between the two rectangles.
218     SkBitmap bitmap;
219     REPORTER_ASSERT(reporter,
220                     bitmap.tryAllocPixels(SkImageInfo::Make(
221                             SkISize::Make(2, 1), kColorType, kPremul_SkAlphaType)));
222     REPORTER_ASSERT(
223             reporter,
224             surface->readPixels(bitmap.info(), bitmap.getPixels(), bitmap.rowBytes(), 1099, 0));
225 
226     SkColor color = bitmap.getColor(0, 0);
227     REPORTER_ASSERT(reporter, color == SK_ColorBLACK,
228                     "Expected: solid black. Actual: A=%02X R=%02X G=%02X B=%02X",
229                     SkColorGetA(color),
230                     SkColorGetR(color),
231                     SkColorGetG(color),
232                     SkColorGetB(color));
233 
234     color = bitmap.getColor(1, 0);
235     REPORTER_ASSERT(reporter, color == SK_ColorRED,
236                     "Expected: solid red. Actual: A=%02X R=%02X G=%02X B=%02X",
237                     SkColorGetA(color),
238                     SkColorGetR(color),
239                     SkColorGetG(color),
240                     SkColorGetB(color));
241 }
242