1 #if ESCAPE_FROM_VSTGUI
2 #include <JuceHeader.h>
3 #include "CScalableBitmap.h"
4 #include "DisplayInfo.h"
5 
CScalableBitmap(VSTGUI::CResourceDescription d,VSTGUI::CFrame * f)6 CScalableBitmap::CScalableBitmap(VSTGUI::CResourceDescription d, VSTGUI::CFrame *f)
7     : VSTGUI::CBitmap(d)
8 {
9     if (d.u.type == VSTGUI::CResourceDescription::kIntegerType)
10     {
11         resourceID = d.u.id;
12         std::string fn = "bmp00" + std::to_string(d.u.id) + "_svg";
13         int bds;
14         auto bd = BinaryData::getNamedResource(fn.c_str(), bds);
15         if (bd)
16         {
17             drawable = juce::Drawable::createFromImageData(bd, bds);
18         }
19     }
20 }
21 
CScalableBitmap(std::string fname,VSTGUI::CFrame * f)22 CScalableBitmap::CScalableBitmap(std::string fname, VSTGUI::CFrame *f)
23     : VSTGUI::CBitmap(VSTGUI::CResourceDescription(0))
24 {
25     this->fname = fname;
26     drawable = juce::Drawable::createFromImageFile(juce::File(fname));
27 }
28 
29 CScalableBitmap::~CScalableBitmap() = default;
30 
setPhysicalZoomFactor(int zoomFactor)31 void CScalableBitmap::setPhysicalZoomFactor(int zoomFactor)
32 {
33     currentPhysicalZoomFactor = zoomFactor;
34 }
35 
draw(VSTGUI::CDrawContext * context,const VSTGUI::CRect & rect,const VSTGUI::CPoint & offset,float alpha)36 void CScalableBitmap::draw(VSTGUI::CDrawContext *context, const VSTGUI::CRect &rect,
37                            const VSTGUI::CPoint &offset, float alpha)
38 {
39     if (pngZooms.size() == 0)
40     {
41         VSTGUI::CBitmap::draw(context, rect, offset, alpha);
42         return;
43     }
44     int useZoom = -1;
45     for (auto &p : pngZooms)
46     {
47         if (p.first <= currentPhysicalZoomFactor)
48             useZoom = p.first;
49     }
50 
51     if (useZoom < 0)
52     {
53         VSTGUI::CBitmap::draw(context, rect, offset, alpha);
54         return;
55     }
56 
57     resolvePNGForZoomLevel(useZoom);
58     if (!pngZooms[useZoom].second)
59     {
60         VSTGUI::CBitmap::draw(context, rect, offset, alpha);
61         return;
62     }
63 
64     // This relies on the internal state a bit but that's OK
65     auto hcdf = Surge::GUI::getDisplayBackingScaleFactor(nullptr);
66     juce::Graphics::ScopedSaveState gs(context->g);
67     static auto warnOnce = false;
68     if (!warnOnce)
69     {
70         std::cout << "WARNING: THERE IS A HARDCODED 125 HERE!!! FIXME" << std::endl;
71         warnOnce = true;
72     }
73     // FIXME: What is this 1.25?
74     auto t = juce::AffineTransform().scaled(1.f / hcdf / 1.25, 1.f / hcdf / 1.25);
75     pngZooms[useZoom].second->drawable->draw(context->g, alpha, t);
76 }
77 
addPNGForZoomLevel(std::string fname,int zoomLevel)78 void CScalableBitmap::addPNGForZoomLevel(std::string fname, int zoomLevel)
79 {
80     pngZooms[zoomLevel] = std::make_pair(fname, nullptr);
81 }
82 
resolvePNGForZoomLevel(int zoomLevel)83 void CScalableBitmap::resolvePNGForZoomLevel(int zoomLevel)
84 {
85     if (pngZooms.find(zoomLevel) == pngZooms.end())
86         return;
87     if (pngZooms[zoomLevel].second)
88         return;
89 
90     pngZooms[zoomLevel].second =
91         std::move(std::make_unique<CScalableBitmap>(pngZooms[zoomLevel].first.c_str(), nullptr));
92 }
93 
94 #else
95 
96 #if MAC
97 #include <CoreFoundation/CoreFoundation.h>
98 #endif
99 
100 #include "globals.h"
101 #include "guihelpers.h"
102 #include "CScalableBitmap.h"
103 #include "UserInteractions.h"
104 #include "UIInstrumentation.h"
105 #include <iomanip>
106 #include <iostream>
107 #include <sstream>
108 #include <fstream>
109 #include "filesystem/import.h"
110 #if MAC
111 #include "vstgui/lib/platform/mac/macglobals.h"
112 #endif
113 #if LINUX
114 #include "ScalablePiggy.h"
115 #endif
116 #if WINDOWS
117 #if !ESCAPE_FROM_VSTGUI
118 #include "vstgui/lib/platform/iplatformresourceinputstream.h"
119 #endif
120 #endif
121 #include <unordered_map>
122 
123 #include <cmath>
124 
125 #include "resource.h"
126 
127 #include <stdio.h>
128 #include <string.h>
129 #include <math.h>
130 #include <DebugHelpers.h>
131 /*
132 ** CScalableBitmap is the only file which uses the SVG Implementatoin so it is also the one
133 ** and only one which ejexts the link symbols.
134 */
135 #define NANOSVG_IMPLEMENTATION
136 #include "nanosvg.h"
137 
138 using namespace VSTGUI;
139 std::atomic<int> CScalableBitmap::instances(0);
140 
141 namespace
142 {
svgParseFromPath(const fs::path & filename,const char * units,float dpi)143 NSVGimage *svgParseFromPath(const fs::path &filename, const char *units, float dpi)
144 {
145     std::error_code ec;
146     const auto length = fs::file_size(filename, ec);
147     if (ec || length == 0)
148         return nullptr;
149 
150     std::unique_ptr<char[]> data;
151     {
152         std::filebuf file;
153         if (!file.open(filename, std::ios::binary | std::ios::in))
154             return nullptr;
155         data.reset(new char[length + 1]);
156         if (file.sgetn(data.get(), length) != length)
157             return nullptr;
158     }
159     data[length] = '\0';
160     return nsvgParse(data.get(), units, dpi);
161 }
162 } // anonymous namespace
163 
164 #if MAC
svgFullFileNameFromBundle(const std::string & filename)165 static const std::string svgFullFileNameFromBundle(const std::string &filename)
166 {
167     CFStringRef cfStr = CFStringCreateWithCString(nullptr, filename.c_str(), kCFStringEncodingUTF8);
168     CFURLRef url = CFBundleCopyResourceURL(VSTGUI::getBundleRef(), cfStr, nullptr, nullptr);
169     CFRelease(cfStr);
170     if (url)
171     {
172         CFStringRef urlStr = CFURLGetString(url);
173         CFRetain(
174             urlStr); // "GET" rule
175                      // https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/Concepts/Ownership.html#//apple_ref/doc/uid/20001148-SW1
176         const char *csp = CFStringGetCStringPtr(urlStr, kCFStringEncodingUTF8);
177         if (csp)
178         {
179             std::string resPath(csp);
180             if (resPath.find("file://") != std::string::npos)
181                 resPath = resPath.substr(7);
182             CFRelease(urlStr);
183             CFRelease(url);
184             return resPath;
185         }
186         CFRelease(urlStr);
187         CFRelease(url);
188     }
189 
190     return "";
191 }
192 
193 #endif
194 
195 #if LINUX
findMemorySVG(const std::string & filename)196 static const struct MemorySVG *findMemorySVG(const std::string &filename)
197 {
198     for (const struct MemorySVG *svg = &memorySVGList[0]; svg->name != NULL; svg++)
199         if (!std::strncmp(filename.c_str(), svg->name, std::strlen(svg->name)))
200             return svg;
201 
202     return nullptr;
203 }
204 #endif
205 
206 #if WINDOWS
svgContentsFromRCFile(int id,char * svgData,int maxSz)207 static int svgContentsFromRCFile(int id, char *svgData, int maxSz)
208 {
209 #ifdef INSTRUMENT_UI
210     Surge::Debug::record("svgContentsFromRCFile::GET");
211 #endif
212     static std::unordered_map<int, char *> leakyStore;
213     if (leakyStore.find(id) == leakyStore.end())
214     {
215 #ifdef INSTRUMENT_UI
216         Surge::Debug::record("svgContentsFromRCFile::READ");
217 #endif
218 
219         VSTGUI::IPlatformResourceInputStream::Ptr istr =
220             VSTGUI::IPlatformResourceInputStream::create(CResourceDescription(id));
221 
222         if (istr == NULL)
223         {
224             return -1;
225         }
226 
227         size_t sz = 1024 * 1024;
228         char *leakThis = new char[sz];
229         memset(leakThis, 0, sz);
230         uint32_t readSize = istr->readRaw(leakThis, sz);
231         leakThis[readSize] = 0;
232         leakyStore[id] = leakThis;
233     }
234 
235     memcpy(svgData, leakyStore[id], maxSz);
236 
237     return 1;
238 }
239 #endif
240 
241 // Remember this is user zoom * display zoom. See comment in CScalableBitmap.h
setPhysicalZoomFactor(int zoomFactor)242 void CScalableBitmap::setPhysicalZoomFactor(int zoomFactor)
243 {
244     currentPhysicalZoomFactor = zoomFactor;
245 }
246 
CScalableBitmap(CResourceDescription desc,VSTGUI::CFrame * f)247 CScalableBitmap::CScalableBitmap(CResourceDescription desc, VSTGUI::CFrame *f)
248     : CBitmap(desc), svgImage(nullptr), frame(f)
249 {
250 #ifdef INSTRUMENT_UI
251     Surge::Debug::record("CScalableBitmap::CScalableBitmap desc");
252 #endif
253 
254     int id = 0;
255     if (desc.type == CResourceDescription::kIntegerType)
256         id = (int32_t)desc.u.id;
257 
258     instances++;
259     // std::cout << "  Construct CScalableBitmap. instances=" << instances << " id=" << id <<
260     // std::endl;
261 
262     resourceID = id;
263 
264     std::stringstream filename;
265     filename << "svg/bmp" << std::setw(5) << std::setfill('0') << id << ".svg";
266 
267 #if MAC
268     std::string fullFileName = svgFullFileNameFromBundle(filename.str());
269     svgImage = nsvgParseFromFile(fullFileName.c_str(), "px", 96);
270 
271     /*
272     ** Some older versions of live are reported to not set the bundle identity
273     ** properly. There are all sorts of complicated ways to deal with this. Here
274     ** though is one which isn't great but gives those users a bit of hope
275     */
276     if (!svgImage)
277     {
278         std::ostringstream fn2;
279 #if TARGET_AUDIOUNIT
280         fn2 << "/Library/Audio/Plug-Ins/Components/Surge.component/Contents/Resources/"
281             << filename.str();
282 #elif TARGET_VST2
283         fn2 << "/Library/Audio/Plug-Ins/VST/Surge.vst/Contents/Resources/" << filename.str();
284 #elif TARGET_VST3
285         fn2 << "/Library/Audio/Plug-Ins/VST3/Surge.vst3/Contents/Resources/" << filename.str();
286 #endif
287 
288         // std::cout << "FailBack from bad module SVG path to best guess: [" << fn2.str() << "]" <<
289         // std::endl;
290         svgImage = nsvgParseFromFile(fn2.str().c_str(), "px", 96);
291     }
292 #endif
293 
294 #if WINDOWS
295     int svgid = id + SCALABLE_SVG_OFFSET;
296     char *svgData = new char[1024 * 1024];
297 
298     if (svgContentsFromRCFile(svgid, svgData, 1024 * 1024) > 0)
299         svgImage = nsvgParse(svgData, "px", 96);
300     else
301         svgImage = NULL;
302 
303     delete[] svgData;
304 #endif
305 
306 #if LINUX
307     if (const MemorySVG *const memSVG = findMemorySVG(filename.str()))
308     {
309         char *svg = new char[memSVG->size + 1];
310         svg[memSVG->size] = '\0';
311         strncpy(svg, (const char *)(memorySVGListStart + memSVG->offset), memSVG->size);
312         svgImage = nsvgParse(svg, "px", 96);
313         delete[] svg;
314     }
315     else
316     {
317         std::cerr << filename.str() << " not found" << std::endl;
318     }
319 #endif
320 
321     if (!svgImage)
322     {
323         std::cout << "Unable to load SVG Image " << filename.str() << std::endl;
324     }
325 
326     extraScaleFactor = 100;
327     currentPhysicalZoomFactor = 100;
328     lastSeenZoom = -1;
329 }
330 
CScalableBitmap(std::string ifname,VSTGUI::CFrame * f)331 CScalableBitmap::CScalableBitmap(std::string ifname, VSTGUI::CFrame *f)
332     : CBitmap(CResourceDescription(0)), svgImage(nullptr), frame(f)
333 {
334 #ifdef INSTRUMENT_UI
335     Surge::Debug::record("CScalableBitmap::CScalableBitmap file");
336 #endif
337 
338     // OK so we have to see what type of file we are
339     fname = ifname;
340     instances++;
341     resourceID = -1;
342 
343     std::string extension = "svg";
344     if (fname.length() > 3)
345         extension = fname.substr(fname.length() - 3);
346 
347     if (_stricmp(extension.c_str(), "svg") == 0)
348     {
349         svgImage = svgParseFromPath(string_to_path(fname), "px", 96);
350 
351         if (!svgImage)
352         {
353             std::cout << "Unable to load SVG Image " << fname << std::endl;
354         }
355     }
356 
357     if (_stricmp(extension.c_str(), "png") == 0)
358     {
359         pngZooms[100] = std::make_pair(fname, std::make_unique<VSTGUI::CBitmap>(fname.c_str()));
360     }
361 
362     extraScaleFactor = 100;
363     currentPhysicalZoomFactor = 100;
364     lastSeenZoom = -1;
365 }
366 
~CScalableBitmap()367 CScalableBitmap::~CScalableBitmap()
368 {
369 #ifdef INSTRUMENT_UI
370     Surge::Debug::record("CScalableBitmap::~CScalableBitmap");
371 #endif
372 
373     for (auto const &pair : offscreenCache)
374     {
375         auto val = pair.second;
376         if (val)
377         {
378             val->forget();
379         }
380     }
381     offscreenCache.clear();
382 
383     if (svgImage)
384     {
385         nsvgDelete(svgImage);
386     }
387     instances--;
388     // std::cout << "  Destroy CScalableBitmap. instances=" << instances << " id=" << resourceID <<
389     // " fn=" << fname << std::endl;
390 }
391 
draw(CDrawContext * context,const CRect & rect,const CPoint & offset,float alpha)392 void CScalableBitmap::draw(CDrawContext *context, const CRect &rect, const CPoint &offset,
393                            float alpha)
394 {
395 #ifdef INSTRUMENT_UI
396     Surge::Debug::record("CScalableBitmap::draw");
397 #endif
398     /*
399      * From 1.6 beta 9 until aeb45a38 we used to have a VUMeter optimization here which we don't
400      * need now CScalableBitmap uses an offscreen cache (which it did after 1.6.3 or so I think?).
401      * Just FYI!
402      */
403 
404     if (svgImage || (pngZooms.find(100) != pngZooms.end()))
405     {
406         /*
407         ** Plan of attack here is to use coffscreencontext into a physicalzoomfactor scaled version
408         *of rect
409         ** see vstgui.surge/vstgui/lib/coffscreencontext.h for how that works. So we just need a
410         *good hash
411         ** on rect.size, off and physical zoom.
412         */
413         if (lastSeenZoom != currentPhysicalZoomFactor)
414         {
415             for (auto const &pair : offscreenCache)
416             {
417                 auto val = pair.second;
418                 if (val)
419                     val->forget();
420             }
421             offscreenCache.clear();
422             lastSeenZoom = currentPhysicalZoomFactor;
423         }
424 
425         CGraphicsTransform tf =
426             CGraphicsTransform().scale(lastSeenZoom / 100.0, lastSeenZoom / 100.0);
427 
428         /*
429         ** VSTGUI has this wierdo bug where it shrinks backgrounds properly but doesn't grow them.
430         *Sigh.
431         ** So asymmetrically treat the extra factor here and only here.
432         */
433         int exs = (extraScaleFactor < 100 ? 100 : extraScaleFactor);
434         CGraphicsTransform xtf = CGraphicsTransform().scale(exs / 100.0, exs / 100.0);
435         CGraphicsTransform itf = tf.inverse();
436         CGraphicsTransform ixtf = xtf.inverse();
437 
438         if (offscreenCache.find(offset) == offscreenCache.end())
439         {
440 #ifdef INSTRUMENT_UI
441             Surge::Debug::record("CScalableBitmap::draw::createOffscreenCache");
442 #endif
443             VSTGUI::CPoint sz = rect.getSize();
444             ixtf.transform(sz);
445 
446             VSTGUI::CPoint offScreenSz = sz;
447             tf.transform(offScreenSz);
448 
449             if (auto offscreen =
450                     COffscreenContext::create(frame, ceil(offScreenSz.x), ceil(offScreenSz.y)))
451             {
452                 offscreen->beginDraw();
453                 VSTGUI::CRect newRect(0, 0, rect.getWidth(), rect.getHeight());
454 
455                 CDrawContext::Transform trsf(*offscreen, tf);
456                 CDrawContext::Transform xtrsf(*offscreen, ixtf);
457 
458                 if (svgImage)
459                 {
460                     drawSVG(offscreen, newRect, offset, alpha);
461                 }
462                 else
463                 {
464                     // OK so look through our zoom PNGs looking for the one above cpz
465                     int zoomScan = 100;
466 
467                     for (auto &zl : pngZooms)
468                     {
469                         zoomScan = zl.first;
470                         if (zoomScan >= currentPhysicalZoomFactor)
471                         {
472                             break;
473                         }
474                     }
475                     auto etf = VSTGUI::CGraphicsTransform().scale(extraScaleFactor / 100.0,
476                                                                   extraScaleFactor / 100.0);
477                     VSTGUI::CDrawContext::Transform t1(*offscreen, etf);
478 
479                     auto ztf =
480                         VSTGUI::CGraphicsTransform().scale(100.0 / zoomScan, 100.0 / zoomScan);
481                     VSTGUI::CDrawContext::Transform t2(*offscreen, ztf);
482 
483                     ztf.inverse().transform(newRect);
484                     auto offs = offset;
485                     ztf.inverse().transform(offs);
486                     if (!pngZooms[zoomScan].second)
487                         resolvePNGForZoomLevel(zoomScan);
488                     if (pngZooms[zoomScan].second)
489                         pngZooms[zoomScan].second->draw(offscreen, newRect, offs, 1.0);
490                 }
491                 offscreen->endDraw();
492                 CBitmap *tmp = offscreen->getBitmap();
493                 if (tmp)
494                 {
495                     offscreenCache[offset] = tmp;
496 #if !LINUX
497                     /*
498                      * See #4081. Basically on linux the bitmap is reference toggled up with our
499                      * version of vstgui. Why? Who knows. But we are gonna kill vstgui RSN so
500                      * for now just don't do the extra remember on LINUX
501                      */
502                     tmp->remember();
503 #endif
504                 }
505             }
506         }
507 
508         if (offscreenCache.find(offset) != offscreenCache.end())
509         {
510             context->saveGlobalState();
511 
512             CDrawContext::Transform trsf(*context, itf);
513             CDrawContext::Transform trsf2(*context, xtf);
514 
515             VSTGUI::CRect drect = rect;
516             tf.transform(drect);
517 
518             VSTGUI::CRect origClip;
519             context->getClipRect(origClip);
520 
521             VSTGUI::CRect clipR = origClip;
522 
523             /*
524             ** The clipR goes up to and including the edge of the size; but at very high zooms that
525             *shows a
526             ** tiny bit of the adjacent image. So make the edge of our clip just inside the size.
527             ** This only matters at high zoom levels
528             */
529             clipR.bottom -= 0.01;
530             clipR.right -= 0.01;
531             context->setClipRect(clipR);
532 
533 #if LINUX
534             alpha = 1.0;
535 #endif
536             offscreenCache[offset]->draw(context, drect, CPoint(0, 0), alpha);
537             context->setClipRect(origClip);
538 
539             context->restoreGlobalState();
540             return;
541         }
542     }
543 
544     /* SVG File is missing */
545     context->setFillColor(VSTGUI::kBlueCColor);
546     context->drawRect(rect, VSTGUI::kDrawFilled);
547     context->setFrameColor(VSTGUI::kRedCColor);
548     context->drawRect(rect, VSTGUI::kDrawStroked);
549     return;
550 }
551 
drawSVG(CDrawContext * dc,const CRect & rect,const CPoint & offset,float alpha)552 void CScalableBitmap::drawSVG(CDrawContext *dc, const CRect &rect, const CPoint &offset,
553                               float alpha)
554 {
555     VSTGUI::CRect viewS = rect;
556 
557     VSTGUI::CDrawMode origMode = dc->getDrawMode();
558     VSTGUI::CDrawMode newMode(VSTGUI::kAntiAliasing);
559     dc->setDrawMode(newMode);
560 
561     VSTGUI::CRect origClip;
562     dc->getClipRect(origClip);
563     VSTGUI::CRect clipR = viewS;
564     /*
565     ** The clipR goes up to and including the edge of the size; but at very high zooms that shows a
566     ** tiny bit of the adjacent image. So make the edge of our clip just inside the size.
567     ** This only matters at high zoom levels
568     */
569     clipR.bottom -= 0.01;
570     clipR.right -= 0.01;
571     dc->setClipRect(clipR);
572 
573     /*
574     ** Our overall transform moves us to the x/y position of our view rects top left point and
575     *shifts us by
576     ** our offset. Don't forgert the extra zoom also!
577     */
578 
579     VSTGUI::CGraphicsTransform tf =
580         VSTGUI::CGraphicsTransform()
581             .translate(viewS.getTopLeft().x - offset.x, viewS.getTopLeft().y - offset.y)
582             .scale(extraScaleFactor / 100.0, extraScaleFactor / 100.0);
583     VSTGUI::CDrawContext::Transform t(*dc, tf);
584 
585 #if LINUX
586     Surge::UI::NonIntegralAntiAliasGuard naag(dc);
587 #endif
588 
589     for (auto shape = svgImage->shapes; shape != NULL; shape = shape->next)
590     {
591         if (!(shape->flags & NSVG_FLAGS_VISIBLE))
592             continue;
593 
594         /*
595         ** We don't need to even bother drawing shapes outside of clip
596         */
597         CRect shapeBounds(shape->bounds[0], shape->bounds[1], shape->bounds[2], shape->bounds[3]);
598         tf.transform(shapeBounds);
599         if (!shapeBounds.rectOverlap(clipR))
600             continue;
601 
602         /*
603         ** Build a path for this shape out of each subordinate path as a
604         ** graphics subpath
605         */
606         VSTGUI::CGraphicsPath *gp = dc->createGraphicsPath();
607         for (auto path = shape->paths; path != NULL; path = path->next)
608         {
609             for (auto i = 0; i < path->npts - 1; i += 3)
610             {
611                 float *p = &path->pts[i * 2];
612 
613                 if (i == 0)
614                     gp->beginSubpath(p[0], p[1]);
615                 gp->addBezierCurve(p[2], p[3], p[4], p[5], p[6], p[7]);
616             }
617             if (path->closed)
618                 gp->closeSubpath();
619         }
620 
621         /*
622         ** Fill with constant or gradient
623         */
624         if (shape->fill.type != NSVG_PAINT_NONE)
625         {
626             if (shape->fill.type == NSVG_PAINT_COLOR)
627             {
628                 dc->setFillColor(svgColorToCColor(shape->fill.color, shape->opacity));
629 
630                 VSTGUI::CDrawContext::PathDrawMode pdm = VSTGUI::CDrawContext::kPathFilled;
631                 if (shape->fillRule == NSVGfillRule::NSVG_FILLRULE_EVENODD)
632                 {
633                     pdm = VSTGUI::CDrawContext::kPathFilledEvenOdd;
634                 }
635                 dc->drawGraphicsPath(gp, pdm);
636             }
637             else if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT)
638             {
639                 bool evenOdd = (shape->fillRule == NSVGfillRule::NSVG_FILLRULE_EVENODD);
640                 NSVGgradient *ngrad = shape->fill.gradient;
641 
642                 float *x = ngrad->xform;
643                 // This re-order is on purpose; vstgui and nanosvg use different order for diagonals
644                 VSTGUI::CGraphicsTransform gradXform(x[0], x[2], x[1], x[3], x[4], x[5]);
645                 VSTGUI::CGradient::ColorStopMap csm;
646                 VSTGUI::CGradient *cg = VSTGUI::CGradient::create(csm);
647 
648                 for (int i = 0; i < ngrad->nstops; ++i)
649                 {
650                     auto stop = ngrad->stops[i];
651                     cg->addColorStop(stop.offset, svgColorToCColor(stop.color, shape->opacity));
652                 }
653                 VSTGUI::CPoint s0(0, 0), s1(0, 1);
654                 VSTGUI::CPoint p0 = gradXform.inverse().transform(s0);
655                 VSTGUI::CPoint p1 = gradXform.inverse().transform(s1);
656 
657                 dc->fillLinearGradient(gp, *cg, p0, p1, evenOdd);
658                 cg->forget();
659             }
660             else if (shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT)
661             {
662                 bool evenOdd = (shape->fillRule == NSVGfillRule::NSVG_FILLRULE_EVENODD);
663                 NSVGgradient *ngrad = shape->fill.gradient;
664 
665                 float *x = ngrad->xform;
666                 // This re-order is on purpose; vstgui and nanosvg use different order for diagonals
667                 VSTGUI::CGraphicsTransform gradXform(x[0], x[2], x[1], x[3], x[4], x[5]);
668                 VSTGUI::CGradient::ColorStopMap csm;
669                 VSTGUI::CGradient *cg = VSTGUI::CGradient::create(csm);
670 
671                 for (int i = 0; i < ngrad->nstops; ++i)
672                 {
673                     auto stop = ngrad->stops[i];
674                     cg->addColorStop(stop.offset, svgColorToCColor(stop.color, shape->opacity));
675                 }
676 
677                 VSTGUI::CPoint s0(0, 0),
678                     s1(0.5, 0); // the box has size -0.5, 0.5 so the radius is 0.5
679                 VSTGUI::CPoint p0 = gradXform.inverse().transform(s0);
680                 VSTGUI::CPoint p1 = gradXform.inverse().transform(s1);
681                 dc->fillRadialGradient(gp, *cg, p0, p1.x, CPoint(0, 0), evenOdd);
682                 cg->forget();
683             }
684             else
685             {
686                 std::cerr << "Unknown Shape Fill Type" << std::endl;
687                 dc->setFillColor(VSTGUI::kRedCColor);
688                 dc->drawGraphicsPath(gp, VSTGUI::CDrawContext::kPathFilled);
689             }
690         }
691 
692         /*
693         ** And then the stroke
694         */
695         if (shape->stroke.type != NSVG_PAINT_NONE)
696         {
697             if (shape->stroke.type == NSVG_PAINT_COLOR)
698             {
699                 dc->setFrameColor(svgColorToCColor(shape->stroke.color, shape->opacity));
700             }
701             else
702             {
703                 std::cerr << "No gradient support yet for stroke" << std::endl;
704                 dc->setFrameColor(VSTGUI::kRedCColor);
705             }
706 
707             dc->setLineWidth(shape->strokeWidth);
708             VSTGUI::CLineStyle cs((VSTGUI::CLineStyle::LineCap)(shape->strokeLineCap),
709                                   (VSTGUI::CLineStyle::LineJoin)(shape->strokeLineJoin));
710             dc->setLineStyle(cs);
711             dc->drawGraphicsPath(gp, VSTGUI::CDrawContext::kPathStroked);
712         }
713         gp->forget();
714     }
715 
716     dc->resetClipRect();
717     dc->setDrawMode(origMode);
718 }
719 
svgColorToCColor(int svgColor,float opacity)720 VSTGUI::CColor CScalableBitmap::svgColorToCColor(int svgColor, float opacity)
721 {
722     int a = ((svgColor & 0xFF000000) >> 24) * opacity;
723     int b = (svgColor & 0x00FF0000) >> 16;
724     int g = (svgColor & 0x0000FF00) >> 8;
725     int r = (svgColor & 0x000000FF);
726     return VSTGUI::CColor(r, g, b, a);
727 }
728 
addPNGForZoomLevel(std::string fname,int zoomLevel)729 void CScalableBitmap::addPNGForZoomLevel(std::string fname, int zoomLevel)
730 {
731     pngZooms[zoomLevel] = std::make_pair(fname, nullptr);
732 }
733 
resolvePNGForZoomLevel(int zoomLevel)734 void CScalableBitmap::resolvePNGForZoomLevel(int zoomLevel)
735 {
736     if (pngZooms.find(zoomLevel) == pngZooms.end())
737         return;
738     if (pngZooms[zoomLevel].second)
739         return;
740 
741     pngZooms[zoomLevel].second =
742         std::move(std::make_unique<VSTGUI::CBitmap>(pngZooms[zoomLevel].first.c_str()));
743 }
744 #endif
745