diff options
-rw-r--r-- | include/core/SkBitmapDevice.h | 9 | ||||
-rw-r--r-- | include/core/SkCanvas.h | 7 | ||||
-rw-r--r-- | include/core/SkDevice.h | 8 | ||||
-rw-r--r-- | include/core/SkRasterHandleAllocator.h | 86 | ||||
-rw-r--r-- | include/utils/mac/SkCGUtils.h | 3 | ||||
-rw-r--r-- | samplecode/SampleBigGradient.cpp | 147 | ||||
-rw-r--r-- | src/core/SkBitmapDevice.cpp | 18 | ||||
-rw-r--r-- | src/core/SkCanvas.cpp | 67 | ||||
-rw-r--r-- | src/utils/mac/SkCreateCGImageRef.cpp | 24 |
9 files changed, 359 insertions, 10 deletions
diff --git a/include/core/SkBitmapDevice.h b/include/core/SkBitmapDevice.h index 7ad4abec21..776b6a0dec 100644 --- a/include/core/SkBitmapDevice.h +++ b/include/core/SkBitmapDevice.h @@ -28,6 +28,7 @@ class SkPaint; class SkPath; class SkPixelRef; class SkPixmap; +class SkRasterHandleAllocator; class SkRRect; class SkSurface; struct SkPoint; @@ -54,12 +55,15 @@ public: * 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, @@ -172,6 +176,7 @@ private: SkImageFilterCache* getImageFilterCache() override; SkBitmap fBitmap; + void* fRasterHandle = nullptr; void setNewSize(const SkISize&); // Used by SkCanvas for resetForNextPicture(). diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h index 63f7f780de..704584a050 100644 --- a/include/core/SkCanvas.h +++ b/include/core/SkCanvas.h @@ -15,6 +15,7 @@ #include "SkDeque.h" #include "SkImage.h" #include "SkPaint.h" +#include "SkRasterHandleAllocator.h" #include "SkRefCnt.h" #include "SkRegion.h" #include "SkSurfaceProps.h" @@ -217,6 +218,8 @@ public: */ 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 @@ -1569,6 +1572,7 @@ private: int fSaveCount; // value returned by getSaveCount() SkMetaData* fMetaData; + std::unique_ptr<SkRasterHandleAllocator> fAllocator; SkSurface_Base* fSurfaceBase; SkSurface_Base* getSurfaceBase() const { return fSurfaceBase; } @@ -1599,6 +1603,7 @@ private: friend class SkPicturePlayback; // SaveFlagsToSaveLayerFlags friend class SkDeferredCanvas; // For use of resetForNextPicture friend class SkOverdrawCanvas; + friend class SkRasterHandleAllocator; enum InitFlags { kDefault_InitFlags = 0, @@ -1606,6 +1611,8 @@ private: }; SkCanvas(const SkIRect& bounds, InitFlags); SkCanvas(SkBaseDevice* device, InitFlags); + SkCanvas(const SkBitmap&, std::unique_ptr<SkRasterHandleAllocator>, + SkRasterHandleAllocator::Handle); void resetForNextPicture(const SkIRect& bounds); diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h index b0aac41d0b..fa427c6a15 100644 --- a/include/core/SkDevice.h +++ b/include/core/SkDevice.h @@ -21,6 +21,7 @@ class SkImageFilterCache; struct SkIRect; class SkMatrix; class SkMetaData; +class SkRasterHandleAllocator; class SkRegion; class SkSpecialImage; class GrRenderTarget; @@ -112,6 +113,8 @@ public: */ const SkIPoint& getOrigin() const { return fOrigin; } + virtual void* getRasterHandle() const { return nullptr; } + protected: enum TileUsage { kPossible_TileUsage, //!< the created device may be drawn tiled @@ -292,15 +295,18 @@ protected: 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; }; /** diff --git a/include/core/SkRasterHandleAllocator.h b/include/core/SkRasterHandleAllocator.h new file mode 100644 index 0000000000..62796cb62a --- /dev/null +++ b/include/core/SkRasterHandleAllocator.h @@ -0,0 +1,86 @@ +/* + * 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 diff --git a/include/utils/mac/SkCGUtils.h b/include/utils/mac/SkCGUtils.h index 592f2f6e41..2dcbb965b4 100644 --- a/include/utils/mac/SkCGUtils.h +++ b/include/utils/mac/SkCGUtils.h @@ -24,8 +24,11 @@ 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. diff --git a/samplecode/SampleBigGradient.cpp b/samplecode/SampleBigGradient.cpp index 8ae099051b..175b728a5c 100644 --- a/samplecode/SampleBigGradient.cpp +++ b/samplecode/SampleBigGradient.cpp @@ -9,6 +9,7 @@ #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 }; @@ -45,3 +46,149 @@ private: 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 diff --git a/src/core/SkBitmapDevice.cpp b/src/core/SkBitmapDevice.cpp index 814019059a..6d63ecf620 100644 --- a/src/core/SkBitmapDevice.cpp +++ b/src/core/SkBitmapDevice.cpp @@ -17,6 +17,7 @@ #include "SkPixelRef.h" #include "SkPixmap.h" #include "SkRasterClip.h" +#include "SkRasterHandleAllocator.h" #include "SkShader.h" #include "SkSpecialImage.h" #include "SkSurface.h" @@ -79,21 +80,25 @@ SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& info) { 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; @@ -101,6 +106,11 @@ SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, 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. @@ -116,7 +126,7 @@ SkBitmapDevice* SkBitmapDevice::Create(const SkImageInfo& origInfo, } } - return new SkBitmapDevice(bitmap, surfaceProps); + return new SkBitmapDevice(bitmap, surfaceProps, hndl); } void SkBitmapDevice::setNewSize(const SkISize& size) { @@ -135,7 +145,7 @@ 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); + return SkBitmapDevice::Create(cinfo.fInfo, surfaceProps, cinfo.fAllocator); } const SkBitmap& SkBitmapDevice::onAccessBitmap() { diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp index cb76051add..4716053197 100644 --- a/src/core/SkCanvas.cpp +++ b/src/core/SkCanvas.cpp @@ -29,6 +29,7 @@ #include "SkPicture.h" #include "SkRadialShadowMapShader.h" #include "SkRasterClip.h" +#include "SkRasterHandleAllocator.h" #include "SkReadPixelsRec.h" #include "SkRRect.h" #include "SkShadowPaintFilterCanvas.h" @@ -772,17 +773,21 @@ SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) 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 @@ -1260,7 +1265,8 @@ void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy stra (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; @@ -3404,3 +3410,58 @@ static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "") 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; +} diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp index 1b7e12bde5..e3735920a1 100644 --- a/src/utils/mac/SkCreateCGImageRef.cpp +++ b/src/utils/mac/SkCreateCGImageRef.cpp @@ -178,6 +178,30 @@ void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { /////////////////////////////////////////////////////////////////////////////////////////////////// +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; |