class SkPath;
class SkPixelRef;
class SkPixmap;
+class SkRasterHandleAllocator;
class SkRRect;
class SkSurface;
struct SkPoint;
* valid for the bitmap to have no pixels associated with it. In that case,
* any drawing to this device will have no effect.
*/
- SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps);
+ SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps,
+ void* externalHandle = nullptr);
- static SkBitmapDevice* Create(const SkImageInfo&, const SkSurfaceProps&);
+ static SkBitmapDevice* Create(const SkImageInfo&, const SkSurfaceProps&,
+ SkRasterHandleAllocator* = nullptr);
protected:
bool onShouldDisableLCD(const SkPaint&) const override;
+ void* getRasterHandle() const override { return fRasterHandle; }
/** These are called inside the per-device-layer loop for each draw call.
When these are called, we have already applied any saveLayer operations,
SkImageFilterCache* getImageFilterCache() override;
SkBitmap fBitmap;
+ void* fRasterHandle = nullptr;
void setNewSize(const SkISize&); // Used by SkCanvas for resetForNextPicture().
#include "SkDeque.h"
#include "SkImage.h"
#include "SkPaint.h"
+#include "SkRasterHandleAllocator.h"
#include "SkRefCnt.h"
#include "SkRegion.h"
#include "SkSurfaceProps.h"
*/
void* accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin = NULL);
+ SkRasterHandleAllocator::Handle accessTopRasterHandle() const;
+
/**
* If the canvas has readable pixels in its base layer (and is not recording to a picture
* or other non-raster target) and has direct access to its pixels (i.e. they are in
int fSaveCount; // value returned by getSaveCount()
SkMetaData* fMetaData;
+ std::unique_ptr<SkRasterHandleAllocator> fAllocator;
SkSurface_Base* fSurfaceBase;
SkSurface_Base* getSurfaceBase() const { return fSurfaceBase; }
friend class SkPicturePlayback; // SaveFlagsToSaveLayerFlags
friend class SkDeferredCanvas; // For use of resetForNextPicture
friend class SkOverdrawCanvas;
+ friend class SkRasterHandleAllocator;
enum InitFlags {
kDefault_InitFlags = 0,
};
SkCanvas(const SkIRect& bounds, InitFlags);
SkCanvas(SkBaseDevice* device, InitFlags);
+ SkCanvas(const SkBitmap&, std::unique_ptr<SkRasterHandleAllocator>,
+ SkRasterHandleAllocator::Handle);
void resetForNextPicture(const SkIRect& bounds);
struct SkIRect;
class SkMatrix;
class SkMetaData;
+class SkRasterHandleAllocator;
class SkRegion;
class SkSpecialImage;
class GrRenderTarget;
*/
const SkIPoint& getOrigin() const { return fOrigin; }
+ virtual void* getRasterHandle() const { return nullptr; }
+
protected:
enum TileUsage {
kPossible_TileUsage, //!< the created device may be drawn tiled
CreateInfo(const SkImageInfo& info,
TileUsage tileUsage,
SkPixelGeometry geo,
- bool preserveLCDText)
+ bool preserveLCDText,
+ SkRasterHandleAllocator* allocator)
: fInfo(info)
, fTileUsage(tileUsage)
, fPixelGeometry(AdjustGeometry(info, tileUsage, geo, preserveLCDText))
+ , fAllocator(allocator)
{}
const SkImageInfo fInfo;
const TileUsage fTileUsage;
const SkPixelGeometry fPixelGeometry;
+ SkRasterHandleAllocator* fAllocator = nullptr;
};
/**
--- /dev/null
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkRasterHandleAllocator_DEFINED
+#define SkRasterHandleAllocator_DEFINED
+
+#include "SkImageInfo.h"
+
+class SkCanvas;
+class SkMatrix;
+
+/**
+ * If a client wants to control the allocation of raster layers in a canvas, it should subclass
+ * SkRasterHandleAllocator. This allocator performs two tasks:
+ * 1. controls how the memory for the pixels is allocated
+ * 2. associates a "handle" to a private object that can track the matrix/clip of the SkCanvas
+ *
+ * This example allocates a canvas, and defers to the allocator to create the base layer.
+ *
+ * std::unique_ptr<SkCanvas> canvas = SkRasterHandleAllocator::MakeCanvas(
+ * SkImageInfo::Make(...),
+ * skstd::make_unique<MySubclassRasterHandleAllocator>(...),
+ * nullptr);
+ *
+ * If you have already allocated the base layer (and its handle, release-proc etc.) then you
+ * can pass those in using the last parameter to MakeCanvas().
+ *
+ * Regardless of how the base layer is allocated, each time canvas->saveLayer() is called,
+ * your allocator's allocHandle() will be called.
+ */
+class SK_API SkRasterHandleAllocator {
+public:
+ virtual ~SkRasterHandleAllocator() {}
+
+ // The value that is returned to clients of the canvas that has this allocator installed.
+ typedef void* Handle;
+
+ struct Rec {
+ // When the allocation goes out of scope, this proc is called to free everything associated
+ // with it: the pixels, the "handle", etc. This is passed the pixel address and fReleaseCtx.
+ void (*fReleaseProc)(void* pixels, void* ctx);
+ void* fReleaseCtx; // context passed to fReleaseProc
+ void* fPixels; // pixels for this allocation
+ size_t fRowBytes; // rowbytes for these pixels
+ Handle fHandle; // public handle returned by SkCanvas::accessTopRasterHandle()
+ };
+
+ /**
+ * Given a requested info, allocate the corresponding pixels/rowbytes, and whatever handle
+ * is desired to give clients access to those pixels. The rec also contains a proc and context
+ * which will be called when this allocation goes out of scope.
+ *
+ * e.g.
+ * when canvas->saveLayer() is called, the allocator will be called to allocate the pixels
+ * for the layer. When canvas->restore() is called, the fReleaseProc will be called.
+ */
+ virtual bool allocHandle(const SkImageInfo&, Rec*) = 0;
+
+ /**
+ * Clients access the handle for a given layer by calling SkCanvas::accessTopRasterHandle().
+ * To allow the handle to reflect the current matrix/clip in the canvs, updateHandle() is
+ * is called. The subclass is responsible to update the handle as it sees fit.
+ */
+ virtual void updateHandle(Handle, const SkMatrix&, const SkIRect&) = 0;
+
+ /**
+ * This creates a canvas which will use the allocator to manage pixel allocations, including
+ * all calls to saveLayer().
+ *
+ * If rec is non-null, then it will be used as the base-layer of pixels/handle.
+ * If rec is null, then the allocator will be called for the base-layer as well.
+ */
+ static std::unique_ptr<SkCanvas> MakeCanvas(std::unique_ptr<SkRasterHandleAllocator>,
+ const SkImageInfo&, const Rec* rec = nullptr);
+
+private:
+ friend class SkBitmapDevice;
+
+ Handle allocBitmap(const SkImageInfo&, SkBitmap*);
+};
+
+#endif
class SkBitmap;
class SkData;
+class SkPixmap;
class SkStreamRewindable;
+SK_API CGContextRef SkCreateCGContext(const SkPixmap&);
+
/**
* Given a CGImage, allocate an SkBitmap and copy the image's pixels into it. If scaleToFit is not
* null, use it to determine the size of the bitmap, and scale the image to fill the bitmap.
#include "SkView.h"
#include "SkCanvas.h"
#include "SkGradientShader.h"
+#include "SkMakeUnique.h"
static sk_sp<SkShader> make_grad(SkScalar w, SkScalar h) {
SkColor colors[] = { 0xFF000000, 0xFF333333 };
static SkView* MyFactory() { return new BigGradientView; }
static SkViewRegister reg(MyFactory);
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_BUILD_FOR_MAC
+
+#include "SkCGUtils.h"
+#include "SkRasterHandleAllocator.h"
+
+class GraphicsPort {
+protected:
+ SkCanvas* fCanvas;
+
+public:
+ GraphicsPort(SkCanvas* canvas) : fCanvas(canvas) {}
+ virtual ~GraphicsPort() {}
+
+ void save() { fCanvas->save(); }
+ void saveLayer(const SkRect& bounds, SkAlpha alpha) {
+ fCanvas->saveLayerAlpha(&bounds, alpha);
+ }
+ void restore() { fCanvas->restore(); }
+
+ void translate(float x, float y) { fCanvas->translate(x, y); }
+ void scale(float s) { fCanvas->scale(s, s); }
+
+ void drawOval(const SkRect& r, SkColor c) {
+ SkPaint p;
+ p.setColor(c);
+ fCanvas->drawOval(r, p);
+ }
+
+ virtual void drawRect(const SkRect& r, SkColor c) {
+ SkPaint p;
+ p.setColor(c);
+ fCanvas->drawRect(r, p);
+ }
+};
+
+class CGGraphicsPort : public GraphicsPort {
+public:
+ CGGraphicsPort(SkCanvas* canvas) : GraphicsPort(canvas) {}
+
+ void drawRect(const SkRect& r, SkColor c) override {
+ CGContextRef cg = (CGContextRef)fCanvas->accessTopRasterHandle();
+
+ CGColorRef color = CGColorCreateGenericRGB(SkColorGetR(c)/255.f,
+ SkColorGetG(c)/255.f,
+ SkColorGetB(c)/255.f,
+ SkColorGetA(c)/255.f);
+
+ CGContextSetFillColorWithColor(cg, color);
+ CGContextFillRect(cg, CGRectMake(r.x(), r.y(), r.width(), r.height()));
+ }
+};
+
+static CGAffineTransform matrix_to_transform(CGContextRef cg, const SkMatrix& ctm) {
+ SkMatrix matrix;
+ matrix.setScale(1, -1);
+ matrix.postTranslate(0, SkIntToScalar(CGBitmapContextGetHeight(cg)));
+ matrix.preConcat(ctm);
+
+ return CGAffineTransformMake(matrix[SkMatrix::kMScaleX],
+ matrix[SkMatrix::kMSkewY],
+ matrix[SkMatrix::kMSkewX],
+ matrix[SkMatrix::kMScaleY],
+ matrix[SkMatrix::kMTransX],
+ matrix[SkMatrix::kMTransY]);
+}
+
+class Allocator_CG : public SkRasterHandleAllocator {
+public:
+ Allocator_CG() {}
+
+ bool allocHandle(const SkImageInfo& info, Rec* rec) override {
+ // let CG allocate the pixels
+ CGContextRef cg = SkCreateCGContext(SkPixmap(info, nullptr, 0));
+ if (!cg) {
+ return false;
+ }
+ rec->fReleaseProc = [](void* pixels, void* ctx){ CGContextRelease((CGContextRef)ctx); };
+ rec->fReleaseCtx = cg;
+ rec->fPixels = CGBitmapContextGetData(cg);
+ rec->fRowBytes = CGBitmapContextGetBytesPerRow(cg);
+ rec->fHandle = cg;
+ CGContextSaveGState(cg); // balanced each time updateContext is called
+ return true;
+ }
+
+ void updateHandle(Handle hndl, const SkMatrix& ctm, const SkIRect& clip) override {
+ CGContextRef cg = (CGContextRef)hndl;
+
+ CGContextRestoreGState(cg);
+ CGContextSaveGState(cg);
+ CGContextClearRect(cg, CGRectMake(clip.x(), clip.y(), clip.width(), clip.height()));
+ CGContextConcatCTM(cg, matrix_to_transform(cg, ctm));
+ }
+};
+
+class RasterAllocatorSample : public SampleView {
+public:
+ RasterAllocatorSample() {}
+
+protected:
+ bool onQuery(SkEvent* evt) override {
+ if (SampleCode::TitleQ(*evt)) {
+ SampleCode::TitleR(evt, "raster-allocator");
+ return true;
+ }
+ return this->INHERITED::onQuery(evt);
+ }
+
+ void doDraw(GraphicsPort* port) {
+ port->drawRect({0, 0, 256, 256}, SK_ColorRED);
+ port->save();
+ port->translate(30, 30);
+ port->drawRect({0, 0, 30, 30}, SK_ColorBLUE);
+ port->drawOval({10, 10, 20, 20}, SK_ColorWHITE);
+ port->restore();
+
+ port->saveLayer({50, 50, 100, 100}, 0x80);
+ port->drawRect({55, 55, 95, 95}, SK_ColorGREEN);
+ port->restore();
+ }
+
+ void onDrawContent(SkCanvas* canvas) override {
+ GraphicsPort skp(canvas);
+ doDraw(&skp);
+
+ const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
+ std::unique_ptr<SkCanvas> c2 =
+ SkRasterHandleAllocator::MakeCanvas(skstd::make_unique<Allocator_CG>(), info);
+ CGGraphicsPort cgp(c2.get());
+ doDraw(&cgp);
+
+ SkPixmap pm;
+ c2->peekPixels(&pm);
+ SkBitmap bm;
+ bm.installPixels(pm);
+ canvas->drawBitmap(bm, 280, 0, nullptr);
+ }
+
+private:
+ typedef SampleView INHERITED;
+};
+DEF_SAMPLE( return new RasterAllocatorSample; )
+#endif
#include "SkPixelRef.h"
#include "SkPixmap.h"
#include "SkRasterClip.h"
+#include "SkRasterHandleAllocator.h"
#include "SkShader.h"
#include "SkSpecialImage.h"
#include "SkSurface.h"
return Create(info, SkSurfaceProps(SkSurfaceProps::kLegacyFontHost_InitType));
}
-SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps)
+SkBitmapDevice::SkBitmapDevice(const SkBitmap& bitmap, const SkSurfaceProps& surfaceProps,
+ SkRasterHandleAllocator::Handle hndl)
: INHERITED(bitmap.info(), surfaceProps)
, fBitmap(bitmap)
+ , fRasterHandle(hndl)
{
SkASSERT(valid_for_bitmap_device(bitmap.info(), nullptr));
fBitmap.lockPixels();
}
SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo,
- const SkSurfaceProps& surfaceProps) {
+ 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 (!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.
}
}
- return new SkBitmapDevice(bitmap, surfaceProps);
+ return new SkBitmapDevice(bitmap, surfaceProps, hndl);
}
void SkBitmapDevice::setNewSize(const SkISize& size) {
SkBaseDevice* SkBitmapDevice::onCreateDevice(const CreateInfo& cinfo, const SkPaint*) {
const SkSurfaceProps surfaceProps(this->surfaceProps().flags(), cinfo.fPixelGeometry);
- return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps);
+ return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator);
}
const SkBitmap& SkBitmapDevice::onAccessBitmap() {
#include "SkPicture.h"
#include "SkRadialShadowMapShader.h"
#include "SkRasterClip.h"
+#include "SkRasterHandleAllocator.h"
#include "SkReadPixelsRec.h"
#include "SkRRect.h"
#include "SkShadowPaintFilterCanvas.h"
this->init(device.get(), kDefault_InitFlags);
}
-SkCanvas::SkCanvas(const SkBitmap& bitmap)
+SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc,
+ SkRasterHandleAllocator::Handle hndl)
: fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage))
, fProps(SkSurfaceProps::kLegacyFontHost_InitType)
+ , fAllocator(std::move(alloc))
, fConservativeRasterClip(false)
{
inc_canvas();
- sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps));
+ sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl));
this->init(device.get(), kDefault_InitFlags);
}
+SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {}
+
SkCanvas::~SkCanvas() {
// free up the contents of our deque
this->restoreToCount(1); // restore everything but the last
(saveLayerFlags & kPreserveLCDText_SaveLayerFlag);
const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage;
const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo,
- preserveLCDText);
+ preserveLCDText,
+ fAllocator.get());
newDevice.reset(priorDevice->onCreateDevice(createInfo, paint));
if (!newDevice) {
return;
static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "");
static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "");
static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "");
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const {
+ if (fAllocator && fMCRec->fTopLayer->fDevice) {
+ const SkBaseDevice* dev = fMCRec->fTopLayer->fDevice;
+ SkRasterHandleAllocator::Handle handle = dev->getRasterHandle();
+ SkIPoint origin = dev->getOrigin();
+ SkMatrix ctm = this->getTotalMatrix();
+ ctm.preTranslate(SkIntToScalar(-origin.x()), SkIntToScalar(-origin.y()));
+
+ SkIRect clip = fMCRec->fRasterClip.getBounds();
+ clip.offset(-origin.x(), -origin.y());
+ if (clip.intersect(0, 0, dev->width(), dev->height())) {
+ clip.setEmpty();
+ }
+
+ fAllocator->updateHandle(handle, ctm, clip);
+ return handle;
+ }
+ return nullptr;
+}
+
+static bool install(SkBitmap* bm, const SkImageInfo& info,
+ const SkRasterHandleAllocator::Rec& rec) {
+ return bm->installPixels(info, rec.fPixels, rec.fRowBytes, nullptr,
+ rec.fReleaseProc, rec.fReleaseCtx);
+}
+
+SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info,
+ SkBitmap* bm) {
+ SkRasterHandleAllocator::Rec rec;
+ if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) {
+ return nullptr;
+ }
+ return rec.fHandle;
+}
+
+std::unique_ptr<SkCanvas>
+SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc,
+ const SkImageInfo& info, const Rec* rec) {
+ if (!alloc || !supported_for_raster_canvas(info)) {
+ return nullptr;
+ }
+
+ SkBitmap bm;
+ Handle hndl;
+
+ if (rec) {
+ hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr;
+ } else {
+ hndl = alloc->allocBitmap(info, &bm);
+ }
+ return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr;
+}
///////////////////////////////////////////////////////////////////////////////////////////////////
+CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
+ CGBitmapInfo cg_bitmap_info = 0;
+ size_t bitsPerComponent = 0;
+ switch (pmap.colorType()) {
+ case kRGBA_8888_SkColorType:
+ bitsPerComponent = 8;
+ cg_bitmap_info = ComputeCGAlphaInfo_RGBA(pmap.alphaType());
+ break;
+ case kBGRA_8888_SkColorType:
+ bitsPerComponent = 8;
+ cg_bitmap_info = ComputeCGAlphaInfo_BGRA(pmap.alphaType());
+ break;
+ default:
+ return nullptr; // no other colortypes are supported (for now)
+ }
+
+ size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
+ CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+ CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
+ bitsPerComponent, rb, cs, cg_bitmap_info);
+ CFRelease(cs);
+ return cg;
+}
+
SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
CGImageRef image) {
CGBitmapInfo cg_bitmap_info = 0;