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