1/* 2 * Copyright 2011 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "gm/gm.h" 9#include "include/core/SkBitmap.h" 10#include "include/core/SkCanvas.h" 11#include "include/core/SkPixmap.h" 12#include "include/core/SkRasterHandleAllocator.h" 13#include "include/core/SkSurface.h" 14 15class GraphicsPort { 16protected: 17 SkCanvas* fCanvas; 18 19public: 20 GraphicsPort(SkCanvas* canvas) : fCanvas(canvas) {} 21 virtual ~GraphicsPort() {} 22 23 void save() { fCanvas->save(); } 24 void saveLayer(const SkRect& bounds, SkAlpha alpha) { 25 fCanvas->saveLayerAlpha(&bounds, alpha); 26 } 27 void restore() { fCanvas->restore(); } 28 29 void translate(float x, float y) { fCanvas->translate(x, y); } 30 void scale(float s) { fCanvas->scale(s, s); } 31 void clip(const SkRect& r) { fCanvas->clipRect(r); } 32 33 void drawOval(const SkRect& r, SkColor c) { 34 SkPaint p; 35 p.setColor(c); 36 fCanvas->drawOval(r, p); 37 } 38 39 virtual void drawRect(const SkRect& r, SkColor c) { 40 SkPaint p; 41 p.setColor(c); 42 fCanvas->drawRect(r, p); 43 } 44 45 SkCanvas* peekCanvas() const { return fCanvas; } 46}; 47 48class SkiaGraphicsPort : public GraphicsPort { 49public: 50 SkiaGraphicsPort(SkCanvas* canvas) : GraphicsPort(canvas) {} 51 52 void drawRect(const SkRect& r, SkColor c) override { 53 SkCanvas* canvas = (SkCanvas*)fCanvas->accessTopRasterHandle(); 54 canvas->drawRect(r, SkPaint(SkColor4f::FromColor(c))); 55 } 56}; 57 58class SkiaAllocator : public SkRasterHandleAllocator { 59public: 60 SkiaAllocator() {} 61 62 bool allocHandle(const SkImageInfo& info, Rec* rec) override { 63 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info); 64 if (!surface) { 65 return false; 66 } 67 SkCanvas* canvas = surface->getCanvas(); 68 SkPixmap pixmap; 69 canvas->peekPixels(&pixmap); 70 71 rec->fReleaseProc = [](void* pixels, void* ctx){ SkSafeUnref((SkSurface*)ctx); }; 72 rec->fReleaseCtx = surface.release(); 73 rec->fPixels = pixmap.writable_addr(); 74 rec->fRowBytes = pixmap.rowBytes(); 75 rec->fHandle = canvas; 76 canvas->save(); // balanced each time updateHandle is called 77 return true; 78 } 79 80 void updateHandle(Handle hndl, const SkMatrix& ctm, const SkIRect& clip) override { 81 SkCanvas* canvas = (SkCanvas*)hndl; 82 canvas->restore(); 83 canvas->save(); 84 canvas->clipRect(SkRect::Make(clip)); 85 canvas->concat(ctm); 86 } 87}; 88 89#ifdef SK_BUILD_FOR_MAC 90 91#include "include/utils/mac/SkCGUtils.h" 92class CGGraphicsPort : public GraphicsPort { 93public: 94 CGGraphicsPort(SkCanvas* canvas) : GraphicsPort(canvas) {} 95 96 void drawRect(const SkRect& r, SkColor c) override { 97 CGContextRef cg = (CGContextRef)fCanvas->accessTopRasterHandle(); 98 99 CGColorRef color = CGColorCreateGenericRGB(SkColorGetR(c)/255.f, 100 SkColorGetG(c)/255.f, 101 SkColorGetB(c)/255.f, 102 SkColorGetA(c)/255.f); 103 104 CGContextSetFillColorWithColor(cg, color); 105 CGContextFillRect(cg, CGRectMake(r.x(), r.y(), r.width(), r.height())); 106 } 107}; 108 109static CGAffineTransform matrix_to_transform(CGContextRef cg, const SkMatrix& ctm) { 110 SkMatrix matrix; 111 matrix.setScale(1, -1); 112 matrix.postTranslate(0, SkIntToScalar(CGBitmapContextGetHeight(cg))); 113 matrix.preConcat(ctm); 114 115 return CGAffineTransformMake(matrix[SkMatrix::kMScaleX], 116 matrix[SkMatrix::kMSkewY], 117 matrix[SkMatrix::kMSkewX], 118 matrix[SkMatrix::kMScaleY], 119 matrix[SkMatrix::kMTransX], 120 matrix[SkMatrix::kMTransY]); 121} 122 123class CGAllocator : public SkRasterHandleAllocator { 124public: 125 CGAllocator() {} 126 127 bool allocHandle(const SkImageInfo& info, Rec* rec) override { 128 // let CG allocate the pixels 129 CGContextRef cg = SkCreateCGContext(SkPixmap(info, nullptr, 0)); 130 if (!cg) { 131 return false; 132 } 133 rec->fReleaseProc = [](void* pixels, void* ctx){ CGContextRelease((CGContextRef)ctx); }; 134 rec->fReleaseCtx = cg; 135 rec->fPixels = CGBitmapContextGetData(cg); 136 rec->fRowBytes = CGBitmapContextGetBytesPerRow(cg); 137 rec->fHandle = cg; 138 CGContextSaveGState(cg); // balanced each time updateHandle is called 139 return true; 140 } 141 142 void updateHandle(Handle hndl, const SkMatrix& ctm, const SkIRect& clip) override { 143 CGContextRef cg = (CGContextRef)hndl; 144 145 CGContextRestoreGState(cg); 146 CGContextSaveGState(cg); 147 CGContextClipToRect(cg, CGRectMake(clip.x(), clip.y(), clip.width(), clip.height())); 148 CGContextConcatCTM(cg, matrix_to_transform(cg, ctm)); 149 } 150}; 151 152using MyPort = CGGraphicsPort; 153using MyAllocator = CGAllocator; 154 155#elif defined(SK_BUILD_FOR_WIN) 156 157#include "src/core/SkLeanWindows.h" 158 159static RECT toRECT(const SkIRect& r) { 160 return { r.left(), r.top(), r.right(), r.bottom() }; 161} 162 163class GDIGraphicsPort : public GraphicsPort { 164public: 165 GDIGraphicsPort(SkCanvas* canvas) : GraphicsPort(canvas) {} 166 167 void drawRect(const SkRect& r, SkColor c) override { 168 HDC hdc = (HDC)fCanvas->accessTopRasterHandle(); 169 170 COLORREF cr = RGB(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c));// SkEndian_Swap32(c) >> 8; 171 RECT rounded = toRECT(r.round()); 172 FillRect(hdc, &rounded, CreateSolidBrush(cr)); 173 174 // Assuming GDI wrote zeros for alpha, this will or-in 0xFF for alpha 175 SkPaint paint; 176 paint.setBlendMode(SkBlendMode::kDstATop); 177 fCanvas->drawRect(r, paint); 178 } 179}; 180 181// We use this static factory function instead of the regular constructor so 182// that we can create the pixel data before calling the constructor. This is 183// required so that we can call the base class' constructor with the pixel 184// data. 185static bool Create(int width, int height, bool is_opaque, SkRasterHandleAllocator::Rec* rec) { 186 BITMAPINFOHEADER hdr; 187 memset(&hdr, 0, sizeof(hdr)); 188 hdr.biSize = sizeof(BITMAPINFOHEADER); 189 hdr.biWidth = width; 190 hdr.biHeight = -height; // Minus means top-down bitmap. 191 hdr.biPlanes = 1; 192 hdr.biBitCount = 32; 193 hdr.biCompression = BI_RGB; // No compression. 194 hdr.biSizeImage = 0; 195 hdr.biXPelsPerMeter = 1; 196 hdr.biYPelsPerMeter = 1; 197 void* pixels; 198 HBITMAP hbitmap = CreateDIBSection(nullptr, (const BITMAPINFO*)&hdr, 0, &pixels, 0, 0); 199 if (!hbitmap) { 200 return false; 201 } 202 203 size_t row_bytes = width * sizeof(SkPMColor); 204 sk_bzero(pixels, row_bytes * height); 205 206 HDC hdc = CreateCompatibleDC(nullptr); 207 if (!hdc) { 208 DeleteObject(hbitmap); 209 return false; 210 } 211 SetGraphicsMode(hdc, GM_ADVANCED); 212 HGDIOBJ origBitmap = SelectObject(hdc, hbitmap); 213 214 struct ReleaseContext { 215 HDC hdc; 216 HGDIOBJ hbitmap; 217 }; 218 rec->fReleaseProc = [](void*, void* context) { 219 ReleaseContext* ctx = static_cast<ReleaseContext*>(context); 220 HBITMAP hbitmap = static_cast<HBITMAP>(SelectObject(ctx->hdc, ctx->hbitmap)); 221 DeleteObject(hbitmap); 222 DeleteDC(ctx->hdc); 223 delete ctx; 224 }; 225 rec->fReleaseCtx = new ReleaseContext{hdc, origBitmap}; 226 rec->fPixels = pixels; 227 rec->fRowBytes = row_bytes; 228 rec->fHandle = hdc; 229 return true; 230} 231 232/** 233* Subclass of SkRasterHandleAllocator that returns an HDC as its "handle". 234*/ 235class GDIAllocator : public SkRasterHandleAllocator { 236public: 237 GDIAllocator() {} 238 239 bool allocHandle(const SkImageInfo& info, Rec* rec) override { 240 SkASSERT(info.colorType() == kN32_SkColorType); 241 return Create(info.width(), info.height(), info.isOpaque(), rec); 242 } 243 244 void updateHandle(Handle handle, const SkMatrix& ctm, const SkIRect& clip_bounds) override { 245 HDC hdc = static_cast<HDC>(handle); 246 247 XFORM xf; 248 xf.eM11 = ctm[SkMatrix::kMScaleX]; 249 xf.eM21 = ctm[SkMatrix::kMSkewX]; 250 xf.eDx = ctm[SkMatrix::kMTransX]; 251 xf.eM12 = ctm[SkMatrix::kMSkewY]; 252 xf.eM22 = ctm[SkMatrix::kMScaleY]; 253 xf.eDy = ctm[SkMatrix::kMTransY]; 254 SetWorldTransform(hdc, &xf); 255 256 RECT clip_bounds_RECT = toRECT(clip_bounds); 257 HRGN hrgn = CreateRectRgnIndirect(&clip_bounds_RECT); 258 SK_MAYBE_UNUSED int result = SelectClipRgn(hdc, hrgn); 259 SkASSERT(result != ERROR); 260 result = DeleteObject(hrgn); 261 SkASSERT(result != 0); 262 } 263}; 264 265using MyPort = GDIGraphicsPort; 266using MyAllocator = GDIAllocator; 267 268#else 269 270using MyPort = SkiaGraphicsPort; 271using MyAllocator = SkiaAllocator; 272 273#endif 274 275DEF_SIMPLE_GM(rasterallocator, canvas, 600, 300) { 276 auto doDraw = [](GraphicsPort* port) { 277 SkAutoCanvasRestore acr(port->peekCanvas(), true); 278 279 port->drawRect({0, 0, 256, 256}, SK_ColorRED); 280 port->save(); 281 port->translate(30, 30); 282 port->drawRect({0, 0, 30, 30}, SK_ColorBLUE); 283 port->drawOval({10, 10, 20, 20}, SK_ColorWHITE); 284 port->restore(); 285 286 port->saveLayer({50, 50, 100, 100}, 0x80); 287 port->drawRect({55, 55, 95, 95}, SK_ColorGREEN); 288 port->restore(); 289 290 port->clip({150, 50, 200, 200}); 291 port->drawRect({0, 0, 256, 256}, 0xFFCCCCCC); 292 }; 293 294 // TODO: this common code fails pic-8888 and serialize-8888 295 //GraphicsPort skiaPort(canvas); 296 //doDraw(&skiaPort); 297 298 const SkImageInfo info = SkImageInfo::MakeN32Premul(256, 256); 299 std::unique_ptr<SkCanvas> nativeCanvas = 300 SkRasterHandleAllocator::MakeCanvas(std::make_unique<MyAllocator>(), info); 301 MyPort nativePort(nativeCanvas.get()); 302 doDraw(&nativePort); 303 304 SkPixmap pm; 305 nativeCanvas->peekPixels(&pm); 306 canvas->drawImage(SkImage::MakeRasterCopy(pm), 280, 0); 307} 308