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 { 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 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 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 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 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 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 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 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 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 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 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