summaryrefslogtreecommitdiffstats
path: root/gfx/skia/skia/src/core/SkBitmapDevice.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/skia/skia/src/core/SkBitmapDevice.cpp')
-rw-r--r--gfx/skia/skia/src/core/SkBitmapDevice.cpp705
1 files changed, 705 insertions, 0 deletions
diff --git a/gfx/skia/skia/src/core/SkBitmapDevice.cpp b/gfx/skia/skia/src/core/SkBitmapDevice.cpp
new file mode 100644
index 0000000000..f00b7f6072
--- /dev/null
+++ b/gfx/skia/skia/src/core/SkBitmapDevice.cpp
@@ -0,0 +1,705 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/core/SkBitmapDevice.h"
+
+#include "include/core/SkBlender.h"
+#include "include/core/SkImageFilter.h"
+#include "include/core/SkMatrix.h"
+#include "include/core/SkPaint.h"
+#include "include/core/SkPath.h"
+#include "include/core/SkPixmap.h"
+#include "include/core/SkRasterHandleAllocator.h"
+#include "include/core/SkShader.h"
+#include "include/core/SkSurface.h"
+#include "include/core/SkVertices.h"
+#include "src/base/SkTLazy.h"
+#include "src/core/SkDraw.h"
+#include "src/core/SkImageFilterCache.h"
+#include "src/core/SkImageFilter_Base.h"
+#include "src/core/SkImagePriv.h"
+#include "src/core/SkRasterClip.h"
+#include "src/core/SkSpecialImage.h"
+#include "src/core/SkStrikeCache.h"
+#include "src/image/SkImage_Base.h"
+#include "src/text/GlyphRun.h"
+
+struct Bounder {
+ SkRect fBounds;
+ bool fHasBounds;
+
+ Bounder(const SkRect& r, const SkPaint& paint) {
+ if ((fHasBounds = paint.canComputeFastBounds())) {
+ fBounds = paint.computeFastBounds(r, &fBounds);
+ }
+ }
+
+ bool hasBounds() const { return fHasBounds; }
+ const SkRect* bounds() const { return fHasBounds ? &fBounds : nullptr; }
+ operator const SkRect* () const { return this->bounds(); }
+};
+
+class SkDrawTiler {
+ enum {
+ // 8K is 1 too big, since 8K << supersample == 32768 which is too big for SkFixed
+ kMaxDim = 8192 - 1
+ };
+
+ SkBitmapDevice* fDevice;
+ SkPixmap fRootPixmap;
+ SkIRect fSrcBounds;
+
+ // Used for tiling and non-tiling
+ SkDraw fDraw;
+
+ // fCurr... are only used if fNeedTiling
+ SkTLazy<SkPostTranslateMatrixProvider> fTileMatrixProvider;
+ SkRasterClip fTileRC;
+ SkIPoint fOrigin;
+
+ bool fDone, fNeedsTiling;
+
+public:
+ static bool NeedsTiling(SkBitmapDevice* dev) {
+ return dev->width() > kMaxDim || dev->height() > kMaxDim;
+ }
+
+ SkDrawTiler(SkBitmapDevice* dev, const SkRect* bounds) : fDevice(dev) {
+ fDone = false;
+
+ // we need fDst to be set, and if we're actually drawing, to dirty the genID
+ if (!dev->accessPixels(&fRootPixmap)) {
+ // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
+ fRootPixmap.reset(dev->imageInfo(), nullptr, 0);
+ }
+
+ // do a quick check, so we don't even have to process "bounds" if there is no need
+ const SkIRect clipR = dev->fRCStack.rc().getBounds();
+ fNeedsTiling = clipR.right() > kMaxDim || clipR.bottom() > kMaxDim;
+ if (fNeedsTiling) {
+ if (bounds) {
+ // Make sure we round first, and then intersect. We can't rely on promoting the
+ // clipR to floats (and then intersecting with devBounds) since promoting
+ // int --> float can make the float larger than the int.
+ // rounding(out) first runs the risk of clamping if the float is larger an intmax
+ // but our roundOut() is saturating, which is fine for this use case
+ //
+ // e.g. the older version of this code did this:
+ // devBounds = mapRect(bounds);
+ // if (devBounds.intersect(SkRect::Make(clipR))) {
+ // fSrcBounds = devBounds.roundOut();
+ // The problem being that the promotion of clipR to SkRect was unreliable
+ //
+ fSrcBounds = dev->localToDevice().mapRect(*bounds).roundOut();
+ if (fSrcBounds.intersect(clipR)) {
+ // Check again, now that we have computed srcbounds.
+ fNeedsTiling = fSrcBounds.right() > kMaxDim || fSrcBounds.bottom() > kMaxDim;
+ } else {
+ fNeedsTiling = false;
+ fDone = true;
+ }
+ } else {
+ fSrcBounds = clipR;
+ }
+ }
+
+ if (fNeedsTiling) {
+ // fDraw.fDst and fMatrixProvider are reset each time in setupTileDraw()
+ fDraw.fRC = &fTileRC;
+ // we'll step/increase it before using it
+ fOrigin.set(fSrcBounds.fLeft - kMaxDim, fSrcBounds.fTop);
+ } else {
+ // don't reference fSrcBounds, as it may not have been set
+ fDraw.fDst = fRootPixmap;
+ fDraw.fMatrixProvider = dev;
+ fDraw.fRC = &dev->fRCStack.rc();
+ fOrigin.set(0, 0);
+ }
+
+ fDraw.fProps = &fDevice->surfaceProps();
+ }
+
+ bool needsTiling() const { return fNeedsTiling; }
+
+ const SkDraw* next() {
+ if (fDone) {
+ return nullptr;
+ }
+ if (fNeedsTiling) {
+ do {
+ this->stepAndSetupTileDraw(); // might set the clip to empty and fDone to true
+ } while (!fDone && fTileRC.isEmpty());
+ // if we exit the loop and we're still empty, we're (past) done
+ if (fTileRC.isEmpty()) {
+ SkASSERT(fDone);
+ return nullptr;
+ }
+ SkASSERT(!fTileRC.isEmpty());
+ } else {
+ fDone = true; // only draw untiled once
+ }
+ return &fDraw;
+ }
+
+private:
+ void stepAndSetupTileDraw() {
+ SkASSERT(!fDone);
+ SkASSERT(fNeedsTiling);
+
+ // We do fRootPixmap.width() - kMaxDim instead of fOrigin.fX + kMaxDim to avoid overflow.
+ if (fOrigin.fX >= fSrcBounds.fRight - kMaxDim) { // too far
+ fOrigin.fX = fSrcBounds.fLeft;
+ fOrigin.fY += kMaxDim;
+ } else {
+ fOrigin.fX += kMaxDim;
+ }
+ // fDone = next origin will be invalid.
+ fDone = fOrigin.fX >= fSrcBounds.fRight - kMaxDim &&
+ fOrigin.fY >= fSrcBounds.fBottom - kMaxDim;
+
+ SkIRect bounds = SkIRect::MakeXYWH(fOrigin.x(), fOrigin.y(), kMaxDim, kMaxDim);
+ SkASSERT(!bounds.isEmpty());
+ bool success = fRootPixmap.extractSubset(&fDraw.fDst, bounds);
+ SkASSERT_RELEASE(success);
+ // now don't use bounds, since fDst has the clipped dimensions.
+
+ fDraw.fMatrixProvider = fTileMatrixProvider.init(fDevice->asMatrixProvider(),
+ SkIntToScalar(-fOrigin.x()),
+ SkIntToScalar(-fOrigin.y()));
+ fDevice->fRCStack.rc().translate(-fOrigin.x(), -fOrigin.y(), &fTileRC);
+ fTileRC.op(SkIRect::MakeWH(fDraw.fDst.width(), fDraw.fDst.height()),
+ SkClipOp::kIntersect);
+ }
+};
+
+// Passing a bounds allows the tiler to only visit the dst-tiles that might intersect the
+// drawing. If null is passed, the tiler has to visit everywhere. The bounds is expected to be
+// in local coordinates, as the tiler itself will transform that into device coordinates.
+//
+#define LOOP_TILER(code, boundsPtr) \
+ SkDrawTiler priv_tiler(this, boundsPtr); \
+ while (const SkDraw* priv_draw = priv_tiler.next()) { \
+ priv_draw->code; \
+ }
+
+// Helper to create an SkDraw from a device
+class SkBitmapDevice::BDDraw : public SkDraw {
+public:
+ BDDraw(SkBitmapDevice* dev) {
+ // we need fDst to be set, and if we're actually drawing, to dirty the genID
+ if (!dev->accessPixels(&fDst)) {
+ // NoDrawDevice uses us (why?) so we have to catch this case w/ no pixels
+ fDst.reset(dev->imageInfo(), nullptr, 0);
+ }
+ fMatrixProvider = dev;
+ fRC = &dev->fRCStack.rc();
+ }
+};
+
+static bool valid_for_bitmap_device(const SkImageInfo& info,
+ SkAlphaType* newAlphaType) {
+ if (info.width() < 0 || info.height() < 0 || kUnknown_SkColorType == info.colorType()) {
+ return false;
+ }
+
+ if (newAlphaType) {
+ *newAlphaType = SkColorTypeIsAlwaysOpaque(info.colorType()) ? kOpaque_SkAlphaType
+ : info.alphaType();
+ }
+
+ return true;
+}
+
+SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap)
+ : INHERITED(bitmap.info(), SkSurfaceProps())
+ , fBitmap(bitmap)
+ , fRCStack(bitmap.width(), bitmap.height())
+ , fGlyphPainter(this->surfaceProps(), bitmap.colorType(), bitmap.colorSpace()) {
+ SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
+}
+
+SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) {
+ return Create(info, SkSurfaceProps());
+}
+
+SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps,
+ SkRasterHandleAllocator::Handle hndl)
+ : INHERITED(bitmap.info(), surfaceProps)
+ , fBitmap(bitmap)
+ , fRasterHandle(hndl)
+ , fRCStack(bitmap.width(), bitmap.height())
+ , fGlyphPainter(this->surfaceProps(), bitmap.colorType(), bitmap.colorSpace()) {
+ SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
+}
+
+SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
+ const SkSurfaceProps& surfaceProps,
+ SkRasterHandleAllocator* allocator) {
+ SkAlphaType newAT = origInfo.alphaType();
+ if (!valid_for_bitmap_device(origInfo, &newAT)) {
+ return nullptr;
+ }
+
+ SkRasterHandleAllocator::Handle hndl = nullptr;
+ const SkImageInfo info = origInfo.makeAlphaType(newAT);
+ SkBitmap bitmap;
+
+ if (kUnknown_SkColorType == info.colorType()) {
+ if (!bitmap.setInfo(info)) {
+ return nullptr;
+ }
+ } else if (allocator) {
+ hndl = allocator->allocBitmap(info, &bitmap);
+ if (!hndl) {
+ return nullptr;
+ }
+ } else if (info.isOpaque()) {
+ // If this bitmap is opaque, we don't have any sensible default color,
+ // so we just return uninitialized pixels.
+ if (!bitmap.tryAllocPixels(info)) {
+ return nullptr;
+ }
+ } else {
+ // This bitmap has transparency, so we'll zero the pixels (to transparent).
+ // We use the flag as a faster alloc-then-eraseColor(SK_ColorTRANSPARENT).
+ if (!bitmap.tryAllocPixelsFlags(info, SkBitmap::kZeroPixels_AllocFlag)) {
+ return nullptr;
+ }
+ }
+
+ return new SkBitmapDevice(bitmap, surfaceProps, hndl);
+}
+
+void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
+ SkASSERT(bm.width() == fBitmap.width());
+ SkASSERT(bm.height() == fBitmap.height());
+ fBitmap = bm; // intent is to use bm's pixelRef (and rowbytes/config)
+ this->privateResize(fBitmap.info().width(), fBitmap.info().height());
+}
+
+SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint* layerPaint) {
+ const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
+
+ // Need to force L32 for now if we have an image filter.
+ // If filters ever support other colortypes, e.g. F16, we can modify this check.
+ SkImageInfo info = cinfo.fInfo;
+ if (layerPaint && layerPaint->getImageFilter()) {
+ // TODO: can we query the imagefilter, to see if it can handle floats (so we don't always
+ // use N32 when the layer itself was float)?
+ info = info.makeColorType(kN32_SkColorType);
+ }
+
+ return SkBitmapDevice::Create(info, surfaceProps, cinfo.fAllocator);
+}
+
+bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) {
+ if (this->onPeekPixels(pmap)) {
+ fBitmap.notifyPixelsChanged();
+ return true;
+ }
+ return false;
+}
+
+bool SkBitmapDevice::onPeekPixels(SkPixmap* pmap) {
+ const SkImageInfo info = fBitmap.info();
+ if (fBitmap.getPixels() && (kUnknown_SkColorType != info.colorType())) {
+ pmap->reset(fBitmap.info(), fBitmap.getPixels(), fBitmap.rowBytes());
+ return true;
+ }
+ return false;
+}
+
+bool SkBitmapDevice::onWritePixels(const SkPixmap& pm, int x, int y) {
+ // since we don't stop creating un-pixeled devices yet, check for no pixels here
+ if (nullptr == fBitmap.getPixels()) {
+ return false;
+ }
+
+ if (fBitmap.writePixels(pm, x, y)) {
+ fBitmap.notifyPixelsChanged();
+ return true;
+ }
+ return false;
+}
+
+bool SkBitmapDevice::onReadPixels(const SkPixmap& pm, int x, int y) {
+ return fBitmap.readPixels(pm, x, y);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkBitmapDevice::drawPaint(const SkPaint& paint) {
+ BDDraw(this).drawPaint(paint);
+}
+
+void SkBitmapDevice::drawPoints(SkCanvas::PointMode mode, size_t count,
+ const SkPoint pts[], const SkPaint& paint) {
+ LOOP_TILER( drawPoints(mode, count, pts, paint, nullptr), nullptr)
+}
+
+void SkBitmapDevice::drawRect(const SkRect& r, const SkPaint& paint) {
+ LOOP_TILER( drawRect(r, paint), Bounder(r, paint))
+}
+
+void SkBitmapDevice::drawOval(const SkRect& oval, const SkPaint& paint) {
+ // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
+ // required to override drawOval.
+ this->drawPath(SkPath::Oval(oval), paint, true);
+}
+
+void SkBitmapDevice::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+#ifdef SK_IGNORE_BLURRED_RRECT_OPT
+ // call the VIRTUAL version, so any subclasses who do handle drawPath aren't
+ // required to override drawRRect.
+ this->drawPath(SkPath::RRect(rrect), paint, true);
+#else
+ LOOP_TILER( drawRRect(rrect, paint), Bounder(rrect.getBounds(), paint))
+#endif
+}
+
+void SkBitmapDevice::drawPath(const SkPath& path,
+ const SkPaint& paint,
+ bool pathIsMutable) {
+ const SkRect* bounds = nullptr;
+ if (SkDrawTiler::NeedsTiling(this) && !path.isInverseFillType()) {
+ bounds = &path.getBounds();
+ }
+ SkDrawTiler tiler(this, bounds ? Bounder(*bounds, paint).bounds() : nullptr);
+ if (tiler.needsTiling()) {
+ pathIsMutable = false;
+ }
+ while (const SkDraw* draw = tiler.next()) {
+ draw->drawPath(path, paint, nullptr, pathIsMutable);
+ }
+}
+
+void SkBitmapDevice::drawBitmap(const SkBitmap& bitmap, const SkMatrix& matrix,
+ const SkRect* dstOrNull, const SkSamplingOptions& sampling,
+ const SkPaint& paint) {
+ const SkRect* bounds = dstOrNull;
+ SkRect storage;
+ if (!bounds && SkDrawTiler::NeedsTiling(this)) {
+ matrix.mapRect(&storage, SkRect::MakeIWH(bitmap.width(), bitmap.height()));
+ Bounder b(storage, paint);
+ if (b.hasBounds()) {
+ storage = *b.bounds();
+ bounds = &storage;
+ }
+ }
+ LOOP_TILER(drawBitmap(bitmap, matrix, dstOrNull, sampling, paint), bounds)
+}
+
+static inline bool CanApplyDstMatrixAsCTM(const SkMatrix& m, const SkPaint& paint) {
+ if (!paint.getMaskFilter()) {
+ return true;
+ }
+
+ // Some mask filters parameters (sigma) depend on the CTM/scale.
+ return m.getType() <= SkMatrix::kTranslate_Mask;
+}
+
+void SkBitmapDevice::drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
+ const SkSamplingOptions& sampling, const SkPaint& paint,
+ SkCanvas::SrcRectConstraint constraint) {
+ SkASSERT(dst.isFinite());
+ SkASSERT(dst.isSorted());
+
+ SkBitmap bitmap;
+ // TODO: Elevate direct context requirement to public API and remove cheat.
+ auto dContext = as_IB(image)->directContext();
+ if (!as_IB(image)->getROPixels(dContext, &bitmap)) {
+ return;
+ }
+
+ SkRect bitmapBounds, tmpSrc, tmpDst;
+ SkBitmap tmpBitmap;
+
+ bitmapBounds.setIWH(bitmap.width(), bitmap.height());
+
+ // Compute matrix from the two rectangles
+ if (src) {
+ tmpSrc = *src;
+ } else {
+ tmpSrc = bitmapBounds;
+ }
+ SkMatrix matrix = SkMatrix::RectToRect(tmpSrc, dst);
+
+ const SkRect* dstPtr = &dst;
+ const SkBitmap* bitmapPtr = &bitmap;
+
+ // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
+ // needed (if the src was clipped). No check needed if src==null.
+ if (src) {
+ if (!bitmapBounds.contains(*src)) {
+ if (!tmpSrc.intersect(bitmapBounds)) {
+ return; // nothing to draw
+ }
+ // recompute dst, based on the smaller tmpSrc
+ matrix.mapRect(&tmpDst, tmpSrc);
+ if (!tmpDst.isFinite()) {
+ return;
+ }
+ dstPtr = &tmpDst;
+ }
+ }
+
+ if (src && !src->contains(bitmapBounds) &&
+ SkCanvas::kFast_SrcRectConstraint == constraint &&
+ sampling != SkSamplingOptions()) {
+ // src is smaller than the bounds of the bitmap, and we are filtering, so we don't know
+ // how much more of the bitmap we need, so we can't use extractSubset or drawBitmap,
+ // but we must use a shader w/ dst bounds (which can access all of the bitmap needed).
+ goto USE_SHADER;
+ }
+
+ if (src) {
+ // since we may need to clamp to the borders of the src rect within
+ // the bitmap, we extract a subset.
+ const SkIRect srcIR = tmpSrc.roundOut();
+ if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
+ return;
+ }
+ bitmapPtr = &tmpBitmap;
+
+ // Since we did an extract, we need to adjust the matrix accordingly
+ SkScalar dx = 0, dy = 0;
+ if (srcIR.fLeft > 0) {
+ dx = SkIntToScalar(srcIR.fLeft);
+ }
+ if (srcIR.fTop > 0) {
+ dy = SkIntToScalar(srcIR.fTop);
+ }
+ if (dx || dy) {
+ matrix.preTranslate(dx, dy);
+ }
+
+#ifdef SK_DRAWBITMAPRECT_FAST_OFFSET
+ SkRect extractedBitmapBounds = SkRect::MakeXYWH(dx, dy,
+ SkIntToScalar(bitmapPtr->width()),
+ SkIntToScalar(bitmapPtr->height()));
+#else
+ SkRect extractedBitmapBounds;
+ extractedBitmapBounds.setIWH(bitmapPtr->width(), bitmapPtr->height());
+#endif
+ if (extractedBitmapBounds == tmpSrc) {
+ // no fractional part in src, we can just call drawBitmap
+ goto USE_DRAWBITMAP;
+ }
+ } else {
+ USE_DRAWBITMAP:
+ // We can go faster by just calling drawBitmap, which will concat the
+ // matrix with the CTM, and try to call drawSprite if it can. If not,
+ // it will make a shader and call drawRect, as we do below.
+ if (CanApplyDstMatrixAsCTM(matrix, paint)) {
+ this->drawBitmap(*bitmapPtr, matrix, dstPtr, sampling, paint);
+ return;
+ }
+ }
+
+ USE_SHADER:
+
+ // construct a shader, so we can call drawRect with the dst
+ auto s = SkMakeBitmapShaderForPaint(paint, *bitmapPtr, SkTileMode::kClamp, SkTileMode::kClamp,
+ sampling, &matrix, kNever_SkCopyPixelsMode);
+ if (!s) {
+ return;
+ }
+
+ SkPaint paintWithShader(paint);
+ paintWithShader.setStyle(SkPaint::kFill_Style);
+ paintWithShader.setShader(std::move(s));
+
+ // Call ourself, in case the subclass wanted to share this setup code
+ // but handle the drawRect code themselves.
+ this->drawRect(*dstPtr, paintWithShader);
+}
+
+void SkBitmapDevice::onDrawGlyphRunList(SkCanvas* canvas,
+ const sktext::GlyphRunList& glyphRunList,
+ const SkPaint& initialPaint,
+ const SkPaint& drawingPaint) {
+ SkASSERT(!glyphRunList.hasRSXForm());
+ LOOP_TILER( drawGlyphRunList(canvas, &fGlyphPainter, glyphRunList, drawingPaint), nullptr )
+}
+
+void SkBitmapDevice::drawVertices(const SkVertices* vertices,
+ sk_sp<SkBlender> blender,
+ const SkPaint& paint,
+ bool skipColorXform) {
+#ifdef SK_LEGACY_IGNORE_DRAW_VERTICES_BLEND_WITH_NO_SHADER
+ if (!paint.getShader()) {
+ blender = SkBlender::Mode(SkBlendMode::kDst);
+ }
+#endif
+ BDDraw(this).drawVertices(vertices, std::move(blender), paint, skipColorXform);
+}
+
+#ifdef SK_ENABLE_SKSL
+void SkBitmapDevice::drawMesh(const SkMesh&, sk_sp<SkBlender>, const SkPaint&) {
+ // TODO: Implement
+}
+#endif
+
+void SkBitmapDevice::drawAtlas(const SkRSXform xform[],
+ const SkRect tex[],
+ const SkColor colors[],
+ int count,
+ sk_sp<SkBlender> blender,
+ const SkPaint& paint) {
+ // set this to true for performance comparisons with the old drawVertices way
+ if ((false)) {
+ this->INHERITED::drawAtlas(xform, tex, colors, count, std::move(blender), paint);
+ return;
+ }
+ BDDraw(this).drawAtlas(xform, tex, colors, count, std::move(blender), paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkBitmapDevice::drawDevice(SkBaseDevice* device, const SkSamplingOptions& sampling,
+ const SkPaint& paint) {
+ SkASSERT(!paint.getImageFilter());
+ SkASSERT(!paint.getMaskFilter());
+
+ this->INHERITED::drawDevice(device, sampling, paint);
+}
+
+void SkBitmapDevice::drawSpecial(SkSpecialImage* src,
+ const SkMatrix& localToDevice,
+ const SkSamplingOptions& sampling,
+ const SkPaint& paint) {
+ SkASSERT(!paint.getImageFilter());
+ SkASSERT(!paint.getMaskFilter());
+ SkASSERT(!src->isTextureBacked());
+
+ SkBitmap resultBM;
+ if (src->getROPixels(&resultBM)) {
+ SkDraw draw;
+ SkMatrixProvider matrixProvider(localToDevice);
+ if (!this->accessPixels(&draw.fDst)) {
+ return; // no pixels to draw to so skip it
+ }
+ draw.fMatrixProvider = &matrixProvider;
+ draw.fRC = &fRCStack.rc();
+ draw.drawBitmap(resultBM, SkMatrix::I(), nullptr, sampling, paint);
+ }
+}
+sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkBitmap& bitmap) {
+ return SkSpecialImage::MakeFromRaster(bitmap.bounds(), bitmap, this->surfaceProps());
+}
+
+sk_sp<SkSpecialImage> SkBitmapDevice::makeSpecial(const SkImage* image) {
+ return SkSpecialImage::MakeFromImage(nullptr, SkIRect::MakeWH(image->width(), image->height()),
+ image->makeNonTextureImage(), this->surfaceProps());
+}
+
+sk_sp<SkSpecialImage> SkBitmapDevice::snapSpecial(const SkIRect& bounds, bool forceCopy) {
+ if (forceCopy) {
+ return SkSpecialImage::CopyFromRaster(bounds, fBitmap, this->surfaceProps());
+ } else {
+ return SkSpecialImage::MakeFromRaster(bounds, fBitmap, this->surfaceProps());
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+sk_sp<SkSurface> SkBitmapDevice::makeSurface(const SkImageInfo& info, const SkSurfaceProps& props) {
+ return SkSurface::MakeRaster(info, &props);
+}
+
+SkImageFilterCache* SkBitmapDevice::getImageFilterCache() {
+ SkImageFilterCache* cache = SkImageFilterCache::Get();
+ cache->ref();
+ return cache;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+void SkBitmapDevice::onSave() {
+ fRCStack.save();
+}
+
+void SkBitmapDevice::onRestore() {
+ fRCStack.restore();
+}
+
+void SkBitmapDevice::onClipRect(const SkRect& rect, SkClipOp op, bool aa) {
+ fRCStack.clipRect(this->localToDevice(), rect, op, aa);
+}
+
+void SkBitmapDevice::onClipRRect(const SkRRect& rrect, SkClipOp op, bool aa) {
+ fRCStack.clipRRect(this->localToDevice(), rrect, op, aa);
+}
+
+void SkBitmapDevice::onClipPath(const SkPath& path, SkClipOp op, bool aa) {
+ fRCStack.clipPath(this->localToDevice(), path, op, aa);
+}
+
+void SkBitmapDevice::onClipShader(sk_sp<SkShader> sh) {
+ fRCStack.clipShader(std::move(sh));
+}
+
+void SkBitmapDevice::onClipRegion(const SkRegion& rgn, SkClipOp op) {
+ SkIPoint origin = this->getOrigin();
+ SkRegion tmp;
+ const SkRegion* ptr = &rgn;
+ if (origin.fX | origin.fY) {
+ // translate from "global/canvas" coordinates to relative to this device
+ rgn.translate(-origin.fX, -origin.fY, &tmp);
+ ptr = &tmp;
+ }
+ fRCStack.clipRegion(*ptr, op);
+}
+
+void SkBitmapDevice::onReplaceClip(const SkIRect& rect) {
+ // Transform from "global/canvas" coordinates to relative to this device
+ SkRect deviceRect = SkMatrixPriv::MapRect(this->globalToDevice(), SkRect::Make(rect));
+ fRCStack.replaceClip(deviceRect.round());
+}
+
+bool SkBitmapDevice::onClipIsWideOpen() const {
+ const SkRasterClip& rc = fRCStack.rc();
+ // If we're AA, we can't be wide-open (we would represent that as BW)
+ return rc.isBW() && rc.bwRgn().isRect() &&
+ rc.bwRgn().getBounds() == SkIRect{0, 0, this->width(), this->height()};
+}
+
+bool SkBitmapDevice::onClipIsAA() const {
+ const SkRasterClip& rc = fRCStack.rc();
+ return !rc.isEmpty() && rc.isAA();
+}
+
+void SkBitmapDevice::onAsRgnClip(SkRegion* rgn) const {
+ const SkRasterClip& rc = fRCStack.rc();
+ if (rc.isAA()) {
+ rgn->setRect(rc.getBounds());
+ } else {
+ *rgn = rc.bwRgn();
+ }
+}
+
+void SkBitmapDevice::validateDevBounds(const SkIRect& drawClipBounds) {
+#ifdef SK_DEBUG
+ const SkIRect& stackBounds = fRCStack.rc().getBounds();
+ SkASSERT(drawClipBounds == stackBounds);
+#endif
+}
+
+SkBaseDevice::ClipType SkBitmapDevice::onGetClipType() const {
+ const SkRasterClip& rc = fRCStack.rc();
+ if (rc.isEmpty()) {
+ return ClipType::kEmpty;
+ } else if (rc.isRect() && !SkToBool(rc.clipShader())) {
+ return ClipType::kRect;
+ } else {
+ return ClipType::kComplex;
+ }
+}
+
+SkIRect SkBitmapDevice::onDevClipBounds() const {
+ return fRCStack.rc().getBounds();
+}