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 "SkBitmap.h"
9 #include "SkRect.h"
10 #include "SkTemplates.h"
11 #include "Test.h"
12
boolStr(bool value)13 static const char* boolStr(bool value) {
14 return value ? "true" : "false";
15 }
16
color_type_name(SkColorType colorType)17 static const char* color_type_name(SkColorType colorType) {
18 switch (colorType) {
19 case kUnknown_SkColorType:
20 return "None";
21 case kAlpha_8_SkColorType:
22 return "A8";
23 case kRGB_565_SkColorType:
24 return "565";
25 case kARGB_4444_SkColorType:
26 return "4444";
27 case kRGBA_8888_SkColorType:
28 return "RGBA";
29 case kBGRA_8888_SkColorType:
30 return "BGRA";
31 case kIndex_8_SkColorType:
32 return "Index8";
33 case kGray_8_SkColorType:
34 return "Gray8";
35 case kRGBA_F16_SkColorType:
36 return "F16";
37 }
38 return "";
39 }
40
report_opaqueness(skiatest::Reporter * reporter,const SkBitmap & src,const SkBitmap & dst)41 static void report_opaqueness(skiatest::Reporter* reporter, const SkBitmap& src,
42 const SkBitmap& dst) {
43 ERRORF(reporter, "src %s opaque:%d, dst %s opaque:%d",
44 color_type_name(src.colorType()), src.isOpaque(),
45 color_type_name(dst.colorType()), dst.isOpaque());
46 }
47
canHaveAlpha(SkColorType ct)48 static bool canHaveAlpha(SkColorType ct) {
49 return kRGB_565_SkColorType != ct;
50 }
51
52 // copyTo() should preserve isOpaque when it makes sense
test_isOpaque(skiatest::Reporter * reporter,const SkBitmap & srcOpaque,const SkBitmap & srcPremul,SkColorType dstColorType)53 static void test_isOpaque(skiatest::Reporter* reporter,
54 const SkBitmap& srcOpaque, const SkBitmap& srcPremul,
55 SkColorType dstColorType) {
56 SkBitmap dst;
57
58 if (canHaveAlpha(srcPremul.colorType()) && canHaveAlpha(dstColorType)) {
59 REPORTER_ASSERT(reporter, srcPremul.copyTo(&dst, dstColorType));
60 REPORTER_ASSERT(reporter, dst.colorType() == dstColorType);
61 if (srcPremul.isOpaque() != dst.isOpaque()) {
62 report_opaqueness(reporter, srcPremul, dst);
63 }
64 }
65
66 REPORTER_ASSERT(reporter, srcOpaque.copyTo(&dst, dstColorType));
67 REPORTER_ASSERT(reporter, dst.colorType() == dstColorType);
68 if (srcOpaque.isOpaque() != dst.isOpaque()) {
69 report_opaqueness(reporter, srcOpaque, dst);
70 }
71 }
72
init_src(const SkBitmap & bitmap)73 static void init_src(const SkBitmap& bitmap) {
74 SkAutoLockPixels lock(bitmap);
75 if (bitmap.getPixels()) {
76 if (bitmap.getColorTable()) {
77 sk_bzero(bitmap.getPixels(), bitmap.getSize());
78 } else {
79 bitmap.eraseColor(SK_ColorWHITE);
80 }
81 }
82 }
83
init_ctable()84 static SkColorTable* init_ctable() {
85 static const SkColor colors[] = {
86 SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
87 };
88 return new SkColorTable(colors, SK_ARRAY_COUNT(colors));
89 }
90
91 struct Pair {
92 SkColorType fColorType;
93 const char* fValid;
94 };
95
96 // Utility functions for copyPixelsTo()/copyPixelsFrom() tests.
97 // getPixel()
98 // setPixel()
99 // getSkConfigName()
100 // struct Coordinates
101 // reportCopyVerification()
102 // writeCoordPixels()
103
104 // Utility function to read the value of a given pixel in bm. All
105 // values converted to uint32_t for simplification of comparisons.
getPixel(int x,int y,const SkBitmap & bm)106 static uint32_t getPixel(int x, int y, const SkBitmap& bm) {
107 uint32_t val = 0;
108 uint16_t val16;
109 uint8_t val8;
110 SkAutoLockPixels lock(bm);
111 const void* rawAddr = bm.getAddr(x,y);
112
113 switch (bm.bytesPerPixel()) {
114 case 4:
115 memcpy(&val, rawAddr, sizeof(uint32_t));
116 break;
117 case 2:
118 memcpy(&val16, rawAddr, sizeof(uint16_t));
119 val = val16;
120 break;
121 case 1:
122 memcpy(&val8, rawAddr, sizeof(uint8_t));
123 val = val8;
124 break;
125 default:
126 break;
127 }
128 return val;
129 }
130
131 // Utility function to set value of any pixel in bm.
132 // bm.getConfig() specifies what format 'val' must be
133 // converted to, but at present uint32_t can handle all formats.
setPixel(int x,int y,uint32_t val,SkBitmap & bm)134 static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) {
135 uint16_t val16;
136 uint8_t val8;
137 SkAutoLockPixels lock(bm);
138 void* rawAddr = bm.getAddr(x,y);
139
140 switch (bm.bytesPerPixel()) {
141 case 4:
142 memcpy(rawAddr, &val, sizeof(uint32_t));
143 break;
144 case 2:
145 val16 = val & 0xFFFF;
146 memcpy(rawAddr, &val16, sizeof(uint16_t));
147 break;
148 case 1:
149 val8 = val & 0xFF;
150 memcpy(rawAddr, &val8, sizeof(uint8_t));
151 break;
152 default:
153 // Ignore.
154 break;
155 }
156 }
157
158 // Helper struct to contain pixel locations, while avoiding need for STL.
159 struct Coordinates {
160
161 const int length;
162 SkIPoint* const data;
163
CoordinatesCoordinates164 explicit Coordinates(int _length): length(_length)
165 , data(new SkIPoint[length]) { }
166
~CoordinatesCoordinates167 ~Coordinates(){
168 delete [] data;
169 }
170
operator []Coordinates171 SkIPoint* operator[](int i) const {
172 // Use with care, no bounds checking.
173 return data + i;
174 }
175 };
176
177 // A function to verify that two bitmaps contain the same pixel values
178 // at all coordinates indicated by coords. Simplifies verification of
179 // copied bitmaps.
reportCopyVerification(const SkBitmap & bm1,const SkBitmap & bm2,Coordinates & coords,const char * msg,skiatest::Reporter * reporter)180 static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2,
181 Coordinates& coords,
182 const char* msg,
183 skiatest::Reporter* reporter){
184 // Confirm all pixels in the list match.
185 for (int i = 0; i < coords.length; ++i) {
186 uint32_t p1 = getPixel(coords[i]->fX, coords[i]->fY, bm1);
187 uint32_t p2 = getPixel(coords[i]->fX, coords[i]->fY, bm2);
188 // SkDebugf("[%d] (%d %d) p1=%x p2=%x\n", i, coords[i]->fX, coords[i]->fY, p1, p2);
189 if (p1 != p2) {
190 ERRORF(reporter, "%s [colortype = %s]", msg, color_type_name(bm1.colorType()));
191 break;
192 }
193 }
194 }
195
196 // Writes unique pixel values at locations specified by coords.
writeCoordPixels(SkBitmap & bm,const Coordinates & coords)197 static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) {
198 for (int i = 0; i < coords.length; ++i)
199 setPixel(coords[i]->fX, coords[i]->fY, i, bm);
200 }
201
202 static const Pair gPairs[] = {
203 { kUnknown_SkColorType, "0000000" },
204 { kAlpha_8_SkColorType, "0100000" },
205 { kIndex_8_SkColorType, "0101111" },
206 { kRGB_565_SkColorType, "0101011" },
207 { kARGB_4444_SkColorType, "0101111" },
208 { kN32_SkColorType, "0101111" },
209 { kRGBA_F16_SkColorType, "0101011" },
210 };
211
212 static const int W = 20;
213 static const int H = 33;
214
setup_src_bitmaps(SkBitmap * srcOpaque,SkBitmap * srcPremul,SkColorType ct)215 static void setup_src_bitmaps(SkBitmap* srcOpaque, SkBitmap* srcPremul,
216 SkColorType ct) {
217 SkColorTable* ctable = nullptr;
218 if (kIndex_8_SkColorType == ct) {
219 ctable = init_ctable();
220 }
221
222 sk_sp<SkColorSpace> colorSpace = nullptr;
223 if (kRGBA_F16_SkColorType == ct) {
224 colorSpace = SkColorSpace::MakeSRGBLinear();
225 }
226
227 srcOpaque->allocPixels(SkImageInfo::Make(W, H, ct, kOpaque_SkAlphaType, colorSpace),
228 nullptr, ctable);
229 srcPremul->allocPixels(SkImageInfo::Make(W, H, ct, kPremul_SkAlphaType, colorSpace),
230 nullptr, ctable);
231 SkSafeUnref(ctable);
232
233 init_src(*srcOpaque);
234 init_src(*srcPremul);
235 }
236
DEF_TEST(BitmapCopy_extractSubset,reporter)237 DEF_TEST(BitmapCopy_extractSubset, reporter) {
238 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
239 SkBitmap srcOpaque, srcPremul;
240 setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType);
241
242 SkBitmap bitmap(srcOpaque);
243 SkBitmap subset;
244 SkIRect r;
245 // Extract a subset which has the same width as the original. This
246 // catches a bug where we cloned the genID incorrectly.
247 r.set(0, 1, W, 3);
248 bitmap.setIsVolatile(true);
249 // Relies on old behavior of extractSubset failing if colortype is unknown
250 if (kUnknown_SkColorType != bitmap.colorType() && bitmap.extractSubset(&subset, r)) {
251 REPORTER_ASSERT(reporter, subset.width() == W);
252 REPORTER_ASSERT(reporter, subset.height() == 2);
253 REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType());
254 REPORTER_ASSERT(reporter, subset.isVolatile() == true);
255
256 // Test copying an extracted subset.
257 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
258 SkBitmap copy;
259 bool success = subset.copyTo(©, gPairs[j].fColorType);
260 if (!success) {
261 // Skip checking that success matches fValid, which is redundant
262 // with the code below.
263 REPORTER_ASSERT(reporter, kIndex_8_SkColorType == gPairs[i].fColorType ||
264 gPairs[i].fColorType != gPairs[j].fColorType);
265 continue;
266 }
267
268 // When performing a copy of an extracted subset, the gen id should
269 // change.
270 REPORTER_ASSERT(reporter, copy.getGenerationID() != subset.getGenerationID());
271
272 REPORTER_ASSERT(reporter, copy.width() == W);
273 REPORTER_ASSERT(reporter, copy.height() == 2);
274
275 if (gPairs[i].fColorType == gPairs[j].fColorType) {
276 SkAutoLockPixels alp0(subset);
277 SkAutoLockPixels alp1(copy);
278 // they should both have, or both not-have, a colortable
279 bool hasCT = subset.getColorTable() != nullptr;
280 REPORTER_ASSERT(reporter, (copy.getColorTable() != nullptr) == hasCT);
281 }
282 }
283 }
284
285 bitmap = srcPremul;
286 bitmap.setIsVolatile(false);
287 if (bitmap.extractSubset(&subset, r)) {
288 REPORTER_ASSERT(reporter, subset.alphaType() == bitmap.alphaType());
289 REPORTER_ASSERT(reporter, subset.isVolatile() == false);
290 }
291 }
292 }
293
DEF_TEST(BitmapCopy,reporter)294 DEF_TEST(BitmapCopy, reporter) {
295 static const bool isExtracted[] = {
296 false, true
297 };
298
299 for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
300 SkBitmap srcOpaque, srcPremul;
301 setup_src_bitmaps(&srcOpaque, &srcPremul, gPairs[i].fColorType);
302
303 for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
304 SkBitmap dst;
305
306 bool success = srcPremul.copyTo(&dst, gPairs[j].fColorType);
307 bool expected = gPairs[i].fValid[j] != '0';
308 if (success != expected) {
309 ERRORF(reporter, "SkBitmap::copyTo from %s to %s. expected %s "
310 "returned %s", color_type_name(gPairs[i].fColorType),
311 color_type_name(gPairs[j].fColorType),
312 boolStr(expected), boolStr(success));
313 }
314
315 bool canSucceed = srcPremul.canCopyTo(gPairs[j].fColorType);
316 if (success != canSucceed) {
317 ERRORF(reporter, "SkBitmap::copyTo from %s to %s. returned %s "
318 "canCopyTo %s", color_type_name(gPairs[i].fColorType),
319 color_type_name(gPairs[j].fColorType),
320 boolStr(success), boolStr(canSucceed));
321 }
322
323 if (success) {
324 REPORTER_ASSERT(reporter, srcPremul.width() == dst.width());
325 REPORTER_ASSERT(reporter, srcPremul.height() == dst.height());
326 REPORTER_ASSERT(reporter, dst.colorType() == gPairs[j].fColorType);
327 test_isOpaque(reporter, srcOpaque, srcPremul, dst.colorType());
328 if (srcPremul.colorType() == dst.colorType()) {
329 SkAutoLockPixels srcLock(srcPremul);
330 SkAutoLockPixels dstLock(dst);
331 REPORTER_ASSERT(reporter, srcPremul.readyToDraw());
332 REPORTER_ASSERT(reporter, dst.readyToDraw());
333 const char* srcP = (const char*)srcPremul.getAddr(0, 0);
334 const char* dstP = (const char*)dst.getAddr(0, 0);
335 REPORTER_ASSERT(reporter, srcP != dstP);
336 REPORTER_ASSERT(reporter, !memcmp(srcP, dstP,
337 srcPremul.getSize()));
338 REPORTER_ASSERT(reporter, srcPremul.getGenerationID() == dst.getGenerationID());
339 } else {
340 REPORTER_ASSERT(reporter, srcPremul.getGenerationID() != dst.getGenerationID());
341 }
342 } else {
343 // dst should be unchanged from its initial state
344 REPORTER_ASSERT(reporter, dst.colorType() == kUnknown_SkColorType);
345 REPORTER_ASSERT(reporter, dst.width() == 0);
346 REPORTER_ASSERT(reporter, dst.height() == 0);
347 }
348 } // for (size_t j = ...
349
350 // Tests for getSafeSize(), getSafeSize64(), copyPixelsTo(),
351 // copyPixelsFrom().
352 //
353 for (size_t copyCase = 0; copyCase < SK_ARRAY_COUNT(isExtracted);
354 ++copyCase) {
355 // Test copying to/from external buffer.
356 // Note: the tests below have hard-coded values ---
357 // Please take care if modifying.
358
359 // Tests for getSafeSize64().
360 // Test with a very large configuration without pixel buffer
361 // attached.
362 SkBitmap tstSafeSize;
363 tstSafeSize.setInfo(SkImageInfo::Make(100000000U, 100000000U,
364 gPairs[i].fColorType, kPremul_SkAlphaType));
365 int64_t safeSize = tstSafeSize.computeSafeSize64();
366 if (safeSize < 0) {
367 ERRORF(reporter, "getSafeSize64() negative: %s",
368 color_type_name(tstSafeSize.colorType()));
369 }
370 bool sizeFail = false;
371 // Compare against hand-computed values.
372 switch (gPairs[i].fColorType) {
373 case kUnknown_SkColorType:
374 break;
375
376 case kAlpha_8_SkColorType:
377 case kIndex_8_SkColorType:
378 if (safeSize != 0x2386F26FC10000LL) {
379 sizeFail = true;
380 }
381 break;
382
383 case kRGB_565_SkColorType:
384 case kARGB_4444_SkColorType:
385 if (safeSize != 0x470DE4DF820000LL) {
386 sizeFail = true;
387 }
388 break;
389
390 case kN32_SkColorType:
391 if (safeSize != 0x8E1BC9BF040000LL) {
392 sizeFail = true;
393 }
394 break;
395
396 default:
397 break;
398 }
399 if (sizeFail) {
400 ERRORF(reporter, "computeSafeSize64() wrong size: %s",
401 color_type_name(tstSafeSize.colorType()));
402 }
403
404 int subW = 2;
405 int subH = 2;
406
407 // Create bitmap to act as source for copies and subsets.
408 SkBitmap src, subset;
409 SkColorTable* ct = nullptr;
410 if (kIndex_8_SkColorType == src.colorType()) {
411 ct = init_ctable();
412 }
413
414 int localSubW;
415 if (isExtracted[copyCase]) { // A larger image to extract from.
416 localSubW = 2 * subW + 1;
417 } else { // Tests expect a 2x2 bitmap, so make smaller.
418 localSubW = subW;
419 }
420 // could fail if we pass kIndex_8 for the colortype
421 if (src.tryAllocPixels(SkImageInfo::Make(localSubW, subH, gPairs[i].fColorType,
422 kPremul_SkAlphaType))) {
423 // failure is fine, as we will notice later on
424 }
425 SkSafeUnref(ct);
426
427 // Either copy src or extract into 'subset', which is used
428 // for subsequent calls to copyPixelsTo/From.
429 bool srcReady = false;
430 // Test relies on older behavior that extractSubset will fail on
431 // kUnknown_SkColorType
432 if (kUnknown_SkColorType != src.colorType() &&
433 isExtracted[copyCase]) {
434 // The extractedSubset() test case allows us to test copy-
435 // ing when src and dst mave possibly different strides.
436 SkIRect r;
437 r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap
438
439 srcReady = src.extractSubset(&subset, r);
440 } else {
441 srcReady = src.copyTo(&subset);
442 }
443
444 // Not all configurations will generate a valid 'subset'.
445 if (srcReady) {
446
447 // Allocate our target buffer 'buf' for all copies.
448 // To simplify verifying correctness of copies attach
449 // buf to a SkBitmap, but copies are done using the
450 // raw buffer pointer.
451 const size_t bufSize = subH *
452 SkColorTypeMinRowBytes(src.colorType(), subW) * 2;
453 SkAutoTMalloc<uint8_t> autoBuf (bufSize);
454 uint8_t* buf = autoBuf.get();
455
456 SkBitmap bufBm; // Attach buf to this bitmap.
457 bool successExpected;
458
459 // Set up values for each pixel being copied.
460 Coordinates coords(subW * subH);
461 for (int x = 0; x < subW; ++x)
462 for (int y = 0; y < subH; ++y)
463 {
464 int index = y * subW + x;
465 SkASSERT(index < coords.length);
466 coords[index]->fX = x;
467 coords[index]->fY = y;
468 }
469
470 writeCoordPixels(subset, coords);
471
472 // Test #1 ////////////////////////////////////////////
473
474 const SkImageInfo info = SkImageInfo::Make(subW, subH,
475 gPairs[i].fColorType,
476 kPremul_SkAlphaType);
477 // Before/after comparisons easier if we attach buf
478 // to an appropriately configured SkBitmap.
479 memset(buf, 0xFF, bufSize);
480 // Config with stride greater than src but that fits in buf.
481 bufBm.installPixels(info, buf, info.minRowBytes() * 2);
482 successExpected = false;
483 // Then attempt to copy with a stride that is too large
484 // to fit in the buffer.
485 REPORTER_ASSERT(reporter,
486 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3)
487 == successExpected);
488
489 if (successExpected)
490 reportCopyVerification(subset, bufBm, coords,
491 "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)",
492 reporter);
493
494 // Test #2 ////////////////////////////////////////////
495 // This test should always succeed, but in the case
496 // of extracted bitmaps only because we handle the
497 // issue of getSafeSize(). Without getSafeSize()
498 // buffer overrun/read would occur.
499 memset(buf, 0xFF, bufSize);
500 bufBm.installPixels(info, buf, subset.rowBytes());
501 successExpected = subset.getSafeSize() <= bufSize;
502 REPORTER_ASSERT(reporter,
503 subset.copyPixelsTo(buf, bufSize) ==
504 successExpected);
505 if (successExpected)
506 reportCopyVerification(subset, bufBm, coords,
507 "copyPixelsTo(buf, bufSize)", reporter);
508
509 // Test #3 ////////////////////////////////////////////
510 // Copy with different stride between src and dst.
511 memset(buf, 0xFF, bufSize);
512 bufBm.installPixels(info, buf, subset.rowBytes()+1);
513 successExpected = true; // Should always work.
514 REPORTER_ASSERT(reporter,
515 subset.copyPixelsTo(buf, bufSize,
516 subset.rowBytes()+1) == successExpected);
517 if (successExpected)
518 reportCopyVerification(subset, bufBm, coords,
519 "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter);
520
521 // Test #4 ////////////////////////////////////////////
522 // Test copy with stride too small.
523 memset(buf, 0xFF, bufSize);
524 bufBm.installPixels(info, buf, info.minRowBytes());
525 successExpected = false;
526 // Request copy with stride too small.
527 REPORTER_ASSERT(reporter,
528 subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1)
529 == successExpected);
530 if (successExpected)
531 reportCopyVerification(subset, bufBm, coords,
532 "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter);
533
534 #if 0 // copyPixelsFrom is gone
535 // Test #5 ////////////////////////////////////////////
536 // Tests the case where the source stride is too small
537 // for the source configuration.
538 memset(buf, 0xFF, bufSize);
539 bufBm.installPixels(info, buf, info.minRowBytes());
540 writeCoordPixels(bufBm, coords);
541 REPORTER_ASSERT(reporter,
542 subset.copyPixelsFrom(buf, bufSize, 1) == false);
543
544 // Test #6 ///////////////////////////////////////////
545 // Tests basic copy from an external buffer to the bitmap.
546 // If the bitmap is "extracted", this also tests the case
547 // where the source stride is different from the dest.
548 // stride.
549 // We've made the buffer large enough to always succeed.
550 bufBm.installPixels(info, buf, info.minRowBytes());
551 writeCoordPixels(bufBm, coords);
552 REPORTER_ASSERT(reporter,
553 subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) ==
554 true);
555 reportCopyVerification(bufBm, subset, coords,
556 "copyPixelsFrom(buf, bufSize)",
557 reporter);
558
559 // Test #7 ////////////////////////////////////////////
560 // Tests the case where the source buffer is too small
561 // for the transfer.
562 REPORTER_ASSERT(reporter,
563 subset.copyPixelsFrom(buf, 1, subset.rowBytes()) ==
564 false);
565
566 #endif
567 }
568 } // for (size_t copyCase ...
569 }
570 }
571
572 #include "SkColorPriv.h"
573 #include "SkUtils.h"
574
575 /**
576 * Construct 4x4 pixels where we can look at a color and determine where it should be in the grid.
577 * alpha = 0xFF, blue = 0x80, red = x, green = y
578 */
fill_4x4_pixels(SkPMColor colors[16])579 static void fill_4x4_pixels(SkPMColor colors[16]) {
580 for (int y = 0; y < 4; ++y) {
581 for (int x = 0; x < 4; ++x) {
582 colors[y*4+x] = SkPackARGB32(0xFF, x, y, 0x80);
583 }
584 }
585 }
586
check_4x4_pixel(SkPMColor color,unsigned x,unsigned y)587 static bool check_4x4_pixel(SkPMColor color, unsigned x, unsigned y) {
588 SkASSERT(x < 4 && y < 4);
589 return 0xFF == SkGetPackedA32(color) &&
590 x == SkGetPackedR32(color) &&
591 y == SkGetPackedG32(color) &&
592 0x80 == SkGetPackedB32(color);
593 }
594
595 /**
596 * Fill with all zeros, which will never match any value from fill_4x4_pixels
597 */
clear_4x4_pixels(SkPMColor colors[16])598 static void clear_4x4_pixels(SkPMColor colors[16]) {
599 sk_memset32(colors, 0, 16);
600 }
601
602 // Much of readPixels is exercised by copyTo testing, since readPixels is the backend for that
603 // method. Here we explicitly test subset copies.
604 //
DEF_TEST(BitmapReadPixels,reporter)605 DEF_TEST(BitmapReadPixels, reporter) {
606 const int W = 4;
607 const int H = 4;
608 const size_t rowBytes = W * sizeof(SkPMColor);
609 const SkImageInfo srcInfo = SkImageInfo::MakeN32Premul(W, H);
610 SkPMColor srcPixels[16];
611 fill_4x4_pixels(srcPixels);
612 SkBitmap srcBM;
613 srcBM.installPixels(srcInfo, srcPixels, rowBytes);
614
615 SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(W, H);
616 SkPMColor dstPixels[16];
617
618 const struct {
619 bool fExpectedSuccess;
620 SkIPoint fRequestedSrcLoc;
621 SkISize fRequestedDstSize;
622 // If fExpectedSuccess, check these, otherwise ignore
623 SkIPoint fExpectedDstLoc;
624 SkIRect fExpectedSrcR;
625 } gRec[] = {
626 { true, { 0, 0 }, { 4, 4 }, { 0, 0 }, { 0, 0, 4, 4 } },
627 { true, { 1, 1 }, { 2, 2 }, { 0, 0 }, { 1, 1, 3, 3 } },
628 { true, { 2, 2 }, { 4, 4 }, { 0, 0 }, { 2, 2, 4, 4 } },
629 { true, {-1,-1 }, { 2, 2 }, { 1, 1 }, { 0, 0, 1, 1 } },
630 { false, {-1,-1 }, { 1, 1 }, { 0, 0 }, { 0, 0, 0, 0 } },
631 };
632
633 for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
634 clear_4x4_pixels(dstPixels);
635
636 dstInfo = dstInfo.makeWH(gRec[i].fRequestedDstSize.width(),
637 gRec[i].fRequestedDstSize.height());
638 bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes,
639 gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y());
640
641 REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success);
642 if (success) {
643 const SkIRect srcR = gRec[i].fExpectedSrcR;
644 const int dstX = gRec[i].fExpectedDstLoc.x();
645 const int dstY = gRec[i].fExpectedDstLoc.y();
646 // Walk the dst pixels, and check if we got what we expected
647 for (int y = 0; y < H; ++y) {
648 for (int x = 0; x < W; ++x) {
649 SkPMColor dstC = dstPixels[y*4+x];
650 // get into src coordinates
651 int sx = x - dstX + srcR.x();
652 int sy = y - dstY + srcR.y();
653 if (srcR.contains(sx, sy)) {
654 REPORTER_ASSERT(reporter, check_4x4_pixel(dstC, sx, sy));
655 } else {
656 REPORTER_ASSERT(reporter, 0 == dstC);
657 }
658 }
659 }
660 }
661 }
662 }
663
DEF_TEST(BitmapCopy_ColorSpaceMatch,r)664 DEF_TEST(BitmapCopy_ColorSpaceMatch, r) {
665 // We should support matching color spaces, even if they are parametric.
666 SkColorSpaceTransferFn fn;
667 fn.fA = 1.f; fn.fB = 0.f; fn.fC = 0.f; fn.fD = 0.f; fn.fE = 0.f; fn.fF = 0.f; fn.fG = 1.8f;
668 sk_sp<SkColorSpace> cs = SkColorSpace::MakeRGB(fn, SkColorSpace::kRec2020_Gamut);
669
670 SkImageInfo info = SkImageInfo::MakeN32Premul(1, 1, cs);
671 SkBitmap bitmap;
672 bitmap.allocPixels(info);
673 bitmap.eraseColor(0);
674
675 SkBitmap copy;
676 bool success = bitmap.copyTo(©, kN32_SkColorType, nullptr);
677 REPORTER_ASSERT(r, success);
678 REPORTER_ASSERT(r, cs.get() == copy.colorSpace());
679 }
680