iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IGraphicsSkia.cpp
1#include <cmath>
2#include <map>
3
4#include "IGraphicsSkia.h"
5
6#pragma warning( push )
7#pragma warning( disable : 4244 )
8#include "include/core/SkMaskFilter.h"
9#include "include/core/SkBlurTypes.h"
10#include "include/core/SkFont.h"
11#include "include/core/SkFontMetrics.h"
12#include "include/core/SkTypeface.h"
13#include "include/core/SkVertices.h"
14#include "include/core/SkSwizzle.h"
15#include "include/core/SkBitmap.h"
16#include "include/core/SkFontMgr.h"
17#include "include/core/SkPathEffect.h"
18#include "include/effects/SkDashPathEffect.h"
19#include "include/effects/SkGradientShader.h"
20#include "include/effects/SkImageFilters.h"
21
22#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
23#include "modules/skparagraph/include/FontCollection.h"
24#include "modules/skparagraph/include/Paragraph.h"
25#include "modules/skparagraph/include/ParagraphBuilder.h"
26#include "modules/skparagraph/include/ParagraphStyle.h"
27#include "modules/skparagraph/include/TextStyle.h"
28#include "modules/skshaper/include/SkShaper.h"
29#include "modules/skunicode/include/SkUnicode_icu.h"
30#endif
31#pragma warning( pop )
32#include "include/gpu/ganesh/SkSurfaceGanesh.h"
33#include "include/gpu/GrBackendSurface.h"
34
35#if defined OS_MAC || defined OS_IOS
36 #include "include/utils/mac/SkCGUtils.h"
37 #if defined IGRAPHICS_GL2
38 #error SKIA doesn't work correctly with IGRAPHICS_GL2
39 #elif defined IGRAPHICS_GL3
40 #include <OpenGL/gl3.h>
41 #elif defined IGRAPHICS_METAL
42 // even though this is a .cpp we are in an objc(pp) compilation unit
43 #import <Metal/Metal.h>
44 #import <QuartzCore/CAMetalLayer.h>
45 #include "include/gpu/ganesh/mtl/GrMtlBackendContext.h"
46 #include "include/gpu/ganesh/mtl/GrMtlDirectContext.h"
47 #include "include/gpu/ganesh/mtl/GrMtlBackendSurface.h"
48 #elif !defined IGRAPHICS_CPU
49 #error Define either IGRAPHICS_GL2, IGRAPHICS_GL3, IGRAPHICS_METAL, or IGRAPHICS_CPU for IGRAPHICS_SKIA with OS_MAC
50 #endif
51
52 #include "include/ports/SkFontMgr_mac_ct.h"
53
54#elif defined OS_WIN
55 #include "include/ports/SkTypeface_win.h"
56
57 #pragma comment(lib, "skia.lib")
58
59 #ifndef IGRAPHICS_NO_SKIA_SKPARAGRAPH
60 #pragma comment(lib, "skparagraph.lib")
61 #pragma comment(lib, "skshaper.lib")
62 #pragma comment(lib, "skunicode_core.lib")
63 #pragma comment(lib, "skunicode_icu.lib")
64 #endif
65
66
67#endif
68
69#if defined IGRAPHICS_GL
70 #include "include/gpu/gl/GrGLInterface.h"
71 #include "include/gpu/ganesh/gl/GrGLDirectContext.h"
72 #include "include/gpu/ganesh/gl/GrGLBackendSurface.h"
73
74 #if defined OS_MAC
75 #include "include/gpu/ganesh/gl/mac/GrGLMakeMacInterface.h"
76 #elif defined OS_WIN
77 #include "include/gpu/ganesh/gl/win/GrGLMakeWinInterface.h"
78 #pragma comment(lib, "opengl32.lib")
79 #endif
80
81#endif
82
83using namespace iplug;
84using namespace igraphics;
85
86extern std::map<std::string, MTLTexturePtr> gTextureMap;
87
88#pragma mark - Private Classes and Structs
89
91{
92public:
93 Bitmap(sk_sp<SkSurface> surface, int width, int height, float scale, float drawScale);
94 Bitmap(const char* path, double sourceScale);
95 Bitmap(const void* pData, int size, double sourceScale);
96 Bitmap(sk_sp<SkImage>, double sourceScale);
97
98private:
99 SkiaDrawable mDrawable;
100};
101
102IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkSurface> surface, int width, int height, float scale, float drawScale)
103{
104 mDrawable.mSurface = surface;
105 mDrawable.mIsSurface = true;
106
107 SetBitmap(&mDrawable, width, height, scale, drawScale);
108}
109
110IGraphicsSkia::Bitmap::Bitmap(const char* path, double sourceScale)
111{
112 sk_sp<SkData> data = SkData::MakeFromFileName(path);
113
114 assert(data && "Unable to load file at path");
115
116 auto image = SkImages::DeferredFromEncodedData(data);
117
118#ifdef IGRAPHICS_CPU
119 image = image->makeRasterImage();
120#endif
121
122 mDrawable.mImage = image;
123
124 mDrawable.mIsSurface = false;
125 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
126}
127
128IGraphicsSkia::Bitmap::Bitmap(const void* pData, int size, double sourceScale)
129{
130 auto data = SkData::MakeWithoutCopy(pData, size);
131 auto image = SkImages::DeferredFromEncodedData(data);
132
133#ifdef IGRAPHICS_CPU
134 image = image->makeRasterImage();
135#endif
136
137 mDrawable.mImage = image;
138
139 mDrawable.mIsSurface = false;
140 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
141}
142
143IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkImage> image, double sourceScale)
144{
145#ifdef IGRAPHICS_CPU
146 mDrawable.mImage = image->makeRasterImage();
147#else
148 mDrawable.mImage = image;
149#endif
150
151 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
152}
153
154struct IGraphicsSkia::Font
155{
156 Font(IFontDataPtr&& data, sk_sp<SkTypeface> typeFace)
157 : mData(std::move(data)), mTypeface(typeFace) {}
158
159 IFontDataPtr mData;
160 sk_sp<SkTypeface> mTypeface;
161};
162
163// Fonts
164StaticStorage<IGraphicsSkia::Font> IGraphicsSkia::sFontCache;
165
166#pragma mark - Utility conversions
167
168BEGIN_IPLUG_NAMESPACE
169BEGIN_IGRAPHICS_NAMESPACE
170
171SkColor SkiaColor(const IColor& color, const IBlend* pBlend)
172{
173 if (pBlend)
174 return SkColorSetARGB(Clip(static_cast<int>(pBlend->mWeight * color.A), 0, 255), color.R, color.G, color.B);
175 else
176 return SkColorSetARGB(color.A, color.R, color.G, color.B);
177}
178
179SkRect SkiaRect(const IRECT& r)
180{
181 return SkRect::MakeLTRB(r.L, r.T, r.R, r.B);
182}
183
184SkBlendMode SkiaBlendMode(const IBlend* pBlend)
185{
186 if (!pBlend)
187 return SkBlendMode::kSrcOver;
188
189 switch (pBlend->mMethod)
190 {
191 case EBlend::SrcOver: return SkBlendMode::kSrcOver;
192 case EBlend::SrcIn: return SkBlendMode::kSrcIn;
193 case EBlend::SrcOut: return SkBlendMode::kSrcOut;
194 case EBlend::SrcAtop: return SkBlendMode::kSrcATop;
195 case EBlend::DstOver: return SkBlendMode::kDstOver;
196 case EBlend::DstIn: return SkBlendMode::kDstIn;
197 case EBlend::DstOut: return SkBlendMode::kDstOut;
198 case EBlend::DstAtop: return SkBlendMode::kDstATop;
199 case EBlend::Add: return SkBlendMode::kPlus;
200 case EBlend::XOR: return SkBlendMode::kXor;
201 }
202
203 return SkBlendMode::kClear;
204}
205
206SkTileMode SkiaTileMode(const IPattern& pattern)
207{
208 switch (pattern.mExtend)
209 {
210 case EPatternExtend::None: return SkTileMode::kDecal;
211 case EPatternExtend::Reflect: return SkTileMode::kMirror;
212 case EPatternExtend::Repeat: return SkTileMode::kRepeat;
213 case EPatternExtend::Pad: return SkTileMode::kClamp;
214 }
215
216 return SkTileMode::kClamp;
217}
218
219SkPaint SkiaPaint(const IPattern& pattern, const IBlend* pBlend)
220{
221 SkPaint paint;
222 paint.setAntiAlias(true);
223 paint.setBlendMode(SkiaBlendMode(pBlend));
224 int numStops = pattern.NStops();
225
226 if (pattern.mType == EPatternType::Solid || numStops < 2)
227 {
228 paint.setColor(SkiaColor(pattern.GetStop(0).mColor, pBlend));
229 }
230 else
231 {
232 double x1 = 0.0;
233 double y1 = 0.0;
234 double x2 = 0.0;
235 double y2 = 1.0;
236
237 IMatrix m = pattern.mTransform;
238 m.Invert();
239 m.TransformPoint(x1, y1);
240 m.TransformPoint(x2, y2);
241
242 SkPoint points[2] =
243 {
244 SkPoint::Make(x1, y1),
245 SkPoint::Make(x2, y2)
246 };
247
248 SkColor colors[8];
249 SkScalar positions[8];
250
251 assert(numStops <= 8);
252
253 for(int i = 0; i < numStops; i++)
254 {
255 const IColorStop& stop = pattern.GetStop(i);
256 colors[i] = SkiaColor(stop.mColor, pBlend);
257 positions[i] = stop.mOffset;
258 }
259
260 switch (pattern.mType)
261 {
262 case EPatternType::Linear:
263 paint.setShader(SkGradientShader::MakeLinear(points, colors, positions, numStops, SkiaTileMode(pattern), 0, nullptr));
264 break;
265
266 case EPatternType::Radial:
267 {
268 float xd = points[0].x() - points[1].x();
269 float yd = points[0].y() - points[1].y();
270 float radius = std::sqrt(xd * xd + yd * yd);
271 paint.setShader(SkGradientShader::MakeRadial(points[0], radius, colors, positions, numStops, SkiaTileMode(pattern), 0, nullptr));
272 break;
273 }
274
275 case EPatternType::Sweep:
276 {
277 SkMatrix matrix = SkMatrix::MakeAll(m.mXX, m.mYX, 0, m.mXY, m.mYY, 0, 0, 0, 1);
278
279 paint.setShader(SkGradientShader::MakeSweep(x1, y1, colors, nullptr, numStops, SkTileMode::kDecal,
280 0, 360 * positions[numStops - 1], 0, &matrix));
281
282 break;
283 }
284
285 default:
286 break;
287 }
288 }
289
290 return paint;
291}
292
293END_IGRAPHICS_NAMESPACE
294END_IPLUG_NAMESPACE
295
296#pragma mark -
297
298static sk_sp<SkFontMgr> SFontMgrFactory()
299{
300#if defined OS_MAC || defined OS_IOS
301 return SkFontMgr_New_CoreText(nullptr);
302#elif defined OS_WIN
303 return SkFontMgr_New_DirectWrite();
304#else
305 #error "Not supported"
306#endif
307}
308
309bool gFontMgrFactoryCreated = false;
310
311sk_sp<SkFontMgr> SkFontMgrRefDefault()
312{
313 static std::once_flag flag;
314 static sk_sp<SkFontMgr> mgr;
315 std::call_once(flag, [] {
316 mgr = SFontMgrFactory();
317 gFontMgrFactoryCreated = true;
318 });
319 return mgr;
320}
321
322#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
323
324bool gSkUnicodeCreated = false;
325
326sk_sp<SkUnicode> GetUnicode()
327{
328 static std::once_flag flag;
329 static sk_sp<SkUnicode> unicode;
330 std::call_once(flag, [] {
331 unicode = SkUnicodes::ICU::Make();
332 gSkUnicodeCreated = true;
333 });
334
335 if (!unicode)
336 {
337 DBGMSG("Could not load unicode data\n");
338 return nullptr;
339 }
340
341 return unicode;
342}
343#endif
344
345IGraphicsSkia::IGraphicsSkia(IGEditorDelegate& dlg, int w, int h, int fps, float scale)
346: IGraphics(dlg, w, h, fps, scale)
347{
348 mMainPath.setIsVolatile(true);
349
350#if defined IGRAPHICS_CPU
351 DBGMSG("IGraphics Skia CPU @ %i FPS\n", fps);
352#elif defined IGRAPHICS_METAL
353 DBGMSG("IGraphics Skia METAL @ %i FPS\n", fps);
354#elif defined IGRAPHICS_GL
355 DBGMSG("IGraphics Skia GL @ %i FPS\n", fps);
356#endif
357 StaticStorage<Font>::Accessor storage(sFontCache);
358 storage.Retain();
359
360#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
361 mFontCollection = sk_make_sp<skia::textlayout::FontCollection>();
362 mFontCollection->enableFontFallback();
363 mFontCollection->setDefaultFontManager(SkFontMgrRefDefault());
364#endif
365}
366
367IGraphicsSkia::~IGraphicsSkia()
368{
369 StaticStorage<Font>::Accessor storage(sFontCache);
370 storage.Release();
371}
372
374{
375 char extLower[32];
376 ToLower(extLower, ext);
377 return (strstr(extLower, "png") != nullptr) || (strstr(extLower, "jpg") != nullptr) || (strstr(extLower, "jpeg") != nullptr);
378}
379
380APIBitmap* IGraphicsSkia::LoadAPIBitmap(const char* fileNameOrResID, int scale, EResourceLocation location, const char* ext)
381{
382//#ifdef OS_IOS
383// if (location == EResourceLocation::kPreloadedTexture)
384// {
385// assert(0 && "SKIA does not yet load KTX textures");
386// GrMtlTextureInfo textureInfo;
387// textureInfo.fTexture.retain((void*)(gTextureMap[fileNameOrResID]));
388// id<MTLTexture> texture = (id<MTLTexture>) textureInfo.fTexture.get();
389//
390// MTLPixelFormat pixelFormat = texture.pixelFormat;
391//
392// auto grBackendTexture = GrBackendTexture(texture.width, texture.height, GrMipMapped::kNo, textureInfo);
393//
394// sk_sp<SkImage> image = SkImage::MakeFromTexture(mGrContext.get(), grBackendTexture, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, kOpaque_SkAlphaType, nullptr);
395// return new Bitmap(image, scale);
396// }
397// else
398//#endif
399#ifdef OS_WIN
400 if (location == EResourceLocation::kWinBinary)
401 {
402 int size = 0;
403 const void* pData = LoadWinResource(fileNameOrResID, ext, size, GetWinModuleHandle());
404 return new Bitmap(pData, size, scale);
405 }
406 else
407#endif
408 return new Bitmap(fileNameOrResID, scale);
409}
410
411APIBitmap* IGraphicsSkia::LoadAPIBitmap(const char* name, const void* pData, int dataSize, int scale)
412{
413 return new Bitmap(pData, dataSize, scale);
414}
415
417{
418#if defined IGRAPHICS_GL
419#if defined OS_MAC
420 auto glInterface = GrGLInterfaces::MakeMac();
421#elif defined OS_WIN
422 auto glInterface = GrGLInterfaces::MakeWin();
423#endif
424 mGrContext = GrDirectContexts::MakeGL(glInterface);
425#elif defined IGRAPHICS_METAL
426 CAMetalLayer* pMTLLayer = (CAMetalLayer*) pContext;
427 id<MTLDevice> device = pMTLLayer.device;
428 id<MTLCommandQueue> commandQueue = [device newCommandQueue];
429 GrMtlBackendContext backendContext = {};
430 backendContext.fDevice.retain((__bridge GrMTLHandle) device);
431 backendContext.fQueue.retain((__bridge GrMTLHandle) commandQueue);
432 mGrContext = GrDirectContexts::MakeMetal(backendContext);
433 mMTLDevice = (void*) device;
434 mMTLCommandQueue = (void*) commandQueue;
435 mMTLLayer = pContext;
436#endif
437
438 DrawResize();
439}
440
442{
444
445#if defined IGRAPHICS_GL
446 mSurface = nullptr;
447 mScreenSurface = nullptr;
448 mGrContext = nullptr;
449#elif defined IGRAPHICS_METAL
450 [(id<MTLCommandQueue>) mMTLCommandQueue release];
451 mMTLCommandQueue = nullptr;
452 mMTLLayer = nullptr;
453 mMTLDevice = nullptr;
454#endif
455}
456
458{
459 ScopedGLContext scopedGLContext{this};
460 auto w = static_cast<int>(std::ceil(static_cast<float>(WindowWidth()) * GetScreenScale()));
461 auto h = static_cast<int>(std::ceil(static_cast<float>(WindowHeight()) * GetScreenScale()));
462
463#if defined IGRAPHICS_GL || defined IGRAPHICS_METAL
464 if (mGrContext.get())
465 {
466 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
467 mSurface = SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kYes, info);
468 }
469#else
470 #ifdef OS_WIN
471 mSurface.reset();
472
473 const size_t bmpSize = sizeof(BITMAPINFOHEADER) + (w * h * sizeof(uint32_t));
474 mSurfaceMemory.Resize(bmpSize);
475 BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(mSurfaceMemory.Get());
476 ZeroMemory(bmpInfo, sizeof(BITMAPINFO));
477 bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
478 bmpInfo->bmiHeader.biWidth = w;
479 bmpInfo->bmiHeader.biHeight = -h; // negative means top-down bitmap. Skia draws top-down.
480 bmpInfo->bmiHeader.biPlanes = 1;
481 bmpInfo->bmiHeader.biBitCount = 32;
482 bmpInfo->bmiHeader.biCompression = BI_RGB;
483 void* pixels = bmpInfo->bmiColors;
484
485 SkImageInfo info = SkImageInfo::Make(w, h, kN32_SkColorType, kPremul_SkAlphaType, nullptr);
486 mSurface = SkSurfaces::WrapPixels(info, pixels, sizeof(uint32_t) * w);
487 #else
488 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
489 mSurface = SkSurfaces::Raster(info);
490 #endif
491#endif
492 if (mSurface)
493 {
494 mCanvas = mSurface->getCanvas();
495 mCanvas->save();
496 }
497}
498
500{
501#if defined IGRAPHICS_GL
502 if (mGrContext.get())
503 {
504 int width = WindowWidth() * GetScreenScale();
505 int height = WindowHeight() * GetScreenScale();
506
507 // Bind to the current main framebuffer
508 int fbo = 0, samples = 0, stencilBits = 0;
509 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
510 glGetIntegerv(GL_SAMPLES, &samples);
511#ifdef IGRAPHICS_GL3
512 glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencilBits);
513#else
514 glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
515#endif
516
517 GrGLFramebufferInfo fbInfo;
518 fbInfo.fFBOID = fbo;
519 fbInfo.fFormat = 0x8058;
520
521 auto backendRT = GrBackendRenderTargets::MakeGL(width, height, samples, stencilBits, fbInfo);
522
523 mScreenSurface = SkSurfaces::WrapBackendRenderTarget(mGrContext.get(), backendRT, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, nullptr, nullptr);
524 assert(mScreenSurface);
525 }
526#elif defined IGRAPHICS_METAL
527 if (mGrContext.get())
528 {
529 int width = WindowWidth() * GetScreenScale();
530 int height = WindowHeight() * GetScreenScale();
531
532 id<CAMetalDrawable> drawable = [(CAMetalLayer*) mMTLLayer nextDrawable];
533
534 GrMtlTextureInfo fbInfo;
535 fbInfo.fTexture.retain((const void*)(drawable.texture));
536 auto backendRT = GrBackendRenderTargets::MakeMtl(width, height, fbInfo);
537 mScreenSurface = SkSurfaces::WrapBackendRenderTarget(mGrContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr);
538
539 mMTLDrawable = (void*) drawable;
540 assert(mScreenSurface);
541 }
542#endif
543
545}
546
548{
549#ifdef IGRAPHICS_CPU
550 #if defined OS_MAC || defined OS_IOS
551 SkPixmap pixmap;
552 mSurface->peekPixels(&pixmap);
553 SkBitmap bmp;
554 bmp.installPixels(pixmap);
555 CGContext* pCGContext = (CGContextRef) GetPlatformContext();
556 CGContextSaveGState(pCGContext);
557 CGContextScaleCTM(pCGContext, 1.0 / GetScreenScale(), 1.0 / GetScreenScale());
558 SkCGDrawBitmap(pCGContext, bmp, 0, 0);
559 CGContextRestoreGState(pCGContext);
560 #elif defined OS_WIN
561 auto w = WindowWidth() * GetScreenScale();
562 auto h = WindowHeight() * GetScreenScale();
563 BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(mSurfaceMemory.Get());
564 HWND hWnd = (HWND) GetWindow();
565 PAINTSTRUCT ps;
566 HDC hdc = BeginPaint(hWnd, &ps);
567 StretchDIBits(hdc, 0, 0, w, h, 0, 0, w, h, bmpInfo->bmiColors, bmpInfo, DIB_RGB_COLORS, SRCCOPY);
568 ReleaseDC(hWnd, hdc);
569 EndPaint(hWnd, &ps);
570 #else
571 #error NOT IMPLEMENTED
572 #endif
573#else // GPU
574 mSurface->draw(mScreenSurface->getCanvas(), 0.0, 0.0, nullptr);
575
576 if (auto dContext = GrAsDirectContext(mScreenSurface->getCanvas()->recordingContext())) {
577 dContext->flushAndSubmit();
578 }
579
580 #ifdef IGRAPHICS_METAL
581 id<MTLCommandBuffer> commandBuffer = [(id<MTLCommandQueue>) mMTLCommandQueue commandBuffer];
582 commandBuffer.label = @"Present";
583
584 [commandBuffer presentDrawable:(id<CAMetalDrawable>) mMTLDrawable];
585 [commandBuffer commit];
586 #endif
587#endif
588}
589
590void IGraphicsSkia::DrawBitmap(const IBitmap& bitmap, const IRECT& dest, int srcX, int srcY, const IBlend* pBlend)
591{
592 SkPaint p;
593
594 p.setAntiAlias(true);
595 p.setBlendMode(SkiaBlendMode(pBlend));
596 if (pBlend)
597 p.setAlpha(Clip(static_cast<int>(pBlend->mWeight * 255), 0, 255));
598
599 SkiaDrawable* image = bitmap.GetAPIBitmap()->GetBitmap();
600
601 double scale1 = 1.0 / (bitmap.GetScale() * bitmap.GetDrawScale());
602 double scale2 = bitmap.GetScale() * bitmap.GetDrawScale();
603
604 mCanvas->save();
605 mCanvas->clipRect(SkiaRect(dest));
606 mCanvas->translate(dest.L, dest.T);
607 mCanvas->scale(scale1, scale1);
608 mCanvas->translate(-srcX * scale2, -srcY * scale2);
609
610#ifdef IGRAPHICS_CPU
611 auto samplingOptions = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
612#else
613 auto samplingOptions = SkSamplingOptions(SkCubicResampler::Mitchell());
614#endif
615
616 if (image->mIsSurface)
617 image->mSurface->draw(mCanvas, 0.0, 0.0, samplingOptions, &p);
618 else
619 mCanvas->drawImage(image->mImage, 0.0, 0.0, samplingOptions, &p);
620
621 mCanvas->restore();
622}
623
624void IGraphicsSkia::PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding)
625{
626 SkPath arc;
627 arc.setIsVolatile(true);
628
629 float sweep = (a2 - a1);
630
631 if (sweep >= 360.f || sweep <= -360.f)
632 {
633 arc.addCircle(cx, cy, r);
634 mMainPath.addPath(arc, mMatrix, SkPath::kAppend_AddPathMode);
635 }
636 else
637 {
638 if (winding == EWinding::CW)
639 {
640 while (sweep < 0)
641 sweep += 360.f;
642 }
643 else
644 {
645 while (sweep > 0)
646 sweep -= 360.f;
647 }
648
649 arc.arcTo(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, sweep, false);
650 mMainPath.addPath(arc, mMatrix, SkPath::kExtend_AddPathMode);
651 }
652}
653
655{
656 SkBitmap bitmap;
657 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
658 mCanvas->readPixels(bitmap, x, y);
659 auto color = bitmap.getColor(0,0);
660 return IColor(SkColorGetA(color), SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
661}
662
663bool IGraphicsSkia::LoadAPIFont(const char* fontID, const PlatformFontPtr& font)
664{
665 StaticStorage<Font>::Accessor storage(sFontCache);
666 Font* cached = storage.Find(fontID);
667
668 if (cached)
669 return true;
670
671 IFontDataPtr data = font->GetFontData();
672
673 if (data->IsValid())
674 {
675 auto wrappedData = SkData::MakeWithoutCopy(data->Get(), data->GetSize());
676 auto typeFace = SkFontMgrRefDefault()->makeFromData(wrappedData);
677 if (typeFace)
678 {
679 storage.Add(new Font(std::move(data), typeFace), fontID);
680 return true;
681 }
682 }
683
684 return false;
685}
686
687void IGraphicsSkia::PrepareAndMeasureText(const IText& text, const char* str, IRECT& r, double& x, double & y, SkFont& font) const
688{
689 SkFontMetrics metrics;
690 SkPaint paint;
691 //SkRect bounds;
692
693 StaticStorage<Font>::Accessor storage(sFontCache);
694 Font* pFont = storage.Find(text.mFont);
695
696 assert(pFont && "No font found - did you forget to load it?");
697
698 font.setTypeface(pFont->mTypeface);
699 font.setHinting(SkFontHinting::kSlight);
700 font.setForceAutoHinting(false);
701 font.setSubpixel(true);
702 font.setSize(text.mSize * pFont->mData->GetHeightEMRatio());
703
704 // Draw / measure
705 const double textWidth = font.measureText(str, strlen(str), SkTextEncoding::kUTF8, nullptr/* &bounds*/);
706 font.getMetrics(&metrics);
707
708 const double textHeight = text.mSize;
709 const double ascender = metrics.fAscent;
710 const double descender = metrics.fDescent;
711
712 switch (text.mAlign)
713 {
714 case EAlign::Near: x = r.L; break;
715 case EAlign::Center: x = r.MW() - (textWidth / 2.0); break;
716 case EAlign::Far: x = r.R - textWidth; break;
717 }
718
719 switch (text.mVAlign)
720 {
721 case EVAlign::Top: y = r.T - ascender; break;
722 case EVAlign::Middle: y = r.MH() - descender + (textHeight / 2.0); break;
723 case EVAlign::Bottom: y = r.B - descender; break;
724 }
725
726 r = IRECT((float) x, (float) y + ascender, (float) (x + textWidth), (float) (y + ascender + textHeight));
727}
728
729float IGraphicsSkia::DoMeasureText(const IText& text, const char* str, IRECT& bounds) const
730{
731 SkFont font;
732 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
733
734 IRECT r = bounds;
735 double x, y;
736 PrepareAndMeasureText(text, str, bounds, x, y, font);
737 DoMeasureTextRotation(text, r, bounds);
738 return bounds.W();
739}
740
741void IGraphicsSkia::DoDrawText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
742{
743 IRECT measured = bounds;
744
745 SkFont font;
746 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
747
748 double x, y;
749
750 PrepareAndMeasureText(text, str, measured, x, y, font);
752 DoTextRotation(text, bounds, measured);
753 SkPaint paint;
754 paint.setColor(SkiaColor(text.mFGColor, pBlend));
755 mCanvas->drawSimpleText(str, strlen(str), SkTextEncoding::kUTF8, x, y, font, paint);
757}
758
759void IGraphicsSkia::PathStroke(const IPattern& pattern, float thickness, const IStrokeOptions& options, const IBlend* pBlend)
760{
761 SkPaint paint = SkiaPaint(pattern, pBlend);
762 paint.setStyle(SkPaint::kStroke_Style);
763
764 switch (options.mCapOption)
765 {
766 case ELineCap::Butt: paint.setStrokeCap(SkPaint::kButt_Cap); break;
767 case ELineCap::Round: paint.setStrokeCap(SkPaint::kRound_Cap); break;
768 case ELineCap::Square: paint.setStrokeCap(SkPaint::kSquare_Cap); break;
769 }
770
771 switch (options.mJoinOption)
772 {
773 case ELineJoin::Miter: paint.setStrokeJoin(SkPaint::kMiter_Join); break;
774 case ELineJoin::Round: paint.setStrokeJoin(SkPaint::kRound_Join); break;
775 case ELineJoin::Bevel: paint.setStrokeJoin(SkPaint::kBevel_Join); break;
776 }
777
778 if (options.mDash.GetCount())
779 {
780 // N.B. support odd counts by reading the array twice
781 int dashCount = options.mDash.GetCount();
782 int dashMax = dashCount & 1 ? dashCount * 2 : dashCount;
783 float dashArray[16];
784
785 for (int i = 0; i < dashMax; i += 2)
786 {
787 dashArray[i + 0] = options.mDash.GetArray()[i % dashCount];
788 dashArray[i + 1] = options.mDash.GetArray()[(i + 1) % dashCount];
789 }
790
791 paint.setPathEffect(SkDashPathEffect::Make(dashArray, dashMax, options.mDash.GetOffset()));
792 }
793
794 paint.setStrokeWidth(thickness);
795 paint.setStrokeMiter(options.mMiterLimit);
796
797 RenderPath(paint);
798
799 if (!options.mPreserve)
800 mMainPath.reset();
801}
802
803void IGraphicsSkia::PathFill(const IPattern& pattern, const IFillOptions& options, const IBlend* pBlend)
804{
805 SkPaint paint = SkiaPaint(pattern, pBlend);
806 paint.setStyle(SkPaint::kFill_Style);
807
808 if (options.mFillRule == EFillRule::Winding)
809 mMainPath.setFillType(SkPathFillType::kWinding);
810 else
811 mMainPath.setFillType(SkPathFillType::kEvenOdd);
812
813 RenderPath(paint);
814
815 if (!options.mPreserve)
816 mMainPath.reset();
817}
818
819#ifdef IGRAPHICS_DRAWFILL_DIRECT
820void IGraphicsSkia::DrawRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness)
821{
822 auto paint = SkiaPaint(color, pBlend);
823 paint.setStyle(SkPaint::Style::kStroke_Style);
824 paint.setStrokeWidth(thickness);
825 mCanvas->drawRect(SkiaRect(bounds), paint);
826}
827
828void IGraphicsSkia::DrawRoundRect(const IColor& color, const IRECT& bounds, float cornerRadius, const IBlend* pBlend, float thickness)
829{
830 auto paint = SkiaPaint(color, pBlend);
831 paint.setStyle(SkPaint::Style::kStroke_Style);
832 paint.setStrokeWidth(thickness);
833 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);
834}
835
836void IGraphicsSkia::DrawArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend, float thickness)
837{
838 auto paint = SkiaPaint(color, pBlend);
839 paint.setStyle(SkPaint::Style::kStroke_Style);
840 paint.setStrokeWidth(thickness);
841 mCanvas->drawArc(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, (a2 - a1), false, paint);
842}
843
844void IGraphicsSkia::DrawCircle(const IColor& color, float cx, float cy, float r, const IBlend* pBlend, float thickness)
845{
846 auto paint = SkiaPaint(color, pBlend);
847 paint.setStyle(SkPaint::Style::kStroke_Style);
848 paint.setStrokeWidth(thickness);
849 mCanvas->drawCircle(cx, cy, r, paint);
850}
851
852void IGraphicsSkia::DrawEllipse(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness)
853{
854 auto paint = SkiaPaint(color, pBlend);
855 paint.setStyle(SkPaint::Style::kStroke_Style);
856 paint.setStrokeWidth(thickness);
857 mCanvas->drawOval(SkiaRect(bounds), paint);
858}
859
860void IGraphicsSkia::FillRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend)
861{
862 auto paint = SkiaPaint(color, pBlend);
863 paint.setStyle(SkPaint::Style::kFill_Style);
864 mCanvas->drawRect(SkiaRect(bounds), paint);
865}
866
867void IGraphicsSkia::FillRoundRect(const IColor& color, const IRECT& bounds, float cornerRadius, const IBlend* pBlend)
868{
869 auto paint = SkiaPaint(color, pBlend);
870 paint.setStyle(SkPaint::Style::kFill_Style);
871 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);
872}
873
874void IGraphicsSkia::FillArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend)
875{
876 auto paint = SkiaPaint(color, pBlend);
877 paint.setStyle(SkPaint::Style::kFill_Style);
878 mCanvas->drawArc(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, (a2 - a1), true, paint);
879}
880
881void IGraphicsSkia::FillCircle(const IColor& color, float cx, float cy, float r, const IBlend* pBlend)
882{
883 auto paint = SkiaPaint(color, pBlend);
884 paint.setStyle(SkPaint::Style::kFill_Style);
885 mCanvas->drawCircle(cx, cy, r, paint);
886}
887
888void IGraphicsSkia::FillEllipse(const IColor& color, const IRECT& bounds, const IBlend* pBlend)
889{
890 auto paint = SkiaPaint(color, pBlend);
891 paint.setStyle(SkPaint::Style::kFill_Style);
892 mCanvas->drawOval(SkiaRect(bounds), paint);
893}
894#endif
895
896void IGraphicsSkia::RenderPath(SkPaint& paint)
897{
898 SkMatrix invMatrix;
899
900 if (!mMatrix.isIdentity() && mMatrix.invert(&invMatrix))
901 {
902 SkPath path;
903 path.setIsVolatile(true);
904 mMainPath.transform(invMatrix, &path);
905 mCanvas->drawPath(path, paint);
906 }
907 else
908 {
909 mCanvas->drawPath(mMainPath, paint);
910 }
911}
912
913void IGraphicsSkia::PathTransformSetMatrix(const IMatrix& m)
914{
915 double xTranslate = 0.0;
916 double yTranslate = 0.0;
917
918 if (!mCanvas)
919 return;
920
921 if (!mLayers.empty())
922 {
923 IRECT bounds = mLayers.top()->Bounds();
924
925 xTranslate = -bounds.L;
926 yTranslate = -bounds.T;
927 }
928
929 mMatrix = SkMatrix::MakeAll(m.mXX, m.mXY, m.mTX, m.mYX, m.mYY, m.mTY, 0, 0, 1);
930 auto scale = GetTotalScale();
931 SkMatrix globalMatrix = SkMatrix::Scale(scale, scale);
932 mClipMatrix = SkMatrix();
933 mFinalMatrix = mMatrix;
934 globalMatrix.preTranslate(xTranslate, yTranslate);
935 mClipMatrix.postConcat(globalMatrix);
936 mFinalMatrix.postConcat(globalMatrix);
937 mCanvas->setMatrix(mFinalMatrix);
938}
939
940void IGraphicsSkia::SetClipRegion(const IRECT& r)
941{
942 mCanvas->restoreToCount(0);
943 mCanvas->save();
944 mCanvas->setMatrix(mClipMatrix);
945 mCanvas->clipRect(SkiaRect(r));
946 mCanvas->setMatrix(mFinalMatrix);
947}
948
949APIBitmap* IGraphicsSkia::CreateAPIBitmap(int width, int height, float scale, double drawScale, bool cacheable)
950{
951 sk_sp<SkSurface> surface;
952 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
953
954 #ifndef IGRAPHICS_CPU
955 if (cacheable)
956 {
957 surface = SkSurfaces::Raster(info);
958 }
959 else
960 {
961 surface = SkSurfaces::RenderTarget(mGrContext.get(), skgpu::Budgeted::kYes, info);
962 }
963 #else
964 surface = SkSurfaces::Raster(info);
965 #endif
966
967 surface->getCanvas()->save();
968
969 return new Bitmap(std::move(surface), width, height, scale, drawScale);
970}
971
973{
974 mCanvas = mLayers.empty() ? mSurface->getCanvas() : mLayers.top()->GetAPIBitmap()->GetBitmap()->mSurface->getCanvas();
975}
976
977static size_t CalcRowBytes(int width)
978{
979 width = ((width + 7) & (-8));
980 return width * sizeof(uint32_t);
981}
982
984{
985#ifdef IGRAPHICS_CPU
986 bool useFilter = false;
987#else
988 bool useFilter = shadow.mPattern.mNStops <= 1;
989#endif
990
991 if (useFilter)
992 {
993 auto makeFilter = [&shadow](float scale)
994 {
995 // The constant of 3.f matches the IGraphics scaling of blur
996 const auto dx = shadow.mXOffset * scale;
997 const auto dy = shadow.mYOffset * scale;
998 const auto r = shadow.mBlurSize * scale / 3.f;
999 const IBlend blend(EBlend::Default, shadow.mOpacity);
1000 const auto color = SkiaColor(shadow.mPattern.GetStop(0).mColor, &blend);
1001
1002 if (shadow.mDrawForeground)
1003 return SkImageFilters::DropShadow(dx, dy, r, r, color, nullptr);
1004 else
1005 return SkImageFilters::DropShadowOnly(dx, dy, r, r, color, nullptr);
1006 };
1007
1008 auto shadowFilter = makeFilter(layer->GetAPIBitmap()->GetScale());
1009
1010 SkPaint p;
1011 SkMatrix m;
1012 m.reset();
1013
1014 p.setAntiAlias(true);
1015 p.setImageFilter(shadowFilter);
1016
1017 sk_sp<SkSurface> surface = layer->GetAPIBitmap()->GetBitmap()->mSurface;
1018 SkCanvas* pCanvas = surface->getCanvas();
1019 sk_sp<SkImage> contents = surface->makeImageSnapshot();
1020
1021 pCanvas->setMatrix(m);
1022 pCanvas->clear(SK_ColorTRANSPARENT);
1023 pCanvas->drawImage(contents.get(), 0.0, 0.0, SkSamplingOptions(), &p);
1024 }
1025 else
1026 {
1027 IGraphics::ApplyLayerDropShadow(layer, shadow);
1028 }
1029}
1030
1031void IGraphicsSkia::GetLayerBitmapData(const ILayerPtr& layer, RawBitmapData& data)
1032{
1033 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();
1034 size_t rowBytes = CalcRowBytes(pDrawable->mSurface->width());
1035 int size = pDrawable->mSurface->height() * static_cast<int>(rowBytes);
1036
1037 data.Resize(size);
1038
1039 if (data.GetSize() >= size)
1040 {
1041 SkImageInfo info = SkImageInfo::MakeN32Premul(pDrawable->mSurface->width(), pDrawable->mSurface->height());
1042 pDrawable->mSurface->readPixels(info, data.Get(), rowBytes, 0, 0);
1043 }
1044}
1045
1046void IGraphicsSkia::ApplyShadowMask(ILayerPtr& layer, RawBitmapData& mask, const IShadow& shadow)
1047{
1048 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();
1049 int width = pDrawable->mSurface->width();
1050 int height = pDrawable->mSurface->height();
1051 size_t rowBytes = CalcRowBytes(width);
1052 double scale = layer->GetAPIBitmap()->GetDrawScale() * layer->GetAPIBitmap()->GetScale();
1053
1054 SkCanvas* pCanvas = pDrawable->mSurface->getCanvas();
1055
1056 SkMatrix m;
1057 m.reset();
1058
1059 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
1060 SkPixmap pixMap(info, mask.Get(), rowBytes);
1061 sk_sp<SkImage> image = SkImages::RasterFromPixmap(pixMap, nullptr, nullptr);
1062 sk_sp<SkImage> foreground;
1063
1064 // Copy the foreground if needed
1065
1066 if (shadow.mDrawForeground)
1067 foreground = pDrawable->mSurface->makeImageSnapshot();
1068
1069 pCanvas->clear(SK_ColorTRANSPARENT);
1070
1071 IBlend blend(EBlend::Default, shadow.mOpacity);
1072 pCanvas->setMatrix(m);
1073 pCanvas->drawImage(image.get(), shadow.mXOffset * scale, shadow.mYOffset * scale);
1074 m = SkMatrix::Scale(scale, scale);
1075 pCanvas->setMatrix(m);
1076 pCanvas->translate(-layer->Bounds().L, -layer->Bounds().T);
1077 SkPaint p = SkiaPaint(shadow.mPattern, &blend);
1078 p.setBlendMode(SkBlendMode::kSrcIn);
1079 pCanvas->drawPaint(p);
1080
1081 if (shadow.mDrawForeground)
1082 {
1083 m.reset();
1084 pCanvas->setMatrix(m);
1085 pCanvas->drawImage(foreground.get(), 0.0, 0.0);
1086 }
1087}
1088
1089void IGraphicsSkia::DrawFastDropShadow(const IRECT& innerBounds, const IRECT& outerBounds, float xyDrop, float roundness, float blur, IBlend* pBlend)
1090{
1091 SkRect r = SkiaRect(innerBounds.GetTranslated(xyDrop, xyDrop));
1092
1093 SkPaint paint = SkiaPaint(COLOR_BLACK_DROP_SHADOW, pBlend);
1094 paint.setStyle(SkPaint::Style::kFill_Style);
1095
1096 paint.setMaskFilter(SkMaskFilter::MakeBlur(kSolid_SkBlurStyle, blur * 0.5)); // 0.5 seems to match nanovg
1097 mCanvas->drawRoundRect(r, roundness, roundness, paint);
1098}
1099
1100void IGraphicsSkia::DrawMultiLineText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
1101{
1102#if !defined IGRAPHICS_NO_SKIA_SKPARAGRAPH
1103 using namespace skia::textlayout;
1104
1105 auto ConvertTextAlign = [](EAlign align) {
1106 switch (align)
1107 {
1108 case EAlign::Near: return TextAlign::kLeft;
1109 case EAlign::Center: return TextAlign::kCenter;
1110 case EAlign::Far: return TextAlign::kRight;
1111 default: return TextAlign::kLeft;
1112 }
1113 };
1114
1115 ParagraphStyle paragraphStyle;
1116 paragraphStyle.setTextAlign(ConvertTextAlign(text.mAlign));
1117
1118 auto builder = ParagraphBuilder::make(paragraphStyle, mFontCollection, GetUnicode());
1119
1120 assert(builder && "Paragraph Builder couldn't be created");
1121
1122 TextStyle textStyle;
1123 textStyle.setColor(SkiaColor(text.mFGColor, pBlend));
1124
1125 std::string fontFamily = text.mFont;
1126
1127 size_t pos = fontFamily.find('-');
1128 if (pos != std::string::npos)
1129 {
1130 fontFamily = fontFamily.substr(0, pos);
1131 }
1132
1133 textStyle.setFontFamilies({SkString(fontFamily)});
1134 textStyle.setFontSize(text.mSize * 0.9);
1135 textStyle.setFontStyle(SkFontStyle(SkFontStyle::kNormal_Weight,
1136 SkFontStyle::kNormal_Width,
1137 SkFontStyle::kUpright_Slant));
1138
1139 builder->pushStyle(textStyle);
1140 builder->addText(str);
1141 builder->pop();
1142
1143 auto paragraph = builder->Build();
1144
1145 paragraph->layout(bounds.W());
1146
1147 float yOffset = 0;
1148 switch (text.mVAlign)
1149 {
1150 case EVAlign::Top:
1151 yOffset = 0;
1152 break;
1153 case EVAlign::Middle:
1154 yOffset = (bounds.H() - paragraph->getHeight()) / 2;
1155 break;
1156 case EVAlign::Bottom:
1157 yOffset = bounds.H() - paragraph->getHeight();
1158 break;
1159 }
1160
1161 mCanvas->save();
1162 mCanvas->translate(bounds.L, bounds.T + yOffset);
1163 paragraph->paint(mCanvas, 0, 0);
1164 mCanvas->restore();
1165#else
1166 DrawText(text, str, bounds, pBlend);
1167#endif
1168}
1169
1171{
1172#ifdef IGRAPHICS_CPU
1173 return "SKIA | CPU";
1174#elif defined IGRAPHICS_GL2
1175 return "SKIA | GL2";
1176#elif defined IGRAPHICS_GL3
1177 return "SKIA | GL3";
1178#elif defined IGRAPHICS_METAL
1179 return "SKIA | Metal";
1180#endif
1181}
const void * LoadWinResource(const char *resID, const char *type, int &sizeInBytes, void *pHInstance)
Load a resource from the binary (windows only).
A base class interface for a bitmap abstraction around the different drawing back end bitmap represen...
BitmapData GetBitmap() const
void SetBitmap(BitmapData pBitmap, int w, int h, float scale, float drawScale)
Used to initialise the members after construction.
User-facing bitmap abstraction that you use to manage bitmap data, independant of draw class/platform...
float GetDrawScale() const
float GetScale() const
APIBitmap * GetAPIBitmap() const
An editor delegate base class that uses IGraphics for the UI.
The lowest level base class of an IGraphics context.
Definition: IGraphics.h:86
virtual void DrawRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0, float thickness=1.f)
Draw a rectangle to the graphics context.
Definition: IGraphics.cpp:2515
virtual void FillEllipse(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0)
Fill an ellipse within a rectangular region of the graphics context.
Definition: IGraphics.cpp:2631
virtual void DrawEllipse(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0, float thickness=1.f)
Draw an ellipse within a rectangular region of the graphics context.
Definition: IGraphics.cpp:2566
void DrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend=0)
Draw some text to the graphics context in a specific rectangle.
Definition: IGraphics.cpp:683
float GetTotalScale() const
Gets the combined draw and screen/display scaling factor.
Definition: IGraphics.h:1129
virtual void * GetWindow()=0
Get a pointer to the platform view e.g.
virtual void DrawRoundRect(const IColor &color, const IRECT &bounds, float cornerRadius=5.f, const IBlend *pBlend=0, float thickness=1.f)
Draw a rounded rectangle to the graphics context.
Definition: IGraphics.cpp:2522
void DoMeasureTextRotation(const IText &text, const IRECT &bounds, IRECT &rect) const
Definition: IGraphics.cpp:2245
int WindowWidth() const
Gets the width of the graphics context including draw scaling.
Definition: IGraphics.h:1105
void PathTransformRestore()
Restore the affine transform of the current path, to the previously saved state.
Definition: IGraphics.cpp:2726
virtual void FillRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0)
Fill a rectangular region of the graphics context with a color.
Definition: IGraphics.cpp:2587
void * GetPlatformContext()
Get the platform level draw context - an HDC or CGContextRef.
Definition: IGraphics.h:935
virtual void DrawArc(const IColor &color, float cx, float cy, float r, float a1, float a2, const IBlend *pBlend=0, float thickness=1.f)
Draw an arc to the graphics context.
Definition: IGraphics.cpp:2543
void RemoveAllControls()
Removes all regular IControls from the control list, as well as special controls (frees memory).
Definition: IGraphics.cpp:194
virtual void DrawCircle(const IColor &color, float cx, float cy, float r, const IBlend *pBlend=0, float thickness=1.f)
Draw a circle to the graphics context.
Definition: IGraphics.cpp:2550
virtual void FillRoundRect(const IColor &color, const IRECT &bounds, float cornerRadius=5.f, const IBlend *pBlend=0)
Fill a rounded rectangle with a color.
Definition: IGraphics.cpp:2594
virtual void * GetWinModuleHandle()
Definition: IGraphics.h:926
virtual void ApplyLayerDropShadow(ILayerPtr &layer, const IShadow &shadow)
Applies a drop shadow directly onto a layer.
Definition: IGraphics.cpp:2093
float GetScreenScale() const
Gets the screen/display scaling factor, e.g.
Definition: IGraphics.h:1121
void PathTransformSave()
Save the current affine transform of the current path.
Definition: IGraphics.cpp:2721
virtual void FillArc(const IColor &color, float cx, float cy, float r, float a1, float a2, const IBlend *pBlend=0)
Fill an arc segment with a color.
Definition: IGraphics.cpp:2615
int WindowHeight() const
Gets the height of the graphics context including draw scaling.
Definition: IGraphics.h:1109
virtual void BeginFrame()
Called at the beginning of drawing.
Definition: IGraphics.cpp:880
virtual void FillCircle(const IColor &color, float cx, float cy, float r, const IBlend *pBlend=0)
Fill a circle with a color.
Definition: IGraphics.cpp:2624
APIBitmap * LoadAPIBitmap(const char *fileNameOrResID, int scale, EResourceLocation location, const char *ext) override
Drawing API method to load a bitmap, called internally.
void BeginFrame() override
Called at the beginning of drawing.
void GetLayerBitmapData(const ILayerPtr &layer, RawBitmapData &data) override
Get the contents of a layer as Raw RGBA bitmap data NOTE: you should only call this within IControl::...
void DrawResize() override
void DoDrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override
void PathStroke(const IPattern &pattern, float thickness, const IStrokeOptions &options, const IBlend *pBlend) override
Stroke the current current path.
void EndFrame() override
Called by some drawing API classes to finally blit the draw bitmap onto the screen or perform other c...
float DoMeasureText(const IText &text, const char *str, IRECT &bounds) const override
void PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding) override
Add an arc to the current path.
void ApplyShadowMask(ILayerPtr &layer, RawBitmapData &mask, const IShadow &shadow) override
Implemented by a graphics backend to apply a calculated shadow mask to a layer, according to the shad...
const char * GetDrawingAPIStr() override
APIBitmap * CreateAPIBitmap(int width, int height, float scale, double drawScale, bool cacheable=false) override
Creates a new API bitmap, either in memory or as a GPU texture.
bool LoadAPIFont(const char *fontID, const PlatformFontPtr &font) override
Drawing API method to load a font from a PlatformFontPtr, called internally.
void OnViewInitialized(void *pContext) override
Called after platform view initialization, so that drawing classes can e.g.
void UpdateLayer() override
Implemented by a graphics backend to prepare for drawing to the layer at the top of the stack.
void DrawMultiLineText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override
Draw some multi-line text to the graphics context in a specific rectangle (NanoVG only)
void PathFill(const IPattern &pattern, const IFillOptions &options, const IBlend *pBlend) override
Fill the current current path.
bool BitmapExtSupported(const char *ext) override
Checks a file extension and reports whether this drawing API supports loading that extension.
IColor GetPoint(int x, int y) override
Get the color at an X, Y location in the graphics context.
void ApplyLayerDropShadow(ILayerPtr &layer, const IShadow &shadow) override
Applies a drop shadow directly onto a layer.
void DrawBitmap(const IBitmap &bitmap, const IRECT &dest, int srcX, int srcY, const IBlend *pBlend) override
Draw a bitmap (raster) image to the graphics context.
void DrawFastDropShadow(const IRECT &innerBounds, const IRECT &outerBounds, float xyDrop, float roundness, float blur, IBlend *pBlend) override
NanoVG only.
void OnViewDestroyed() override
Called after a platform view is destroyed, so that drawing classes can e.g.
std::unique_ptr< ILayer > ILayerPtr
ILayerPtr is a managed pointer for transferring the ownership of layers.
Used to manage stroke behaviour for path based drawing back ends.
BEGIN_IPLUG_NAMESPACE T Clip(T x, T lo, T hi)
Clips the value x between lo and hi.
static void ToLower(char *cDest, const char *cSrc)
Used to manage composite/blend operations, independent of draw class/platform.
Used to manage color data, independent of draw class/platform.
Used to represent a point/stop in a gradient.
Used to manage fill behaviour.
Used to store transformation matrices.
IMatrix & Invert()
Changes the matrix to be the inverse of its original value.
void TransformPoint(double &x, double &y, double x0, double y0) const
Transforms the point x, y.
Used to store pattern information for gradients.
const IColorStop & GetStop(int idx) const
Get the IColorStop at a particular index (will crash if out of bounds)
int NStops() const
Used to manage a rectangular area, independent of draw class/platform.
IRECT GetTranslated(float x, float y) const
Get a translated copy of this rectangle.
float MH() const
float W() const
float H() const
float MW() const
Used to specify properties of a drop-shadow to a layer.
IText is used to manage font and text/text entry style for a piece of text on the UI,...