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