308 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| /*
 | |
|  * Copyright 2011 Google Inc.
 | |
|  *
 | |
|  * Use of this source code is governed by a BSD-style license that can be
 | |
|  * found in the LICENSE file.
 | |
|  */
 | |
| 
 | |
| #include "gm/gm.h"
 | |
| #include "include/core/SkBitmap.h"
 | |
| #include "include/core/SkCanvas.h"
 | |
| #include "include/core/SkPixmap.h"
 | |
| #include "include/core/SkRasterHandleAllocator.h"
 | |
| #include "include/core/SkSurface.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 clip(const SkRect& r) { fCanvas->clipRect(r); }
 | |
| 
 | |
|     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);
 | |
|     }
 | |
| 
 | |
|     SkCanvas* peekCanvas() const { return fCanvas; }
 | |
| };
 | |
| 
 | |
| class SkiaGraphicsPort : public GraphicsPort {
 | |
| public:
 | |
|     SkiaGraphicsPort(SkCanvas* canvas) : GraphicsPort(canvas) {}
 | |
| 
 | |
|     void drawRect(const SkRect& r, SkColor c) override {
 | |
|         SkCanvas* canvas = (SkCanvas*)fCanvas->accessTopRasterHandle();
 | |
|         canvas->drawRect(r, SkPaint(SkColor4f::FromColor(c)));
 | |
|     }
 | |
| };
 | |
| 
 | |
| class SkiaAllocator : public SkRasterHandleAllocator {
 | |
| public:
 | |
|     SkiaAllocator() {}
 | |
| 
 | |
|     bool allocHandle(const SkImageInfo& info, Rec* rec) override {
 | |
|         sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
 | |
|         if (!surface) {
 | |
|             return false;
 | |
|         }
 | |
|         SkCanvas* canvas = surface->getCanvas();
 | |
|         SkPixmap pixmap;
 | |
|         canvas->peekPixels(&pixmap);
 | |
| 
 | |
|         rec->fReleaseProc = [](void* pixels, void* ctx){ SkSafeUnref((SkSurface*)ctx); };
 | |
|         rec->fReleaseCtx = surface.release();
 | |
|         rec->fPixels = pixmap.writable_addr();
 | |
|         rec->fRowBytes = pixmap.rowBytes();
 | |
|         rec->fHandle = canvas;
 | |
|         canvas->save();    // balanced each time updateHandle is called
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void updateHandle(Handle hndl, const SkMatrix& ctm, const SkIRect& clip) override {
 | |
|         SkCanvas* canvas = (SkCanvas*)hndl;
 | |
|         canvas->restore();
 | |
|         canvas->save();
 | |
|         canvas->clipRect(SkRect::Make(clip));
 | |
|         canvas->concat(ctm);
 | |
|     }
 | |
| };
 | |
| 
 | |
| #ifdef SK_BUILD_FOR_MAC
 | |
| 
 | |
| #include "include/utils/mac/SkCGUtils.h"
 | |
| 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 CGAllocator : public SkRasterHandleAllocator {
 | |
| public:
 | |
|     CGAllocator() {}
 | |
| 
 | |
|     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 updateHandle is called
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void updateHandle(Handle hndl, const SkMatrix& ctm, const SkIRect& clip) override {
 | |
|         CGContextRef cg = (CGContextRef)hndl;
 | |
| 
 | |
|         CGContextRestoreGState(cg);
 | |
|         CGContextSaveGState(cg);
 | |
|         CGContextClipToRect(cg, CGRectMake(clip.x(), clip.y(), clip.width(), clip.height()));
 | |
|         CGContextConcatCTM(cg, matrix_to_transform(cg, ctm));
 | |
|     }
 | |
| };
 | |
| 
 | |
| using MyPort = CGGraphicsPort;
 | |
| using MyAllocator = CGAllocator;
 | |
| 
 | |
| #elif defined(SK_BUILD_FOR_WIN)
 | |
| 
 | |
| #include "src/core/SkLeanWindows.h"
 | |
| 
 | |
| static RECT toRECT(const SkIRect& r) {
 | |
|     return { r.left(), r.top(), r.right(), r.bottom() };
 | |
| }
 | |
| 
 | |
| class GDIGraphicsPort : public GraphicsPort {
 | |
| public:
 | |
|     GDIGraphicsPort(SkCanvas* canvas) : GraphicsPort(canvas) {}
 | |
| 
 | |
|     void drawRect(const SkRect& r, SkColor c) override {
 | |
|         HDC hdc = (HDC)fCanvas->accessTopRasterHandle();
 | |
| 
 | |
|         COLORREF cr = RGB(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));// SkEndian_Swap32(c) >> 8;
 | |
|         RECT rounded = toRECT(r.round());
 | |
|         FillRect(hdc, &rounded, CreateSolidBrush(cr));
 | |
| 
 | |
|         // Assuming GDI wrote zeros for alpha, this will or-in 0xFF for alpha
 | |
|         SkPaint paint;
 | |
|         paint.setBlendMode(SkBlendMode::kDstATop);
 | |
|         fCanvas->drawRect(r, paint);
 | |
|     }
 | |
| };
 | |
| 
 | |
| // We use this static factory function instead of the regular constructor so
 | |
| // that we can create the pixel data before calling the constructor. This is
 | |
| // required so that we can call the base class' constructor with the pixel
 | |
| // data.
 | |
| static bool Create(int width, int height, bool is_opaque, SkRasterHandleAllocator::Rec* rec) {
 | |
|     BITMAPINFOHEADER hdr;
 | |
|     memset(&hdr, 0, sizeof(hdr));
 | |
|     hdr.biSize = sizeof(BITMAPINFOHEADER);
 | |
|     hdr.biWidth = width;
 | |
|     hdr.biHeight = -height;  // Minus means top-down bitmap.
 | |
|     hdr.biPlanes = 1;
 | |
|     hdr.biBitCount = 32;
 | |
|     hdr.biCompression = BI_RGB;  // No compression.
 | |
|     hdr.biSizeImage = 0;
 | |
|     hdr.biXPelsPerMeter = 1;
 | |
|     hdr.biYPelsPerMeter = 1;
 | |
|     void* pixels;
 | |
|     HBITMAP hbitmap = CreateDIBSection(nullptr, (const BITMAPINFO*)&hdr, 0, &pixels, 0, 0);
 | |
|     if (!hbitmap) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     size_t row_bytes = width * sizeof(SkPMColor);
 | |
|     sk_bzero(pixels, row_bytes * height);
 | |
| 
 | |
|     HDC hdc = CreateCompatibleDC(nullptr);
 | |
|     if (!hdc) {
 | |
|         DeleteObject(hbitmap);
 | |
|         return false;
 | |
|     }
 | |
|     SetGraphicsMode(hdc, GM_ADVANCED);
 | |
|     HGDIOBJ origBitmap = SelectObject(hdc, hbitmap);
 | |
| 
 | |
|     struct ReleaseContext {
 | |
|         HDC hdc;
 | |
|         HGDIOBJ hbitmap;
 | |
|     };
 | |
|     rec->fReleaseProc = [](void*, void* context) {
 | |
|         ReleaseContext* ctx = static_cast<ReleaseContext*>(context);
 | |
|         HBITMAP hbitmap = static_cast<HBITMAP>(SelectObject(ctx->hdc, ctx->hbitmap));
 | |
|         DeleteObject(hbitmap);
 | |
|         DeleteDC(ctx->hdc);
 | |
|         delete ctx;
 | |
|     };
 | |
|     rec->fReleaseCtx = new ReleaseContext{hdc, origBitmap};
 | |
|     rec->fPixels = pixels;
 | |
|     rec->fRowBytes = row_bytes;
 | |
|     rec->fHandle = hdc;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| /**
 | |
| *  Subclass of SkRasterHandleAllocator that returns an HDC as its "handle".
 | |
| */
 | |
| class GDIAllocator : public SkRasterHandleAllocator {
 | |
| public:
 | |
|     GDIAllocator() {}
 | |
| 
 | |
|     bool allocHandle(const SkImageInfo& info, Rec* rec) override {
 | |
|         SkASSERT(info.colorType() == kN32_SkColorType);
 | |
|         return Create(info.width(), info.height(), info.isOpaque(), rec);
 | |
|     }
 | |
| 
 | |
|     void updateHandle(Handle handle, const SkMatrix& ctm, const SkIRect& clip_bounds) override {
 | |
|         HDC hdc = static_cast<HDC>(handle);
 | |
| 
 | |
|         XFORM xf;
 | |
|         xf.eM11 = ctm[SkMatrix::kMScaleX];
 | |
|         xf.eM21 = ctm[SkMatrix::kMSkewX];
 | |
|         xf.eDx = ctm[SkMatrix::kMTransX];
 | |
|         xf.eM12 = ctm[SkMatrix::kMSkewY];
 | |
|         xf.eM22 = ctm[SkMatrix::kMScaleY];
 | |
|         xf.eDy = ctm[SkMatrix::kMTransY];
 | |
|         SetWorldTransform(hdc, &xf);
 | |
| 
 | |
|         RECT clip_bounds_RECT = toRECT(clip_bounds);
 | |
|         HRGN hrgn = CreateRectRgnIndirect(&clip_bounds_RECT);
 | |
|         SK_MAYBE_UNUSED int result = SelectClipRgn(hdc, hrgn);
 | |
|         SkASSERT(result != ERROR);
 | |
|         result = DeleteObject(hrgn);
 | |
|         SkASSERT(result != 0);
 | |
|     }
 | |
| };
 | |
| 
 | |
| using MyPort = GDIGraphicsPort;
 | |
| using MyAllocator = GDIAllocator;
 | |
| 
 | |
| #else
 | |
| 
 | |
| using MyPort = SkiaGraphicsPort;
 | |
| using MyAllocator = SkiaAllocator;
 | |
| 
 | |
| #endif
 | |
| 
 | |
| DEF_SIMPLE_GM(rasterallocator, canvas, 600, 300) {
 | |
|     auto doDraw = [](GraphicsPort* port) {
 | |
|         SkAutoCanvasRestore acr(port->peekCanvas(), true);
 | |
| 
 | |
|         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();
 | |
| 
 | |
|         port->clip({150, 50, 200, 200});
 | |
|         port->drawRect({0, 0, 256, 256}, 0xFFCCCCCC);
 | |
|     };
 | |
| 
 | |
|     // TODO: this common code fails pic-8888 and serialize-8888
 | |
|     //GraphicsPort skiaPort(canvas);
 | |
|     //doDraw(&skiaPort);
 | |
| 
 | |
|     const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256);
 | |
|     std::unique_ptr<SkCanvas> nativeCanvas =
 | |
|         SkRasterHandleAllocator::MakeCanvas(std::make_unique<MyAllocator>(), info);
 | |
|     MyPort nativePort(nativeCanvas.get());
 | |
|     doDraw(&nativePort);
 | |
| 
 | |
|     SkPixmap pm;
 | |
|     nativeCanvas->peekPixels(&pm);
 | |
|     canvas->drawImage(SkImage::MakeRasterCopy(pm), 280, 0);
 | |
| }
 |