xref: /aosp_15_r20/external/skia/tests/PremulAlphaRoundTripTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2011 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/SkColor.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkSurface.h"
15 #include "include/core/SkTypes.h"
16 #include "include/gpu/GpuTypes.h"
17 #include "include/gpu/ganesh/GrDirectContext.h"
18 #include "include/gpu/ganesh/SkSurfaceGanesh.h"
19 #include "include/private/base/SkDebug.h"
20 #include "src/core/SkConvertPixels.h"
21 #include "src/gpu/ganesh/GrDataUtils.h"
22 #include "src/gpu/ganesh/GrPixmap.h"
23 #include "tests/CtsEnforcement.h"
24 #include "tests/Test.h"
25 
26 #include <array>
27 #include <cstddef>
28 #include <cstdint>
29 
30 struct GrContextOptions;
31 
pack_unpremul_rgba(SkColor c)32 static uint32_t pack_unpremul_rgba(SkColor c) {
33     uint32_t packed;
34     uint8_t* byte = reinterpret_cast<uint8_t*>(&packed);
35     byte[0] = SkColorGetR(c);
36     byte[1] = SkColorGetG(c);
37     byte[2] = SkColorGetB(c);
38     byte[3] = SkColorGetA(c);
39     return packed;
40 }
41 
pack_unpremul_bgra(SkColor c)42 static uint32_t pack_unpremul_bgra(SkColor c) {
43     uint32_t packed;
44     uint8_t* byte = reinterpret_cast<uint8_t*>(&packed);
45     byte[0] = SkColorGetB(c);
46     byte[1] = SkColorGetG(c);
47     byte[2] = SkColorGetR(c);
48     byte[3] = SkColorGetA(c);
49     return packed;
50 }
51 
52 typedef uint32_t (*PackUnpremulProc)(SkColor);
53 
54 const struct {
55     SkColorType         fColorType;
56     PackUnpremulProc    fPackProc;
57 } gUnpremul[] = {
58     { kRGBA_8888_SkColorType, pack_unpremul_rgba },
59     { kBGRA_8888_SkColorType, pack_unpremul_bgra },
60 };
61 
fill_surface(SkSurface * surf,SkColorType colorType,PackUnpremulProc proc)62 static void fill_surface(SkSurface* surf, SkColorType colorType, PackUnpremulProc proc) {
63     // Don't strictly need a bitmap, but its a handy way to allocate the pixels
64     SkBitmap bmp;
65     bmp.allocN32Pixels(256, 256);
66 
67     for (int a = 0; a < 256; ++a) {
68         uint32_t* pixels = bmp.getAddr32(0, a);
69         for (int r = 0; r < 256; ++r) {
70             pixels[r] = proc(SkColorSetARGB(a, r, 0, 0));
71         }
72     }
73 
74     const SkImageInfo info = SkImageInfo::Make(bmp.dimensions(), colorType, kUnpremul_SkAlphaType);
75     surf->writePixels({info, bmp.getPixels(), bmp.rowBytes()}, 0, 0);
76 }
77 
test_premul_alpha_roundtrip(skiatest::Reporter * reporter,SkSurface * surf)78 static void test_premul_alpha_roundtrip(skiatest::Reporter* reporter, SkSurface* surf) {
79     for (size_t upmaIdx = 0; upmaIdx < std::size(gUnpremul); ++upmaIdx) {
80         fill_surface(surf, gUnpremul[upmaIdx].fColorType, gUnpremul[upmaIdx].fPackProc);
81 
82         const SkImageInfo info = SkImageInfo::Make(256, 256, gUnpremul[upmaIdx].fColorType,
83                                                    kUnpremul_SkAlphaType);
84         SkBitmap readBmp1;
85         readBmp1.allocPixels(info);
86         SkBitmap readBmp2;
87         readBmp2.allocPixels(info);
88 
89         readBmp1.eraseColor(0);
90         readBmp2.eraseColor(0);
91 
92         surf->readPixels(readBmp1, 0, 0);
93         surf->writePixels(readBmp1, 0, 0);
94         surf->readPixels(readBmp2, 0, 0);
95 
96         bool success = true;
97         for (int y = 0; y < 256 && success; ++y) {
98             const uint32_t* pixels1 = readBmp1.getAddr32(0, y);
99             const uint32_t* pixels2 = readBmp2.getAddr32(0, y);
100             for (int x = 0; x < 256 && success; ++x) {
101                 // We see sporadic failures here. May help to see where it goes wrong.
102                 if (pixels1[x] != pixels2[x]) {
103                     SkDebugf("%x != %x, x = %d, y = %d\n", pixels1[x], pixels2[x], x, y);
104                 }
105                 REPORTER_ASSERT(reporter, success = pixels1[x] == pixels2[x]);
106             }
107         }
108     }
109 }
110 
DEF_TEST(PremulAlphaRoundTrip,reporter)111 DEF_TEST(PremulAlphaRoundTrip, reporter) {
112     const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
113 
114     sk_sp<SkSurface> surf(SkSurfaces::Raster(info));
115 
116     test_premul_alpha_roundtrip(reporter, surf.get());
117 }
DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(PremulAlphaRoundTrip_Gpu,reporter,ctxInfo,CtsEnforcement::kApiLevel_T)118 DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(PremulAlphaRoundTrip_Gpu,
119                                        reporter,
120                                        ctxInfo,
121                                        CtsEnforcement::kApiLevel_T) {
122     const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
123 
124     sk_sp<SkSurface> surf(
125             SkSurfaces::RenderTarget(ctxInfo.directContext(), skgpu::Budgeted::kNo, info));
126     test_premul_alpha_roundtrip(reporter, surf.get());
127 }
128 
DEF_TEST(PremulAlphaRoundTripGrConvertPixels,reporter)129 DEF_TEST(PremulAlphaRoundTripGrConvertPixels, reporter) {
130     // Code that does the same thing as above, but using GrConvertPixels. This simulates what
131     // happens if you run the above on a machine with a GPU that doesn't have a valid PM/UPM
132     // conversion pair of FPs.
133     const SkImageInfo upmInfo =
134             SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
135     const SkImageInfo pmInfo =
136             SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
137 
138     GrPixmap src = GrPixmap::Allocate(upmInfo);
139     uint32_t* srcPixels = (uint32_t*)src.addr();
140     for (int y = 0; y < 256; ++y) {
141         for (int x = 0; x < 256; ++x) {
142             srcPixels[y * 256 + x] = pack_unpremul_rgba(SkColorSetARGB(y, x, x, x));
143         }
144     }
145 
146     GrPixmap surf = GrPixmap::Allocate(pmInfo);
147     GrConvertPixels(surf, src);
148 
149     GrPixmap read1 = GrPixmap::Allocate(upmInfo);
150     GrConvertPixels(read1, surf);
151 
152     GrPixmap surf2 = GrPixmap::Allocate(pmInfo);
153     GrConvertPixels(surf2, read1);
154 
155     GrPixmap read2 = GrPixmap::Allocate(upmInfo);
156     GrConvertPixels(read2, surf2);
157 
158     auto get_pixel = [](const GrPixmap& pm, int x, int y) {
159         const uint32_t* addr = (const uint32_t*)pm.addr();
160         return addr[y * 256 + x];
161     };
162     auto dump_pixel_history = [&](int x, int y) {
163         SkDebugf("Pixel history for (%d, %d):\n", x, y);
164         SkDebugf("Src : %08x\n", get_pixel(src, x, y));
165         SkDebugf(" -> : %08x\n", get_pixel(surf, x, y));
166         SkDebugf(" <- : %08x\n", get_pixel(read1, x, y));
167         SkDebugf(" -> : %08x\n", get_pixel(surf2, x, y));
168         SkDebugf(" <- : %08x\n", get_pixel(read2, x, y));
169     };
170 
171     bool success = true;
172     for (int y = 0; y < 256 && success; ++y) {
173         const uint32_t* pixels1 = (const uint32_t*) read1.addr();
174         const uint32_t* pixels2 = (const uint32_t*) read2.addr();
175         for (int x = 0; x < 256 && success; ++x) {
176             uint32_t c1 = pixels1[y * 256 + x],
177                      c2 = pixels2[y * 256 + x];
178             // If this ever fails, it's helpful to see where it goes wrong.
179             if (c1 != c2) {
180                 dump_pixel_history(x, y);
181             }
182             REPORTER_ASSERT(reporter, success = c1 == c2);
183         }
184     }
185 }
186 
DEF_TEST(PremulAlphaRoundTripSkConvertPixels,reporter)187 DEF_TEST(PremulAlphaRoundTripSkConvertPixels, reporter) {
188     // ... and now using SkConvertPixels, just for completeness
189     const SkImageInfo upmInfo =
190             SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType);
191     const SkImageInfo pmInfo =
192             SkImageInfo::Make(256, 256, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
193 
194     SkBitmap src; src.allocPixels(upmInfo);
195     uint32_t* srcPixels = src.getAddr32(0, 0);
196     for (int y = 0; y < 256; ++y) {
197         for (int x = 0; x < 256; ++x) {
198             srcPixels[y * 256 + x] = pack_unpremul_rgba(SkColorSetARGB(y, x, x, x));
199         }
200     }
201 
202     auto convert = [](const SkBitmap& dst, const SkBitmap& src){
203         SkAssertResult(SkConvertPixels(dst.info(), dst.getAddr(0, 0), dst.rowBytes(),
204                                        src.info(), src.getAddr(0, 0), src.rowBytes()));
205     };
206 
207     SkBitmap surf; surf.allocPixels(pmInfo);
208     convert(surf, src);
209 
210     SkBitmap read1; read1.allocPixels(upmInfo);
211     convert(read1, surf);
212 
213     SkBitmap surf2; surf2.allocPixels(pmInfo);
214     convert(surf2, read1);
215 
216     SkBitmap read2; read2.allocPixels(upmInfo);
217     convert(read2, surf2);
218 
219     auto dump_pixel_history = [&](int x, int y) {
220         SkDebugf("Pixel history for (%d, %d):\n", x, y);
221         SkDebugf("Src : %08x\n", *src.getAddr32(x, y));
222         SkDebugf(" -> : %08x\n", *surf.getAddr32(x, y));
223         SkDebugf(" <- : %08x\n", *read1.getAddr32(x, y));
224         SkDebugf(" -> : %08x\n", *surf2.getAddr32(x, y));
225         SkDebugf(" <- : %08x\n", *read2.getAddr32(x, y));
226     };
227 
228     bool success = true;
229     for (int y = 0; y < 256 && success; ++y) {
230         const uint32_t* pixels1 = read1.getAddr32(0, 0);
231         const uint32_t* pixels2 = read2.getAddr32(0, 0);
232         for (int x = 0; x < 256 && success; ++x) {
233             uint32_t c1 = pixels1[y * 256 + x],
234                      c2 = pixels2[y * 256 + x];
235             // If this ever fails, it's helpful to see where it goes wrong.
236             if (c1 != c2) {
237                 dump_pixel_history(x, y);
238             }
239             REPORTER_ASSERT(reporter, success = c1 == c2);
240         }
241     }
242 }
243 
244