• 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 "SkBitmapDevice.h"
9 #include "SkCanvas.h"
10 #include "SkColorPriv.h"
11 #include "SkMathPriv.h"
12 #include "SkRegion.h"
13 #include "SkSurface.h"
14 #include "Test.h"
15 
16 #if SK_SUPPORT_GPU
17 #include "GrContextFactory.h"
18 #include "SkGpuDevice.h"
19 #endif
20 
21 static const int DEV_W = 100, DEV_H = 100;
22 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
23 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
24                                                 DEV_H * SK_Scalar1);
25 
getCanvasColor(int x,int y)26 static SkPMColor getCanvasColor(int x, int y) {
27     SkASSERT(x >= 0 && x < DEV_W);
28     SkASSERT(y >= 0 && y < DEV_H);
29 
30     U8CPU r = x;
31     U8CPU g = y;
32     U8CPU b = 0xc;
33 
34     U8CPU a = 0xff;
35     switch ((x+y) % 5) {
36         case 0:
37             a = 0xff;
38             break;
39         case 1:
40             a = 0x80;
41             break;
42         case 2:
43             a = 0xCC;
44             break;
45         case 4:
46             a = 0x01;
47             break;
48         case 3:
49             a = 0x00;
50             break;
51     }
52     return SkPremultiplyARGBInline(a, r, g, b);
53 }
54 
getBitmapColor(int x,int y,int w)55 static SkPMColor getBitmapColor(int x, int y, int w) {
56     int n = y * w + x;
57 
58     U8CPU b = n & 0xff;
59     U8CPU g = (n >> 8) & 0xff;
60     U8CPU r = (n >> 16) & 0xff;
61     return SkPackARGB32(0xff, r, g , b);
62 }
63 
convertToPMColor(SkColorType ct,SkAlphaType at,const uint32_t * addr,bool * doUnpremul)64 static SkPMColor convertToPMColor(SkColorType ct, SkAlphaType at, const uint32_t* addr,
65                                   bool* doUnpremul) {
66     *doUnpremul = (kUnpremul_SkAlphaType == at);
67 
68     const uint8_t* c = reinterpret_cast<const uint8_t*>(addr);
69     U8CPU a,r,g,b;
70     switch (ct) {
71         case kBGRA_8888_SkColorType:
72             b = static_cast<U8CPU>(c[0]);
73             g = static_cast<U8CPU>(c[1]);
74             r = static_cast<U8CPU>(c[2]);
75             a = static_cast<U8CPU>(c[3]);
76             break;
77         case kRGBA_8888_SkColorType:
78             r = static_cast<U8CPU>(c[0]);
79             g = static_cast<U8CPU>(c[1]);
80             b = static_cast<U8CPU>(c[2]);
81             a = static_cast<U8CPU>(c[3]);
82             break;
83         default:
84             SkDEBUGFAIL("Unexpected colortype");
85             return 0;
86     }
87 
88     if (*doUnpremul) {
89         r = SkMulDiv255Ceiling(r, a);
90         g = SkMulDiv255Ceiling(g, a);
91         b = SkMulDiv255Ceiling(b, a);
92     }
93     return SkPackARGB32(a, r, g, b);
94 }
95 
fillCanvas(SkCanvas * canvas)96 static void fillCanvas(SkCanvas* canvas) {
97     static SkBitmap bmp;
98     if (bmp.isNull()) {
99         bmp.allocN32Pixels(DEV_W, DEV_H);
100         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
101         for (int y = 0; y < DEV_H; ++y) {
102             for (int x = 0; x < DEV_W; ++x) {
103                 SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
104                 *pixel = getCanvasColor(x, y);
105             }
106         }
107     }
108     canvas->save();
109     canvas->setMatrix(SkMatrix::I());
110     canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
111     SkPaint paint;
112     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
113     canvas->drawBitmap(bmp, 0, 0, &paint);
114     canvas->restore();
115 }
116 
fillBitmap(SkBitmap * bitmap)117 static void fillBitmap(SkBitmap* bitmap) {
118     SkASSERT(bitmap->lockPixelsAreWritable());
119     SkAutoLockPixels alp(*bitmap);
120     int w = bitmap->width();
121     int h = bitmap->height();
122     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
123     for (int y = 0; y < h; ++y) {
124         for (int x = 0; x < w; ++x) {
125             SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel());
126             *pixel = getBitmapColor(x, y, w);
127         }
128     }
129 }
130 
checkPixel(SkPMColor a,SkPMColor b,bool didPremulConversion)131 static bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
132     if (!didPremulConversion) {
133         return a == b;
134     }
135     int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
136     int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
137     int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
138     int32_t aB = SkGetPackedB32(a);
139 
140     int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
141     int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
142     int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
143     int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
144 
145     return aA == bA &&
146            SkAbs32(aR - bR) <= 1 &&
147            SkAbs32(aG - bG) <= 1 &&
148            SkAbs32(aB - bB) <= 1;
149 }
150 
151 // checks the bitmap contains correct pixels after the readPixels
152 // if the bitmap was prefilled with pixels it checks that these weren't
153 // overwritten in the area outside the readPixels.
checkRead(skiatest::Reporter * reporter,const SkBitmap & bitmap,int x,int y,bool checkCanvasPixels,bool checkBitmapPixels)154 static bool checkRead(skiatest::Reporter* reporter,
155                       const SkBitmap& bitmap,
156                       int x, int y,
157                       bool checkCanvasPixels,
158                       bool checkBitmapPixels) {
159     SkASSERT(4 == bitmap.bytesPerPixel());
160     SkASSERT(!bitmap.isNull());
161     SkASSERT(checkCanvasPixels || checkBitmapPixels);
162 
163     const SkColorType ct = bitmap.colorType();
164     const SkAlphaType at = bitmap.alphaType();
165 
166     int bw = bitmap.width();
167     int bh = bitmap.height();
168 
169     SkIRect srcRect = SkIRect::MakeXYWH(x, y, bw, bh);
170     SkIRect clippedSrcRect = DEV_RECT;
171     if (!clippedSrcRect.intersect(srcRect)) {
172         clippedSrcRect.setEmpty();
173     }
174     SkAutoLockPixels alp(bitmap);
175     for (int by = 0; by < bh; ++by) {
176         for (int bx = 0; bx < bw; ++bx) {
177             int devx = bx + srcRect.fLeft;
178             int devy = by + srcRect.fTop;
179 
180             const uint32_t* pixel = bitmap.getAddr32(bx, by);
181 
182             if (clippedSrcRect.contains(devx, devy)) {
183                 if (checkCanvasPixels) {
184                     SkPMColor canvasPixel = getCanvasColor(devx, devy);
185                     bool didPremul;
186                     SkPMColor pmPixel = convertToPMColor(ct, at, pixel, &didPremul);
187                     bool check;
188                     REPORTER_ASSERT(reporter, check = checkPixel(pmPixel, canvasPixel, didPremul));
189                     if (!check) {
190                         return false;
191                     }
192                 }
193             } else if (checkBitmapPixels) {
194                 REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw) == *pixel);
195                 if (getBitmapColor(bx, by, bw) != *pixel) {
196                     return false;
197                 }
198             }
199         }
200     }
201     return true;
202 }
203 
204 enum BitmapInit {
205     kFirstBitmapInit = 0,
206 
207     kNoPixels_BitmapInit = kFirstBitmapInit,
208     kTight_BitmapInit,
209     kRowBytes_BitmapInit,
210 
211     kBitmapInitCnt
212 };
213 
nextBMI(BitmapInit bmi)214 static BitmapInit nextBMI(BitmapInit bmi) {
215     int x = bmi;
216     return static_cast<BitmapInit>(++x);
217 }
218 
init_bitmap(SkBitmap * bitmap,const SkIRect & rect,BitmapInit init,SkColorType ct,SkAlphaType at)219 static void init_bitmap(SkBitmap* bitmap, const SkIRect& rect, BitmapInit init, SkColorType ct,
220                         SkAlphaType at) {
221     SkImageInfo info = SkImageInfo::Make(rect.width(), rect.height(), ct, at);
222     size_t rowBytes = 0;
223     bool alloc = true;
224     switch (init) {
225         case kNoPixels_BitmapInit:
226             alloc = false;
227         case kTight_BitmapInit:
228             break;
229         case kRowBytes_BitmapInit:
230             rowBytes = (info.width() + 16) * sizeof(SkPMColor);
231             break;
232         default:
233             SkASSERT(0);
234             break;
235     }
236 
237     if (alloc) {
238         bitmap->allocPixels(info);
239     } else {
240         bitmap->setInfo(info, rowBytes);
241     }
242 }
243 
DEF_GPUTEST(ReadPixels,reporter,factory)244 DEF_GPUTEST(ReadPixels, reporter, factory) {
245     const SkIRect testRects[] = {
246         // entire thing
247         DEV_RECT,
248         // larger on all sides
249         SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
250         // fully contained
251         SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
252         // outside top left
253         SkIRect::MakeLTRB(-10, -10, -1, -1),
254         // touching top left corner
255         SkIRect::MakeLTRB(-10, -10, 0, 0),
256         // overlapping top left corner
257         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
258         // overlapping top left and top right corners
259         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
260         // touching entire top edge
261         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
262         // overlapping top right corner
263         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
264         // contained in x, overlapping top edge
265         SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
266         // outside top right corner
267         SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
268         // touching top right corner
269         SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
270         // overlapping top left and bottom left corners
271         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
272         // touching entire left edge
273         SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
274         // overlapping bottom left corner
275         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
276         // contained in y, overlapping left edge
277         SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
278         // outside bottom left corner
279         SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
280         // touching bottom left corner
281         SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
282         // overlapping bottom left and bottom right corners
283         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
284         // touching entire left edge
285         SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
286         // overlapping bottom right corner
287         SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
288         // overlapping top right and bottom right corners
289         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
290     };
291 
292     for (int dtype = 0; dtype < 3; ++dtype) {
293         int glCtxTypeCnt = 1;
294 #if SK_SUPPORT_GPU
295         if (0 != dtype)  {
296             glCtxTypeCnt = GrContextFactory::kGLContextTypeCnt;
297         }
298 #endif
299         const SkImageInfo info = SkImageInfo::MakeN32Premul(DEV_W, DEV_H);
300         for (int glCtxType = 0; glCtxType < glCtxTypeCnt; ++glCtxType) {
301             SkAutoTUnref<SkSurface> surface;
302             if (0 == dtype) {
303                 surface.reset(SkSurface::NewRaster(info));
304             } else {
305 #if SK_SUPPORT_GPU
306                 GrContextFactory::GLContextType type =
307                     static_cast<GrContextFactory::GLContextType>(glCtxType);
308                 if (!GrContextFactory::IsRenderingGLContext(type)) {
309                     continue;
310                 }
311                 GrContext* context = factory->get(type);
312                 if (NULL == context) {
313                     continue;
314                 }
315                 GrTextureDesc desc;
316                 desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
317                 desc.fWidth = DEV_W;
318                 desc.fHeight = DEV_H;
319                 desc.fConfig = kSkia8888_GrPixelConfig;
320                 desc.fOrigin = 1 == dtype ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
321                 GrAutoScratchTexture ast(context, desc, GrContext::kExact_ScratchTexMatch);
322                 SkAutoTUnref<GrTexture> tex(ast.detach());
323                 surface.reset(SkSurface::NewRenderTargetDirect(tex->asRenderTarget()));
324 #else
325                 continue;
326 #endif
327             }
328             SkCanvas& canvas = *surface->getCanvas();
329             fillCanvas(&canvas);
330 
331             static const struct {
332                 SkColorType fColorType;
333                 SkAlphaType fAlphaType;
334             } gReadConfigs[] = {
335                 { kRGBA_8888_SkColorType,   kPremul_SkAlphaType },
336                 { kRGBA_8888_SkColorType,   kUnpremul_SkAlphaType },
337                 { kBGRA_8888_SkColorType,   kPremul_SkAlphaType },
338                 { kBGRA_8888_SkColorType,   kUnpremul_SkAlphaType },
339             };
340             for (size_t rect = 0; rect < SK_ARRAY_COUNT(testRects); ++rect) {
341                 const SkIRect& srcRect = testRects[rect];
342                 for (BitmapInit bmi = kFirstBitmapInit; bmi < kBitmapInitCnt; bmi = nextBMI(bmi)) {
343                     for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) {
344                         SkBitmap bmp;
345                         init_bitmap(&bmp, srcRect, bmi,
346                                     gReadConfigs[c].fColorType, gReadConfigs[c].fAlphaType);
347 
348                         // if the bitmap has pixels allocated before the readPixels,
349                         // note that and fill them with pattern
350                         bool startsWithPixels = !bmp.isNull();
351                         if (startsWithPixels) {
352                             fillBitmap(&bmp);
353                         }
354                         uint32_t idBefore = surface->generationID();
355                         bool success = canvas.readPixels(&bmp, srcRect.fLeft, srcRect.fTop);
356                         uint32_t idAfter = surface->generationID();
357 
358                         // we expect to succeed when the read isn't fully clipped
359                         // out.
360                         bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT);
361                         // determine whether we expected the read to succeed.
362                         REPORTER_ASSERT(reporter, success == expectSuccess);
363                         // read pixels should never change the gen id
364                         REPORTER_ASSERT(reporter, idBefore == idAfter);
365 
366                         if (success || startsWithPixels) {
367                             checkRead(reporter, bmp, srcRect.fLeft, srcRect.fTop,
368                                       success, startsWithPixels);
369                         } else {
370                             // if we had no pixels beforehand and the readPixels
371                             // failed then our bitmap should still not have pixels
372                             REPORTER_ASSERT(reporter, bmp.isNull());
373                         }
374                     }
375                     // check the old webkit version of readPixels that clips the
376                     // bitmap size
377                     SkBitmap wkbmp;
378                     bool success = canvas.readPixels(srcRect, &wkbmp);
379                     SkIRect clippedRect = DEV_RECT;
380                     if (clippedRect.intersect(srcRect)) {
381                         REPORTER_ASSERT(reporter, success);
382                         REPORTER_ASSERT(reporter, kN32_SkColorType == wkbmp.colorType());
383                         REPORTER_ASSERT(reporter, kPremul_SkAlphaType == wkbmp.alphaType());
384                         checkRead(reporter, wkbmp, clippedRect.fLeft,
385                                   clippedRect.fTop, true, false);
386                     } else {
387                         REPORTER_ASSERT(reporter, !success);
388                     }
389                 }
390             }
391         }
392     }
393 }
394