From 910ca0fd014ad9263e54e0cfa4199768b6c7fade Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Wed, 25 Apr 2018 13:04:05 -0400 Subject: Experiment to track coverage in a layer Bug: skia: Change-Id: I5ed334f63e64991944394dc8103092a2c6280546 Reviewed-on: https://skia-review.googlesource.com/122000 Commit-Queue: Mike Reed Reviewed-by: Mike Klein --- docs/SkCanvas_Reference.bmh | 5 +++++ gm/savelayer.cpp | 39 +++++++++++++++++++++++++++++++++++++ include/core/SkCanvas.h | 2 ++ src/core/SkAutoBlitterChoose.h | 29 ++++++++++++++++++---------- src/core/SkBitmapDevice.cpp | 44 ++++++++++++++++++++++++++++++++++++++---- src/core/SkBitmapDevice.h | 14 ++++++++++++-- src/core/SkBlitter.h | 29 ++++++++++++++++++++++++++++ src/core/SkCanvas.cpp | 6 ++++-- src/core/SkDevice.h | 3 +++ src/core/SkDraw.cpp | 27 ++++++++++++-------------- src/core/SkDraw.h | 3 +++ src/core/SkDraw_vertices.cpp | 2 +- tests/LayerDrawLooperTest.cpp | 3 ++- 13 files changed, 171 insertions(+), 35 deletions(-) diff --git a/docs/SkCanvas_Reference.bmh b/docs/SkCanvas_Reference.bmh index de33770122..0bce15e947 100644 --- a/docs/SkCanvas_Reference.bmh +++ b/docs/SkCanvas_Reference.bmh @@ -1684,6 +1684,7 @@ documentation purposes, this enum is named rather than anonymous enum { kPreserveLCDText_SaveLayerFlag = 1 << 1, kInitWithPrevious_SaveLayerFlag = 1 << 2, + kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag = 1 << 3, kDontClipToLayer_Legacy_SaveLayerFlag = kDontClipToLayer_PrivateSaveLayerFlag, }; ## @@ -1700,6 +1701,10 @@ defining how Layer allocated by saveLayer operates. Initializes Layer with the contents of the previous Layer. ## +#Const kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag 8 + Experimental -- don't use +## + #Const kDontClipToLayer_Legacy_SaveLayerFlag 0x80000000 #Deprecated soon ## diff --git a/gm/savelayer.cpp b/gm/savelayer.cpp index 32f95bfc7d..77218206c7 100644 --- a/gm/savelayer.cpp +++ b/gm/savelayer.cpp @@ -238,3 +238,42 @@ DEF_SIMPLE_GM(savelayer_clipmask, canvas, 1200, 1200) { } } } + +DEF_SIMPLE_GM(savelayer_coverage, canvas, 500, 500) { + canvas->saveLayer(nullptr, nullptr); + + SkRect r = { 0, 0, 200, 200 }; + SkPaint layerPaint; + layerPaint.setBlendMode(SkBlendMode::kModulate); + + auto image = GetResourceAsImage("images/mandrill_128.png"); + + auto proc = [layerPaint](SkCanvas* canvas, SkCanvas::SaveLayerRec& rec) { + SkPaint paint; + paint.setColor(SK_ColorRED); + + canvas->saveLayer(rec); + canvas->drawCircle(100, 100, 50, paint); + paint.setColor(0x8800FF00); + canvas->drawRect({10, 90, 190, 110}, paint); + canvas->restore(); + }; + + const int yflags[] = { 0, SkCanvas::kInitWithPrevious_SaveLayerFlag }; + for (int y = 0; y <= 1; ++y) { + const int xflags[] = { 0, SkCanvas::kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag }; + for (int x = 0; x <= 1; ++x) { + canvas->save(); + canvas->translate(x * 200.f, y * 200.f); + + SkCanvas::SaveLayerRec rec(&r, &layerPaint, yflags[y] | xflags[x]); + canvas->drawImageRect(image, r, nullptr); + proc(canvas, rec); + + canvas->restore(); + } + } + + canvas->restore(); +} + diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index a819cda06a..73f7a3e8d9 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -609,6 +609,8 @@ public: /** Initializes layer with the contents of the previous layer. */ kInitWithPrevious_SaveLayerFlag = 1 << 2, + kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag = 1 << 3, + #ifdef SK_SUPPORT_LEGACY_CLIPTOLAYERFLAG /** To be deprecated soon. */ kDontClipToLayer_Legacy_SaveLayerFlag = kDontClipToLayer_PrivateSaveLayerFlag, diff --git a/src/core/SkAutoBlitterChoose.h b/src/core/SkAutoBlitterChoose.h index 3c2e8afdb8..b6c0433fbf 100644 --- a/src/core/SkAutoBlitterChoose.h +++ b/src/core/SkAutoBlitterChoose.h @@ -10,6 +10,7 @@ #include "SkArenaAlloc.h" #include "SkBlitter.h" +#include "SkDraw.h" class SkMatrix; class SkPaint; @@ -17,27 +18,35 @@ class SkPixmap; class SkAutoBlitterChoose : SkNoncopyable { public: - SkAutoBlitterChoose() { - fBlitter = nullptr; - } - SkAutoBlitterChoose(const SkPixmap& dst, const SkMatrix& matrix, - const SkPaint& paint, bool drawCoverage = false) { - fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage); + SkAutoBlitterChoose() {} + SkAutoBlitterChoose(const SkDraw& draw, const SkMatrix* matrix, const SkPaint& paint, + bool drawCoverage = false) { + this->choose(draw, matrix, paint, drawCoverage); } SkBlitter* operator->() { return fBlitter; } SkBlitter* get() const { return fBlitter; } - SkBlitter* choose(const SkPixmap& dst, const SkMatrix& matrix, - const SkPaint& paint, bool drawCoverage = false) { + SkBlitter* choose(const SkDraw& draw, const SkMatrix* matrix, const SkPaint& paint, + bool drawCoverage = false) { SkASSERT(!fBlitter); - fBlitter = SkBlitter::Choose(dst, matrix, paint, &fAlloc, drawCoverage); + if (!matrix) { + matrix = draw.fMatrix; + } + fBlitter = SkBlitter::Choose(draw.fDst, *matrix, paint, &fAlloc, drawCoverage); + + if (draw.fCoverage) { + // hmm, why can't choose ignore the paint if drawCoverage is true? + SkBlitter* coverageBlitter = SkBlitter::Choose(*draw.fCoverage, *matrix, SkPaint(), + &fAlloc, true); + fBlitter = fAlloc.make(fBlitter, coverageBlitter); + } return fBlitter; } private: // Owned by fAlloc, which will handle the delete. - SkBlitter* fBlitter; + SkBlitter* fBlitter = nullptr; SkSTArenaAlloc fAlloc; }; diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index 507dd352e4..b49818c2a1 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -10,6 +10,7 @@ #include "SkImageFilter.h" #include "SkImageFilterCache.h" #include "SkMallocPixelRef.h" +#include "SkMakeUnique.h" #include "SkMatrix.h" #include "SkPaint.h" #include "SkPath.h" @@ -104,6 +105,8 @@ public: fDraw.fMatrix = &dev->ctm(); fDraw.fRC = &dev->fRCStack.rc(); fOrigin.set(0, 0); + + fDraw.fCoverage = dev->accessCoverage(); } } @@ -180,6 +183,7 @@ public: } fMatrix = &dev->ctm(); fRC = &dev->fRCStack.rc(); + fCoverage = dev->accessCoverage(); } }; @@ -238,17 +242,24 @@ SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) { } SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, - SkRasterHandleAllocator::Handle hndl) + SkRasterHandleAllocator::Handle hndl, const SkBitmap* coverage) : INHERITED(bitmap.info(), surfaceProps) , fBitmap(bitmap) , fRasterHandle(hndl) , fRCStack(bitmap.width(), bitmap.height()) { SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr)); + + if (coverage) { + SkASSERT(coverage->width() == bitmap.width()); + SkASSERT(coverage->height() == bitmap.height()); + fCoverage = skstd::make_unique(*coverage); + } } SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, const SkSurfaceProps& surfaceProps, + bool trackCoverage, SkRasterHandleAllocator* allocator) { SkAlphaType newAT = origInfo.alphaType(); if (!valid_for_bitmap_device(origInfo, &newAT)) { @@ -282,7 +293,16 @@ SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, } } - return new SkBitmapDevice(bitmap, surfaceProps, hndl); + SkBitmap coverage; + if (trackCoverage) { + SkImageInfo ci = SkImageInfo::Make(info.width(), info.height(), kAlpha_8_SkColorType, + kPremul_SkAlphaType); + if (!coverage.tryAllocPixelsFlags(ci, SkBitmap::kZeroPixels_AllocFlag)) { + return nullptr; + } + } + + return new SkBitmapDevice(bitmap, surfaceProps, hndl, trackCoverage ? &coverage : nullptr); } void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { @@ -294,7 +314,8 @@ void SkBitmapDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) { SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) { const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry); - return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator); + return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fTrackCoverage, + cinfo.fAllocator); } bool SkBitmapDevice::onAccessPixels(SkPixmap* pmap) { @@ -593,7 +614,22 @@ void SkBitmapDevice::drawDevice(SkBaseDevice* device, int x, int y, const SkPain if (paint->getMaskFilter()) { paint.writable()->setMaskFilter(paint->getMaskFilter()->makeWithMatrix(this->ctm())); } - this->drawSprite(static_cast(device)->fBitmap, x, y, *paint); + + // hack to test coverage + SkBitmapDevice* src = static_cast(device); + if (src->fCoverage) { + SkDraw draw; + draw.fDst = fBitmap.pixmap(); + draw.fMatrix = &SkMatrix::I(); + draw.fRC = &fRCStack.rc(); + SkPaint paint(origPaint); + paint.setShader(SkShader::MakeBitmapShader(src->fBitmap, SkShader::kClamp_TileMode, + SkShader::kClamp_TileMode, nullptr)); + draw.drawBitmap(*src->fCoverage.get(), + SkMatrix::MakeTrans(SkIntToScalar(x),SkIntToScalar(y)), nullptr, paint); + } else { + this->drawSprite(src->fBitmap, x, y, *paint); + } } /////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/SkBitmapDevice.h b/src/core/SkBitmapDevice.h index e040b6b0c6..f80cba6ee4 100644 --- a/src/core/SkBitmapDevice.h +++ b/src/core/SkBitmapDevice.h @@ -55,10 +55,19 @@ public: * any drawing to this device will have no effect. */ SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps, - void* externalHandle = nullptr); + void* externalHandle, const SkBitmap* coverage); static SkBitmapDevice* Create(const SkImageInfo&, const SkSurfaceProps&, - SkRasterHandleAllocator* = nullptr); + bool trackCoverage, + SkRasterHandleAllocator*); + + static SkBitmapDevice* Create(const SkImageInfo& info, const SkSurfaceProps& props) { + return Create(info, props, false, nullptr); + } + + const SkPixmap* accessCoverage() const { + return fCoverage ? &fCoverage->pixmap() : nullptr; + } protected: void* getRasterHandle() const override { return fRasterHandle; } @@ -165,6 +174,7 @@ private: SkBitmap fBitmap; void* fRasterHandle = nullptr; SkRasterClipStack fRCStack; + std::unique_ptr fCoverage; // if non-null, will have the same dimensions as fBitmap typedef SkBaseDevice INHERITED; }; diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h index c280ac37b7..cf3edd31eb 100644 --- a/src/core/SkBlitter.h +++ b/src/core/SkBlitter.h @@ -297,4 +297,33 @@ private: SkRgnClipBlitter fRgnBlitter; }; +#define SHARD(code) fA->code; fB->code; + +class SkPairBlitter : public SkBlitter { + SkBlitter* fA = nullptr; + SkBlitter* fB = nullptr; +public: + SkPairBlitter(SkBlitter* a, SkBlitter* b) : fA(a), fB(b) {} + + void blitH(int x, int y, int width) override { SHARD(blitH(x, y, width)) } + void blitAntiH(int x, int y, const SkAlpha alphas[], const int16_t runs[]) override { + SHARD(blitAntiH(x, y, alphas, runs)) + } + void blitV(int x, int y, int height, SkAlpha alpha) override { + SHARD(blitV(x, y, height, alpha)) + } + void blitRect(int x, int y, int width, int height) override { + SHARD(blitRect(x, y, width, height)) + } + void blitAntiRect(int x, int y, int width, int height, + SkAlpha leftAlpha, SkAlpha rightAlpha) override { + SHARD(blitAntiRect(x, y, width, height, leftAlpha, rightAlpha)) + } + void blitMask(const SkMask& mask, const SkIRect& clip) override { SHARD(blitMask(mask, clip)) } + const SkPixmap* justAnOpaqueColor(uint32_t* value) override { return nullptr; } + void blitAntiH2(int x, int y, U8CPU a0, U8CPU a1) override { SHARD(blitAntiH2(x, y, a0, a1)) } + void blitAntiV2(int x, int y, U8CPU a0, U8CPU a1) override { SHARD(blitAntiV2(x, y, a0, a1)) } +}; +#undef SHARD + #endif diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index 3f2439d538..52644727a6 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -644,7 +644,7 @@ SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) { inc_canvas(); - sk_sp device(new SkBitmapDevice(bitmap, fProps)); + sk_sp device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr)); this->init(device.get(), kDefault_InitFlags); } @@ -656,7 +656,7 @@ SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr device(new SkBitmapDevice(bitmap, fProps, hndl)); + sk_sp device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr)); this->init(device.get(), kDefault_InitFlags); } @@ -1058,8 +1058,10 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra const bool preserveLCDText = kOpaque_SkAlphaType == info.alphaType() || (saveLayerFlags & kPreserveLCDText_SaveLayerFlag); const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage; + const bool trackCoverage = SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag); const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo, preserveLCDText, + trackCoverage, fAllocator.get()); newDevice.reset(priorDevice->onCreateDevice(createInfo, paint)); if (!newDevice) { diff --git a/src/core/SkDevice.h b/src/core/SkDevice.h index fb91c8a444..40b9589ce5 100644 --- a/src/core/SkDevice.h +++ b/src/core/SkDevice.h @@ -305,16 +305,19 @@ protected: TileUsage tileUsage, SkPixelGeometry geo, bool preserveLCDText, + bool trackCoverage, SkRasterHandleAllocator* allocator) : fInfo(info) , fTileUsage(tileUsage) , fPixelGeometry(AdjustGeometry(info, tileUsage, geo, preserveLCDText)) + , fTrackCoverage(trackCoverage) , fAllocator(allocator) {} const SkImageInfo fInfo; const TileUsage fTileUsage; const SkPixelGeometry fPixelGeometry; + const bool fTrackCoverage = false; SkRasterHandleAllocator* fAllocator = nullptr; }; diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp index 71d0d13463..362c8b50fd 100644 --- a/src/core/SkDraw.cpp +++ b/src/core/SkDraw.cpp @@ -220,7 +220,7 @@ void SkDraw::drawPaint(const SkPaint& paint) const { } // normal case: use a blitter - SkAutoBlitterChoose blitter(fDst, *fMatrix, paint); + SkAutoBlitterChoose blitter(*this, nullptr, paint); SkScan::FillIRect(devRect, *fRC, blitter.get()); } @@ -497,7 +497,7 @@ void SkDraw::drawPoints(SkCanvas::PointMode mode, size_t count, PtProcRec rec; if (!device && rec.init(mode, paint, fMatrix, fRC)) { - SkAutoBlitterChoose blitter(fDst, *fMatrix, paint); + SkAutoBlitterChoose blitter(*this, nullptr, paint); SkPoint devPts[MAX_DEV_PTS]; const SkMatrix* matrix = fMatrix; @@ -798,7 +798,7 @@ void SkDraw::drawRect(const SkRect& prePaintRect, const SkPaint& paint, return; } - SkAutoBlitterChoose blitterStorage(fDst, *matrix, paint); + SkAutoBlitterChoose blitterStorage(*this, matrix, paint); const SkRasterClip& clip = *fRC; SkBlitter* blitter = blitterStorage.get(); @@ -846,7 +846,7 @@ void SkDraw::drawDevMask(const SkMask& srcM, const SkPaint& paint) const { } SkAutoMaskFreeImage ami(dstM.fImage); - SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); + SkAutoBlitterChoose blitterChooser(*this, nullptr, paint); SkBlitter* blitter = blitterChooser.get(); SkAAClipBlitterWrapper wrapper; @@ -920,7 +920,7 @@ void SkDraw::drawRRect(const SkRRect& rrect, const SkPaint& paint) const { // Transform the rrect into device space. SkRRect devRRect; if (rrect.transform(*fMatrix, &devRRect)) { - SkAutoBlitterChoose blitter(fDst, *fMatrix, paint); + SkAutoBlitterChoose blitter(*this, nullptr, paint); if (as_MFB(paint.getMaskFilter())->filterRRect(devRRect, *fMatrix, *fRC, blitter.get())) { return; // filterRRect() called the blitter, so we're done @@ -954,7 +954,7 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC SkBlitter* blitter = nullptr; SkAutoBlitterChoose blitterStorage; if (nullptr == customBlitter) { - blitter = blitterStorage.choose(fDst, *fMatrix, paint, drawCoverage); + blitter = blitterStorage.choose(*this, nullptr, paint, drawCoverage); } else { blitter = customBlitter; } @@ -1022,9 +1022,8 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC iData->fElement->setDrawFn([proc, devPath, paint, drawCoverage](SkArenaAlloc* alloc, const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) { SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds); - SkAutoBlitterChoose blitterStorage; - proc(devPath, *tileDraw.fRC, blitterStorage.choose(tileDraw.fDst, *tileDraw.fMatrix, - paint, drawCoverage)); + SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage); + proc(devPath, *tileDraw.fRC, blitterStorage.get()); }); } else { // We can use DAA to do scan conversion in the init-once phase. @@ -1036,10 +1035,8 @@ void SkDraw::drawDevPath(const SkPath& devPath, const SkPaint& paint, bool drawC const SkThreadedBMPDevice::DrawState& ds, const SkIRect& tileBounds) { SkASSERT(record->fType != SkDAARecord::Type::kToBeComputed); SkThreadedBMPDevice::TileDraw tileDraw(ds, tileBounds); - SkAutoBlitterChoose blitterStorage; - SkBlitter* blitter = blitterStorage.choose(tileDraw.fDst, *tileDraw.fMatrix, - paint, drawCoverage); - SkScan::AntiFillPath(devPath, *tileDraw.fRC, blitter, record); + SkAutoBlitterChoose blitterStorage(tileDraw, nullptr, paint, drawCoverage); + SkScan::AntiFillPath(devPath, *tileDraw.fRC, blitterStorage.get(), record); }); } } @@ -1547,7 +1544,7 @@ void SkDraw::drawText(const char text[], size_t byteLength, SkScalar x, SkScalar paint, props, this->scalerContextFlags(), fMatrix); // The Blitter Choose needs to be live while using the blitter below. - SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); + SkAutoBlitterChoose blitterChooser(*this, nullptr, paint); SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter()); @@ -1627,7 +1624,7 @@ void SkDraw::drawPosText(const char text[], size_t byteLength, const SkScalar po paint, props, this->scalerContextFlags(), fMatrix); // The Blitter Choose needs to be live while using the blitter below. - SkAutoBlitterChoose blitterChooser(fDst, *fMatrix, paint); + SkAutoBlitterChoose blitterChooser(*this, nullptr, paint); SkAAClipBlitterWrapper wrapper(*fRC, blitterChooser.get()); DrawOneGlyph drawOneGlyph(*this, paint, cache.get(), wrapper.getBlitter()); SkPaint::Align textAlignment = paint.getTextAlign(); diff --git a/src/core/SkDraw.h b/src/core/SkDraw.h index 8598475e2c..eabccf0d8d 100644 --- a/src/core/SkDraw.h +++ b/src/core/SkDraw.h @@ -152,6 +152,9 @@ public: const SkMatrix* fMatrix; // required const SkRasterClip* fRC; // required + // optional, will be same dimensions as fDst if present + const SkPixmap* fCoverage = nullptr; + #ifdef SK_DEBUG void validate() const; #else diff --git a/src/core/SkDraw_vertices.cpp b/src/core/SkDraw_vertices.cpp index 810ded1307..8c1ad1f007 100644 --- a/src/core/SkDraw_vertices.cpp +++ b/src/core/SkDraw_vertices.cpp @@ -294,7 +294,7 @@ void SkDraw::drawVertices(SkVertices::VertexMode vmode, int count, // no colors[] and no texture, stroke hairlines with paint's color. SkPaint p; p.setStyle(SkPaint::kStroke_Style); - SkAutoBlitterChoose blitter(fDst, *fMatrix, p); + SkAutoBlitterChoose blitter(*this, nullptr, p); // Abort early if we failed to create a shader context. if (blitter->isNullBlitter()) { return; diff --git a/tests/LayerDrawLooperTest.cpp b/tests/LayerDrawLooperTest.cpp index f3705b77be..1a1e68b0c8 100644 --- a/tests/LayerDrawLooperTest.cpp +++ b/tests/LayerDrawLooperTest.cpp @@ -27,7 +27,8 @@ static SkBitmap make_bm(int w, int h) { // TODO: can this be derived from SkBaseDevice? class FakeDevice : public SkBitmapDevice { public: - FakeDevice() : INHERITED(make_bm(100, 100), SkSurfaceProps(0, kUnknown_SkPixelGeometry)) { + FakeDevice() : INHERITED(make_bm(100, 100), SkSurfaceProps(0, kUnknown_SkPixelGeometry), + nullptr, nullptr) { } void drawRect(const SkRect& r, const SkPaint& paint) override { -- cgit v1.2.3