SkDevice.cpp revision 9bf380ce7f848dfb5886dd52b82746521454b739
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#include "SkDevice.h" 9#include "SkDeviceProperties.h" 10#include "SkDraw.h" 11#include "SkImageFilter.h" 12#include "SkMetaData.h" 13#include "SkRasterClip.h" 14#include "SkRect.h" 15#include "SkRRect.h" 16#include "SkShader.h" 17 18SK_DEFINE_INST_COUNT(SkDevice) 19 20/////////////////////////////////////////////////////////////////////////////// 21 22#define CHECK_FOR_NODRAW_ANNOTATION(paint) \ 23 do { if (paint.isNoDrawAnnotation()) { return; } } while (0) 24 25/////////////////////////////////////////////////////////////////////////////// 26 27SkDevice::SkDevice(const SkBitmap& bitmap) 28 : fBitmap(bitmap), fLeakyProperties(SkDeviceProperties::MakeDefault()) 29#ifdef SK_DEBUG 30 , fAttachedToCanvas(false) 31#endif 32{ 33 fOrigin.setZero(); 34 fMetaData = NULL; 35 36 SkASSERT(SkBitmap::kARGB_4444_Config != bitmap.config()); 37} 38 39SkDevice::SkDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties) 40 : fBitmap(bitmap), fLeakyProperties(deviceProperties) 41#ifdef SK_DEBUG 42 , fAttachedToCanvas(false) 43#endif 44{ 45 fOrigin.setZero(); 46 fMetaData = NULL; 47} 48 49SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque) 50 : fLeakyProperties(SkDeviceProperties::MakeDefault()) 51#ifdef SK_DEBUG 52 , fAttachedToCanvas(false) 53#endif 54{ 55 fOrigin.setZero(); 56 fMetaData = NULL; 57 58 fBitmap.setConfig(config, width, height); 59 fBitmap.allocPixels(); 60 fBitmap.setIsOpaque(isOpaque); 61 if (!isOpaque) { 62 fBitmap.eraseColor(SK_ColorTRANSPARENT); 63 } 64} 65 66SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque, 67 const SkDeviceProperties& deviceProperties) 68 : fLeakyProperties(deviceProperties) 69#ifdef SK_DEBUG 70 , fAttachedToCanvas(false) 71#endif 72{ 73 fOrigin.setZero(); 74 fMetaData = NULL; 75 76 fBitmap.setConfig(config, width, height); 77 fBitmap.allocPixels(); 78 fBitmap.setIsOpaque(isOpaque); 79 if (!isOpaque) { 80 fBitmap.eraseColor(SK_ColorTRANSPARENT); 81 } 82} 83 84SkDevice::~SkDevice() { 85 delete fMetaData; 86} 87 88void SkDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { 89 SkASSERT(bm.width() == fBitmap.width()); 90 SkASSERT(bm.height() == fBitmap.height()); 91 fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config) 92 fBitmap.lockPixels(); 93} 94 95SkDevice* SkDevice::createCompatibleDevice(SkBitmap::Config config, 96 int width, int height, 97 bool isOpaque) { 98 return this->onCreateCompatibleDevice(config, width, height, 99 isOpaque, kGeneral_Usage); 100} 101 102SkDevice* SkDevice::createCompatibleDeviceForSaveLayer(SkBitmap::Config config, 103 int width, int height, 104 bool isOpaque) { 105 return this->onCreateCompatibleDevice(config, width, height, 106 isOpaque, kSaveLayer_Usage); 107} 108 109SkDevice* SkDevice::onCreateCompatibleDevice(SkBitmap::Config config, 110 int width, int height, 111 bool isOpaque, 112 Usage usage) { 113 return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque, fLeakyProperties)); 114} 115 116SkMetaData& SkDevice::getMetaData() { 117 // metadata users are rare, so we lazily allocate it. If that changes we 118 // can decide to just make it a field in the device (rather than a ptr) 119 if (NULL == fMetaData) { 120 fMetaData = new SkMetaData; 121 } 122 return *fMetaData; 123} 124 125void SkDevice::lockPixels() { 126 if (fBitmap.lockPixelsAreWritable()) { 127 fBitmap.lockPixels(); 128 } 129} 130 131void SkDevice::unlockPixels() { 132 if (fBitmap.lockPixelsAreWritable()) { 133 fBitmap.unlockPixels(); 134 } 135} 136 137const SkBitmap& SkDevice::accessBitmap(bool changePixels) { 138 const SkBitmap& bitmap = this->onAccessBitmap(&fBitmap); 139 if (changePixels) { 140 bitmap.notifyPixelsChanged(); 141 } 142 return bitmap; 143} 144 145void SkDevice::getGlobalBounds(SkIRect* bounds) const { 146 if (bounds) { 147 bounds->setXYWH(fOrigin.x(), fOrigin.y(), 148 fBitmap.width(), fBitmap.height()); 149 } 150} 151 152void SkDevice::clear(SkColor color) { 153 fBitmap.eraseColor(color); 154} 155 156const SkBitmap& SkDevice::onAccessBitmap(SkBitmap* bitmap) {return *bitmap;} 157 158void SkDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& region, 159 const SkClipStack& clipStack) { 160} 161 162bool SkDevice::canHandleImageFilter(SkImageFilter*) { 163 return false; 164} 165 166bool SkDevice::filterImage(SkImageFilter* filter, const SkBitmap& src, 167 const SkMatrix& ctm, SkBitmap* result, 168 SkIPoint* offset) { 169 return false; 170} 171 172bool SkDevice::allowImageFilter(SkImageFilter*) { 173 return true; 174} 175 176/////////////////////////////////////////////////////////////////////////////// 177 178bool SkDevice::readPixels(SkBitmap* bitmap, int x, int y, 179 SkCanvas::Config8888 config8888) { 180 if (SkBitmap::kARGB_8888_Config != bitmap->config() || 181 NULL != bitmap->getTexture()) { 182 return false; 183 } 184 185 const SkBitmap& src = this->accessBitmap(false); 186 187 SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap->width(), 188 bitmap->height()); 189 SkIRect devbounds = SkIRect::MakeWH(src.width(), src.height()); 190 if (!srcRect.intersect(devbounds)) { 191 return false; 192 } 193 194 SkBitmap tmp; 195 SkBitmap* bmp; 196 if (bitmap->isNull()) { 197 tmp.setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), 198 bitmap->height()); 199 if (!tmp.allocPixels()) { 200 return false; 201 } 202 bmp = &tmp; 203 } else { 204 bmp = bitmap; 205 } 206 207 SkIRect subrect = srcRect; 208 subrect.offset(-x, -y); 209 SkBitmap bmpSubset; 210 bmp->extractSubset(&bmpSubset, subrect); 211 212 bool result = this->onReadPixels(bmpSubset, 213 srcRect.fLeft, 214 srcRect.fTop, 215 config8888); 216 if (result && bmp == &tmp) { 217 tmp.swap(*bitmap); 218 } 219 return result; 220} 221 222#if SK_PMCOLOR_BYTE_ORDER(B,G,R,A) 223 const SkCanvas::Config8888 SkDevice::kPMColorAlias = 224 SkCanvas::kBGRA_Premul_Config8888; 225#elif SK_PMCOLOR_BYTE_ORDER(R,G,B,A) 226 const SkCanvas::Config8888 SkDevice::kPMColorAlias = 227 SkCanvas::kRGBA_Premul_Config8888; 228#else 229 const SkCanvas::Config8888 SkDevice::kPMColorAlias = 230 (SkCanvas::Config8888) -1; 231#endif 232 233#include <SkConfig8888.h> 234 235bool SkDevice::onReadPixels(const SkBitmap& bitmap, 236 int x, int y, 237 SkCanvas::Config8888 config8888) { 238 SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config()); 239 SkASSERT(!bitmap.isNull()); 240 SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))); 241 242 SkIRect srcRect = SkIRect::MakeXYWH(x, y, bitmap.width(), 243 bitmap.height()); 244 const SkBitmap& src = this->accessBitmap(false); 245 246 SkBitmap subset; 247 if (!src.extractSubset(&subset, srcRect)) { 248 return false; 249 } 250 if (SkBitmap::kARGB_8888_Config != subset.config()) { 251 // It'd be preferable to do this directly to bitmap. 252 subset.copyTo(&subset, SkBitmap::kARGB_8888_Config); 253 } 254 SkAutoLockPixels alp(bitmap); 255 uint32_t* bmpPixels = reinterpret_cast<uint32_t*>(bitmap.getPixels()); 256 SkCopyBitmapToConfig8888(bmpPixels, bitmap.rowBytes(), config8888, subset); 257 return true; 258} 259 260void SkDevice::writePixels(const SkBitmap& bitmap, 261 int x, int y, 262 SkCanvas::Config8888 config8888) { 263 if (bitmap.isNull() || bitmap.getTexture()) { 264 return; 265 } 266 const SkBitmap* sprite = &bitmap; 267 // check whether we have to handle a config8888 that doesn't match SkPMColor 268 if (SkBitmap::kARGB_8888_Config == bitmap.config() && 269 SkCanvas::kNative_Premul_Config8888 != config8888 && 270 kPMColorAlias != config8888) { 271 272 // We're going to have to convert from a config8888 to the native config 273 // First we clip to the device bounds. 274 SkBitmap dstBmp = this->accessBitmap(true); 275 SkIRect spriteRect = SkIRect::MakeXYWH(x, y, 276 bitmap.width(), bitmap.height()); 277 SkIRect devRect = SkIRect::MakeWH(dstBmp.width(), dstBmp.height()); 278 if (!spriteRect.intersect(devRect)) { 279 return; 280 } 281 282 // write directly to the device if it has pixels and is SkPMColor 283 bool drawSprite; 284 if (SkBitmap::kARGB_8888_Config == dstBmp.config() && !dstBmp.isNull()) { 285 // we can write directly to the dst when doing the conversion 286 dstBmp.extractSubset(&dstBmp, spriteRect); 287 drawSprite = false; 288 } else { 289 // we convert to a temporary bitmap and draw that as a sprite 290 dstBmp.setConfig(SkBitmap::kARGB_8888_Config, 291 spriteRect.width(), 292 spriteRect.height()); 293 if (!dstBmp.allocPixels()) { 294 return; 295 } 296 drawSprite = true; 297 } 298 299 // copy pixels to dstBmp and convert from config8888 to native config. 300 SkAutoLockPixels alp(bitmap); 301 uint32_t* srcPixels = bitmap.getAddr32(spriteRect.fLeft - x, 302 spriteRect.fTop - y); 303 SkCopyConfig8888ToBitmap(dstBmp, 304 srcPixels, 305 bitmap.rowBytes(), 306 config8888); 307 308 if (drawSprite) { 309 // we've clipped the sprite when we made a copy 310 x = spriteRect.fLeft; 311 y = spriteRect.fTop; 312 sprite = &dstBmp; 313 } else { 314 return; 315 } 316 } 317 318 SkPaint paint; 319 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 320 SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height())); 321 SkDraw draw; 322 draw.fRC = &clip; 323 draw.fClip = &clip.bwRgn(); 324 draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap 325 draw.fMatrix = &SkMatrix::I(); 326 this->drawSprite(draw, *sprite, x, y, paint); 327} 328 329/////////////////////////////////////////////////////////////////////////////// 330 331void SkDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) { 332 draw.drawPaint(paint); 333} 334 335void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count, 336 const SkPoint pts[], const SkPaint& paint) { 337 CHECK_FOR_NODRAW_ANNOTATION(paint); 338 draw.drawPoints(mode, count, pts, paint); 339} 340 341void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) { 342 CHECK_FOR_NODRAW_ANNOTATION(paint); 343 draw.drawRect(r, paint); 344} 345 346void SkDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) { 347 CHECK_FOR_NODRAW_ANNOTATION(paint); 348 349 SkPath path; 350 path.addOval(oval); 351 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't 352 // required to override drawOval. 353 this->drawPath(draw, path, paint, NULL, true); 354} 355 356void SkDevice::drawRRect(const SkDraw& draw, const SkRRect& rrect, const SkPaint& paint) { 357 CHECK_FOR_NODRAW_ANNOTATION(paint); 358 359 SkPath path; 360 path.addRRect(rrect); 361 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't 362 // required to override drawRRect. 363 this->drawPath(draw, path, paint, NULL, true); 364} 365 366void SkDevice::drawPath(const SkDraw& draw, const SkPath& path, 367 const SkPaint& paint, const SkMatrix* prePathMatrix, 368 bool pathIsMutable) { 369 CHECK_FOR_NODRAW_ANNOTATION(paint); 370 draw.drawPath(path, paint, prePathMatrix, pathIsMutable); 371} 372 373void SkDevice::drawBitmap(const SkDraw& draw, const SkBitmap& bitmap, 374 const SkMatrix& matrix, const SkPaint& paint) { 375 draw.drawBitmap(bitmap, matrix, paint); 376} 377 378void SkDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap, 379 const SkRect* src, const SkRect& dst, 380 const SkPaint& paint) { 381 SkMatrix matrix; 382 SkRect bitmapBounds, tmpSrc, tmpDst; 383 SkBitmap tmpBitmap; 384 385 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); 386 387 // Compute matrix from the two rectangles 388 if (src) { 389 tmpSrc = *src; 390 } else { 391 tmpSrc = bitmapBounds; 392 } 393 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 394 395 const SkRect* dstPtr = &dst; 396 const SkBitmap* bitmapPtr = &bitmap; 397 398 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if 399 // needed (if the src was clipped). No check needed if src==null. 400 if (src) { 401 if (!bitmapBounds.contains(*src)) { 402 if (!tmpSrc.intersect(bitmapBounds)) { 403 return; // nothing to draw 404 } 405 // recompute dst, based on the smaller tmpSrc 406 matrix.mapRect(&tmpDst, tmpSrc); 407 dstPtr = &tmpDst; 408 } 409 410 // since we may need to clamp to the borders of the src rect within 411 // the bitmap, we extract a subset. 412 SkIRect srcIR; 413 tmpSrc.roundOut(&srcIR); 414 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { 415 return; 416 } 417 bitmapPtr = &tmpBitmap; 418 419 // Since we did an extract, we need to adjust the matrix accordingly 420 SkScalar dx = 0, dy = 0; 421 if (srcIR.fLeft > 0) { 422 dx = SkIntToScalar(srcIR.fLeft); 423 } 424 if (srcIR.fTop > 0) { 425 dy = SkIntToScalar(srcIR.fTop); 426 } 427 if (dx || dy) { 428 matrix.preTranslate(dx, dy); 429 } 430 431 SkRect extractedBitmapBounds; 432 extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); 433 if (extractedBitmapBounds == tmpSrc) { 434 // no fractional part in src, we can just call drawBitmap 435 goto USE_DRAWBITMAP; 436 } 437 } else { 438 USE_DRAWBITMAP: 439 // We can go faster by just calling drawBitmap, which will concat the 440 // matrix with the CTM, and try to call drawSprite if it can. If not, 441 // it will make a shader and call drawRect, as we do below. 442 this->drawBitmap(draw, *bitmapPtr, matrix, paint); 443 return; 444 } 445 446 // construct a shader, so we can call drawRect with the dst 447 SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr, 448 SkShader::kClamp_TileMode, 449 SkShader::kClamp_TileMode); 450 if (NULL == s) { 451 return; 452 } 453 s->setLocalMatrix(matrix); 454 455 SkPaint paintWithShader(paint); 456 paintWithShader.setStyle(SkPaint::kFill_Style); 457 paintWithShader.setShader(s)->unref(); 458 459 // Call ourself, in case the subclass wanted to share this setup code 460 // but handle the drawRect code themselves. 461 this->drawRect(draw, *dstPtr, paintWithShader); 462} 463 464void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap, 465 int x, int y, const SkPaint& paint) { 466 draw.drawSprite(bitmap, x, y, paint); 467} 468 469void SkDevice::drawText(const SkDraw& draw, const void* text, size_t len, 470 SkScalar x, SkScalar y, const SkPaint& paint) { 471 draw.drawText((const char*)text, len, x, y, paint); 472} 473 474void SkDevice::drawPosText(const SkDraw& draw, const void* text, size_t len, 475 const SkScalar xpos[], SkScalar y, 476 int scalarsPerPos, const SkPaint& paint) { 477 draw.drawPosText((const char*)text, len, xpos, y, scalarsPerPos, paint); 478} 479 480void SkDevice::drawTextOnPath(const SkDraw& draw, const void* text, 481 size_t len, const SkPath& path, 482 const SkMatrix* matrix, 483 const SkPaint& paint) { 484 draw.drawTextOnPath((const char*)text, len, path, matrix, paint); 485} 486 487#ifdef SK_BUILD_FOR_ANDROID 488void SkDevice::drawPosTextOnPath(const SkDraw& draw, const void* text, size_t len, 489 const SkPoint pos[], const SkPaint& paint, 490 const SkPath& path, const SkMatrix* matrix) { 491 draw.drawPosTextOnPath((const char*)text, len, pos, paint, path, matrix); 492} 493#endif 494 495void SkDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode, 496 int vertexCount, 497 const SkPoint verts[], const SkPoint textures[], 498 const SkColor colors[], SkXfermode* xmode, 499 const uint16_t indices[], int indexCount, 500 const SkPaint& paint) { 501 draw.drawVertices(vmode, vertexCount, verts, textures, colors, xmode, 502 indices, indexCount, paint); 503} 504 505void SkDevice::drawDevice(const SkDraw& draw, SkDevice* device, 506 int x, int y, const SkPaint& paint) { 507 const SkBitmap& src = device->accessBitmap(false); 508 draw.drawSprite(src, x, y, paint); 509} 510 511/////////////////////////////////////////////////////////////////////////////// 512 513bool SkDevice::filterTextFlags(const SkPaint& paint, TextFlags* flags) { 514 if (!paint.isLCDRenderText() || !paint.isAntiAlias()) { 515 // we're cool with the paint as is 516 return false; 517 } 518 519 if (SkBitmap::kARGB_8888_Config != fBitmap.config() || 520 paint.getRasterizer() || 521 paint.getPathEffect() || 522 paint.isFakeBoldText() || 523 paint.getStyle() != SkPaint::kFill_Style || 524 !SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode)) { 525 // turn off lcd 526 flags->fFlags = paint.getFlags() & ~SkPaint::kLCDRenderText_Flag; 527 flags->fHinting = paint.getHinting(); 528 return true; 529 } 530 // we're cool with the paint as is 531 return false; 532} 533