iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IGraphicsNanoVG.cpp
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#include <cmath>
12
13#include "IGraphicsNanoVG.h"
14#include "ITextEntryControl.h"
15
16#if defined IGRAPHICS_GL
17 #if defined OS_MAC
18 #if defined IGRAPHICS_GL2
19 #define NANOVG_GL2_IMPLEMENTATION
20 #elif defined IGRAPHICS_GL3
21 #include <OpenGL/gl3.h>
22 #define NANOVG_GL3_IMPLEMENTATION
23 #elif defined IGRAPHICS_GLES2
24 #include <libGLESv2/angle_gl.h>
25 #define NANOVG_GLES2_IMPLEMENTATION
26 #elif defined IGRAPHICS_GLES3
27 #include <libGLESv2/angle_gl.h>
28 #define NANOVG_GLES3_IMPLEMENTATION
29 #endif
30 #elif defined OS_IOS
31 #if defined IGRAPHICS_GLES2
32 #include <libGLESv2/angle_gl.h>
33 #define NANOVG_GLES2_IMPLEMENTATION
34 #elif defined IGRAPHICS_GLES3
35 #include <libGLESv2/angle_gl.h>
36 #define NANOVG_GLES3_IMPLEMENTATION
37 #else
38 #error Define either IGRAPHICS_GLES2 or IGRAPHICS_GLES3 for IGRAPHICS_NANOVG with OS_IOS
39 #endif
40 #elif defined OS_WIN
41 #pragma comment(lib, "opengl32.lib")
42 #if defined IGRAPHICS_GL2
43 #define NANOVG_GL2_IMPLEMENTATION
44 #elif defined IGRAPHICS_GL3
45 #define NANOVG_GL3_IMPLEMENTATION
46 #else
47 #error Define either IGRAPHICS_GL2 or IGRAPHICS_GL3 when using IGRAPHICS_GL and IGRAPHICS_NANOVG with OS_WIN
48 #endif
49 #elif defined OS_LINUX
50 #error NOT IMPLEMENTED
51 #elif defined OS_WEB
52 #if defined IGRAPHICS_GLES2
53 #define NANOVG_GLES2_IMPLEMENTATION
54 #elif defined IGRAPHICS_GLES3
55 #define NANOVG_GLES3_IMPLEMENTATION
56 #else
57 #error Define either IGRAPHICS_GLES2 or IGRAPHICS_GLES3 when using IGRAPHICS_GL and IGRAPHICS_NANOVG with OS_WEB
58 #endif
59 #endif
60 #include "nanovg_gl.h"
61 #include "nanovg_gl_utils.h"
62#elif defined IGRAPHICS_METAL
63 #include "nanovg_mtl.h"
64 #if defined OS_MAC
65 //even though this is a .cpp we are in an objc(pp) compilation unit
66 #import <Metal/Metal.h>
67 #endif
68#else
69 #error you must define either IGRAPHICS_GL2, IGRAPHICS_GLES2 etc or IGRAPHICS_METAL when using IGRAPHICS_NANOVG
70#endif
71
72#include <string>
73#include <map>
74
75using namespace iplug;
76using namespace igraphics;
77
78#pragma mark - Private Classes and Structs
79
81{
82public:
83 Bitmap(NVGcontext* pContext, const char* path, double sourceScale, int nvgImageID, bool shared = false);
84 Bitmap(IGraphicsNanoVG* pGraphics, NVGcontext* pContext, int width, int height, float scale, float drawScale);
85 Bitmap(NVGcontext* pContext, int width, int height, const uint8_t* pData, float scale, float drawScale);
86 virtual ~Bitmap();
87 NVGframebuffer* GetFBO() const { return mFBO; }
88private:
89 IGraphicsNanoVG *mGraphics = nullptr;
90 NVGcontext* mVG;
91 NVGframebuffer* mFBO = nullptr;
92 bool mSharedTexture = false;
93};
94
95IGraphicsNanoVG::Bitmap::Bitmap(NVGcontext* pContext, const char* path, double sourceScale, int nvgImageID, bool shared)
96{
97 assert(nvgImageID > 0);
98
99 mVG = pContext;
100 mSharedTexture = shared;
101 int w = 0, h = 0;
102 nvgImageSize(mVG, nvgImageID, &w, &h);
103
104 SetBitmap(nvgImageID, w, h, sourceScale, 1.f);
105}
106
107IGraphicsNanoVG::Bitmap::Bitmap(IGraphicsNanoVG* pGraphics, NVGcontext* pContext, int width, int height, float scale, float drawScale)
108{
109 assert(width > 0 && height > 0);
110 mGraphics = pGraphics;
111 mVG = pContext;
112 mFBO = nvgCreateFramebuffer(pContext, width, height, 0);
113
114 nvgBindFramebuffer(mFBO);
115
116#ifdef IGRAPHICS_METAL
117 mnvgClearWithColor(mVG, nvgRGBAf(0, 0, 0, 0));
118#else
119 glViewport(0, 0, width, height);
120 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
121 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
122#endif
123 nvgBeginFrame(mVG, width, height, 1.f);
124 nvgEndFrame(mVG);
125
126 SetBitmap(mFBO->image, width, height, scale, drawScale);
127}
128
129IGraphicsNanoVG::Bitmap::Bitmap(NVGcontext* pContext, int width, int height, const uint8_t* pData, float scale, float drawScale)
130{
131 int idx = nvgCreateImageRGBA(pContext, width, height, 0, pData);
132 mVG = pContext;
133 SetBitmap(idx, width, height, scale, drawScale);
134}
135
136IGraphicsNanoVG::Bitmap::~Bitmap()
137{
138 if(!mSharedTexture)
139 {
140 if(mFBO)
141 mGraphics->DeleteFBO(mFBO);
142 else
143 nvgDeleteImage(mVG, GetBitmap());
144 }
145}
146
147// Fonts
148static StaticStorage<IFontData> sFontCache;
149
150extern std::map<std::string, MTLTexturePtr> gTextureMap;
151
152// Retrieving pixels
153static void nvgReadPixels(NVGcontext* pContext, int image, int x, int y, int width, int height, void* pData)
154{
155#if defined(IGRAPHICS_GL)
156 glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pData);
157#elif defined(IGRAPHICS_METAL)
158 mnvgReadPixels(pContext, image, x, y, width, height, pData);
159#endif
160}
161
162#pragma mark - Utilities
163
164BEGIN_IPLUG_NAMESPACE
165BEGIN_IGRAPHICS_NAMESPACE
166
167NVGcolor NanoVGColor(const IColor& color, const IBlend* pBlend)
168{
169 NVGcolor c;
170 c.r = (float) color.R / 255.0f;
171 c.g = (float) color.G / 255.0f;
172 c.b = (float) color.B / 255.0f;
173 c.a = (BlendWeight(pBlend) * color.A) / 255.0f;
174 return c;
175}
176
177void NanoVGRect(NVGcontext* pContext, const IRECT& r)
178{
179 nvgRect(pContext, r.L, r.T, r.W(), r.H());
180}
181
182void NanoVGSetBlendMode(NVGcontext* pContext, const IBlend* pBlend)
183{
184 if (!pBlend)
185 {
186 nvgGlobalCompositeOperation(pContext, NVG_SOURCE_OVER);
187 return;
188 }
189
190 switch (pBlend->mMethod)
191 {
192 case EBlend::SrcOver: nvgGlobalCompositeOperation(pContext, NVG_SOURCE_OVER); break;
193 case EBlend::SrcIn: nvgGlobalCompositeOperation(pContext, NVG_SOURCE_IN); break;
194 case EBlend::SrcOut: nvgGlobalCompositeOperation(pContext, NVG_SOURCE_OUT); break;
195 case EBlend::SrcAtop: nvgGlobalCompositeOperation(pContext, NVG_ATOP); break;
196 case EBlend::DstOver: nvgGlobalCompositeOperation(pContext, NVG_DESTINATION_OVER); break;
197 case EBlend::DstIn: nvgGlobalCompositeOperation(pContext, NVG_DESTINATION_IN); break;
198 case EBlend::DstOut: nvgGlobalCompositeOperation(pContext, NVG_DESTINATION_OUT); break;
199 case EBlend::DstAtop: nvgGlobalCompositeOperation(pContext, NVG_DESTINATION_ATOP); break;
200 case EBlend::Add: nvgGlobalCompositeBlendFunc(pContext, NVG_SRC_ALPHA, NVG_DST_ALPHA); break;
201 case EBlend::XOR: nvgGlobalCompositeOperation(pContext, NVG_XOR); break;
202 }
203}
204
205NVGpaint NanoVGPaint(NVGcontext* pContext, const IPattern& pattern, const IBlend* pBlend)
206{
207 assert(pattern.NStops() > 0);
208
209 double s[2], e[2];
210
211 NVGcolor icol = NanoVGColor(pattern.GetStop(0).mColor, pBlend);
212 NVGcolor ocol = NanoVGColor(pattern.GetStop(pattern.NStops() - 1).mColor, pBlend);
213
214 // Invert transform
215 IMatrix inverse = IMatrix(pattern.mTransform).Invert();
216 inverse.TransformPoint(s[0], s[1], 0.0, 0.0);
217
218 if (pattern.mType == EPatternType::Radial)
219 {
220 return nvgRadialGradient(pContext, s[0], s[1], inverse.mXX * pattern.GetStop(0).mOffset, inverse.mXX, icol, ocol);
221 }
222 else
223 {
224 inverse.TransformPoint(e[0], e[1], 0.0, 1.0);
225
226 return nvgLinearGradient(pContext, s[0], s[1], e[0], e[1], icol, ocol);
227 }
228}
229
230END_IGRAPHICS_NAMESPACE
231END_IPLUG_NAMESPACE
232
233#pragma mark -
234
235IGraphicsNanoVG::IGraphicsNanoVG(IGEditorDelegate& dlg, int w, int h, int fps, float scale)
236: IGraphics(dlg, w, h, fps, scale)
237{
238 DBGMSG("IGraphics NanoVG @ %i FPS\n", fps);
239 StaticStorage<IFontData>::Accessor storage(sFontCache);
240 storage.Retain();
241}
242
243IGraphicsNanoVG::~IGraphicsNanoVG()
244{
245 StaticStorage<IFontData>::Accessor storage(sFontCache);
246 storage.Release();
247 ClearFBOStack();
248}
249
251{
252#if defined IGRAPHICS_METAL
253 return "NanoVG | Metal";
254#else
255 #if defined OS_WEB
256 return "NanoVG | WebGL";
257 #else
258 #if defined IGRAPHICS_GL2
259 return "NanoVG | GL2";
260 #elif defined IGRAPHICS_GL3
261 return "NanoVG | GL3";
262 #elif defined IGRAPHICS_GLES2
263 return "NanoVG | GLES2";
264 #elif defined IGRAPHICS_GLES3
265 return "NanoVG | GLES3";
266 #endif
267 #endif
268#endif
269}
270
272{
273 char extLower[32];
274 ToLower(extLower, ext);
275 return (strstr(extLower, "png") != nullptr) || (strstr(extLower, "jpg") != nullptr) || (strstr(extLower, "jpeg") != nullptr);
276}
277
278IBitmap IGraphicsNanoVG::LoadBitmap(const char* name, int nStates, bool framesAreHorizontal, int targetScale)
279{
280 if (targetScale == 0)
281 targetScale = GetRoundedScreenScale();
282
283 // NanoVG does not use the global static cache, since bitmaps are textures linked to a context
284 StaticStorage<APIBitmap>::Accessor storage(mBitmapCache);
285 APIBitmap* pAPIBitmap = storage.Find(name, targetScale);
286
287 // If the bitmap is not already cached at the targetScale
288 if (!pAPIBitmap)
289 {
290 const char* ext = name + strlen(name) - 1;
291 while (ext >= name && *ext != '.') --ext;
292 ++ext;
293
294 WDL_String fullPathOrResourceID;
295 int sourceScale = 0;
296 EResourceLocation resourceFound = SearchImageResource(name, ext, fullPathOrResourceID, targetScale, sourceScale);
297
298 bool bitmapTypeSupported = BitmapExtSupported(ext); // KTX textures pass this test (if ext is .ktx.png)
299
300 if(resourceFound == EResourceLocation::kNotFound || !bitmapTypeSupported)
301 {
302 assert(0 && "Bitmap not found");
303 return IBitmap(); // return invalid IBitmap
304 }
305
306 pAPIBitmap = LoadAPIBitmap(fullPathOrResourceID.Get(), sourceScale, resourceFound, ext);
307
308 storage.Add(pAPIBitmap, name, sourceScale);
309
310 assert(pAPIBitmap && "Bitmap not loaded");
311 }
312
313 return IBitmap(pAPIBitmap, nStates, framesAreHorizontal, name);
314}
315
316APIBitmap* IGraphicsNanoVG::LoadAPIBitmap(const char* fileNameOrResID, int scale, EResourceLocation location, const char* ext)
317{
318 int idx = 0;
319 int nvgImageFlags = 0;
320
321 // Preloaded textures are only supported with Metal, not ANGLE/GLES
322#if defined OS_IOS && defined IGRAPHICS_METAL
323 if (location == EResourceLocation::kPreloadedTexture)
324 {
325 idx = mnvgCreateImageFromHandle(mVG, gTextureMap[fileNameOrResID], nvgImageFlags);
326 }
327 else
328#endif
329
330#ifdef OS_WIN
331 if (location == EResourceLocation::kWinBinary)
332 {
333 const void* pResData = nullptr;
334
335 int size = 0;
336 pResData = LoadWinResource(fileNameOrResID, ext, size, GetWinModuleHandle());
337
338 if (pResData)
339 {
340 ScopedGLContext scopedGLCtx {this};
341 idx = nvgCreateImageMem(mVG, nvgImageFlags, (unsigned char*) pResData, size);
342 }
343 }
344 else
345#endif
346 if (location == EResourceLocation::kAbsolutePath)
347 {
348 ScopedGLContext scopedGLCtx {this};
349 idx = nvgCreateImage(mVG, fileNameOrResID, nvgImageFlags);
350 }
351
352 return new Bitmap(mVG, fileNameOrResID, scale, idx, location == EResourceLocation::kPreloadedTexture);
353}
354
355APIBitmap* IGraphicsNanoVG::LoadAPIBitmap(const char* name, const void* pData, int dataSize, int scale)
356{
357 StaticStorage<APIBitmap>::Accessor storage(mBitmapCache);
358 APIBitmap* pBitmap = storage.Find(name, scale);
359
360 if (!pBitmap)
361 {
362 int idx = 0;
363 int nvgImageFlags = 0;
364
365 {
366 ScopedGLContext scopedGLCtx {this};
367 idx = nvgCreateImageMem(mVG, nvgImageFlags, (unsigned char*)pData, dataSize);
368 }
369
370 pBitmap = new Bitmap(mVG, name, scale, idx, false);
371
372 storage.Add(pBitmap, name, scale);
373 }
374
375 return pBitmap;
376}
377
378APIBitmap* IGraphicsNanoVG::CreateAPIBitmap(int width, int height, float scale, double drawScale, bool cacheable)
379{
380 if (mInDraw)
381 {
382 nvgEndFrame(mVG);
383 }
384
385 APIBitmap* pAPIBitmap = new Bitmap(this, mVG, width, height, scale, drawScale);
386
387 if (mInDraw)
388 {
389 nvgBindFramebuffer(mMainFrameBuffer); // begin main frame buffer update
390 nvgBeginFrame(mVG, WindowWidth(), WindowHeight(), GetScreenScale());
391 }
392
393 return pAPIBitmap;
394}
395
396void IGraphicsNanoVG::GetLayerBitmapData(const ILayerPtr& layer, RawBitmapData& data)
397{
398 const APIBitmap* pBitmap = layer->GetAPIBitmap();
399 int size = pBitmap->GetWidth() * pBitmap->GetHeight() * 4;
400
401 data.Resize(size);
402
403 if (data.GetSize() >= size)
404 {
405 PushLayer(layer.get());
406 nvgReadPixels(mVG, pBitmap->GetBitmap(), 0, 0, pBitmap->GetWidth(), pBitmap->GetHeight(), data.Get());
407 PopLayer();
408 }
409}
410
411void IGraphicsNanoVG::ApplyShadowMask(ILayerPtr& layer, RawBitmapData& mask, const IShadow& shadow)
412{
413 const APIBitmap* pBitmap = layer->GetAPIBitmap();
414 int width = pBitmap->GetWidth();
415 int height = pBitmap->GetHeight();
416 int size = width * height * 4;
417
418 if (mask.GetSize() >= size)
419 {
420 if (!shadow.mDrawForeground)
421 {
422 PushLayer(layer.get());
423 nvgGlobalCompositeBlendFunc(mVG, NVG_ZERO, NVG_ZERO);
424 PathRect(layer->Bounds());
425 nvgFillColor(mVG, NanoVGColor(COLOR_TRANSPARENT));
426 nvgFill(mVG);
427 PopLayer();
428 }
429
430 IRECT bounds(layer->Bounds());
431
432 Bitmap maskRawBitmap(mVG, width, height, mask.Get(), pBitmap->GetScale(), pBitmap->GetDrawScale());
433 APIBitmap* shadowBitmap = CreateAPIBitmap(width, height, pBitmap->GetScale(), pBitmap->GetDrawScale());
434 IBitmap tempLayerBitmap(shadowBitmap, 1, false);
435 IBitmap maskBitmap(&maskRawBitmap, 1, false);
436 ILayer shadowLayer(shadowBitmap, layer->Bounds(), nullptr, IRECT());
437
439 PushLayer(layer.get());
440 PushLayer(&shadowLayer);
441 DrawBitmap(maskBitmap, bounds, 0, 0, nullptr);
442 IBlend blend1(EBlend::SrcIn, 1.0);
443 PathRect(layer->Bounds());
444 PathTransformTranslate(-shadow.mXOffset, -shadow.mYOffset);
445 PathFill(shadow.mPattern, IFillOptions(), &blend1);
446 PopLayer();
447 IBlend blend2(EBlend::DstOver, shadow.mOpacity);
448 bounds.Translate(shadow.mXOffset, shadow.mYOffset);
449 DrawBitmap(tempLayerBitmap, bounds, 0, 0, &blend2);
450 PopLayer();
452 }
453}
454
456{
457#if defined IGRAPHICS_METAL
458 mVG = nvgCreateContext(pContext, NVG_ANTIALIAS | NVG_TRIPLE_BUFFER); //TODO: NVG_STENCIL_STROKES currently has issues
459#else
460 mVG = nvgCreateContext(NVG_ANTIALIAS /*| NVG_STENCIL_STROKES*/);
461#endif
462
463 if (mVG == nullptr)
464 DBGMSG("Could not init nanovg.\n");
465}
466
468{
469 // need to remove all the controls to free framebuffers, before deleting context
471
472 StaticStorage<APIBitmap>::Accessor storage(mBitmapCache);
473 storage.Clear();
474
475 if(mMainFrameBuffer != nullptr)
476 nvgDeleteFramebuffer(mMainFrameBuffer);
477
478 mMainFrameBuffer = nullptr;
479
480 if(mVG)
481 nvgDeleteContext(mVG);
482
483 mVG = nullptr;
484}
485
487{
488 ScopedGLContext scopedGLCtx {this};
489
490 if (mMainFrameBuffer != nullptr)
491 nvgDeleteFramebuffer(mMainFrameBuffer);
492
493 if (mVG)
494 {
495 mMainFrameBuffer = nvgCreateFramebuffer(mVG, WindowWidth() * GetScreenScale(), WindowHeight() * GetScreenScale(), 0);
496
497 if (mMainFrameBuffer == nullptr)
498 DBGMSG("Could not init FBO.\n");
499 }
500}
501
503{
504 mInDraw = true;
505 IGraphics::BeginFrame(); // start perf graph timing
506
507#ifdef IGRAPHICS_GL
508 glViewport(0, 0, WindowWidth() * GetScreenScale(), WindowHeight() * GetScreenScale());
509 glClearColor(0.f, 0.f, 0.f, 0.f);
510 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
511 #if defined OS_MAC || defined OS_IOS
512 glGetIntegerv(GL_FRAMEBUFFER_BINDING, &mInitialFBO); // stash apple fbo
513 #endif
514#endif
515
516 nvgBindFramebuffer(mMainFrameBuffer); // begin main frame buffer update
517 nvgBeginFrame(mVG, WindowWidth(), WindowHeight(), GetScreenScale());
518}
519
521{
522 nvgEndFrame(mVG); // end main frame buffer update
523 nvgBindFramebuffer(nullptr);
524 nvgBeginFrame(mVG, WindowWidth(), WindowHeight(), GetScreenScale());
525
526 NVGpaint img = nvgImagePattern(mVG, 0, 0, WindowWidth(), WindowHeight(), 0, mMainFrameBuffer->image, 1.0f);
527
528 nvgSave(mVG);
529 nvgResetTransform(mVG);
530 nvgTranslate(mVG, mXTranslation, mYTranslation);
531 nvgBeginPath(mVG);
532 nvgRect(mVG, 0, 0, WindowWidth(), WindowHeight());
533 nvgFillPaint(mVG, img);
534 nvgFill(mVG);
535 nvgRestore(mVG);
536
537#if (defined OS_MAC || defined OS_IOS) && defined IGRAPHICS_GL
538 glBindFramebuffer(GL_FRAMEBUFFER, mInitialFBO); // restore apple fbo
539#endif
540
541 nvgEndFrame(mVG);
542
543 mInDraw = false;
544 ClearFBOStack();
545}
546
547void IGraphicsNanoVG::DrawBitmap(const IBitmap& bitmap, const IRECT& dest, int srcX, int srcY, const IBlend* pBlend)
548{
549 APIBitmap* pAPIBitmap = bitmap.GetAPIBitmap();
550
551 assert(pAPIBitmap);
552
553 // First generate a scaled image paint
554 NVGpaint imgPaint;
555 double scale = 1.0 / (pAPIBitmap->GetScale() * pAPIBitmap->GetDrawScale());
556
557 nvgTransformScale(imgPaint.xform, scale, scale);
558
559 imgPaint.xform[4] = dest.L - srcX;
560 imgPaint.xform[5] = dest.T - srcY;
561 imgPaint.extent[0] = bitmap.W() * bitmap.GetScale();
562 imgPaint.extent[1] = bitmap.H() * bitmap.GetScale();
563 imgPaint.image = pAPIBitmap->GetBitmap();
564 imgPaint.radius = imgPaint.feather = 0.f;
565 imgPaint.innerColor = imgPaint.outerColor = nvgRGBAf(1, 1, 1, BlendWeight(pBlend));
566
567 // Now draw
568
569 nvgBeginPath(mVG); // Clears any existing path
570 nvgRect(mVG, dest.L, dest.T, dest.W(), dest.H());
571 nvgFillPaint(mVG, imgPaint);
572 NanoVGSetBlendMode(mVG, pBlend);
573 nvgFill(mVG);
574 nvgGlobalCompositeOperation(mVG, NVG_SOURCE_OVER);
575 nvgBeginPath(mVG); // Clears the bitmap rect from the path state
576}
577
579{
580 nvgBeginPath(mVG);
581}
582
584{
585 nvgClosePath(mVG);
586}
587
588void IGraphicsNanoVG::PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding)
589{
590 nvgArc(mVG, cx, cy, r, DegToRad(a1 - 90.f), DegToRad(a2 - 90.f), winding == EWinding::CW ? NVG_CW : NVG_CCW);
591}
592
593void IGraphicsNanoVG::PathMoveTo(float x, float y)
594{
595 nvgMoveTo(mVG, x, y);
596}
597
598void IGraphicsNanoVG::PathLineTo(float x, float y)
599{
600 nvgLineTo(mVG, x, y);
601}
602
603void IGraphicsNanoVG::PathCubicBezierTo(float c1x, float c1y, float c2x, float c2y, float x2, float y2)
604{
605 nvgBezierTo(mVG, c1x, c1y, c2x, c2y, x2, y2);
606}
607
608void IGraphicsNanoVG::PathQuadraticBezierTo(float cx, float cy, float x2, float y2)
609{
610 nvgQuadTo(mVG, cx, cy, x2, y2);
611}
612
614{
615 nvgPathWinding(mVG, clockwise ? NVG_CW : NVG_CCW);
616}
617
619{
620 return COLOR_BLACK; //TODO:
621}
622
623void IGraphicsNanoVG::PrepareAndMeasureText(const IText& text, const char* str, IRECT& r, double& x, double & y) const
624{
625 float fbounds[4];
626
627 assert(nvgFindFont(mVG, text.mFont) != -1 && "No font found - did you forget to load it?");
628
629 nvgFontBlur(mVG, 0);
630 nvgFontSize(mVG, text.mSize);
631 nvgFontFace(mVG, text.mFont);
632
633 int align = 0;
634
635 switch (text.mAlign)
636 {
637 case EAlign::Near: align = NVG_ALIGN_LEFT; x = r.L; break;
638 case EAlign::Center: align = NVG_ALIGN_CENTER; x = r.MW(); break;
639 case EAlign::Far: align = NVG_ALIGN_RIGHT; x = r.R; break;
640 }
641
642 switch (text.mVAlign)
643 {
644 case EVAlign::Top: align |= NVG_ALIGN_TOP; y = r.T; break;
645 case EVAlign::Middle: align |= NVG_ALIGN_MIDDLE; y = r.MH(); break;
646 case EVAlign::Bottom: align |= NVG_ALIGN_BOTTOM; y = r.B; break;
647 }
648
649 nvgTextAlign(mVG, align);
650 nvgTextBounds(mVG, x, y, str, NULL, fbounds);
651
652 r = IRECT(fbounds[0], fbounds[1], fbounds[2], fbounds[3]);
653}
654
655float IGraphicsNanoVG::DoMeasureText(const IText& text, const char* str, IRECT& bounds) const
656{
657 IRECT r = bounds;
658 double x, y;
659 PrepareAndMeasureText(text, str, bounds, x, y);
660 DoMeasureTextRotation(text, r, bounds);
661
662 return bounds.W();
663}
664
665void IGraphicsNanoVG::DoDrawText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
666{
667 IRECT measured = bounds;
668 double x, y;
669
670 PrepareAndMeasureText(text, str, measured, x, y);
672 DoTextRotation(text, bounds, measured);
673 nvgFillColor(mVG, NanoVGColor(text.mFGColor, pBlend));
674 NanoVGSetBlendMode(mVG, pBlend);
675 nvgText(mVG, x, y, str, NULL);
676 nvgGlobalCompositeOperation(mVG, NVG_SOURCE_OVER);
678}
679
680void IGraphicsNanoVG::PathStroke(const IPattern& pattern, float thickness, const IStrokeOptions& options, const IBlend* pBlend)
681{
682 // First set options
683 switch (options.mCapOption)
684 {
685 case ELineCap::Butt: nvgLineCap(mVG, NVG_BUTT); break;
686 case ELineCap::Round: nvgLineCap(mVG, NVG_ROUND); break;
687 case ELineCap::Square: nvgLineCap(mVG, NVG_SQUARE); break;
688 }
689
690 switch (options.mJoinOption)
691 {
692 case ELineJoin::Miter: nvgLineJoin(mVG, NVG_MITER); break;
693 case ELineJoin::Round: nvgLineJoin(mVG, NVG_ROUND); break;
694 case ELineJoin::Bevel: nvgLineJoin(mVG, NVG_BEVEL); break;
695 }
696
697 nvgMiterLimit(mVG, options.mMiterLimit);
698 nvgStrokeWidth(mVG, thickness);
699
700 // NanoVG does not support dashed paths
701 if (pattern.mType == EPatternType::Solid)
702 nvgStrokeColor(mVG, NanoVGColor(pattern.GetStop(0).mColor, pBlend));
703 else
704 nvgStrokePaint(mVG, NanoVGPaint(mVG, pattern, pBlend));
705
706 nvgPathWinding(mVG, NVG_CCW);
707 NanoVGSetBlendMode(mVG, pBlend);
708 nvgStroke(mVG);
709 nvgGlobalCompositeOperation(mVG, NVG_SOURCE_OVER);
710
711 if (!options.mPreserve)
712 nvgBeginPath(mVG); // Clears the path state
713}
714
715void IGraphicsNanoVG::PathFill(const IPattern& pattern, const IFillOptions& options, const IBlend* pBlend)
716{
717 switch(options.mFillRule)
718 {
719 // This concept of fill vs. even/odd winding does not really translate to nanovg.
720 // Instead the caller is responsible for settting winding correctly for each subpath
721 // based on whether it's a solid (NVG_CCW) or hole (NVG_CW).
722 case EFillRule::Winding:
723 nvgPathWinding(mVG, NVG_CCW);
724 break;
725 case EFillRule::EvenOdd:
726 nvgPathWinding(mVG, NVG_CW);
727 break;
728 case EFillRule::Preserve:
729 // don't set a winding rule for the path, to preserve individual windings on subpaths
730 default:
731 break;
732 }
733
734 if (pattern.mType == EPatternType::Solid)
735 nvgFillColor(mVG, NanoVGColor(pattern.GetStop(0).mColor, pBlend));
736 else
737 nvgFillPaint(mVG, NanoVGPaint(mVG, pattern, pBlend));
738
739 NanoVGSetBlendMode(mVG, pBlend);
740 nvgFill(mVG);
741 nvgGlobalCompositeOperation(mVG, NVG_SOURCE_OVER);
742
743 if (!options.mPreserve)
744 nvgBeginPath(mVG); // Clears the path state
745}
746
747bool IGraphicsNanoVG::LoadAPIFont(const char* fontID, const PlatformFontPtr& font)
748{
749 StaticStorage<IFontData>::Accessor storage(sFontCache);
750 IFontData* cached = storage.Find(fontID);
751
752 if (cached)
753 {
754 nvgCreateFontFaceMem(mVG, fontID, cached->Get(), cached->GetSize(), cached->GetFaceIdx(), 0);
755 return true;
756 }
757
758 IFontDataPtr data = font->GetFontData();
759
760 if (data->IsValid() && nvgCreateFontFaceMem(mVG, fontID, data->Get(), data->GetSize(), data->GetFaceIdx(), 0) != -1)
761 {
762 storage.Add(data.release(), fontID);
763 return true;
764 }
765
766 return false;
767}
768
769void IGraphicsNanoVG::UpdateLayer()
770{
771 if (mLayers.empty())
772 {
773 nvgEndFrame(mVG);
774#ifdef IGRAPHICS_GL
775 glViewport(0, 0, WindowWidth() * GetScreenScale(), WindowHeight() * GetScreenScale());
776#endif
777 nvgBindFramebuffer(mMainFrameBuffer);
778 nvgBeginFrame(mVG, WindowWidth(), WindowHeight(), GetScreenScale());
779 }
780 else
781 {
782 nvgEndFrame(mVG);
783#ifdef IGRAPHICS_GL
784 const double scale = GetBackingPixelScale();
785 glViewport(0, 0, mLayers.top()->Bounds().W() * scale, mLayers.top()->Bounds().H() * scale);
786#endif
787 nvgBindFramebuffer(dynamic_cast<const Bitmap*>(mLayers.top()->GetAPIBitmap())->GetFBO());
788 nvgBeginFrame(mVG, mLayers.top()->Bounds().W() * GetDrawScale(), mLayers.top()->Bounds().H() * GetDrawScale(), GetScreenScale());
789 }
790}
791
792void IGraphicsNanoVG::PathTransformSetMatrix(const IMatrix& m)
793{
794 double xTranslate = 0.0;
795 double yTranslate = 0.0;
796
797 if (!mLayers.empty())
798 {
799 IRECT bounds = mLayers.top()->Bounds();
800
801 xTranslate = -bounds.L;
802 yTranslate = -bounds.T;
803 }
804
805 nvgResetTransform(mVG);
806 nvgScale(mVG, GetDrawScale(), GetDrawScale());
807 nvgTranslate(mVG, xTranslate, yTranslate);
808 nvgTransform(mVG, m.mXX, m.mYX, m.mXY, m.mYY, m.mTX, m.mTY);
809}
810
811void IGraphicsNanoVG::SetClipRegion(const IRECT& r)
812{
813 nvgScissor(mVG, r.L, r.T, r.W(), r.H());
814}
815
816void IGraphicsNanoVG::DrawDottedLine(const IColor& color, float x1, float y1, float x2, float y2, const IBlend* pBlend, float thickness, float dashLen)
817{
818 const float xd = x1 - x2;
819 const float yd = y1 - y2;
820 const float len = std::sqrt(xd * xd + yd * yd);
821
822 const float segs = std::round(len / dashLen);
823 const float incr = 1.f / segs;
824
825 float xs = x1;
826 float ys = y1;
827
828 PathMoveTo(xs, ys);
829
830 for (int i = 1; i < static_cast<int>(segs); i+=2)
831 {
832 float progress = incr * static_cast<float>(i);
833
834 float xe = x1 + progress * (x2 - x1);
835 float ye = y1 + progress * (y2 - y1);
836
837 PathLineTo(xe, ye);
838
839 progress += incr;
840
841 xs = x1 + progress * (x2 - x1);
842 ys = y1 + progress * (y2 - y1);
843
844 PathMoveTo(xs, ys);
845 }
846
847 PathStroke(color, thickness, IStrokeOptions(), pBlend);
848}
849
850void IGraphicsNanoVG::DrawDottedRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness, float dashLen)
851{
852 const int xsegs = static_cast<int>(std::ceil(bounds.W() / (dashLen * 2.f)));
853 const int ysegs = static_cast<int>(std::ceil(bounds.H() / (dashLen * 2.f)));
854
855 float x1 = bounds.L;
856 float y1 = bounds.T;
857
858 float x2 = x1;
859 float y2 = y1;
860
861 PathMoveTo(x1, y1);
862
863 for(int j = 0; j < 2; j++)
864 {
865 for (int i = 0; i < xsegs; i++)
866 {
867 x2 = Clip(x1 + dashLen, bounds.L, bounds.R);
868 PathLineTo(x2, y2);
869 x1 = Clip(x2 + dashLen, bounds.L, bounds.R);
870 PathMoveTo(x1, y1);
871 }
872
873 x2 = x1;
874
875 for (int i = 0; i < ysegs; i++)
876 {
877 y2 = Clip(y1 + dashLen, bounds.T, bounds.B);
878 PathLineTo(x2, y2);
879 y1 = Clip(y2 + dashLen, bounds.T, bounds.B);
880 PathMoveTo(x1, y1);
881 }
882
883 y2 = y1;
884
885 dashLen = -dashLen;
886 }
887
888 PathStroke(color, thickness, IStrokeOptions(), pBlend);
889}
890
891void IGraphicsNanoVG::DeleteFBO(NVGframebuffer* pBuffer)
892{
893 if (!mInDraw)
894 nvgDeleteFramebuffer(pBuffer);
895 else
896 {
897 WDL_MutexLock lock(&mFBOMutex);
898 mFBOStack.push(pBuffer);
899 }
900}
901
902void IGraphicsNanoVG::ClearFBOStack()
903{
904 WDL_MutexLock lock(&mFBOMutex);
905 while (!mFBOStack.empty())
906 {
907 nvgDeleteFramebuffer(mFBOStack.top());
908 mFBOStack.pop();
909 }
910}
911
912void IGraphicsNanoVG::DrawFastDropShadow(const IRECT& innerBounds, const IRECT& outerBounds, float xyDrop, float roundness, float blur, IBlend* pBlend)
913{
914 NVGpaint shadowPaint = nvgBoxGradient(mVG, innerBounds.L + xyDrop, innerBounds.T + xyDrop, innerBounds.W(), innerBounds.H(), roundness, blur, NanoVGColor(COLOR_BLACK_DROP_SHADOW, pBlend), NanoVGColor(COLOR_TRANSPARENT, nullptr));
915 nvgBeginPath(mVG);
916 nvgRect(mVG, outerBounds.L, outerBounds.T, outerBounds.W(), outerBounds.H());
917 nvgFillPaint(mVG, shadowPaint);
918 nvgGlobalCompositeOperation(mVG, NVG_SOURCE_OVER);
919 nvgFill(mVG);
920 nvgBeginPath(mVG);
921}
922
923void IGraphicsNanoVG::DrawMultiLineText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
924{
925 nvgSave(mVG);
926 nvgFontSize(mVG, text.mSize);
927 nvgFontFace(mVG, text.mFont);
928
929 float x = 0.0, y = 0.0;
930 const float width = bounds.W();
931 int align = 0;
932 float yOffsetScale = 0.0;
933
934 switch (text.mAlign)
935 {
936 case EAlign::Near: align = NVG_ALIGN_LEFT; x = bounds.L; break;
937 case EAlign::Center: align = NVG_ALIGN_CENTER; x = bounds.MW(); break;
938 case EAlign::Far: align = NVG_ALIGN_RIGHT; x = bounds.R; break;
939 }
940
941 switch (text.mVAlign)
942 {
943 case EVAlign::Top:
944 {
945 align |= NVG_ALIGN_TOP;
946 y = bounds.T;
947 yOffsetScale = 0.0;
948 break;
949 }
950 case EVAlign::Middle:
951 {
952 align |= NVG_ALIGN_MIDDLE;
953 y = bounds.MH();
954 yOffsetScale = 0.5;
955 break;
956 }
957 case EVAlign::Bottom:
958 {
959 align |= NVG_ALIGN_BOTTOM;
960 y = bounds.B;
961 yOffsetScale = 1.0;
962 break;
963 }
964 }
965
966 nvgTextAlign(mVG, align);
967
968 NVGtextRow rows[3];
969 const char* start;
970 const char* end;
971 int nRows = 0;
972 int lines = 0;
973 float lineHeight;
974 nvgTextMetrics(mVG, NULL, NULL, &lineHeight);
975 nvgFillColor(mVG, NanoVGColor(text.mFGColor, pBlend));
976
977 for (auto run : {0, 1})
978 {
979 start = str;
980 end = str + strlen(str);
981 while ((nRows = nvgTextBreakLines(mVG, start, end, width, rows, 3))) {
982 for (int i = 0; i < nRows; i++) {
983 if (run == 0)
984 {
985 lines++;
986 }
987 else
988 {
989 NVGtextRow* row = &rows[i];
990 nvgText(mVG, x, y - ((lines*lineHeight)*yOffsetScale), row->start, row->end);
991 y += lineHeight;
992 }
993 }
994 start = rows[nRows-1].next;
995 }
996 }
997
998 nvgRestore(mVG);
999}
const void * LoadWinResource(const char *resID, const char *type, int &sizeInBytes, void *pHInstance)
Load a resource from the binary (windows only).
A Text entry widget drawn by IGraphics to optionally override platform text entries.
A base class interface for a bitmap abstraction around the different drawing back end bitmap represen...
float GetScale() const
BitmapData GetBitmap() const
void SetBitmap(BitmapData pBitmap, int w, int h, float scale, float drawScale)
Used to initialise the members after construction.
int GetHeight() const
float GetDrawScale() const
int GetWidth() const
User-facing bitmap abstraction that you use to manage bitmap data, independant of draw class/platform...
int W() const
float GetScale() const
int H() 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
ILayer * PopLayer()
Pop a layer off the stack.
Definition: IGraphics.cpp:2032
void PathTransformTranslate(float x, float y)
Apply a translation transform to the current path.
Definition: IGraphics.cpp:2748
void PathRect(const IRECT &bounds)
Add a rectangle to the current path.
Definition: IGraphics.cpp:2653
virtual float GetBackingPixelScale() const
Definition: IGraphics.h:1797
void DoMeasureTextRotation(const IText &text, const IRECT &bounds, IRECT &rect) const
Measures text bounds accounting for rotation.
Definition: IGraphics.cpp:2245
int WindowWidth() const
Gets the width of the graphics context including draw scaling.
Definition: IGraphics.h:1106
EResourceLocation SearchImageResource(const char *fileName, const char *type, WDL_String &result, int targetScale, int &sourceScale)
Search for a bitmap image resource matching the target scale.
Definition: IGraphics.cpp:1892
void PathTransformRestore()
Restore the affine transform of the current path, to the previously saved state.
Definition: IGraphics.cpp:2726
void PushLayer(ILayer *pLayer)
Push a layer on to the stack.
Definition: IGraphics.cpp:2023
int GetRoundedScreenScale() const
Gets the screen/display scaling factor, rounded up.
Definition: IGraphics.h:1126
void RemoveAllControls()
Removes all regular IControls from the control list, as well as special controls (frees memory).
Definition: IGraphics.cpp:194
virtual void * GetWinModuleHandle()
Definition: IGraphics.h:926
float GetScreenScale() const
Gets the screen/display scaling factor, e.g.
Definition: IGraphics.h:1122
void PathTransformSave()
Save the current affine transform of the current path.
Definition: IGraphics.cpp:2721
float GetDrawScale() const
Gets the graphics context scaling factor.
Definition: IGraphics.h:1118
int WindowHeight() const
Gets the height of the graphics context including draw scaling.
Definition: IGraphics.h:1110
virtual void BeginFrame()
Called at the beginning of drawing.
Definition: IGraphics.cpp:880
IGraphics draw class using NanoVG
const char * GetDrawingAPIStr() override
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...
void DrawResize() override
Called to update the drawing surface after a resize.
void PathClear() override
Clear the stack of path drawing commands.
IBitmap LoadBitmap(const char *name, int nStates, bool framesAreHorizontal, int targetScale) override
Load a bitmap image from disk or from windows resource.
void PathFill(const IPattern &pattern, const IFillOptions &options, const IBlend *pBlend) override
Fill the current current path.
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 DrawDottedRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend, float thickness, float dashLen) override
Draw a dotted rectangle to the graphics context.
void PathSetWinding(bool clockwise) override
NanoVG only.
APIBitmap * LoadAPIBitmap(const char *fileNameOrResID, int scale, EResourceLocation location, const char *ext) override
Drawing API method to load a bitmap, called internally.
void PathStroke(const IPattern &pattern, float thickness, const IStrokeOptions &options, const IBlend *pBlend) override
Stroke the current current path.
void PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding) override
Add an arc to the current path.
void PathLineTo(float x, float y) override
Add a line to the current path from the current point to the specified location.
void OnViewDestroyed() override
Called after a platform view is destroyed, so that drawing classes can e.g.
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 DrawDottedLine(const IColor &color, float x1, float y1, float x2, float y2, const IBlend *pBlend, float thickness, float dashLen) override
Draw a dotted line to the graphics context.
void PathCubicBezierTo(float c1x, float c1y, float c2x, float c2y, float x2, float y2) override
Add a cubic bezier to the current path from the current point to the specified location.
IColor GetPoint(int x, int y) override
Get the color at an X, Y location in the graphics context.
bool LoadAPIFont(const char *fontID, const PlatformFontPtr &font) override
Drawing API method to load a font from a PlatformFontPtr, called internally.
void PathClose() override
Close the path that is being specified.
void DoDrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend) override
Internal method to draw text.
void DrawFastDropShadow(const IRECT &innerBounds, const IRECT &outerBounds, float xyDrop=5.f, float roundness=0.f, float blur=10.f, IBlend *pBlend=nullptr) override
NanoVG only.
void PathMoveTo(float x, float y) override
Move the current point in the current path.
void OnViewInitialized(void *pContext) override
Called after platform view initialization, so that drawing classes can e.g.
void BeginFrame() override
Called at the beginning of drawing.
void EndFrame() override
Called by some drawing API classes to finally blit the draw bitmap onto the screen or perform other c...
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.
void PathQuadraticBezierTo(float cx, float cy, float x2, float y2) override
Add a quadratic bezier to the current path from the current point to the specified location.
bool BitmapExtSupported(const char *ext) override
Checks a file extension and reports whether this drawing API supports loading that extension.
float DoMeasureText(const IText &text, const char *str, IRECT &bounds) const override
Internal method to measure text dimensions.
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.
An abstraction that is used to store a temporary raster image/framebuffer.
std::unique_ptr< ILayer > ILayerPtr
ILayerPtr is a managed pointer for transferring the ownership of layers.
float BlendWeight(const IBlend *pBlend)
Helper function to extract the blend weight value from an IBlend ptr if it is valid.
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)
Converts a C string to lowercase.
Used to manage composite/blend operations, independent of draw class/platform.
Used to manage color data, independent of draw class/platform.
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 a point using this matrix.
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.
float MH() const
float W() const
float H() const
void Translate(float x, float y)
Translate this rectangle.
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,...