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