SkBitmapDevice.cpp revision 2f6b5a47a50cdd218bc3302273be3a4a71add8fb
1/* 2 * Copyright 2013 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 "SkBitmapDevice.h" 9#include "SkDraw.h" 10#include "SkImageFilter.h" 11#include "SkImageFilterCache.h" 12#include "SkMallocPixelRef.h" 13#include "SkMatrix.h" 14#include "SkPaint.h" 15#include "SkPath.h" 16#include "SkPixelRef.h" 17#include "SkPixmap.h" 18#include "SkRasterClip.h" 19#include "SkRasterHandleAllocator.h" 20#include "SkShader.h" 21#include "SkSpecialImage.h" 22#include "SkSurface.h" 23#include "SkVertices.h" 24 25class SkColorTable; 26 27static bool valid_for_bitmap_device(const SkImageInfo& info, 28 SkAlphaType* newAlphaType) { 29 if (info.width() < 0 || info.height() < 0) { 30 return false; 31 } 32 33 // TODO: can we stop supporting kUnknown in SkBitmkapDevice? 34 if (kUnknown_SkColorType == info.colorType()) { 35 if (newAlphaType) { 36 *newAlphaType = kUnknown_SkAlphaType; 37 } 38 return true; 39 } 40 41 switch (info.alphaType()) { 42 case kPremul_SkAlphaType: 43 case kOpaque_SkAlphaType: 44 break; 45 default: 46 return false; 47 } 48 49 SkAlphaType canonicalAlphaType = info.alphaType(); 50 51 switch (info.colorType()) { 52 case kAlpha_8_SkColorType: 53 break; 54 case kRGB_565_SkColorType: 55 canonicalAlphaType = kOpaque_SkAlphaType; 56 break; 57 case kN32_SkColorType: 58 break; 59 case kRGBA_F16_SkColorType: 60 break; 61 default: 62 return false; 63 } 64 65 if (newAlphaType) { 66 *newAlphaType = canonicalAlphaType; 67 } 68 return true; 69} 70 71SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap) 72 : INHERITED(bitmap.info(), SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)) 73 , fBitmap(bitmap) 74 , fRCStack(bitmap.width(), bitmap.height()) 75{ 76 SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); 77 fBitmap.lockPixels(); 78} 79 80SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) { 81 return Create(info, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType)); 82} 83 84SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, 85 SkRasterHandleAllocator::Handle hndl) 86 : INHERITED(bitmap.info(), surfaceProps) 87 , fBitmap(bitmap) 88 , fRasterHandle(hndl) 89 , fRCStack(bitmap.width(), bitmap.height()) 90{ 91 SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); 92 fBitmap.lockPixels(); 93} 94 95SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, 96 const SkSurfaceProps& surfaceProps, 97 SkRasterHandleAllocator* allocator) { 98 SkAlphaType newAT = origInfo.alphaType(); 99 if (!valid_for_bitmap_device(origInfo, &newAT)) { 100 return nullptr; 101 } 102 103 SkRasterHandleAllocator::Handle hndl = nullptr; 104 const SkImageInfo info = origInfo.makeAlphaType(newAT); 105 SkBitmap bitmap; 106 107 if (kUnknown_SkColorType == info.colorType()) { 108 if (!bitmap.setInfo(info)) { 109 return nullptr; 110 } 111 } else if (allocator) { 112 hndl = allocator->allocBitmap(info, &bitmap); 113 if (!hndl) { 114 return nullptr; 115 } 116 } else if (info.isOpaque()) { 117 // If this bitmap is opaque, we don't have any sensible default color, 118 // so we just return uninitialized pixels. 119 if (!bitmap.tryAllocPixels(info)) { 120 return nullptr; 121 } 122 } else { 123 // This bitmap has transparency, so we'll zero the pixels (to transparent). 124 // We use a ZeroedPRFactory as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT). 125 SkMallocPixelRef::ZeroedPRFactory factory; 126 if (!bitmap.tryAllocPixels(info, &factory, nullptr/*color table*/)) { 127 return nullptr; 128 } 129 } 130 131 return new SkBitmapDevice(bitmap, surfaceProps, hndl); 132} 133 134void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { 135 SkASSERT(bm.width() == fBitmap.width()); 136 SkASSERT(bm.height() == fBitmap.height()); 137 fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config) 138 fBitmap.lockPixels(); 139 this->privateResize(fBitmap.info().width(), fBitmap.info().height()); 140} 141 142SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) { 143 const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry); 144 return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator); 145} 146 147bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) { 148 if (this->onPeekPixels(pmap)) { 149 fBitmap.notifyPixelsChanged(); 150 return true; 151 } 152 return false; 153} 154 155bool SkBitmapDevice::onPeekPixels(SkPixmap* pmap) { 156 const SkImageInfo info = fBitmap.info(); 157 if (fBitmap.getPixels() && (kUnknown_SkColorType != info.colorType())) { 158 SkColorTable* ctable = nullptr; 159 pmap->reset(fBitmap.info(), fBitmap.getPixels(), fBitmap.rowBytes(), ctable); 160 return true; 161 } 162 return false; 163} 164 165bool SkBitmapDevice::onWritePixels(const SkImageInfo& srcInfo, const void* srcPixels, 166 size_t srcRowBytes, int x, int y) { 167 // since we don't stop creating un-pixeled devices yet, check for no pixels here 168 if (nullptr == fBitmap.getPixels()) { 169 return false; 170 } 171 172 if (fBitmap.writePixels(SkPixmap(srcInfo, srcPixels, srcRowBytes), x, y)) { 173 fBitmap.notifyPixelsChanged(); 174 return true; 175 } 176 return false; 177} 178 179bool SkBitmapDevice::onReadPixels(const SkImageInfo& dstInfo, void* dstPixels, size_t dstRowBytes, 180 int x, int y) { 181 return fBitmap.readPixels(dstInfo, dstPixels, dstRowBytes, x, y); 182} 183 184/////////////////////////////////////////////////////////////////////////////// 185 186class SkBitmapDevice::BDDraw : public SkDraw { 187public: 188 BDDraw(SkBitmapDevice* dev) { 189 // we need fDst to be set, and if we're actually drawing, to dirty the genID 190 if (!dev->accessPixels(&fDst)) { 191 // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels 192 fDst.reset(dev->imageInfo(), nullptr, 0); 193 } 194 fMatrix = &dev->ctm(); 195 fRC = &dev->fRCStack.rc(); 196 } 197}; 198 199void SkBitmapDevice::drawPaint(const SkPaint& paint) { 200 BDDraw(this).drawPaint(paint); 201} 202 203void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count, 204 const SkPoint pts[], const SkPaint& paint) { 205 BDDraw(this).drawPoints(mode, count, pts, paint, nullptr); 206} 207 208void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) { 209 BDDraw(this).drawRect(r, paint); 210} 211 212void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) { 213 SkPath path; 214 path.addOval(oval); 215 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't 216 // required to override drawOval. 217 this->drawPath(path, paint, nullptr, true); 218} 219 220void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) { 221#ifdef SK_IGNORE_BLURRED_RRECT_OPT 222 SkPath path; 223 224 path.addRRect(rrect); 225 // call the VIRTUAL version, so any subclasses who do handle drawPath aren't 226 // required to override drawRRect. 227 this->drawPath(path, paint, nullptr, true); 228#else 229 BDDraw(this).drawRRect(rrect, paint); 230#endif 231} 232 233void SkBitmapDevice::drawPath(const SkPath& path, 234 const SkPaint& paint, const SkMatrix* prePathMatrix, 235 bool pathIsMutable) { 236 BDDraw(this).drawPath(path, paint, prePathMatrix, pathIsMutable); 237} 238 239void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, 240 const SkMatrix& matrix, const SkPaint& paint) { 241 LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality()); 242 BDDraw(this).drawBitmap(bitmap, matrix, nullptr, paint); 243} 244 245static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) { 246 if (!paint.getMaskFilter()) { 247 return true; 248 } 249 250 // Some mask filters parameters (sigma) depend on the CTM/scale. 251 return m.getType() <= SkMatrix::kTranslate_Mask; 252} 253 254void SkBitmapDevice::drawBitmapRect(const SkBitmap& bitmap, 255 const SkRect* src, const SkRect& dst, 256 const SkPaint& paint, SkCanvas::SrcRectConstraint constraint) { 257 SkMatrix matrix; 258 SkRect bitmapBounds, tmpSrc, tmpDst; 259 SkBitmap tmpBitmap; 260 261 bitmapBounds.isetWH(bitmap.width(), bitmap.height()); 262 263 // Compute matrix from the two rectangles 264 if (src) { 265 tmpSrc = *src; 266 } else { 267 tmpSrc = bitmapBounds; 268 } 269 matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit); 270 271 LogDrawScaleFactor(SkMatrix::Concat(this->ctm(), matrix), paint.getFilterQuality()); 272 273 const SkRect* dstPtr = &dst; 274 const SkBitmap* bitmapPtr = &bitmap; 275 276 // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if 277 // needed (if the src was clipped). No check needed if src==null. 278 if (src) { 279 if (!bitmapBounds.contains(*src)) { 280 if (!tmpSrc.intersect(bitmapBounds)) { 281 return; // nothing to draw 282 } 283 // recompute dst, based on the smaller tmpSrc 284 matrix.mapRect(&tmpDst, tmpSrc); 285 dstPtr = &tmpDst; 286 } 287 } 288 289 if (src && !src->contains(bitmapBounds) && 290 SkCanvas::kFast_SrcRectConstraint == constraint && 291 paint.getFilterQuality() != kNone_SkFilterQuality) { 292 // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know 293 // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap, 294 // but we must use a shader w/ dst bounds (which can access all of the bitmap needed). 295 goto USE_SHADER; 296 } 297 298 if (src) { 299 // since we may need to clamp to the borders of the src rect within 300 // the bitmap, we extract a subset. 301 const SkIRect srcIR = tmpSrc.roundOut(); 302 if (!bitmap.extractSubset(&tmpBitmap, srcIR)) { 303 return; 304 } 305 bitmapPtr = &tmpBitmap; 306 307 // Since we did an extract, we need to adjust the matrix accordingly 308 SkScalar dx = 0, dy = 0; 309 if (srcIR.fLeft > 0) { 310 dx = SkIntToScalar(srcIR.fLeft); 311 } 312 if (srcIR.fTop > 0) { 313 dy = SkIntToScalar(srcIR.fTop); 314 } 315 if (dx || dy) { 316 matrix.preTranslate(dx, dy); 317 } 318 319 SkRect extractedBitmapBounds; 320 extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height()); 321 if (extractedBitmapBounds == tmpSrc) { 322 // no fractional part in src, we can just call drawBitmap 323 goto USE_DRAWBITMAP; 324 } 325 } else { 326 USE_DRAWBITMAP: 327 // We can go faster by just calling drawBitmap, which will concat the 328 // matrix with the CTM, and try to call drawSprite if it can. If not, 329 // it will make a shader and call drawRect, as we do below. 330 if (CanApplyDstMatrixAsCTM(matrix, paint)) { 331 BDDraw(this).drawBitmap(*bitmapPtr, matrix, dstPtr, paint); 332 return; 333 } 334 } 335 336 USE_SHADER: 337 338 // TODO(herb): Move this over to SkArenaAlloc when arena alloc has a facility to return sk_sps. 339 // Since the shader need only live for our stack-frame, pass in a custom allocator. This 340 // can save malloc calls, and signals to SkMakeBitmapShader to not try to copy the bitmap 341 // if its mutable, since that precaution is not needed (give the short lifetime of the shader). 342 343 // construct a shader, so we can call drawRect with the dst 344 auto s = SkMakeBitmapShader(*bitmapPtr, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, 345 &matrix, kNever_SkCopyPixelsMode); 346 if (!s) { 347 return; 348 } 349 350 SkPaint paintWithShader(paint); 351 paintWithShader.setStyle(SkPaint::kFill_Style); 352 paintWithShader.setShader(s); 353 354 // Call ourself, in case the subclass wanted to share this setup code 355 // but handle the drawRect code themselves. 356 this->drawRect(*dstPtr, paintWithShader); 357} 358 359void SkBitmapDevice::drawSprite(const SkBitmap& bitmap, int x, int y, const SkPaint& paint) { 360 BDDraw(this).drawSprite(bitmap, x, y, paint); 361} 362 363void SkBitmapDevice::drawText(const void* text, size_t len, 364 SkScalar x, SkScalar y, const SkPaint& paint) { 365 BDDraw(this).drawText((const char*)text, len, x, y, paint, &fSurfaceProps); 366} 367 368void SkBitmapDevice::drawPosText(const void* text, size_t len, const SkScalar xpos[], 369 int scalarsPerPos, const SkPoint& offset, const SkPaint& paint) { 370 BDDraw(this).drawPosText((const char*)text, len, xpos, scalarsPerPos, offset, paint, 371 &fSurfaceProps); 372} 373 374void SkBitmapDevice::drawVertices(const SkVertices* vertices, SkBlendMode bmode, 375 const SkPaint& paint) { 376 BDDraw(this).drawVertices(vertices->mode(), vertices->vertexCount(), vertices->positions(), 377 vertices->texCoords(), vertices->colors(), bmode, 378 vertices->indices(), vertices->indexCount(), paint); 379} 380 381void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPaint& paint) { 382 SkASSERT(!paint.getImageFilter()); 383 BDDraw(this).drawSprite(static_cast<SkBitmapDevice*>(device)->fBitmap, x, y, paint); 384} 385 386/////////////////////////////////////////////////////////////////////////////// 387 388void SkBitmapDevice::drawSpecial(SkSpecialImage* srcImg, int x, int y, 389 const SkPaint& paint) { 390 SkASSERT(!srcImg->isTextureBacked()); 391 392 SkBitmap resultBM; 393 394 SkImageFilter* filter = paint.getImageFilter(); 395 if (filter) { 396 SkIPoint offset = SkIPoint::Make(0, 0); 397 SkMatrix matrix = this->ctm(); 398 matrix.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y)); 399 const SkIRect clipBounds = fRCStack.rc().getBounds().makeOffset(-x, -y); 400 sk_sp<SkImageFilterCache> cache(this->getImageFilterCache()); 401 SkImageFilter::OutputProperties outputProperties(fBitmap.colorSpace()); 402 SkImageFilter::Context ctx(matrix, clipBounds, cache.get(), outputProperties); 403 404 sk_sp<SkSpecialImage> resultImg(filter->filterImage(srcImg, ctx, &offset)); 405 if (resultImg) { 406 SkPaint tmpUnfiltered(paint); 407 tmpUnfiltered.setImageFilter(nullptr); 408 if (resultImg->getROPixels(&resultBM)) { 409 this->drawSprite(resultBM, x + offset.x(), y + offset.y(), tmpUnfiltered); 410 } 411 } 412 } else { 413 if (srcImg->getROPixels(&resultBM)) { 414 this->drawSprite(resultBM, x, y, paint); 415 } 416 } 417} 418 419sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) { 420 return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap); 421} 422 423sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) { 424 return SkSpecialImage::MakeFromImage(SkIRect::MakeWH(image->width(), image->height()), 425 image->makeNonTextureImage(), fBitmap.colorSpace()); 426} 427 428sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial() { 429 return this->makeSpecial(fBitmap); 430} 431 432/////////////////////////////////////////////////////////////////////////////// 433 434sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) { 435 return SkSurface::MakeRaster(info, &props); 436} 437 438SkImageFilterCache* SkBitmapDevice::getImageFilterCache() { 439 SkImageFilterCache* cache = SkImageFilterCache::Get(); 440 cache->ref(); 441 return cache; 442} 443 444/////////////////////////////////////////////////////////////////////////////////////////////////// 445 446bool SkBitmapDevice::onShouldDisableLCD(const SkPaint& paint) const { 447 if (kN32_SkColorType != fBitmap.colorType() || 448 paint.getRasterizer() || 449 paint.getPathEffect() || 450 paint.isFakeBoldText() || 451 paint.getStyle() != SkPaint::kFill_Style || 452 !paint.isSrcOver()) 453 { 454 return true; 455 } 456 return false; 457} 458 459/////////////////////////////////////////////////////////////////////////////////////////////////// 460 461void SkBitmapDevice::onSave() { 462 fRCStack.save(); 463} 464 465void SkBitmapDevice::onRestore() { 466 fRCStack.restore(); 467} 468 469void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) { 470 fRCStack.clipRect(this->ctm(), rect, op, aa); 471} 472 473void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) { 474 fRCStack.clipRRect(this->ctm(), rrect, op, aa); 475} 476 477void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) { 478 fRCStack.clipPath(this->ctm(), path, op, aa); 479} 480 481void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) { 482 SkIPoint origin = this->getOrigin(); 483 SkRegion tmp; 484 const SkRegion* ptr = &rgn; 485 if (origin.fX | origin.fY) { 486 // translate from "global/canvas" coordinates to relative to this device 487 rgn.translate(-origin.fX, -origin.fY, &tmp); 488 ptr = &tmp; 489 } 490 fRCStack.clipRegion(*ptr, op); 491} 492 493void SkBitmapDevice::onSetDeviceClipRestriction(SkIRect* mutableClipRestriction) { 494 fRCStack.setDeviceClipRestriction(mutableClipRestriction); 495 if (!mutableClipRestriction->isEmpty()) { 496 SkRegion rgn(*mutableClipRestriction); 497 fRCStack.clipRegion(rgn, SkClipOp::kIntersect); 498 } 499} 500 501bool SkBitmapDevice::onClipIsAA() const { 502 const SkRasterClip& rc = fRCStack.rc(); 503 return !rc.isEmpty() && rc.isAA(); 504} 505 506void SkBitmapDevice::onAsRgnClip(SkRegion* rgn) const { 507 const SkRasterClip& rc = fRCStack.rc(); 508 if (rc.isAA()) { 509 rgn->setRect(rc.getBounds()); 510 } else { 511 *rgn = rc.bwRgn(); 512 } 513} 514 515void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) { 516#ifdef SK_DEBUG 517 const SkIRect& stackBounds = fRCStack.rc().getBounds(); 518 SkASSERT(drawClipBounds == stackBounds); 519#endif 520} 521 522SkBaseDevice::ClipType SkBitmapDevice::onGetClipType() const { 523 const SkRasterClip& rc = fRCStack.rc(); 524 if (rc.isEmpty()) { 525 return kEmpty_ClipType; 526 } else if (rc.isRect()) { 527 return kRect_ClipType; 528 } else { 529 return kComplex_ClipType; 530 } 531} 532