• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2011 Google Inc.
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 #include "Test.h"
10 #include "SkCanvas.h"
11 #include "SkRegion.h"
12 #include "SkGpuDevice.h"
13 
14 static const int DEV_W = 100, DEV_H = 100;
15 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
16 static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
17                                                 DEV_H * SK_Scalar1);
18 static const U8CPU DEV_PAD = 0xee;
19 
20 namespace {
getCanvasColor(int x,int y)21 SkPMColor getCanvasColor(int x, int y) {
22     SkASSERT(x >= 0 && x < DEV_W);
23     SkASSERT(y >= 0 && y < DEV_H);
24 
25     U8CPU r = x;
26     U8CPU g = y;
27     U8CPU b = 0xc;
28 
29     U8CPU a = 0x0;
30     switch ((x+y) % 5) {
31         case 0:
32             a = 0xff;
33             break;
34         case 1:
35             a = 0x80;
36             break;
37         case 2:
38             a = 0xCC;
39             break;
40         case 3:
41             a = 0x00;
42             break;
43         case 4:
44             a = 0x01;
45             break;
46     }
47     return SkPremultiplyARGBInline(a, r, g, b);
48 }
49 
config8888IsPremul(SkCanvas::Config8888 config8888)50 bool config8888IsPremul(SkCanvas::Config8888 config8888) {
51     switch (config8888) {
52         case SkCanvas::kNative_Premul_Config8888:
53         case SkCanvas::kBGRA_Premul_Config8888:
54         case SkCanvas::kRGBA_Premul_Config8888:
55             return true;
56         case SkCanvas::kNative_Unpremul_Config8888:
57         case SkCanvas::kBGRA_Unpremul_Config8888:
58         case SkCanvas::kRGBA_Unpremul_Config8888:
59             return false;
60         default:
61             SkASSERT(0);
62             return false;
63     }
64 }
65 
66 // assumes any premu/.unpremul has been applied
packConfig8888(SkCanvas::Config8888 config8888,U8CPU a,U8CPU r,U8CPU g,U8CPU b)67 uint32_t packConfig8888(SkCanvas::Config8888 config8888,
68                         U8CPU a, U8CPU r, U8CPU g, U8CPU b) {
69     uint32_t r32;
70     uint8_t* result = reinterpret_cast<uint8_t*>(&r32);
71     switch (config8888) {
72         case SkCanvas::kNative_Premul_Config8888:
73         case SkCanvas::kNative_Unpremul_Config8888:
74             r32 = SkPackARGB32NoCheck(a, r, g, b);
75             break;
76         case SkCanvas::kBGRA_Premul_Config8888:
77         case SkCanvas::kBGRA_Unpremul_Config8888:
78             result[0] = b;
79             result[1] = g;
80             result[2] = r;
81             result[3] = a;
82             break;
83         case SkCanvas::kRGBA_Premul_Config8888:
84         case SkCanvas::kRGBA_Unpremul_Config8888:
85             result[0] = r;
86             result[1] = g;
87             result[2] = b;
88             result[3] = a;
89             break;
90         default:
91             SkASSERT(0);
92             return 0;
93     }
94     return r32;
95 }
96 
getBitmapColor(int x,int y,int w,int h,SkCanvas::Config8888 config8888)97 uint32_t getBitmapColor(int x, int y, int w, int h, SkCanvas::Config8888 config8888) {
98     int n = y * w + x;
99     U8CPU b = n & 0xff;
100     U8CPU g = (n >> 8) & 0xff;
101     U8CPU r = (n >> 16) & 0xff;
102     U8CPU a = 0;
103     switch ((x+y) % 5) {
104         case 4:
105             a = 0xff;
106             break;
107         case 3:
108             a = 0x80;
109             break;
110         case 2:
111             a = 0xCC;
112             break;
113         case 1:
114             a = 0x01;
115             break;
116         case 0:
117             a = 0x00;
118             break;
119     }
120     if (config8888IsPremul(config8888)) {
121         r = SkMulDiv255Ceiling(r, a);
122         g = SkMulDiv255Ceiling(g, a);
123         b = SkMulDiv255Ceiling(b, a);
124     }
125     return packConfig8888(config8888, a, r, g , b);
126 }
127 
fillCanvas(SkCanvas * canvas)128 void fillCanvas(SkCanvas* canvas) {
129     static SkBitmap bmp;
130     if (bmp.isNull()) {
131         bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
132         bool alloc = bmp.allocPixels();
133         SkASSERT(alloc);
134         SkAutoLockPixels alp(bmp);
135         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
136         for (int y = 0; y < DEV_H; ++y) {
137             for (int x = 0; x < DEV_W; ++x) {
138                 SkPMColor* pixel = reinterpret_cast<SkPMColor*>(pixels + y * bmp.rowBytes() + x * bmp.bytesPerPixel());
139                 *pixel = getCanvasColor(x, y);
140             }
141         }
142     }
143     canvas->save();
144     canvas->setMatrix(SkMatrix::I());
145     canvas->clipRect(DEV_RECT_S, SkRegion::kReplace_Op);
146     SkPaint paint;
147     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
148     canvas->drawBitmap(bmp, 0, 0, &paint);
149     canvas->restore();
150 }
151 
convertConfig8888ToPMColor(SkCanvas::Config8888 config8888,uint32_t color,bool * premul)152 SkPMColor convertConfig8888ToPMColor(SkCanvas::Config8888 config8888,
153                                      uint32_t color,
154                                      bool* premul) {
155     const uint8_t* c = reinterpret_cast<uint8_t*>(&color);
156     U8CPU a,r,g,b;
157     *premul = false;
158     switch (config8888) {
159         case SkCanvas::kNative_Premul_Config8888:
160             return color;
161         case SkCanvas::kNative_Unpremul_Config8888:
162             *premul = true;
163             a = SkGetPackedA32(color);
164             r = SkGetPackedR32(color);
165             g = SkGetPackedG32(color);
166             b = SkGetPackedB32(color);
167             break;
168         case SkCanvas::kBGRA_Unpremul_Config8888:
169             *premul = true; // fallthru
170         case SkCanvas::kBGRA_Premul_Config8888:
171             a = static_cast<U8CPU>(c[3]);
172             r = static_cast<U8CPU>(c[2]);
173             g = static_cast<U8CPU>(c[1]);
174             b = static_cast<U8CPU>(c[0]);
175             break;
176         case SkCanvas::kRGBA_Unpremul_Config8888:
177             *premul = true; // fallthru
178         case SkCanvas::kRGBA_Premul_Config8888:
179             a = static_cast<U8CPU>(c[3]);
180             r = static_cast<U8CPU>(c[0]);
181             g = static_cast<U8CPU>(c[1]);
182             b = static_cast<U8CPU>(c[2]);
183             break;
184         default:
185             GrCrash("Unexpected Config8888");
186     }
187     if (*premul) {
188         r = SkMulDiv255Ceiling(r, a);
189         g = SkMulDiv255Ceiling(g, a);
190         b = SkMulDiv255Ceiling(b, a);
191     }
192     return SkPackARGB32(a, r, g, b);
193 }
194 
checkPixel(SkPMColor a,SkPMColor b,bool didPremulConversion)195 bool checkPixel(SkPMColor a, SkPMColor b, bool didPremulConversion) {
196     if (!didPremulConversion) {
197         return a == b;
198     }
199     int32_t aA = static_cast<int32_t>(SkGetPackedA32(a));
200     int32_t aR = static_cast<int32_t>(SkGetPackedR32(a));
201     int32_t aG = static_cast<int32_t>(SkGetPackedG32(a));
202     int32_t aB = SkGetPackedB32(a);
203 
204     int32_t bA = static_cast<int32_t>(SkGetPackedA32(b));
205     int32_t bR = static_cast<int32_t>(SkGetPackedR32(b));
206     int32_t bG = static_cast<int32_t>(SkGetPackedG32(b));
207     int32_t bB = static_cast<int32_t>(SkGetPackedB32(b));
208 
209     return aA == bA &&
210            SkAbs32(aR - bR) <= 1 &&
211            SkAbs32(aG - bG) <= 1 &&
212            SkAbs32(aB - bB) <= 1;
213 }
214 
checkWrite(skiatest::Reporter * reporter,SkCanvas * canvas,const SkBitmap & bitmap,int writeX,int writeY,SkCanvas::Config8888 config8888)215 bool checkWrite(skiatest::Reporter* reporter,
216                 SkCanvas* canvas,
217                 const SkBitmap& bitmap,
218                 int writeX, int writeY,
219                 SkCanvas::Config8888 config8888) {
220     SkDevice* dev = canvas->getDevice();
221     if (!dev) {
222         return false;
223     }
224     SkBitmap devBmp = dev->accessBitmap(false);
225     if (devBmp.width() != DEV_W ||
226         devBmp.height() != DEV_H ||
227         devBmp.config() != SkBitmap::kARGB_8888_Config ||
228         devBmp.isNull()) {
229         return false;
230     }
231     SkAutoLockPixels alp(devBmp);
232 
233     intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels());
234     size_t canvasRowBytes = devBmp.rowBytes();
235     SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
236     bool success = true;
237     for (int cy = 0; cy < DEV_H; ++cy) {
238         const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels);
239         for (int cx = 0; cx < DEV_W; ++cx) {
240             SkPMColor canvasPixel = canvasRow[cx];
241             if (writeRect.contains(cx, cy)) {
242                 int bx = cx - writeX;
243                 int by = cy - writeY;
244                 uint32_t bmpColor8888 = getBitmapColor(bx, by, bitmap.width(), bitmap.height(), config8888);
245                 bool mul;
246                 SkPMColor bmpPMColor = convertConfig8888ToPMColor(config8888, bmpColor8888, &mul);
247                 bool check;
248                 REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul));
249                 if (!check) {
250                     success = false;
251                 }
252             } else {
253                 bool check;
254                 SkPMColor testColor = getCanvasColor(cx, cy);
255                 REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
256                 if (!check) {
257                     success = false;
258                 }
259             }
260         }
261         if (cy != DEV_H -1) {
262             const char* pad = reinterpret_cast<const char*>(canvasPixels + 4 * DEV_W);
263             for (size_t px = 0; px < canvasRowBytes - 4 * DEV_W; ++px) {
264                 bool check;
265                 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
266                 if (!check) {
267                     success = false;
268                 }
269             }
270         }
271         canvasPixels += canvasRowBytes;
272     }
273 
274     return success;
275 }
276 
277 enum DevType {
278     kRaster_DevType,
279     kGpu_DevType,
280 };
281 
282 struct CanvasConfig {
283     DevType fDevType;
284     bool fTightRowBytes;
285 };
286 
287 static const CanvasConfig gCanvasConfigs[] = {
288     {kRaster_DevType, true},
289     {kRaster_DevType, false},
290 #ifdef SK_SCALAR_IS_FLOAT
291     {kGpu_DevType, true}, // row bytes has no meaning on gpu devices
292 #endif
293 };
294 
setupCanvas(SkCanvas * canvas,const CanvasConfig & c,GrContext * grCtx)295 bool setupCanvas(SkCanvas* canvas, const CanvasConfig& c, GrContext* grCtx) {
296     switch (c.fDevType) {
297         case kRaster_DevType: {
298             SkBitmap bmp;
299             size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100;
300             bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, rowBytes);
301             if (!bmp.allocPixels()) {
302                 return false;
303             }
304             // if rowBytes isn't tight then set the padding to a known value
305             if (rowBytes) {
306                 SkAutoLockPixels alp(bmp);
307                 memset(bmp.getPixels(), DEV_PAD, bmp.getSafeSize());
308             }
309             canvas->setDevice(new SkDevice(bmp))->unref();
310             } break;
311         case kGpu_DevType:
312             canvas->setDevice(new SkGpuDevice(grCtx,
313                                               SkBitmap::kARGB_8888_Config,
314                                               DEV_W, DEV_H))->unref();
315             break;
316     }
317     return true;
318 }
319 
setupBitmap(SkBitmap * bitmap,SkCanvas::Config8888 config8888,int w,int h,bool tightRowBytes)320 bool setupBitmap(SkBitmap* bitmap,
321               SkCanvas::Config8888 config8888,
322               int w, int h,
323               bool tightRowBytes) {
324     size_t rowBytes = tightRowBytes ? 0 : 4 * w + 60;
325     bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h, rowBytes);
326     if (!bitmap->allocPixels()) {
327         return false;
328     }
329     SkAutoLockPixels alp(*bitmap);
330     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap->getPixels());
331     for (int y = 0; y < h; ++y) {
332         for (int x = 0; x < w; ++x) {
333             uint32_t* pixel = reinterpret_cast<uint32_t*>(pixels + y * bitmap->rowBytes() + x * 4);
334             *pixel = getBitmapColor(x, y, w, h, config8888);
335         }
336     }
337     return true;
338 }
339 
WritePixelsTest(skiatest::Reporter * reporter,GrContext * context)340 void WritePixelsTest(skiatest::Reporter* reporter, GrContext* context) {
341     SkCanvas canvas;
342 
343     const SkIRect testRects[] = {
344         // entire thing
345         DEV_RECT,
346         // larger on all sides
347         SkIRect::MakeLTRB(-10, -10, DEV_W + 10, DEV_H + 10),
348         // fully contained
349         SkIRect::MakeLTRB(DEV_W / 4, DEV_H / 4, 3 * DEV_W / 4, 3 * DEV_H / 4),
350         // outside top left
351         SkIRect::MakeLTRB(-10, -10, -1, -1),
352         // touching top left corner
353         SkIRect::MakeLTRB(-10, -10, 0, 0),
354         // overlapping top left corner
355         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H / 4),
356         // overlapping top left and top right corners
357         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, DEV_H / 4),
358         // touching entire top edge
359         SkIRect::MakeLTRB(-10, -10, DEV_W  + 10, 0),
360         // overlapping top right corner
361         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W  + 10, DEV_H / 4),
362         // contained in x, overlapping top edge
363         SkIRect::MakeLTRB(DEV_W / 4, -10, 3 * DEV_W  / 4, DEV_H / 4),
364         // outside top right corner
365         SkIRect::MakeLTRB(DEV_W + 1, -10, DEV_W + 10, -1),
366         // touching top right corner
367         SkIRect::MakeLTRB(DEV_W, -10, DEV_W + 10, 0),
368         // overlapping top left and bottom left corners
369         SkIRect::MakeLTRB(-10, -10, DEV_W / 4, DEV_H + 10),
370         // touching entire left edge
371         SkIRect::MakeLTRB(-10, -10, 0, DEV_H + 10),
372         // overlapping bottom left corner
373         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W / 4, DEV_H + 10),
374         // contained in y, overlapping left edge
375         SkIRect::MakeLTRB(-10, DEV_H / 4, DEV_W / 4, 3 * DEV_H / 4),
376         // outside bottom left corner
377         SkIRect::MakeLTRB(-10, DEV_H + 1, -1, DEV_H + 10),
378         // touching bottom left corner
379         SkIRect::MakeLTRB(-10, DEV_H, 0, DEV_H + 10),
380         // overlapping bottom left and bottom right corners
381         SkIRect::MakeLTRB(-10, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
382         // touching entire left edge
383         SkIRect::MakeLTRB(0, DEV_H, DEV_W, DEV_H + 10),
384         // overlapping bottom right corner
385         SkIRect::MakeLTRB(3 * DEV_W / 4, 3 * DEV_H / 4, DEV_W + 10, DEV_H + 10),
386         // overlapping top right and bottom right corners
387         SkIRect::MakeLTRB(3 * DEV_W / 4, -10, DEV_W + 10, DEV_H + 10),
388     };
389 
390     for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
391         REPORTER_ASSERT(reporter, setupCanvas(&canvas, gCanvasConfigs[i], context));
392 
393         static const SkCanvas::Config8888 gReadConfigs[] = {
394             SkCanvas::kNative_Premul_Config8888,
395             SkCanvas::kNative_Unpremul_Config8888,
396             SkCanvas::kBGRA_Premul_Config8888,
397             SkCanvas::kBGRA_Unpremul_Config8888,
398             SkCanvas::kRGBA_Premul_Config8888,
399             SkCanvas::kRGBA_Unpremul_Config8888,
400         };
401         for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
402             const SkIRect& rect = testRects[r];
403             for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
404                 for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) {
405                     fillCanvas(&canvas);
406                     SkCanvas::Config8888 config8888 = gReadConfigs[c];
407                     SkBitmap bmp;
408                     REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp)));
409                     canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888);
410                     REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888));
411                 }
412             }
413         }
414     }
415 }
416 }
417 
418 #include "TestClassDef.h"
419 DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest)
420 
421