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