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 "SkDashPathEffect.h"
9#include "SkGradientShader.h"
10#include "SkMaskFilter.h"
11#include "SkFont.h"
12#include "SkFontMetrics.h"
13#include "SkTypeface.h"
14#include "SkVertices.h"
15#include "SkSwizzle.h"
16#pragma warning( pop )
17
18#if defined OS_MAC || defined OS_IOS
19 #include "SkCGUtils.h"
20 #if defined IGRAPHICS_GL2
21 #include <OpenGL/gl.h>
22 #elif defined IGRAPHICS_GL3
23 #include <OpenGL/gl3.h>
24 #elif defined IGRAPHICS_METAL
25 //even though this is a .cpp we are in an objc(pp) compilation unit
26 #import <Metal/Metal.h>
27 #import <QuartzCore/CAMetalLayer.h>
28 #include "include/gpu/mtl/GrMtlBackendContext.h"
29 #elif !defined IGRAPHICS_CPU
30 #error Define either IGRAPHICS_GL2, IGRAPHICS_GL3, IGRAPHICS_METAL, or IGRAPHICS_CPU for IGRAPHICS_SKIA with OS_MAC
31 #endif
32#elif defined OS_WIN
33 #pragma comment(lib, "libpng.lib")
34 #pragma comment(lib, "zlib.lib")
35 #pragma comment(lib, "skia.lib")
36 #pragma comment(lib, "svg.lib")
37 #pragma comment(lib, "skshaper.lib")
38 #pragma comment(lib, "skunicode.lib")
39 #pragma comment(lib, "opengl32.lib")
40#endif
41
42#if defined IGRAPHICS_GL
43 #include "gl/GrGLInterface.h"
44#endif
45
46using namespace iplug;
47using namespace igraphics;
48
49extern std::map<std::string, MTLTexturePtr> gTextureMap;
50
51#pragma mark - Private Classes and Structs
52
54{
55public:
56 Bitmap(sk_sp<SkSurface> surface, int width, int height, float scale, float drawScale);
57 Bitmap(const char* path, double sourceScale);
58 Bitmap(const void* pData, int size, double sourceScale);
59 Bitmap(sk_sp<SkImage>, double sourceScale);
60
61private:
62 SkiaDrawable mDrawable;
63};
64
65IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkSurface> surface, int width, int height, float scale, float drawScale)
66{
67 mDrawable.mSurface = surface;
68 mDrawable.mIsSurface = true;
69
70 SetBitmap(&mDrawable, width, height, scale, drawScale);
71}
72
73IGraphicsSkia::Bitmap::Bitmap(const char* path, double sourceScale)
74{
75 sk_sp<SkData> data = SkData::MakeFromFileName(path);
76
77 assert(data && "Unable to load file at path");
78
79 auto image = SkImage::MakeFromEncoded(data);
80
81#ifdef IGRAPHICS_CPU
82 image = image->makeRasterImage();
83#endif
84
85 mDrawable.mImage = image;
86
87 mDrawable.mIsSurface = false;
88 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
89}
90
91IGraphicsSkia::Bitmap::Bitmap(const void* pData, int size, double sourceScale)
92{
93 auto data = SkData::MakeWithoutCopy(pData, size);
94 auto image = SkImage::MakeFromEncoded(data);
95
96#ifdef IGRAPHICS_CPU
97 image = image->makeRasterImage();
98#endif
99
100 mDrawable.mImage = image;
101
102 mDrawable.mIsSurface = false;
103 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
104}
105
106IGraphicsSkia::Bitmap::Bitmap(sk_sp<SkImage> image, double sourceScale)
107{
108#ifdef IGRAPHICS_CPU
109 mDrawable.mImage = image->makeRasterImage();
110#else
111 mDrawable.mImage = image;
112#endif
113
114 SetBitmap(&mDrawable, mDrawable.mImage->width(), mDrawable.mImage->height(), sourceScale, 1.f);
115}
116
117struct IGraphicsSkia::Font
118{
119 Font(IFontDataPtr&& data, sk_sp<SkTypeface> typeFace)
120 : mData(std::move(data)), mTypeface(typeFace) {}
121
122 IFontDataPtr mData;
123 sk_sp<SkTypeface> mTypeface;
124};
125
126// Fonts
127StaticStorage<IGraphicsSkia::Font> IGraphicsSkia::sFontCache;
128
129#pragma mark - Utility conversions
130
131BEGIN_IPLUG_NAMESPACE
132BEGIN_IGRAPHICS_NAMESPACE
133
134SkColor SkiaColor(const IColor& color, const IBlend* pBlend)
135{
136 if (pBlend)
137 return SkColorSetARGB(Clip(static_cast<int>(pBlend->mWeight * color.A), 0, 255), color.R, color.G, color.B);
138 else
139 return SkColorSetARGB(color.A, color.R, color.G, color.B);
140}
141
142SkRect SkiaRect(const IRECT& r)
143{
144 return SkRect::MakeLTRB(r.L, r.T, r.R, r.B);
145}
146
147SkBlendMode SkiaBlendMode(const IBlend* pBlend)
148{
149 if (!pBlend)
150 return SkBlendMode::kSrcOver;
151
152 switch (pBlend->mMethod)
153 {
154 case EBlend::SrcOver: return SkBlendMode::kSrcOver;
155 case EBlend::SrcIn: return SkBlendMode::kSrcIn;
156 case EBlend::SrcOut: return SkBlendMode::kSrcOut;
157 case EBlend::SrcAtop: return SkBlendMode::kSrcATop;
158 case EBlend::DstOver: return SkBlendMode::kDstOver;
159 case EBlend::DstIn: return SkBlendMode::kDstIn;
160 case EBlend::DstOut: return SkBlendMode::kDstOut;
161 case EBlend::DstAtop: return SkBlendMode::kDstATop;
162 case EBlend::Add: return SkBlendMode::kPlus;
163 case EBlend::XOR: return SkBlendMode::kXor;
164 }
165
166 return SkBlendMode::kClear;
167}
168
169SkTileMode SkiaTileMode(const IPattern& pattern)
170{
171 switch (pattern.mExtend)
172 {
173 case EPatternExtend::None: return SkTileMode::kDecal;
174 case EPatternExtend::Reflect: return SkTileMode::kMirror;
175 case EPatternExtend::Repeat: return SkTileMode::kRepeat;
176 case EPatternExtend::Pad: return SkTileMode::kClamp;
177 }
178
179 return SkTileMode::kClamp;
180}
181
182SkPaint SkiaPaint(const IPattern& pattern, const IBlend* pBlend)
183{
184 SkPaint paint;
185 paint.setAntiAlias(true);
186 paint.setBlendMode(SkiaBlendMode(pBlend));
187 int numStops = pattern.NStops();
188
189 if (pattern.mType == EPatternType::Solid || numStops < 2)
190 {
191 paint.setColor(SkiaColor(pattern.GetStop(0).mColor, pBlend));
192 }
193 else
194 {
195 double x1 = 0.0;
196 double y1 = 0.0;
197 double x2 = 0.0;
198 double y2 = 1.0;
199
200 IMatrix m = pattern.mTransform;
201 m.Invert();
202 m.TransformPoint(x1, y1);
203 m.TransformPoint(x2, y2);
204
205 SkPoint points[2] =
206 {
207 SkPoint::Make(x1, y1),
208 SkPoint::Make(x2, y2)
209 };
210
211 SkColor colors[8];
212 SkScalar positions[8];
213
214 assert(numStops <= 8);
215
216 for(int i = 0; i < numStops; i++)
217 {
218 const IColorStop& stop = pattern.GetStop(i);
219 colors[i] = SkiaColor(stop.mColor, pBlend);
220 positions[i] = stop.mOffset;
221 }
222
223 switch (pattern.mType)
224 {
225 case EPatternType::Linear:
226 paint.setShader(SkGradientShader::MakeLinear(points, colors, positions, numStops, SkiaTileMode(pattern), 0, nullptr));
227 break;
228
229 case EPatternType::Radial:
230 {
231 float xd = points[0].x() - points[1].x();
232 float yd = points[0].y() - points[1].y();
233 float radius = std::sqrt(xd * xd + yd * yd);
234 paint.setShader(SkGradientShader::MakeRadial(points[0], radius, colors, positions, numStops, SkiaTileMode(pattern), 0, nullptr));
235 break;
236 }
237
238 case EPatternType::Sweep:
239 {
240 SkMatrix matrix = SkMatrix::MakeAll(m.mXX, m.mYX, 0, m.mXY, m.mYY, 0, 0, 0, 1);
241
242 paint.setShader(SkGradientShader::MakeSweep(x1, y1, colors, nullptr, numStops, SkTileMode::kDecal,
243 0, 360 * positions[numStops - 1], 0, &matrix));
244
245 break;
246 }
247
248 default:
249 break;
250 }
251 }
252
253 return paint;
254}
255
256END_IGRAPHICS_NAMESPACE
257END_IPLUG_NAMESPACE
258
259#pragma mark -
260
261IGraphicsSkia::IGraphicsSkia(IGEditorDelegate& dlg, int w, int h, int fps, float scale)
262: IGraphics(dlg, w, h, fps, scale)
263{
264 mMainPath.setIsVolatile(true);
265
266#if defined IGRAPHICS_CPU
267 DBGMSG("IGraphics Skia CPU @ %i FPS\n", fps);
268#elif defined IGRAPHICS_METAL
269 DBGMSG("IGraphics Skia METAL @ %i FPS\n", fps);
270#elif defined IGRAPHICS_GL
271 DBGMSG("IGraphics Skia GL @ %i FPS\n", fps);
272#endif
273 StaticStorage<Font>::Accessor storage(sFontCache);
274 storage.Retain();
275}
276
277IGraphicsSkia::~IGraphicsSkia()
278{
279 StaticStorage<Font>::Accessor storage(sFontCache);
280 storage.Release();
281}
282
284{
285 char extLower[32];
286 ToLower(extLower, ext);
287 return (strstr(extLower, "png") != nullptr) || (strstr(extLower, "jpg") != nullptr) || (strstr(extLower, "jpeg") != nullptr);
288}
289
290APIBitmap* IGraphicsSkia::LoadAPIBitmap(const char* fileNameOrResID, int scale, EResourceLocation location, const char* ext)
291{
292//#ifdef OS_IOS
293// if (location == EResourceLocation::kPreloadedTexture)
294// {
295// assert(0 && "SKIA does not yet load KTX textures");
296// GrMtlTextureInfo textureInfo;
297// textureInfo.fTexture.retain((void*)(gTextureMap[fileNameOrResID]));
298// id<MTLTexture> texture = (id<MTLTexture>) textureInfo.fTexture.get();
299//
300// MTLPixelFormat pixelFormat = texture.pixelFormat;
301//
302// auto grBackendTexture = GrBackendTexture(texture.width, texture.height, GrMipMapped::kNo, textureInfo);
303//
304// sk_sp<SkImage> image = SkImage::MakeFromTexture(mGrContext.get(), grBackendTexture, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, kOpaque_SkAlphaType, nullptr);
305// return new Bitmap(image, scale);
306// }
307// else
308//#endif
309#ifdef OS_WIN
310 if (location == EResourceLocation::kWinBinary)
311 {
312 int size = 0;
313 const void* pData = LoadWinResource(fileNameOrResID, ext, size, GetWinModuleHandle());
314 return new Bitmap(pData, size, scale);
315 }
316 else
317#endif
318 return new Bitmap(fileNameOrResID, scale);
319}
320
321APIBitmap* IGraphicsSkia::LoadAPIBitmap(const char* name, const void* pData, int dataSize, int scale)
322{
323 return new Bitmap(pData, dataSize, scale);
324}
325
327{
328#if defined IGRAPHICS_GL
329 auto glInterface = GrGLMakeNativeInterface();
330 mGrContext = GrDirectContext::MakeGL(glInterface);
331#elif defined IGRAPHICS_METAL
332 CAMetalLayer* pMTLLayer = (CAMetalLayer*) pContext;
333 id<MTLDevice> device = pMTLLayer.device;
334 id<MTLCommandQueue> commandQueue = [device newCommandQueue];
335 GrMtlBackendContext backendContext = {};
336 backendContext.fDevice.retain((__bridge GrMTLHandle) device);
337 backendContext.fQueue.retain((__bridge GrMTLHandle) commandQueue);
338 mGrContext = GrDirectContext::MakeMetal(backendContext);
339 mMTLDevice = (void*) device;
340 mMTLCommandQueue = (void*) commandQueue;
341 mMTLLayer = pContext;
342#endif
343
344 DrawResize();
345}
346
348{
350
351#if defined IGRAPHICS_GL
352 mSurface = nullptr;
353 mScreenSurface = nullptr;
354 mGrContext = nullptr;
355#elif defined IGRAPHICS_METAL
356 [(id<MTLCommandQueue>) mMTLCommandQueue release];
357 mMTLCommandQueue = nullptr;
358 mMTLLayer = nullptr;
359 mMTLDevice = nullptr;
360#endif
361}
362
364{
365 auto w = static_cast<int>(std::ceil(static_cast<float>(WindowWidth()) * GetScreenScale()));
366 auto h = static_cast<int>(std::ceil(static_cast<float>(WindowHeight()) * GetScreenScale()));
367
368#if defined IGRAPHICS_GL || defined IGRAPHICS_METAL
369 if (mGrContext.get())
370 {
371 SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
372 mSurface = SkSurface::MakeRenderTarget(mGrContext.get(), SkBudgeted::kYes, info);
373 }
374#else
375 #ifdef OS_WIN
376 mSurface.reset();
377
378 const size_t bmpSize = sizeof(BITMAPINFOHEADER) + (w * h * sizeof(uint32_t));
379 mSurfaceMemory.Resize(bmpSize);
380 BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(mSurfaceMemory.Get());
381 ZeroMemory(bmpInfo, sizeof(BITMAPINFO));
382 bmpInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
383 bmpInfo->bmiHeader.biWidth = w;
384 bmpInfo->bmiHeader.biHeight = -h; // negative means top-down bitmap. Skia draws top-down.
385 bmpInfo->bmiHeader.biPlanes = 1;
386 bmpInfo->bmiHeader.biBitCount = 32;
387 bmpInfo->bmiHeader.biCompression = BI_RGB;
388 void* pixels = bmpInfo->bmiColors;
389
390 SkImageInfo info = SkImageInfo::Make(w, h, kN32_SkColorType, kPremul_SkAlphaType, nullptr);
391 mSurface = SkSurface::MakeRasterDirect(info, pixels, sizeof(uint32_t) * w);
392 #else
393 mSurface = SkSurface::MakeRasterN32Premul(w, h);
394 #endif
395#endif
396 if (mSurface)
397 {
398 mCanvas = mSurface->getCanvas();
399 mCanvas->save();
400 }
401}
402
404{
405#if defined IGRAPHICS_GL
406 if (mGrContext.get())
407 {
408 int width = WindowWidth() * GetScreenScale();
409 int height = WindowHeight() * GetScreenScale();
410
411 // Bind to the current main framebuffer
412 int fbo = 0, samples = 0, stencilBits = 0;
413 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
414 glGetIntegerv(GL_SAMPLES, &samples);
415#ifdef IGRAPHICS_GL3
416 glGetFramebufferAttachmentParameteriv(GL_DRAW_FRAMEBUFFER, GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, &stencilBits);
417#else
418 glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
419#endif
420
421 GrGLFramebufferInfo fbinfo;
422 fbinfo.fFBOID = fbo;
423 fbinfo.fFormat = 0x8058;
424
425 GrBackendRenderTarget backendRT(width, height, samples, stencilBits, fbinfo);
426
427 mScreenSurface = SkSurface::MakeFromBackendRenderTarget(mGrContext.get(), backendRT, kBottomLeft_GrSurfaceOrigin, kRGBA_8888_SkColorType, nullptr, nullptr);
428 assert(mScreenSurface);
429 }
430#elif defined IGRAPHICS_METAL
431 if (mGrContext.get())
432 {
433 int width = WindowWidth() * GetScreenScale();
434 int height = WindowHeight() * GetScreenScale();
435
436 id<CAMetalDrawable> drawable = [(CAMetalLayer*) mMTLLayer nextDrawable];
437
438 GrMtlTextureInfo fbInfo;
439 fbInfo.fTexture.retain((const void*)(drawable.texture));
440 GrBackendRenderTarget backendRT(width, height, 1 /* sample count/MSAA */, fbInfo);
441
442 mScreenSurface = SkSurface::MakeFromBackendRenderTarget(mGrContext.get(), backendRT, kTopLeft_GrSurfaceOrigin, kBGRA_8888_SkColorType, nullptr, nullptr);
443
444 mMTLDrawable = (void*) drawable;
445 assert(mScreenSurface);
446 }
447#endif
448
450}
451
453{
454#ifdef IGRAPHICS_CPU
455 #if defined OS_MAC || defined OS_IOS
456 SkPixmap pixmap;
457 mSurface->peekPixels(&pixmap);
458 SkBitmap bmp;
459 bmp.installPixels(pixmap);
460 CGContext* pCGContext = (CGContextRef) GetPlatformContext();
461 CGContextSaveGState(pCGContext);
462 CGContextScaleCTM(pCGContext, 1.0 / GetScreenScale(), 1.0 / GetScreenScale());
463 SkCGDrawBitmap(pCGContext, bmp, 0, 0);
464 CGContextRestoreGState(pCGContext);
465 #elif defined OS_WIN
466 auto w = WindowWidth() * GetScreenScale();
467 auto h = WindowHeight() * GetScreenScale();
468 BITMAPINFO* bmpInfo = reinterpret_cast<BITMAPINFO*>(mSurfaceMemory.Get());
469 HWND hWnd = (HWND) GetWindow();
470 PAINTSTRUCT ps;
471 HDC hdc = BeginPaint(hWnd, &ps);
472 StretchDIBits(hdc, 0, 0, w, h, 0, 0, w, h, bmpInfo->bmiColors, bmpInfo, DIB_RGB_COLORS, SRCCOPY);
473 ReleaseDC(hWnd, hdc);
474 EndPaint(hWnd, &ps);
475 #else
476 #error NOT IMPLEMENTED
477 #endif
478#else // GPU
479 mSurface->draw(mScreenSurface->getCanvas(), 0.0, 0.0, nullptr);
480
481 mScreenSurface->getCanvas()->flush();
482
483 #ifdef IGRAPHICS_METAL
484 id<MTLCommandBuffer> commandBuffer = [(id<MTLCommandQueue>) mMTLCommandQueue commandBuffer];
485 commandBuffer.label = @"Present";
486
487 [commandBuffer presentDrawable:(id<CAMetalDrawable>) mMTLDrawable];
488 [commandBuffer commit];
489 #endif
490#endif
491}
492
493void IGraphicsSkia::DrawBitmap(const IBitmap& bitmap, const IRECT& dest, int srcX, int srcY, const IBlend* pBlend)
494{
495 SkPaint p;
496
497 p.setAntiAlias(true);
498 p.setBlendMode(SkiaBlendMode(pBlend));
499 if (pBlend)
500 p.setAlpha(Clip(static_cast<int>(pBlend->mWeight * 255), 0, 255));
501
502 SkiaDrawable* image = bitmap.GetAPIBitmap()->GetBitmap();
503
504 double scale1 = 1.0 / (bitmap.GetScale() * bitmap.GetDrawScale());
505 double scale2 = bitmap.GetScale() * bitmap.GetDrawScale();
506
507 mCanvas->save();
508 mCanvas->clipRect(SkiaRect(dest));
509 mCanvas->translate(dest.L, dest.T);
510 mCanvas->scale(scale1, scale1);
511 mCanvas->translate(-srcX * scale2, -srcY * scale2);
512
513#ifdef IGRAPHICS_CPU
514 auto samplingOptions = SkSamplingOptions(SkFilterMode::kLinear, SkMipmapMode::kNone);
515#else
516 auto samplingOptions = SkSamplingOptions(SkCubicResampler::Mitchell());
517#endif
518
519 if (image->mIsSurface)
520 image->mSurface->draw(mCanvas, 0.0, 0.0, samplingOptions, &p);
521 else
522 mCanvas->drawImage(image->mImage, 0.0, 0.0, samplingOptions, &p);
523
524 mCanvas->restore();
525}
526
527void IGraphicsSkia::PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding)
528{
529 SkPath arc;
530 arc.setIsVolatile(true);
531
532 float sweep = (a2 - a1);
533
534 if (sweep >= 360.f || sweep <= -360.f)
535 {
536 arc.addCircle(cx, cy, r);
537 mMainPath.addPath(arc, mMatrix, SkPath::kAppend_AddPathMode);
538 }
539 else
540 {
541 if (winding == EWinding::CW)
542 {
543 while (sweep < 0)
544 sweep += 360.f;
545 }
546 else
547 {
548 while (sweep > 0)
549 sweep -= 360.f;
550 }
551
552 arc.arcTo(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, sweep, false);
553 mMainPath.addPath(arc, mMatrix, SkPath::kExtend_AddPathMode);
554 }
555}
556
558{
559 SkBitmap bitmap;
560 bitmap.allocPixels(SkImageInfo::MakeN32Premul(1, 1));
561 mCanvas->readPixels(bitmap, x, y);
562 auto color = bitmap.getColor(0,0);
563 return IColor(SkColorGetA(color), SkColorGetR(color), SkColorGetG(color), SkColorGetB(color));
564}
565
566bool IGraphicsSkia::LoadAPIFont(const char* fontID, const PlatformFontPtr& font)
567{
568 StaticStorage<Font>::Accessor storage(sFontCache);
569 Font* cached = storage.Find(fontID);
570
571 if (cached)
572 return true;
573
574 IFontDataPtr data = font->GetFontData();
575
576 if (data->IsValid())
577 {
578 auto wrappedData = SkData::MakeWithoutCopy(data->Get(), data->GetSize());
579 int index = data->GetFaceIdx();
580 auto typeface = SkTypeface::MakeFromData(wrappedData, index);
581
582 if (typeface)
583 {
584 storage.Add(new Font(std::move(data), typeface), fontID);
585 return true;
586 }
587 }
588
589 return false;
590}
591
592void IGraphicsSkia::PrepareAndMeasureText(const IText& text, const char* str, IRECT& r, double& x, double & y, SkFont& font) const
593{
594 SkFontMetrics metrics;
595 SkPaint paint;
596 //SkRect bounds;
597
598 StaticStorage<Font>::Accessor storage(sFontCache);
599 Font* pFont = storage.Find(text.mFont);
600
601 assert(pFont && "No font found - did you forget to load it?");
602
603 font.setTypeface(pFont->mTypeface);
604 font.setHinting(SkFontHinting::kSlight);
605 font.setForceAutoHinting(false);
606 font.setSubpixel(true);
607 font.setSize(text.mSize * pFont->mData->GetHeightEMRatio());
608
609 // Draw / measure
610 const double textWidth = font.measureText(str, strlen(str), SkTextEncoding::kUTF8, nullptr/* &bounds*/);
611 font.getMetrics(&metrics);
612
613 const double textHeight = text.mSize;
614 const double ascender = metrics.fAscent;
615 const double descender = metrics.fDescent;
616
617 switch (text.mAlign)
618 {
619 case EAlign::Near: x = r.L; break;
620 case EAlign::Center: x = r.MW() - (textWidth / 2.0); break;
621 case EAlign::Far: x = r.R - textWidth; break;
622 }
623
624 switch (text.mVAlign)
625 {
626 case EVAlign::Top: y = r.T - ascender; break;
627 case EVAlign::Middle: y = r.MH() - descender + (textHeight / 2.0); break;
628 case EVAlign::Bottom: y = r.B - descender; break;
629 }
630
631 r = IRECT((float) x, (float) y + ascender, (float) (x + textWidth), (float) (y + ascender + textHeight));
632}
633
634float IGraphicsSkia::DoMeasureText(const IText& text, const char* str, IRECT& bounds) const
635{
636 SkFont font;
637 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
638
639 IRECT r = bounds;
640 double x, y;
641 PrepareAndMeasureText(text, str, bounds, x, y, font);
642 DoMeasureTextRotation(text, r, bounds);
643 return bounds.W();
644}
645
646void IGraphicsSkia::DoDrawText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
647{
648 IRECT measured = bounds;
649
650 SkFont font;
651 font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
652
653 double x, y;
654
655 PrepareAndMeasureText(text, str, measured, x, y, font);
657 DoTextRotation(text, bounds, measured);
658 SkPaint paint;
659 paint.setColor(SkiaColor(text.mFGColor, pBlend));
660 mCanvas->drawSimpleText(str, strlen(str), SkTextEncoding::kUTF8, x, y, font, paint);
662}
663
664void IGraphicsSkia::PathStroke(const IPattern& pattern, float thickness, const IStrokeOptions& options, const IBlend* pBlend)
665{
666 SkPaint paint = SkiaPaint(pattern, pBlend);
667 paint.setStyle(SkPaint::kStroke_Style);
668
669 switch (options.mCapOption)
670 {
671 case ELineCap::Butt: paint.setStrokeCap(SkPaint::kButt_Cap); break;
672 case ELineCap::Round: paint.setStrokeCap(SkPaint::kRound_Cap); break;
673 case ELineCap::Square: paint.setStrokeCap(SkPaint::kSquare_Cap); break;
674 }
675
676 switch (options.mJoinOption)
677 {
678 case ELineJoin::Miter: paint.setStrokeJoin(SkPaint::kMiter_Join); break;
679 case ELineJoin::Round: paint.setStrokeJoin(SkPaint::kRound_Join); break;
680 case ELineJoin::Bevel: paint.setStrokeJoin(SkPaint::kBevel_Join); break;
681 }
682
683 if (options.mDash.GetCount())
684 {
685 // N.B. support odd counts by reading the array twice
686 int dashCount = options.mDash.GetCount();
687 int dashMax = dashCount & 1 ? dashCount * 2 : dashCount;
688 float dashArray[16];
689
690 for (int i = 0; i < dashMax; i += 2)
691 {
692 dashArray[i + 0] = options.mDash.GetArray()[i % dashCount];
693 dashArray[i + 1] = options.mDash.GetArray()[(i + 1) % dashCount];
694 }
695
696 paint.setPathEffect(SkDashPathEffect::Make(dashArray, dashMax, options.mDash.GetOffset()));
697 }
698
699 paint.setStrokeWidth(thickness);
700 paint.setStrokeMiter(options.mMiterLimit);
701
702 RenderPath(paint);
703
704 if (!options.mPreserve)
705 mMainPath.reset();
706}
707
708void IGraphicsSkia::PathFill(const IPattern& pattern, const IFillOptions& options, const IBlend* pBlend)
709{
710 SkPaint paint = SkiaPaint(pattern, pBlend);
711 paint.setStyle(SkPaint::kFill_Style);
712
713 if (options.mFillRule == EFillRule::Winding)
714 mMainPath.setFillType(SkPathFillType::kWinding);
715 else
716 mMainPath.setFillType(SkPathFillType::kEvenOdd);
717
718 RenderPath(paint);
719
720 if (!options.mPreserve)
721 mMainPath.reset();
722}
723
724#ifdef IGRAPHICS_DRAWFILL_DIRECT
725void IGraphicsSkia::DrawRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness)
726{
727 auto paint = SkiaPaint(color, pBlend);
728 paint.setStyle(SkPaint::Style::kStroke_Style);
729 paint.setStrokeWidth(thickness);
730 mCanvas->drawRect(SkiaRect(bounds), paint);
731}
732
733void IGraphicsSkia::DrawRoundRect(const IColor& color, const IRECT& bounds, float cornerRadius, const IBlend* pBlend, float thickness)
734{
735 auto paint = SkiaPaint(color, pBlend);
736 paint.setStyle(SkPaint::Style::kStroke_Style);
737 paint.setStrokeWidth(thickness);
738 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);
739}
740
741void IGraphicsSkia::DrawArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend, float thickness)
742{
743 auto paint = SkiaPaint(color, pBlend);
744 paint.setStyle(SkPaint::Style::kStroke_Style);
745 paint.setStrokeWidth(thickness);
746 mCanvas->drawArc(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, (a2 - a1), false, paint);
747}
748
749void IGraphicsSkia::DrawCircle(const IColor& color, float cx, float cy, float r, const IBlend* pBlend, float thickness)
750{
751 auto paint = SkiaPaint(color, pBlend);
752 paint.setStyle(SkPaint::Style::kStroke_Style);
753 paint.setStrokeWidth(thickness);
754 mCanvas->drawCircle(cx, cy, r, paint);
755}
756
757void IGraphicsSkia::DrawEllipse(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness)
758{
759 auto paint = SkiaPaint(color, pBlend);
760 paint.setStyle(SkPaint::Style::kStroke_Style);
761 paint.setStrokeWidth(thickness);
762 mCanvas->drawOval(SkiaRect(bounds), paint);
763}
764
765void IGraphicsSkia::FillRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend)
766{
767 auto paint = SkiaPaint(color, pBlend);
768 paint.setStyle(SkPaint::Style::kFill_Style);
769 mCanvas->drawRect(SkiaRect(bounds), paint);
770}
771
772void IGraphicsSkia::FillRoundRect(const IColor& color, const IRECT& bounds, float cornerRadius, const IBlend* pBlend)
773{
774 auto paint = SkiaPaint(color, pBlend);
775 paint.setStyle(SkPaint::Style::kFill_Style);
776 mCanvas->drawRoundRect(SkiaRect(bounds), cornerRadius, cornerRadius, paint);
777}
778
779void IGraphicsSkia::FillArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend)
780{
781 auto paint = SkiaPaint(color, pBlend);
782 paint.setStyle(SkPaint::Style::kFill_Style);
783 mCanvas->drawArc(SkRect::MakeLTRB(cx - r, cy - r, cx + r, cy + r), a1 - 90.f, (a2 - a1), true, paint);
784}
785
786void IGraphicsSkia::FillCircle(const IColor& color, float cx, float cy, float r, const IBlend* pBlend)
787{
788 auto paint = SkiaPaint(color, pBlend);
789 paint.setStyle(SkPaint::Style::kFill_Style);
790 mCanvas->drawCircle(cx, cy, r, paint);
791}
792
793void IGraphicsSkia::FillEllipse(const IColor& color, const IRECT& bounds, const IBlend* pBlend)
794{
795 auto paint = SkiaPaint(color, pBlend);
796 paint.setStyle(SkPaint::Style::kFill_Style);
797 mCanvas->drawOval(SkiaRect(bounds), paint);
798}
799#endif
800
801void IGraphicsSkia::RenderPath(SkPaint& paint)
802{
803 SkMatrix invMatrix;
804
805 if (!mMatrix.isIdentity() && mMatrix.invert(&invMatrix))
806 {
807 SkPath path;
808 path.setIsVolatile(true);
809 mMainPath.transform(invMatrix, &path);
810 mCanvas->drawPath(path, paint);
811 }
812 else
813 {
814 mCanvas->drawPath(mMainPath, paint);
815 }
816}
817
818void IGraphicsSkia::PathTransformSetMatrix(const IMatrix& m)
819{
820 double xTranslate = 0.0;
821 double yTranslate = 0.0;
822
823 if (!mCanvas)
824 return;
825
826 if (!mLayers.empty())
827 {
828 IRECT bounds = mLayers.top()->Bounds();
829
830 xTranslate = -bounds.L;
831 yTranslate = -bounds.T;
832 }
833
834 mMatrix = SkMatrix::MakeAll(m.mXX, m.mXY, m.mTX, m.mYX, m.mYY, m.mTY, 0, 0, 1);
835 auto scale = GetTotalScale();
836 SkMatrix globalMatrix = SkMatrix::Scale(scale, scale);
837 mClipMatrix = SkMatrix();
838 mFinalMatrix = mMatrix;
839 globalMatrix.preTranslate(xTranslate, yTranslate);
840 mClipMatrix.postConcat(globalMatrix);
841 mFinalMatrix.postConcat(globalMatrix);
842 mCanvas->setMatrix(mFinalMatrix);
843}
844
845void IGraphicsSkia::SetClipRegion(const IRECT& r)
846{
847 mCanvas->restoreToCount(0);
848 mCanvas->save();
849 mCanvas->setMatrix(mClipMatrix);
850 mCanvas->clipRect(SkiaRect(r));
851 mCanvas->setMatrix(mFinalMatrix);
852}
853
854APIBitmap* IGraphicsSkia::CreateAPIBitmap(int width, int height, float scale, double drawScale, bool cacheable)
855{
856 sk_sp<SkSurface> surface;
857
858 #ifndef IGRAPHICS_CPU
859 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
860 if (cacheable)
861 {
862 surface = SkSurface::MakeRasterN32Premul(width, height);
863 }
864 else
865 {
866 surface = SkSurface::MakeRenderTarget(mGrContext.get(), SkBudgeted::kYes, info);
867 }
868 #else
869 surface = SkSurface::MakeRasterN32Premul(width, height);
870 #endif
871
872 surface->getCanvas()->save();
873
874 return new Bitmap(std::move(surface), width, height, scale, drawScale);
875}
876
878{
879 mCanvas = mLayers.empty() ? mSurface->getCanvas() : mLayers.top()->GetAPIBitmap()->GetBitmap()->mSurface->getCanvas();
880}
881
882static size_t CalcRowBytes(int width)
883{
884 width = ((width + 7) & (-8));
885 return width * sizeof(uint32_t);
886}
887
888void IGraphicsSkia::GetLayerBitmapData(const ILayerPtr& layer, RawBitmapData& data)
889{
890 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();
891 size_t rowBytes = CalcRowBytes(pDrawable->mSurface->width());
892 int size = pDrawable->mSurface->height() * static_cast<int>(rowBytes);
893
894 data.Resize(size);
895
896 if (data.GetSize() >= size)
897 {
898 SkImageInfo info = SkImageInfo::MakeN32Premul(pDrawable->mSurface->width(), pDrawable->mSurface->height());
899 pDrawable->mSurface->readPixels(info, data.Get(), rowBytes, 0, 0);
900 }
901}
902
903void IGraphicsSkia::ApplyShadowMask(ILayerPtr& layer, RawBitmapData& mask, const IShadow& shadow)
904{
905 SkiaDrawable* pDrawable = layer->GetAPIBitmap()->GetBitmap();
906 int width = pDrawable->mSurface->width();
907 int height = pDrawable->mSurface->height();
908 size_t rowBytes = CalcRowBytes(width);
909 double scale = layer->GetAPIBitmap()->GetDrawScale() * layer->GetAPIBitmap()->GetScale();
910
911 SkCanvas* pCanvas = pDrawable->mSurface->getCanvas();
912
913 SkMatrix m;
914 m.reset();
915
916 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
917 SkPixmap pixMap(info, mask.Get(), rowBytes);
918 sk_sp<SkImage> image = SkImage::MakeFromRaster(pixMap, nullptr, nullptr);
919 sk_sp<SkImage> foreground;
920
921 // Copy the foreground if needed
922
923 if (shadow.mDrawForeground)
924 foreground = pDrawable->mSurface->makeImageSnapshot();
925
926 pCanvas->clear(SK_ColorTRANSPARENT);
927
928 IBlend blend(EBlend::Default, shadow.mOpacity);
929 pCanvas->setMatrix(m);
930 pCanvas->drawImage(image.get(), shadow.mXOffset * scale, shadow.mYOffset * scale);
931 m = SkMatrix::Scale(scale, scale);
932 pCanvas->setMatrix(m);
933 pCanvas->translate(-layer->Bounds().L, -layer->Bounds().T);
934 SkPaint p = SkiaPaint(shadow.mPattern, &blend);
935 p.setBlendMode(SkBlendMode::kSrcIn);
936 pCanvas->drawPaint(p);
937
938 if (shadow.mDrawForeground)
939 {
940 m.reset();
941 pCanvas->setMatrix(m);
942 pCanvas->drawImage(foreground.get(), 0.0, 0.0);
943 }
944}
945
946void IGraphicsSkia::DrawFastDropShadow(const IRECT& innerBounds, const IRECT& outerBounds, float xyDrop, float roundness, float blur, IBlend* pBlend)
947{
948 SkRect r = SkiaRect(innerBounds.GetTranslated(xyDrop, xyDrop));
949
950 SkPaint paint = SkiaPaint(COLOR_BLACK_DROP_SHADOW, pBlend);
951 paint.setStyle(SkPaint::Style::kFill_Style);
952
953 paint.setMaskFilter(SkMaskFilter::MakeBlur(kSolid_SkBlurStyle, blur * 0.5)); // 0.5 seems to match nanovg
954 mCanvas->drawRoundRect(r, roundness, roundness, paint);
955}
956
958{
959#ifdef IGRAPHICS_CPU
960 return "SKIA | CPU";
961#elif defined IGRAPHICS_GL2
962 return "SKIA | GL2";
963#elif defined IGRAPHICS_GL3
964 return "SKIA | GL3";
965#elif defined IGRAPHICS_METAL
966 return "SKIA | Metal";
967#endif
968}
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 for a SOMETHING that uses IGraphics for it's 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:2497
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:2613
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:2548
float GetTotalScale() const
Gets the combined draw and screen/display scaling factor.
Definition: IGraphics.h:1113
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:2504
void DoMeasureTextRotation(const IText &text, const IRECT &bounds, IRECT &rect) const
Definition: IGraphics.cpp:2227
int WindowWidth() const
Gets the width of the graphics context including draw scaling.
Definition: IGraphics.h:1089
void PathTransformRestore()
Restore the affine transform of the current path, to the previously saved state.
Definition: IGraphics.cpp:2708
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:2569
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:2525
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:2532
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:2576
virtual void * GetWinModuleHandle()
Definition: IGraphics.h:926
float GetScreenScale() const
Gets the screen/display scaling factor, e.g.
Definition: IGraphics.h:1105
void PathTransformSave()
Save the current affine transform of the current path.
Definition: IGraphics.cpp:2703
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:2597
int WindowHeight() const
Gets the height of the graphics context including draw scaling.
Definition: IGraphics.h:1093
virtual void BeginFrame()
Called at the beginning of drawing.
Definition: IGraphics.cpp:867
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:2606
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 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 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 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,...