• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
boolStr(bool value)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 
report_opaqueness(skiatest::Reporter * reporter,const SkBitmap & src,const SkBitmap & dst)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 
canHaveAlpha(SkColorType ct)28 static bool canHaveAlpha(SkColorType ct) {
29     return kRGB_565_SkColorType != ct;
30 }
31 
32 // copyTo() should preserve isOpaque when it makes sense
test_isOpaque(skiatest::Reporter * reporter,const SkBitmap & srcOpaque,const SkBitmap & srcPremul,SkColorType dstColorType)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 
init_src(const SkBitmap & bitmap)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 
init_ctable(SkAlphaType alphaType)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.
getPixel(int x,int y,const SkBitmap & bm)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.
setPixel(int x,int y,uint32_t val,SkBitmap & bm)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 
CoordinatesCoordinates144     explicit Coordinates(int _length): length(_length)
145                                      , data(new SkIPoint[length]) { }
146 
~CoordinatesCoordinates147     ~Coordinates(){
148         delete [] data;
149     }
150 
operator []Coordinates151     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.
reportCopyVerification(const SkBitmap & bm1,const SkBitmap & bm2,Coordinates & coords,const char * msg,skiatest::Reporter * reporter)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.
writeCoordPixels(SkBitmap & bm,const Coordinates & 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,     "011101"  },
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 
setup_src_bitmaps(SkBitmap * srcOpaque,SkBitmap * srcPremul,SkColorType ct)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 
DEF_TEST(BitmapCopy_extractSubset,reporter)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 
DEF_TEST(BitmapCopy,reporter)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             if (isExtracted[copyCase]) { // A larger image to extract from.
391                 src.allocPixels(SkImageInfo::Make(2 * subW + 1, subH,
392                                                   gPairs[i].fColorType,
393                                                   kPremul_SkAlphaType));
394             } else { // Tests expect a 2x2 bitmap, so make smaller.
395                 src.allocPixels(SkImageInfo::Make(subW, subH,
396                                                   gPairs[i].fColorType,
397                                                   kPremul_SkAlphaType));
398             }
399             SkSafeUnref(ct);
400 
401             // Either copy src or extract into 'subset', which is used
402             // for subsequent calls to copyPixelsTo/From.
403             bool srcReady = false;
404             // Test relies on older behavior that extractSubset will fail on
405             // kUnknown_SkColorType
406             if (kUnknown_SkColorType != src.colorType() &&
407                 isExtracted[copyCase]) {
408                 // The extractedSubset() test case allows us to test copy-
409                 // ing when src and dst mave possibly different strides.
410                 SkIRect r;
411                 r.set(1, 0, 1 + subW, subH); // 2x2 extracted bitmap
412 
413                 srcReady = src.extractSubset(&subset, r);
414             } else {
415                 srcReady = src.copyTo(&subset);
416             }
417 
418             // Not all configurations will generate a valid 'subset'.
419             if (srcReady) {
420 
421                 // Allocate our target buffer 'buf' for all copies.
422                 // To simplify verifying correctness of copies attach
423                 // buf to a SkBitmap, but copies are done using the
424                 // raw buffer pointer.
425                 const size_t bufSize = subH *
426                     SkColorTypeMinRowBytes(src.colorType(), subW) * 2;
427                 SkAutoMalloc autoBuf (bufSize);
428                 uint8_t* buf = static_cast<uint8_t*>(autoBuf.get());
429 
430                 SkBitmap bufBm; // Attach buf to this bitmap.
431                 bool successExpected;
432 
433                 // Set up values for each pixel being copied.
434                 Coordinates coords(subW * subH);
435                 for (int x = 0; x < subW; ++x)
436                     for (int y = 0; y < subH; ++y)
437                     {
438                         int index = y * subW + x;
439                         SkASSERT(index < coords.length);
440                         coords[index]->fX = x;
441                         coords[index]->fY = y;
442                     }
443 
444                 writeCoordPixels(subset, coords);
445 
446                 // Test #1 ////////////////////////////////////////////
447 
448                 const SkImageInfo info = SkImageInfo::Make(subW, subH,
449                                                            gPairs[i].fColorType,
450                                                            kPremul_SkAlphaType);
451                 // Before/after comparisons easier if we attach buf
452                 // to an appropriately configured SkBitmap.
453                 memset(buf, 0xFF, bufSize);
454                 // Config with stride greater than src but that fits in buf.
455                 bufBm.installPixels(info, buf, info.minRowBytes() * 2);
456                 successExpected = false;
457                 // Then attempt to copy with a stride that is too large
458                 // to fit in the buffer.
459                 REPORTER_ASSERT(reporter,
460                     subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes() * 3)
461                     == successExpected);
462 
463                 if (successExpected)
464                     reportCopyVerification(subset, bufBm, coords,
465                         "copyPixelsTo(buf, bufSize, 1.5*maxRowBytes)",
466                         reporter);
467 
468                 // Test #2 ////////////////////////////////////////////
469                 // This test should always succeed, but in the case
470                 // of extracted bitmaps only because we handle the
471                 // issue of getSafeSize(). Without getSafeSize()
472                 // buffer overrun/read would occur.
473                 memset(buf, 0xFF, bufSize);
474                 bufBm.installPixels(info, buf, subset.rowBytes());
475                 successExpected = subset.getSafeSize() <= bufSize;
476                 REPORTER_ASSERT(reporter,
477                     subset.copyPixelsTo(buf, bufSize) ==
478                         successExpected);
479                 if (successExpected)
480                     reportCopyVerification(subset, bufBm, coords,
481                     "copyPixelsTo(buf, bufSize)", reporter);
482 
483                 // Test #3 ////////////////////////////////////////////
484                 // Copy with different stride between src and dst.
485                 memset(buf, 0xFF, bufSize);
486                 bufBm.installPixels(info, buf, subset.rowBytes()+1);
487                 successExpected = true; // Should always work.
488                 REPORTER_ASSERT(reporter,
489                         subset.copyPixelsTo(buf, bufSize,
490                             subset.rowBytes()+1) == successExpected);
491                 if (successExpected)
492                     reportCopyVerification(subset, bufBm, coords,
493                     "copyPixelsTo(buf, bufSize, rowBytes+1)", reporter);
494 
495                 // Test #4 ////////////////////////////////////////////
496                 // Test copy with stride too small.
497                 memset(buf, 0xFF, bufSize);
498                 bufBm.installPixels(info, buf, info.minRowBytes());
499                 successExpected = false;
500                 // Request copy with stride too small.
501                 REPORTER_ASSERT(reporter,
502                     subset.copyPixelsTo(buf, bufSize, bufBm.rowBytes()-1)
503                         == successExpected);
504                 if (successExpected)
505                     reportCopyVerification(subset, bufBm, coords,
506                     "copyPixelsTo(buf, bufSize, rowBytes()-1)", reporter);
507 
508 #if 0   // copyPixelsFrom is gone
509                 // Test #5 ////////////////////////////////////////////
510                 // Tests the case where the source stride is too small
511                 // for the source configuration.
512                 memset(buf, 0xFF, bufSize);
513                 bufBm.installPixels(info, buf, info.minRowBytes());
514                 writeCoordPixels(bufBm, coords);
515                 REPORTER_ASSERT(reporter,
516                     subset.copyPixelsFrom(buf, bufSize, 1) == false);
517 
518                 // Test #6 ///////////////////////////////////////////
519                 // Tests basic copy from an external buffer to the bitmap.
520                 // If the bitmap is "extracted", this also tests the case
521                 // where the source stride is different from the dest.
522                 // stride.
523                 // We've made the buffer large enough to always succeed.
524                 bufBm.installPixels(info, buf, info.minRowBytes());
525                 writeCoordPixels(bufBm, coords);
526                 REPORTER_ASSERT(reporter,
527                     subset.copyPixelsFrom(buf, bufSize, bufBm.rowBytes()) ==
528                         true);
529                 reportCopyVerification(bufBm, subset, coords,
530                     "copyPixelsFrom(buf, bufSize)",
531                     reporter);
532 
533                 // Test #7 ////////////////////////////////////////////
534                 // Tests the case where the source buffer is too small
535                 // for the transfer.
536                 REPORTER_ASSERT(reporter,
537                     subset.copyPixelsFrom(buf, 1, subset.rowBytes()) ==
538                         false);
539 
540 #endif
541             }
542         } // for (size_t copyCase ...
543     }
544 }
545