xref: /aosp_15_r20/external/skia/tests/BitmapCopyTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2011 Google Inc.
3*c8dee2aaSAndroid Build Coastguard Worker  *
4*c8dee2aaSAndroid Build Coastguard Worker  * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker  * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker  */
7*c8dee2aaSAndroid Build Coastguard Worker 
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColor.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorPriv.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorSpace.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkColorType.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkImageInfo.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPoint.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRect.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRefCnt.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkMemset.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "tests/Test.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "tools/ToolUtils.h"
23*c8dee2aaSAndroid Build Coastguard Worker 
24*c8dee2aaSAndroid Build Coastguard Worker #include <array>
25*c8dee2aaSAndroid Build Coastguard Worker #include <cstddef>
26*c8dee2aaSAndroid Build Coastguard Worker 
init_src(const SkBitmap & bitmap)27*c8dee2aaSAndroid Build Coastguard Worker static void init_src(const SkBitmap& bitmap) {
28*c8dee2aaSAndroid Build Coastguard Worker     if (bitmap.getPixels()) {
29*c8dee2aaSAndroid Build Coastguard Worker         bitmap.eraseColor(SK_ColorWHITE);
30*c8dee2aaSAndroid Build Coastguard Worker     }
31*c8dee2aaSAndroid Build Coastguard Worker }
32*c8dee2aaSAndroid Build Coastguard Worker 
33*c8dee2aaSAndroid Build Coastguard Worker struct Pair {
34*c8dee2aaSAndroid Build Coastguard Worker     SkColorType fColorType;
35*c8dee2aaSAndroid Build Coastguard Worker     const char* fValid;
36*c8dee2aaSAndroid Build Coastguard Worker };
37*c8dee2aaSAndroid Build Coastguard Worker 
38*c8dee2aaSAndroid Build Coastguard Worker // Utility functions for copyPixelsTo()/copyPixelsFrom() tests.
39*c8dee2aaSAndroid Build Coastguard Worker // getPixel()
40*c8dee2aaSAndroid Build Coastguard Worker // setPixel()
41*c8dee2aaSAndroid Build Coastguard Worker // getSkConfigName()
42*c8dee2aaSAndroid Build Coastguard Worker // struct Coordinates
43*c8dee2aaSAndroid Build Coastguard Worker // reportCopyVerification()
44*c8dee2aaSAndroid Build Coastguard Worker // writeCoordPixels()
45*c8dee2aaSAndroid Build Coastguard Worker 
46*c8dee2aaSAndroid Build Coastguard Worker // Helper struct to contain pixel locations, while avoiding need for STL.
47*c8dee2aaSAndroid Build Coastguard Worker struct Coordinates {
48*c8dee2aaSAndroid Build Coastguard Worker 
49*c8dee2aaSAndroid Build Coastguard Worker     const int length;
50*c8dee2aaSAndroid Build Coastguard Worker     SkIPoint* const data;
51*c8dee2aaSAndroid Build Coastguard Worker 
CoordinatesCoordinates52*c8dee2aaSAndroid Build Coastguard Worker     explicit Coordinates(int _length): length(_length)
53*c8dee2aaSAndroid Build Coastguard Worker                                      , data(new SkIPoint[length]) { }
54*c8dee2aaSAndroid Build Coastguard Worker 
~CoordinatesCoordinates55*c8dee2aaSAndroid Build Coastguard Worker     ~Coordinates(){
56*c8dee2aaSAndroid Build Coastguard Worker         delete [] data;
57*c8dee2aaSAndroid Build Coastguard Worker     }
58*c8dee2aaSAndroid Build Coastguard Worker 
operator []Coordinates59*c8dee2aaSAndroid Build Coastguard Worker     SkIPoint* operator[](int i) const {
60*c8dee2aaSAndroid Build Coastguard Worker         // Use with care, no bounds checking.
61*c8dee2aaSAndroid Build Coastguard Worker         return data + i;
62*c8dee2aaSAndroid Build Coastguard Worker     }
63*c8dee2aaSAndroid Build Coastguard Worker };
64*c8dee2aaSAndroid Build Coastguard Worker 
65*c8dee2aaSAndroid Build Coastguard Worker static const Pair gPairs[] = {
66*c8dee2aaSAndroid Build Coastguard Worker     { kUnknown_SkColorType,     "0000000"  },
67*c8dee2aaSAndroid Build Coastguard Worker     { kAlpha_8_SkColorType,     "0100000"  },
68*c8dee2aaSAndroid Build Coastguard Worker     { kRGB_565_SkColorType,     "0101011"  },
69*c8dee2aaSAndroid Build Coastguard Worker     { kARGB_4444_SkColorType,   "0101111"  },
70*c8dee2aaSAndroid Build Coastguard Worker     { kN32_SkColorType,         "0101111"  },
71*c8dee2aaSAndroid Build Coastguard Worker     { kRGBA_F16_SkColorType,    "0101011"  },
72*c8dee2aaSAndroid Build Coastguard Worker };
73*c8dee2aaSAndroid Build Coastguard Worker 
setup_src_bitmaps(SkBitmap * srcOpaque,SkBitmap * srcPremul,SkColorType ct)74*c8dee2aaSAndroid Build Coastguard Worker static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul,
75*c8dee2aaSAndroid Build Coastguard Worker                               SkColorType ct) {
76*c8dee2aaSAndroid Build Coastguard Worker     const int W = 20;
77*c8dee2aaSAndroid Build Coastguard Worker     const int H = 33;
78*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkColorSpace> colorSpace = nullptr;
79*c8dee2aaSAndroid Build Coastguard Worker     if (kRGBA_F16_SkColorType == ct) {
80*c8dee2aaSAndroid Build Coastguard Worker         colorSpace = SkColorSpace::MakeSRGB();
81*c8dee2aaSAndroid Build Coastguard Worker     }
82*c8dee2aaSAndroid Build Coastguard Worker 
83*c8dee2aaSAndroid Build Coastguard Worker     srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace));
84*c8dee2aaSAndroid Build Coastguard Worker     srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace));
85*c8dee2aaSAndroid Build Coastguard Worker     init_src(*srcOpaque);
86*c8dee2aaSAndroid Build Coastguard Worker     init_src(*srcPremul);
87*c8dee2aaSAndroid Build Coastguard Worker }
88*c8dee2aaSAndroid Build Coastguard Worker 
DEF_TEST(BitmapCopy_extractSubset,reporter)89*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(BitmapCopy_extractSubset, reporter) {
90*c8dee2aaSAndroid Build Coastguard Worker     const int W = 20;
91*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < std::size(gPairs); i++) {
92*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap srcOpaque, srcPremul;
93*c8dee2aaSAndroid Build Coastguard Worker         setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType);
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap bitmap(srcOpaque);
96*c8dee2aaSAndroid Build Coastguard Worker         SkBitmap subset;
97*c8dee2aaSAndroid Build Coastguard Worker         SkIRect r;
98*c8dee2aaSAndroid Build Coastguard Worker         // Extract a subset which has the same width as the original. This
99*c8dee2aaSAndroid Build Coastguard Worker         // catches a bug where we cloned the genID incorrectly.
100*c8dee2aaSAndroid Build Coastguard Worker         r.setLTRB(0, 1, W, 3);
101*c8dee2aaSAndroid Build Coastguard Worker         // Relies on old behavior of extractSubset failing if colortype is unknown
102*c8dee2aaSAndroid Build Coastguard Worker         if (kUnknown_SkColorType != bitmap.colorType() && bitmap.extractSubset(&subset, r)) {
103*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, subset.width() == W);
104*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, subset.height() == 2);
105*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType());
106*c8dee2aaSAndroid Build Coastguard Worker 
107*c8dee2aaSAndroid Build Coastguard Worker             // Test copying an extracted subset.
108*c8dee2aaSAndroid Build Coastguard Worker             for (size_t j = 0; j < std::size(gPairs); j++) {
109*c8dee2aaSAndroid Build Coastguard Worker                 SkBitmap copy;
110*c8dee2aaSAndroid Build Coastguard Worker                 bool     success = ToolUtils::copy_to(&copy, gPairs[j].fColorType, subset);
111*c8dee2aaSAndroid Build Coastguard Worker                 if (!success) {
112*c8dee2aaSAndroid Build Coastguard Worker                     // Skip checking that success matches fValid, which is redundant
113*c8dee2aaSAndroid Build Coastguard Worker                     // with the code below.
114*c8dee2aaSAndroid Build Coastguard Worker                     REPORTER_ASSERT(reporter, gPairs[i].fColorType != gPairs[j].fColorType);
115*c8dee2aaSAndroid Build Coastguard Worker                     continue;
116*c8dee2aaSAndroid Build Coastguard Worker                 }
117*c8dee2aaSAndroid Build Coastguard Worker 
118*c8dee2aaSAndroid Build Coastguard Worker                 // When performing a copy of an extracted subset, the gen id should
119*c8dee2aaSAndroid Build Coastguard Worker                 // change.
120*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, copy.getGenerationID() != subset.getGenerationID());
121*c8dee2aaSAndroid Build Coastguard Worker 
122*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, copy.width() == W);
123*c8dee2aaSAndroid Build Coastguard Worker                 REPORTER_ASSERT(reporter, copy.height() == 2);
124*c8dee2aaSAndroid Build Coastguard Worker             }
125*c8dee2aaSAndroid Build Coastguard Worker         }
126*c8dee2aaSAndroid Build Coastguard Worker 
127*c8dee2aaSAndroid Build Coastguard Worker         bitmap = srcPremul;
128*c8dee2aaSAndroid Build Coastguard Worker         if (bitmap.extractSubset(&subset, r)) {
129*c8dee2aaSAndroid Build Coastguard Worker             REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType());
130*c8dee2aaSAndroid Build Coastguard Worker         }
131*c8dee2aaSAndroid Build Coastguard Worker     }
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker 
134*c8dee2aaSAndroid Build Coastguard Worker /**
135*c8dee2aaSAndroid Build Coastguard Worker  *  Construct 4x4 pixels where we can look at a color and determine where it should be in the grid.
136*c8dee2aaSAndroid Build Coastguard Worker  *  alpha = 0xFF, blue = 0x80, red = x, green = y
137*c8dee2aaSAndroid Build Coastguard Worker  */
fill_4x4_pixels(SkPMColor colors[16])138*c8dee2aaSAndroid Build Coastguard Worker static void fill_4x4_pixels(SkPMColor colors[16]) {
139*c8dee2aaSAndroid Build Coastguard Worker     for (int y = 0; y < 4; ++y) {
140*c8dee2aaSAndroid Build Coastguard Worker         for (int x = 0; x < 4; ++x) {
141*c8dee2aaSAndroid Build Coastguard Worker             colors[y*4+x] = SkPackARGB32(0xFF, x, y, 0x80);
142*c8dee2aaSAndroid Build Coastguard Worker         }
143*c8dee2aaSAndroid Build Coastguard Worker     }
144*c8dee2aaSAndroid Build Coastguard Worker }
145*c8dee2aaSAndroid Build Coastguard Worker 
check_4x4_pixel(SkPMColor color,unsigned x,unsigned y)146*c8dee2aaSAndroid Build Coastguard Worker static bool check_4x4_pixel(SkPMColor color, unsigned x, unsigned y) {
147*c8dee2aaSAndroid Build Coastguard Worker     SkASSERT(x < 4 && y < 4);
148*c8dee2aaSAndroid Build Coastguard Worker     return  0xFF == SkGetPackedA32(color) &&
149*c8dee2aaSAndroid Build Coastguard Worker             x    == SkGetPackedR32(color) &&
150*c8dee2aaSAndroid Build Coastguard Worker             y    == SkGetPackedG32(color) &&
151*c8dee2aaSAndroid Build Coastguard Worker             0x80 == SkGetPackedB32(color);
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker /**
155*c8dee2aaSAndroid Build Coastguard Worker  *  Fill with all zeros, which will never match any value from fill_4x4_pixels
156*c8dee2aaSAndroid Build Coastguard Worker  */
clear_4x4_pixels(SkPMColor colors[16])157*c8dee2aaSAndroid Build Coastguard Worker static void clear_4x4_pixels(SkPMColor colors[16]) {
158*c8dee2aaSAndroid Build Coastguard Worker     SkOpts::memset32(colors, 0, 16);
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker 
161*c8dee2aaSAndroid Build Coastguard Worker // Much of readPixels is exercised by copyTo testing, since readPixels is the backend for that
162*c8dee2aaSAndroid Build Coastguard Worker // method. Here we explicitly test subset copies.
163*c8dee2aaSAndroid Build Coastguard Worker //
DEF_TEST(BitmapReadPixels,reporter)164*c8dee2aaSAndroid Build Coastguard Worker DEF_TEST(BitmapReadPixels, reporter) {
165*c8dee2aaSAndroid Build Coastguard Worker     const int W = 4;
166*c8dee2aaSAndroid Build Coastguard Worker     const int H = 4;
167*c8dee2aaSAndroid Build Coastguard Worker     const size_t rowBytes = W * sizeof(SkPMColor);
168*c8dee2aaSAndroid Build Coastguard Worker     const SkImageInfo srcInfo = SkImageInfo::MakeN32Premul(W, H);
169*c8dee2aaSAndroid Build Coastguard Worker     SkPMColor srcPixels[16];
170*c8dee2aaSAndroid Build Coastguard Worker     fill_4x4_pixels(srcPixels);
171*c8dee2aaSAndroid Build Coastguard Worker     SkBitmap srcBM;
172*c8dee2aaSAndroid Build Coastguard Worker     srcBM.installPixels(srcInfo, srcPixels, rowBytes);
173*c8dee2aaSAndroid Build Coastguard Worker 
174*c8dee2aaSAndroid Build Coastguard Worker     SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(W, H);
175*c8dee2aaSAndroid Build Coastguard Worker     SkPMColor dstPixels[16];
176*c8dee2aaSAndroid Build Coastguard Worker 
177*c8dee2aaSAndroid Build Coastguard Worker     const struct {
178*c8dee2aaSAndroid Build Coastguard Worker         bool     fExpectedSuccess;
179*c8dee2aaSAndroid Build Coastguard Worker         SkIPoint fRequestedSrcLoc;
180*c8dee2aaSAndroid Build Coastguard Worker         SkISize  fRequestedDstSize;
181*c8dee2aaSAndroid Build Coastguard Worker         // If fExpectedSuccess, check these, otherwise ignore
182*c8dee2aaSAndroid Build Coastguard Worker         SkIPoint fExpectedDstLoc;
183*c8dee2aaSAndroid Build Coastguard Worker         SkIRect  fExpectedSrcR;
184*c8dee2aaSAndroid Build Coastguard Worker     } gRec[] = {
185*c8dee2aaSAndroid Build Coastguard Worker         { true,  { 0, 0 }, { 4, 4 }, { 0, 0 }, { 0, 0, 4, 4 } },
186*c8dee2aaSAndroid Build Coastguard Worker         { true,  { 1, 1 }, { 2, 2 }, { 0, 0 }, { 1, 1, 3, 3 } },
187*c8dee2aaSAndroid Build Coastguard Worker         { true,  { 2, 2 }, { 4, 4 }, { 0, 0 }, { 2, 2, 4, 4 } },
188*c8dee2aaSAndroid Build Coastguard Worker         { true,  {-1,-1 }, { 2, 2 }, { 1, 1 }, { 0, 0, 1, 1 } },
189*c8dee2aaSAndroid Build Coastguard Worker         { false, {-1,-1 }, { 1, 1 }, { 0, 0 }, { 0, 0, 0, 0 } },
190*c8dee2aaSAndroid Build Coastguard Worker     };
191*c8dee2aaSAndroid Build Coastguard Worker 
192*c8dee2aaSAndroid Build Coastguard Worker     for (size_t i = 0; i < std::size(gRec); ++i) {
193*c8dee2aaSAndroid Build Coastguard Worker         clear_4x4_pixels(dstPixels);
194*c8dee2aaSAndroid Build Coastguard Worker 
195*c8dee2aaSAndroid Build Coastguard Worker         dstInfo = dstInfo.makeDimensions(gRec[i].fRequestedDstSize);
196*c8dee2aaSAndroid Build Coastguard Worker         bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes,
197*c8dee2aaSAndroid Build Coastguard Worker                                         gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y());
198*c8dee2aaSAndroid Build Coastguard Worker 
199*c8dee2aaSAndroid Build Coastguard Worker         REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success);
200*c8dee2aaSAndroid Build Coastguard Worker         if (success) {
201*c8dee2aaSAndroid Build Coastguard Worker             const SkIRect srcR = gRec[i].fExpectedSrcR;
202*c8dee2aaSAndroid Build Coastguard Worker             const int dstX = gRec[i].fExpectedDstLoc.x();
203*c8dee2aaSAndroid Build Coastguard Worker             const int dstY = gRec[i].fExpectedDstLoc.y();
204*c8dee2aaSAndroid Build Coastguard Worker             // Walk the dst pixels, and check if we got what we expected
205*c8dee2aaSAndroid Build Coastguard Worker             for (int y = 0; y < H; ++y) {
206*c8dee2aaSAndroid Build Coastguard Worker                 for (int x = 0; x < W; ++x) {
207*c8dee2aaSAndroid Build Coastguard Worker                     SkPMColor dstC = dstPixels[y*4+x];
208*c8dee2aaSAndroid Build Coastguard Worker                     // get into src coordinates
209*c8dee2aaSAndroid Build Coastguard Worker                     int sx = x - dstX + srcR.x();
210*c8dee2aaSAndroid Build Coastguard Worker                     int sy = y - dstY + srcR.y();
211*c8dee2aaSAndroid Build Coastguard Worker                     if (srcR.contains(sx, sy)) {
212*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(reporter, check_4x4_pixel(dstC, sx, sy));
213*c8dee2aaSAndroid Build Coastguard Worker                     } else {
214*c8dee2aaSAndroid Build Coastguard Worker                         REPORTER_ASSERT(reporter, 0 == dstC);
215*c8dee2aaSAndroid Build Coastguard Worker                     }
216*c8dee2aaSAndroid Build Coastguard Worker                 }
217*c8dee2aaSAndroid Build Coastguard Worker             }
218*c8dee2aaSAndroid Build Coastguard Worker         }
219*c8dee2aaSAndroid Build Coastguard Worker     }
220*c8dee2aaSAndroid Build Coastguard Worker }
221