iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IGraphics.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 "IGraphics.h"
12
13#define NANOSVG_IMPLEMENTATION
14#pragma warning(disable:4244) // float conversion
15#include "nanosvg.h"
16
17#if defined VST3_API
18#include "pluginterfaces/base/ustring.h"
19#include "IPlugVST3.h"
20using VST3_API_BASE = iplug::IPlugVST3;
21#elif defined VST3C_API
22#include "pluginterfaces/base/ustring.h"
24#include "IPlugVST3_View.h"
25using VST3_API_BASE = iplug::IPlugVST3Controller;
26#endif
27
28#include "IPlugParameter.h"
29#include "IPlugPluginBase.h"
30
31#include "IControl.h"
32#include "IControls.h"
33#include "IGraphicsLiveEdit.h"
34#include "IFPSDisplayControl.h"
36#include "IPopupMenuControl.h"
37#include "ITextEntryControl.h"
38#include "IBubbleControl.h"
39
40using namespace iplug;
41using namespace igraphics;
42
43static StaticStorage<APIBitmap> sBitmapCache;
44static StaticStorage<SVGHolder> sSVGCache;
45
46IGraphics::IGraphics(IGEditorDelegate& dlg, int w, int h, int fps, float scale)
47: mWidth(w)
48, mHeight(h)
49, mFPS(fps)
50, mDrawScale(scale)
51, mMinScale(DEFAULT_MIN_DRAW_SCALE)
52, mMaxScale(DEFAULT_MAX_DRAW_SCALE)
53, mDelegate(&dlg)
54{
55 StaticStorage<APIBitmap>::Accessor bitmapStorage(sBitmapCache);
56 bitmapStorage.Retain();
57 StaticStorage<SVGHolder>::Accessor svgStorage(sSVGCache);
58 svgStorage.Retain();
59}
60
61IGraphics::~IGraphics()
62{
63 // N.B. - the OS levels have destructed, so we can't show/hide the cursor
64 // Thus, this prevents a call to a pure virtual in ReleaseMouseCapture
65
66 mCursorHidden = false;
68
69 StaticStorage<APIBitmap>::Accessor bitmapStorage(sBitmapCache);
70 bitmapStorage.Release();
71 StaticStorage<SVGHolder>::Accessor svgStorage(sSVGCache);
72 svgStorage.Release();
73}
74
76{
77 mScreenScale = scale;
78 int windowWidth = WindowWidth() * GetPlatformWindowScale();
79 int windowHeight = WindowHeight() * GetPlatformWindowScale();
80
81 assert(windowWidth > 0 && windowHeight > 0 && "Window dimensions invalid");
82
83 bool parentResized = GetDelegate()->EditorResizeFromUI(windowWidth, windowHeight, true);
84 PlatformResize(parentResized);
87 DrawResize();
88}
89
90void IGraphics::Resize(int w, int h, float scale, bool needsPlatformResize)
91{
93
94 scale = Clip(scale, mMinScale, mMaxScale);
95
96 if (w == Width() && h == Height() && scale == GetDrawScale()) return;
97
98 //DBGMSG("resize %i, resize %i, scale %f\n", w, h, scale);
100
101 mDrawScale = scale;
102 mWidth = w;
103 mHeight = h;
104
105 if (mCornerResizer)
106 mCornerResizer->OnRescale();
107
108 int windowWidth = WindowWidth() * GetPlatformWindowScale();
109 int windowHeight = WindowHeight() * GetPlatformWindowScale();
110
111 bool parentResized = GetDelegate()->EditorResizeFromUI(windowWidth, windowHeight, needsPlatformResize);
112 PlatformResize(parentResized);
115 DrawResize();
116
117 if(mLayoutOnResize)
118 GetDelegate()->LayoutUI(this);
119}
120
121void IGraphics::SetLayoutOnResize(bool layoutOnResize)
122{
123 mLayoutOnResize = layoutOnResize;
124}
125
126void IGraphics::SetScaleConstraints(float lo, float hi)
127{
128 mMinScale = std::min(lo, hi);
129 mMaxScale = std::max(lo, hi);
130}
131
133{
134 mControls.DeletePtr(GetControlWithTag(ctrlTag), true);
135 mCtrlTags.erase(ctrlTag);
137}
138
140{
141 int idx = NControls()-1;
142 while (idx >= fromIdx)
143 {
144 IControl* pControl = GetControl(idx);
145
146 if(ControlIsCaptured(pControl))
148
149 if(pControl == mMouseOver)
150 ClearMouseOver();
151
152 if(pControl == mInTextEntry)
154
155 if(pControl == mInPopupMenu)
156 mInPopupMenu = nullptr;
157
158 if(pControl->GetTag() > kNoTag)
159 mCtrlTags.erase(pControl->GetTag());
160
161 mControls.Delete(idx--, true);
162 }
163
165}
166
168{
170}
171
173{
174 if(ControlIsCaptured(pControl))
176
177 if(pControl == mMouseOver)
178 ClearMouseOver();
179
180 if(pControl == mInTextEntry)
182
183 if(pControl == mInPopupMenu)
184 mInPopupMenu = nullptr;
185
186 if(pControl->GetTag() > kNoTag)
187 mCtrlTags.erase(pControl->GetTag());
188
189 mControls.DeletePtr(pControl, true);
190
192}
193
195{
197 ClearMouseOver();
198
199 mPopupControl = nullptr;
200 mTextEntryControl = nullptr;
201 mCornerResizer = nullptr;
202 mPerfDisplay = nullptr;
203
204#ifndef NDEBUG
205 mLiveEdit = nullptr;
206#endif
207
208 mBubbleControls.Empty(true);
209
210 mCtrlTags.clear();
211 mControls.Empty(true);
212}
213
214void IGraphics::SetControlPosition(IControl* pControl, float x, float y)
215{
216 pControl->SetPosition(x, y);
217 if (!pControl->IsHidden())
219}
220
221void IGraphics::SetControlSize(IControl* pControl, float w, float h)
222{
223 pControl->SetSize(w, h);
224 if (!pControl->IsHidden())
226}
227
229{
230 pControl->SetTargetAndDrawRECTs(r);
231 if (!pControl->IsHidden())
233}
234
236{
237 if (!mInTextEntry)
238 return;
239
240 const IParam* pParam = mTextEntryValIdx > kNoValIdx ? mInTextEntry->GetParam(mTextEntryValIdx) : nullptr;
241
242 if (pParam)
243 {
244 const double v = pParam->StringToValue(str);
245 mInTextEntry->SetValueFromUserInput(pParam->ToNormalized(v), mTextEntryValIdx);
246 }
247 else
248 {
249 mInTextEntry->OnTextEntryCompletion(str, mTextEntryValIdx);
250 }
251
253}
254
256{
257 if (!mInPopupMenu)
258 return;
259
260 if (mIsContextMenu)
261 mInPopupMenu->OnContextSelection(pMenu ? pMenu->GetChosenItemIdx() : -1);
262 else
263 mInPopupMenu->OnPopupMenuSelection(!pMenu || pMenu->GetChosenItemIdx() == -1 ? nullptr : pMenu, mPopupMenuValIdx);
264
265 int nVals = mInPopupMenu->NVals();
266
267 for (int v = 0; v < nVals; v++)
268 {
269 int paramIdx = mInPopupMenu->GetParamIdx(v);
270
271 if (paramIdx > kNoParameter)
272 {
274 }
275 }
276
277 mInPopupMenu = nullptr;
278}
279
281{
282 if (!mInPopupMenu)
283 return;
284
285 mInPopupMenu->OnDeleteFromPopupMenu(pMenu, itemIdx);
286}
287
288void IGraphics::AttachBackground(const char* fileName)
289{
290 IControl* pBG = new IBitmapControl(0, 0, LoadBitmap(fileName, 1, false), kNoParameter, EBlend::Default);
291 pBG->SetDelegate(*GetDelegate());
292 mControls.Insert(0, pBG);
293}
294
295void IGraphics::AttachSVGBackground(const char* fileName)
296{
297 IControl* pBG = new ISVGControl(GetBounds(), LoadSVG(fileName), true);
298 pBG->SetDelegate(*GetDelegate());
299 mControls.Insert(0, pBG);
300}
301
303{
304 IControl* pBG = new IPanelControl(GetBounds(), color);
305 pBG->SetDelegate(*GetDelegate());
306 mControls.Insert(0, pBG);
307}
308
309IControl* IGraphics::AttachControl(IControl* pControl, int ctrlTag, const char* group)
310{
311 if(ctrlTag > kNoTag)
312 {
313 auto result = mCtrlTags.insert(std::make_pair(ctrlTag, pControl));
314 assert(result.second && "AttachControl failed: ctrl tags must be unique");
315
316 if (!result.second)
317 return nullptr;
318 }
319
320 pControl->SetDelegate(*GetDelegate());
321 pControl->SetGroup(group);
322 mControls.Add(pControl);
323
324 pControl->OnAttached();
325 return pControl;
326}
327
328void IGraphics::AttachCornerResizer(EUIResizerMode sizeMode, bool layoutOnResize, const IColor& color, const IColor& mouseOverColor, const IColor& dragColor, float size)
329{
330 AttachCornerResizer(new ICornerResizerControl(GetBounds(), size, color, mouseOverColor, dragColor), sizeMode, layoutOnResize);
331}
332
333void IGraphics::AttachCornerResizer(ICornerResizerControl* pControl, EUIResizerMode sizeMode, bool layoutOnResize)
334{
335#ifndef AUv3_API
336 assert(!mCornerResizer); // only want one corner resizer
337
338 std::unique_ptr<ICornerResizerControl> control(pControl);
339
340 if (!mCornerResizer)
341 {
342 mCornerResizer.swap(control);
343 mGUISizeMode = sizeMode;
344 mLayoutOnResize = layoutOnResize;
345 mCornerResizer->SetDelegate(*GetDelegate());
346 }
347#else
348DBGMSG("AttachCornerResizer() is disabled for AUv3");
349#endif
350}
351
353{
354 IBubbleControl* pControl = new IBubbleControl(text);
355 AttachBubbleControl(pControl);
356}
357
359{
360 pControl->SetDelegate(*GetDelegate());
361 mBubbleControls.Add(pControl);
362}
363
364void IGraphics::AttachPopupMenuControl(const IText& text, const IRECT& bounds)
365{
366 if (!mPopupControl)
367 {
368 mPopupControl = std::make_unique<IPopupMenuControl>(kNoParameter, text, IRECT(), bounds);
369 mPopupControl->SetDelegate(*GetDelegate());
370 }
371}
372
374{
375 mPopupControl = nullptr;
376}
377
379{
380 if (!mTextEntryControl)
381 {
382 mTextEntryControl = std::make_unique<ITextEntryControl>();
383 mTextEntryControl->SetDelegate(*GetDelegate());
384 }
385}
386
388{
389 mTextEntryControl = nullptr;
390}
391
392void IGraphics::ShowBubbleControl(IControl* pCaller, float x, float y, const char* str, EDirection dir, IRECT minimumContentBounds)
393{
394 assert(mBubbleControls.GetSize() && "No bubble controls attached");
395
397 {
398 std::vector<ITouchID> touchIDsForCaller;
399 GetTouches(pCaller, touchIDsForCaller);
400 std::vector<IBubbleControl*> availableBubbleControls;
401 int nBubbleControls = mBubbleControls.GetSize();
402
403 if(touchIDsForCaller.size() == 1)
404 {
405 ITouchID touchID = touchIDsForCaller[0];
406
407 // first search to see if this touch matches existing bubble controls
408 for(int i=0;i<nBubbleControls;i++)
409 {
410 IBubbleControl* pBubbleControl = mBubbleControls.Get(i);
411 if(pBubbleControl->GetTouchID() == touchID)
412 {
413 pBubbleControl->ShowBubble(pCaller, x, y, str, dir, minimumContentBounds, touchID);
414 return;
415 }
416 else
417 availableBubbleControls.push_back(pBubbleControl);
418 }
419
420 if(availableBubbleControls.size())
421 {
422 // this works but why?
423 static int whichBubbleControl = 0;
424 availableBubbleControls[whichBubbleControl++]->ShowBubble(pCaller, x, y, str, dir, minimumContentBounds, touchID);
425 whichBubbleControl %= nBubbleControls;
426 }
427 }
428// else
429// {
430// assert(0 && "multi-touch controls with bubble controls not yet supported!");
431// }
432 }
433 else
434 mBubbleControls.Get(0)->ShowBubble(pCaller, x, y, str, dir, minimumContentBounds);
435}
436
438{
439 if (enable)
440 {
441 if (!mPerfDisplay)
442 {
443 mPerfDisplay = std::make_unique<IFPSDisplayControl>(GetBounds().GetPadded(-10).GetFromTLHC(200, 50));
444 mPerfDisplay->SetDelegate(*GetDelegate());
445 }
446 }
447 else
448 {
449 mPerfDisplay = nullptr;
450 ClearMouseOver();
451 }
452
454}
455
457{
458 const auto it = mCtrlTags.find(ctrlTag);
459
460 if (it != mCtrlTags.end())
461 {
462 return it->second;
463 }
464 else
465 {
466 assert("There is no control attached with this tag");
467 return nullptr;
468 }
469}
470
472{
473 for (auto c = 0; c < NControls(); c++)
474 {
475 IControl* pControl = GetControl(c);
476
477 if (pControl->LinkedToParam(paramIdx) > kNoValIdx)
478 {
479 return pControl;
480 }
481 }
482
483 return nullptr;
484}
485
486void IGraphics::HideControl(int paramIdx, bool hide)
487{
488 ForMatchingControls(&IControl::Hide, paramIdx, hide);
489}
490
491void IGraphics::DisableControl(int paramIdx, bool disable)
492{
493 ForMatchingControls(&IControl::SetDisabled, paramIdx, disable);
494}
495
496void IGraphics::ForControlWithParam(int paramIdx, IControlFunction func)
497{
498 for (auto c = 0; c < NControls(); c++)
499 {
500 IControl* pControl = GetControl(c);
501
502 if (pControl->LinkedToParam(paramIdx) > kNoValIdx)
503 {
504 func(pControl);
505 // Could be more than one, don't break until we check them all.
506 }
507 }
508}
509
510void IGraphics::ForControlWithParam(const std::initializer_list<int>& params, IControlFunction func)
511{
512 for (auto c = 0; c < NControls(); c++)
513 {
514 IControl* pControl = GetControl(c);
515
516 for (auto param : params)
517 {
518 if (pControl->LinkedToParam(param) > kNoValIdx)
519 {
520 func(pControl);
521 // Could be more than one, don't break until we check them all.
522 }
523 }
524 }
525}
526
527void IGraphics::ForControlInGroup(const char* group, IControlFunction func)
528{
529 for (auto c = 0; c < NControls(); c++)
530 {
531 IControl* pControl = GetControl(c);
532
533 if (CStringHasContents(pControl->GetGroup()))
534 {
535 if (strcmp(pControl->GetGroup(), group) == 0)
536 func(pControl);
537 // Could be more than one, don't break until we check them all.
538 }
539 }
540}
541
542void IGraphics::ForStandardControlsFunc(IControlFunction func)
543{
544 for (auto c = 0; c < NControls(); c++)
545 func(GetControl(c));
546}
547
548void IGraphics::ForAllControlsFunc(IControlFunction func)
549{
551
552 if (mPerfDisplay)
553 func(mPerfDisplay.get());
554
555#ifndef NDEBUG
556 if (mLiveEdit)
557 func(mLiveEdit.get());
558#endif
559
560 if (mCornerResizer)
561 func(mCornerResizer.get());
562
563 if (mTextEntryControl)
564 func(mTextEntryControl.get());
565
566 if (mPopupControl)
567 func(mPopupControl.get());
568
569 if (mBubbleControls.GetSize())
570 {
571 for(int i = 0;i<mBubbleControls.GetSize();i++)
572 {
573 func(mBubbleControls.Get(i));
574 }
575 }
576}
577
578template<typename T, typename... Args>
579void IGraphics::ForAllControls(T method, Args... args)
580{
581 ForAllControlsFunc([method, args...](IControl* pControl) { (pControl->*method)(args...); });
582}
583
584template<typename T, typename... Args>
585void IGraphics::ForMatchingControls(T method, int paramIdx, Args... args)
586{
587 ForControlWithParam(paramIdx, [method, args...](IControl* pControl) { (pControl->*method)(args...); });
588}
589
591{
593}
594
596{
597 ForAllControls(&IControl::SetClean);
598}
599
601{
602 auto func = [](IControl* pControl)
603 {
604 if (pControl->GetParamIdx() > kNoParameter)
605 pControl->SetTooltip(pControl->GetParam()->GetName());
606 };
607
609}
610
611void IGraphics::UpdatePeers(IControl* pCaller, int callerValIdx) // TODO: this could be really slow
612{
613 double value = pCaller->GetValue(callerValIdx);
614 int paramIdx = pCaller->GetParamIdx(callerValIdx);
615
616 auto func = [pCaller, paramIdx, value](IControl* pControl)
617 {
618 int valIdx = pControl->LinkedToParam(paramIdx);
619
620 // Not actually called from the delegate, but we don't want to push the updates back to the delegate
621 if ((valIdx > kNoValIdx) && (pControl != pCaller))
622 {
623 pControl->SetValueFromDelegate(value, valIdx);
624 }
625 };
626
628}
629
630void IGraphics::PromptUserInput(IControl& control, const IRECT& bounds, int valIdx)
631{
632 assert(valIdx > kNoValIdx);
633
634 const IParam* pParam = control.GetParam(valIdx);
635
636 if(pParam)
637 {
638 IParam::EParamType type = pParam->Type();
639 const int nDisplayTexts = pParam->NDisplayTexts();
640 WDL_String currentText;
641
642 if ( type == IParam::kTypeEnum || (type == IParam::kTypeBool && nDisplayTexts))
643 {
644 pParam->GetDisplay(currentText);
645 mPromptPopupMenu.Clear();
646
647 // Fill the menu
648 for (int i = 0; i < nDisplayTexts; ++i)
649 {
650 const char* str = pParam->GetDisplayText(i);
651 // TODO: what if two parameters have the same text?
652 if (!strcmp(str, currentText.Get())) // strings are equal
653 mPromptPopupMenu.AddItem( new IPopupMenu::Item(str, IPopupMenu::Item::kChecked), -1 );
654 else // not equal
655 mPromptPopupMenu.AddItem( new IPopupMenu::Item(str), -1 );
656
657 mPromptPopupMenu.SetRootTitle(pParam->GetName());
658 }
659
660 CreatePopupMenu(control, mPromptPopupMenu, bounds, valIdx);
661 }
662 // TODO: what if there are Int/Double Params with a display text e.g. -96db = "mute"
663 else // type == IParam::kTypeInt || type == IParam::kTypeDouble
664 {
665 pParam->GetDisplay(currentText, false);
666
667 if(control.GetPromptShowsParamLabel())
668 {
669 currentText.Append(" ");
670 currentText.Append(pParam->GetLabel());
671 }
672
673 CreateTextEntry(control, control.GetText(), bounds, currentText.Get(), valIdx);
674 }
675 }
676}
677
678void IGraphics::DrawText(const IText& text, const char* str, const IRECT& bounds, const IBlend* pBlend)
679{
680 if (!str || str[0] == '\0')
681 return;
682
683 DoDrawText(text, str, bounds, pBlend);
684}
685
686float IGraphics::MeasureText(const IText& text, const char* str, IRECT& bounds) const
687{
688 if (!str || str[0] == '\0')
689 return 0.f;
690
691 return DoMeasureText(text, str, bounds);
692}
693
694void IGraphics::DrawText(const IText& text, const char* str, float x, float y, const IBlend* pBlend)
695{
696 IRECT bounds = { x, y, x, y };
697 DrawText(text, str, bounds, pBlend);
698}
699
700void IGraphics::DrawBitmap(const IBitmap& bitmap, const IRECT& bounds, int bmpState, const IBlend* pBlend)
701{
702 int srcX = 0;
703 int srcY = 0;
704
705 bmpState = Clip(bmpState, 1, bitmap.N());
706
707 if (bitmap.N() > 1 && bmpState > 1)
708 {
709 if (bitmap.GetFramesAreHorizontal())
710 {
711 srcX = bitmap.W() * (bmpState - 1) / bitmap.N();
712 }
713 else
714 {
715 srcY = bitmap.H() * (bmpState - 1) / bitmap.N();
716 }
717 }
718
719 return DrawBitmap(bitmap, bounds, srcX, srcY, pBlend);
720}
721
722void IGraphics::DrawBitmapedText(const IBitmap& bitmap, const IRECT& bounds, IText& text, IBlend* pBlend, const char* str, bool vCenter, bool multiline, int charWidth, int charHeight, int charOffset)
723{
724 if (CStringHasContents(str))
725 {
726 int stringLength = (int) strlen(str);
727
728 float basicYOffset = 0.;
729 float basicXOffset = 0.;
730
731 if (vCenter)
732 basicYOffset = bounds.T + ((bounds.H() - charHeight) / 2.f);
733 else
734 basicYOffset = bounds.T;
735
736 if (text.mAlign == EAlign::Center)
737 basicXOffset = bounds.L + ((bounds.W() - (stringLength * charWidth)) / 2.f);
738 else if (text.mAlign == EAlign::Near)
739 basicXOffset = bounds.L;
740 else if (text.mAlign == EAlign::Far)
741 basicXOffset = bounds.R - (stringLength * charWidth);
742
743 int widthAsOneLine = charWidth * stringLength;
744
745 int nLines;
746 int stridx = 0;
747
748 int nCharsThatFitIntoLine;
749
750 if(multiline)
751 {
752 if (widthAsOneLine > bounds.W())
753 {
754 nCharsThatFitIntoLine = int(bounds.W() / (float)charWidth);
755 nLines = int(float(widthAsOneLine) / bounds.W()) + 1;
756 }
757 else // line is shorter than width of bounds
758 {
759 nCharsThatFitIntoLine = stringLength;
760 nLines = 1;
761 }
762 }
763 else
764 {
765 nCharsThatFitIntoLine = int(bounds.W() / (float) charWidth);
766 nLines = 1;
767 }
768
769 for (int line=0; line<nLines; line++)
770 {
771 float yOffset = basicYOffset + line * charHeight;
772
773 for (int linepos=0; linepos<nCharsThatFitIntoLine; linepos++)
774 {
775 if (str[stridx] == '\0') return;
776
777 int frameOffset = (int) str[stridx++] - 31; // calculate which frame to look up
778
779 float xOffset = ((float) linepos * ((float) charWidth + (float) charOffset)) + basicXOffset; // calculate xOffset for character we're drawing
780 IRECT charRect = IRECT(xOffset, yOffset, xOffset + charWidth, yOffset + charHeight);
781 DrawBitmap(bitmap, charRect, frameOffset, pBlend);
782 }
783 }
784 }
785}
786
787void IGraphics::DrawLineAcross(const IColor& color, const IRECT& bounds, EDirection dir, float pos, const IBlend* pBlend, float thickness)
788{
789 if (dir == EDirection::Horizontal)
790 DrawHorizontalLine(color, bounds, pos, pBlend, thickness);
791 else
792 DrawVerticalLine(color, bounds, pos, pBlend, thickness);
793}
794
795void IGraphics::DrawVerticalLine(const IColor& color, const IRECT& bounds, float x, const IBlend* pBlend, float thickness)
796{
797 x = Clip(x, 0.0f, 1.0f);
798 float xi = bounds.L + int(x * (bounds.R - bounds.L));
799 return DrawVerticalLine(color, xi, bounds.T, bounds.B, pBlend, thickness);
800}
801
802void IGraphics::DrawHorizontalLine(const IColor& color, const IRECT& bounds, float y, const IBlend* pBlend, float thickness)
803{
804 y = Clip(y, 0.0f, 1.0f);
805 float yi = bounds.B - (y * (float) (bounds.B - bounds.T));
806 return DrawHorizontalLine(color, yi, bounds.L, bounds.R, pBlend, thickness);
807}
808
809void IGraphics::DrawVerticalLine(const IColor& color, float xi, float yLo, float yHi, const IBlend* pBlend, float thickness)
810{
811 DrawLine(color, xi, yLo, xi, yHi, pBlend, thickness);
812}
813
814void IGraphics::DrawHorizontalLine(const IColor& color, float yi, float xLo, float xHi, const IBlend* pBlend, float thickness)
815{
816 DrawLine(color, xLo, yi, xHi, yi, pBlend, thickness);
817}
818
819void IGraphics::DrawRadialLine(const IColor& color, float cx, float cy, float angle, float rMin, float rMax, const IBlend* pBlend, float thickness)
820{
821 float data[2][2];
822 RadialPoints(angle, cx, cy, rMin, rMax, 2, data);
823 DrawLine(color, data[0][0], data[0][1], data[1][0], data[1][1], pBlend, thickness);
824}
825
826void IGraphics::PathRadialLine(float cx, float cy, float angle, float rMin, float rMax)
827{
828 float data[2][2];
829 RadialPoints(angle, cx, cy, rMin, rMax, 2, data);
830 PathLine(data[0][0], data[0][1], data[1][0], data[1][1]);
831}
832
834{
835 if (mDisplayTickFunc)
836 mDisplayTickFunc();
837
838 ForAllControlsFunc([](IControl* pControl) { pControl->Animate(); } );
839
840 bool dirty = false;
841
842 auto func = [&dirty, &rects](IControl* pControl) {
843 if (pControl->IsDirty())
844 {
845 // N.B padding outlines for single line outlines
846 auto rectToAdd = pControl->GetRECT().GetPadded(0.75);
847
848 if (pControl->GetParent())
849 {
850 rectToAdd.Clank(pControl->GetParent()->GetRECT().GetPadded(0.75));
851 }
852
853 rects.Add(rectToAdd);
854 dirty = true;
855 }
856 };
857
858 ForAllControlsFunc(func);
859
860#ifdef USE_IDLE_CALLS
861 if (dirty)
862 {
863 mIdleTicks = 0;
864 }
865 else if (++mIdleTicks > IDLE_TICKS)
866 {
867 OnGUIIdle();
868 mIdleTicks = 0;
869 }
870#endif
871
872 return dirty;
873}
874
876{
877 if(mPerfDisplay)
878 {
879 const double timestamp = GetTimestamp();
880 const double timeDiff = timestamp - mPrevTimestamp;
881 mPerfDisplay->Update((float) timeDiff);
882 mPrevTimestamp = timestamp;
883 }
884}
885
886// Draw a control in a region if it needs to be drawn
887void IGraphics::DrawControl(IControl* pControl, const IRECT& bounds, float scale)
888{
889 if (pControl && (!pControl->IsHidden() || pControl == GetControl(0)))
890 {
891 // N.B. Padding allows single line outlines on controls
892 IRECT controlBounds = pControl->GetRECT().GetPadded(0.75).GetPixelAligned(scale);
893 IRECT clipBounds = bounds.Intersect(controlBounds);
894
895 if (clipBounds.W() <= 0.0 || clipBounds.H() <= 0)
896 return;
897
898 IControl* pParent = pControl->GetParent();
899
900 while (pParent)
901 {
902 IRECT parentBounds = pParent->GetRECT().GetPadded(0.75).GetPixelAligned(scale);
903
904 if(!clipBounds.Intersects(parentBounds))
905 return;
906
907 clipBounds.Clank(parentBounds);
908
909 pParent = pParent->GetParent();
910 }
911
912 PrepareRegion(clipBounds);
913 pControl->Draw(*this);
914#ifdef AAX_API
915 pControl->DrawPTHighlight(*this);
916#endif
917
918#ifndef NDEBUG
919 if (mShowControlBounds)
920 {
921 PrepareRegion(clipBounds);
922 DrawRect(CONTROL_BOUNDS_COLOR, pControl->GetRECT());
923 }
924#endif
925
926 CompleteRegion(clipBounds);
927 }
928}
929
930void IGraphics::Draw(const IRECT& bounds, float scale)
931{
932 ForAllControlsFunc([this, bounds, scale](IControl* pControl) { DrawControl(pControl, bounds, scale); });
933
934#ifndef NDEBUG
935 if (mShowAreaDrawn)
936 {
937 PrepareRegion(bounds);
938 static IColor c;
939 c.Randomise(50);
940 FillRect(c, bounds);
941 CompleteRegion(bounds);
942 }
943#endif
944}
945
947{
948 if (!rects.Size())
949 return;
950
951 float scale = GetBackingPixelScale();
952
953 BeginFrame();
954
955 if (mStrict)
956 {
957 IRECT r = rects.Bounds();
958 r.PixelAlign(scale);
959 Draw(r, scale);
960 }
961 else
962 {
963 rects.PixelAlign(scale);
964 rects.Optimize();
965
966 for (auto i = 0; i < rects.Size(); i++)
967 Draw(rects.Get(i), scale);
968 }
969
970 EndFrame();
971}
972
974{
975 mStrict = strict;
977}
978
979void IGraphics::OnMouseDown(const std::vector<IMouseInfo>& points)
980{
981// Trace("IGraphics::OnMouseDown", __LINE__, "x:%0.2f, y:%0.2f, mod:LRSCA: %i%i%i%i%i", x, y, mod.L, mod.R, mod.S, mod.C, mod.A);
982
983 bool singlePoint = points.size() == 1;
984
985 if(singlePoint)
986 {
987 mMouseDownX = points[0].x;
988 mMouseDownY = points[0].y;
989 }
990
991 for (auto& point : points)
992 {
993 float x = point.x;
994 float y = point.y;
995 const IMouseMod& mod = point.ms;
996
997 IControl* pCapturedControl = GetMouseControl(x, y, true, false, mod.touchID);
998
999 if (pCapturedControl)
1000 {
1001 int nVals = pCapturedControl->NVals();
1002#if defined AAX_API || !defined IGRAPHICS_NO_CONTEXT_MENU
1003 int valIdx = pCapturedControl->GetValIdxForPos(x, y);
1004 int paramIdx = pCapturedControl->GetParamIdx((valIdx > kNoValIdx) ? valIdx : 0);
1005#endif
1006
1007#ifdef AAX_API
1008 if (mAAXViewContainer && paramIdx > kNoParameter)
1009 {
1010 auto GetAAXModifiersFromIMouseMod = [](const IMouseMod& mod) {
1011 uint32_t modifiers = 0;
1012
1013 if (mod.A) modifiers |= AAX_eModifiers_Option; // ALT Key on Windows, ALT/Option key on mac
1014
1015#ifdef OS_WIN
1016 if (mod.C) modifiers |= AAX_eModifiers_Command;
1017#else
1018 if (mod.C) modifiers |= AAX_eModifiers_Control;
1019 if (mod.R) modifiers |= AAX_eModifiers_Command;
1020#endif
1021 if (mod.S) modifiers |= AAX_eModifiers_Shift;
1022 if (mod.R) modifiers |= AAX_eModifiers_SecondaryButton;
1023
1024 return modifiers;
1025 };
1026
1027 uint32_t aaxModifiersForPT = GetAAXModifiersFromIMouseMod(mod);
1028#ifdef OS_WIN
1029 // required to get start/windows and alt keys
1030 uint32_t aaxModifiersFromPT = 0;
1031 mAAXViewContainer->GetModifiers(&aaxModifiersFromPT);
1032 aaxModifiersForPT |= aaxModifiersFromPT;
1033#endif
1034 WDL_String paramID;
1035 paramID.SetFormatted(32, "%i", paramIdx+1);
1036
1037 if (mAAXViewContainer->HandleParameterMouseDown(paramID.Get(), aaxModifiersForPT) == AAX_SUCCESS)
1038 {
1040 return; // event handled by PT
1041 }
1042 }
1043#endif
1044
1045#ifndef IGRAPHICS_NO_CONTEXT_MENU
1046 if (mod.R && paramIdx > kNoParameter)
1047 {
1049 PopupHostContextMenuForParam(pCapturedControl, paramIdx, x, y);
1050 return;
1051 }
1052#endif
1053
1054 for (int v = 0; v < nVals; v++)
1055 {
1056 if (pCapturedControl->GetParamIdx(v) > kNoParameter)
1058 }
1059
1060 pCapturedControl->OnMouseDown(x, y, mod);
1061 }
1062 }
1063}
1064
1065void IGraphics::OnMouseUp(const std::vector<IMouseInfo>& points)
1066{
1067// Trace("IGraphics::OnMouseUp", __LINE__, "x:%0.2f, y:%0.2f, mod:LRSCA: %i%i%i%i%i", x, y, mod.L, mod.R, mod.S, mod.C, mod.A);
1068
1069 if (ControlIsCaptured())
1070 {
1071 for (auto& point : points)
1072 {
1073 float x = point.x;
1074 float y = point.y;
1075 const IMouseMod& mod = point.ms;
1076 auto itr = mCapturedMap.find(mod.touchID);
1077
1078 if(itr != mCapturedMap.end())
1079 {
1080 IControl* pCapturedControl = itr->second;
1081
1082 pCapturedControl->OnMouseUp(x, y, mod);
1083
1084 int nVals = pCapturedControl->NVals();
1085
1086 for (int v = 0; v < nVals; v++)
1087 {
1088 if (pCapturedControl->GetParamIdx(v) > kNoParameter)
1090 }
1091
1092 mCapturedMap.erase(mod.touchID);
1093 }
1094 }
1095 }
1096
1097 if (mResizingInProcess)
1098 {
1099 EndDragResize();
1100 }
1101
1102 if (points.size() == 1 && !points[0].ms.IsTouch())
1103 OnMouseOver(points[0].x, points[0].y, points[0].ms);
1104}
1105
1106void IGraphics::OnTouchCancelled(const std::vector<IMouseInfo>& points)
1107{
1108 if (ControlIsCaptured())
1109 {
1110 //work out which of mCapturedMap controls the cancel relates to
1111 for (auto& point : points)
1112 {
1113 float x = point.x;
1114 float y = point.y;
1115 const IMouseMod& mod = point.ms;
1116
1117 auto itr = mCapturedMap.find(mod.touchID);
1118
1119 if(itr != mCapturedMap.end())
1120 {
1121 IControl* pCapturedControl = itr->second;
1122 pCapturedControl->OnTouchCancelled(x, y, mod);
1123 mCapturedMap.erase(mod.touchID); // remove from captured list
1124
1125 // DBGMSG("DEL - NCONTROLS captured = %lu\n", mCapturedMap.size());
1126 }
1127 }
1128 }
1129}
1130
1131bool IGraphics::OnMouseOver(float x, float y, const IMouseMod& mod)
1132{
1133 Trace("IGraphics::OnMouseOver", __LINE__, "x:%0.2f, y:%0.2f, mod:LRSCA: %i%i%i%i%i",
1134 x, y, mod.L, mod.R, mod.S, mod.C, mod.A);
1135
1136 // N.B. GetMouseControl handles which controls can receive mouseovers
1137 IControl* pControl = GetMouseControl(x, y, false, true);
1138
1139 if (pControl != mMouseOver)
1140 {
1141 if (mMouseOver)
1142 mMouseOver->OnMouseOut();
1143
1144 mMouseOver = pControl;
1145 }
1146
1147 if (mMouseOver)
1148 mMouseOver->OnMouseOver(x, y, mod);
1149
1150 return pControl;
1151}
1152
1154{
1155 Trace("IGraphics::OnMouseOut", __LINE__, "");
1156
1157 // Store the old cursor type so this gets restored when the mouse enters again
1158 mCursorType = SetMouseCursor(ECursor::ARROW);
1160 ClearMouseOver();
1161}
1162
1163void IGraphics::OnMouseDrag(const std::vector<IMouseInfo>& points)
1164{
1165 Trace("IGraphics::OnMouseDrag:", __LINE__, "x:%0.2f, y:%0.2f, dX:%0.2f, dY:%0.2f, mod:LRSCA: %i%i%i%i%i",
1166 points[0].x, points[0].y, points[0].dX, points[0].dY, points[0].ms.L, points[0].ms.R, points[0].ms.S, points[0].ms.C, points[0].ms.A);
1167
1168 if (mResizingInProcess && points.size() == 1)
1169 OnDragResize(points[0].x, points[0].y);
1171 {
1172 IControl *textEntry = nullptr;
1173
1175 textEntry = mTextEntryControl.get();
1176
1177 for (auto& point : points)
1178 {
1179 float x = point.x;
1180 float y = point.y;
1181 float dX = point.dX;
1182 float dY = point.dY;
1183 IMouseMod mod = point.ms;
1184
1185 auto itr = mCapturedMap.find(mod.touchID);
1186
1187 if (itr != mCapturedMap.end())
1188 {
1189 IControl* pCapturedControl = itr->second;
1190
1191 if (textEntry && pCapturedControl != textEntry)
1192 pCapturedControl = nullptr;
1193
1194 if (pCapturedControl && (dX != 0 || dY != 0))
1195 {
1196 pCapturedControl->OnMouseDrag(x, y, dX, dY, mod);
1197 }
1198 }
1199 }
1200 }
1201}
1202
1203bool IGraphics::OnMouseDblClick(float x, float y, const IMouseMod& mod)
1204{
1205 Trace("IGraphics::OnMouseDblClick", __LINE__, "x:%0.2f, y:%0.2f, mod:LRSCA: %i%i%i%i%i",
1206 x, y, mod.L, mod.R, mod.S, mod.C, mod.A);
1207
1208 IControl* pControl = GetMouseControl(x, y, true);
1209
1210 if (pControl)
1211 {
1212 if (pControl->GetMouseDblAsSingleClick())
1213 {
1214 IMouseInfo info;
1215 info.x = x;
1216 info.y = y;
1217 info.ms = mod;
1218 std::vector<IMouseInfo> list {info};
1219 OnMouseDown(list);
1220 }
1221 else
1222 {
1223 pControl->OnMouseDblClick(x, y, mod);
1225 }
1226 }
1227
1228 return pControl;
1229}
1230
1231bool IGraphics::OnMouseWheel(float x, float y, const IMouseMod& mod, float d)
1232{
1233 IControl* pControl = GetMouseControl(x, y, false);
1234
1235 if (pControl)
1236 pControl->OnMouseWheel(x, y, mod, d);
1237
1238 return pControl;
1239}
1240
1241bool IGraphics::OnKeyDown(float x, float y, const IKeyPress& key)
1242{
1243 Trace("IGraphics::OnKeyDown", __LINE__, "x:%0.2f, y:%0.2f, key:%s",
1244 x, y, key.utf8);
1245
1246 bool handled = false;
1247
1248 IControl* pControl = GetMouseControl(x, y, false);
1249
1250 if (pControl && pControl != GetControl(0))
1251 handled = pControl->OnKeyDown(x, y, key);
1252
1253 if(!handled)
1254 handled = mKeyHandlerFunc ? mKeyHandlerFunc(key, false) : false;
1255
1256 return handled;
1257}
1258
1259bool IGraphics::OnKeyUp(float x, float y, const IKeyPress& key)
1260{
1261 Trace("IGraphics::OnKeyUp", __LINE__, "x:%0.2f, y:%0.2f, key:%s",
1262 x, y, key.utf8);
1263
1264 bool handled = false;
1265
1266 IControl* pControl = GetMouseControl(x, y, false);
1267
1268 if (pControl && pControl != GetControl(0))
1269 handled = pControl->OnKeyUp(x, y, key);
1270
1271 if(!handled)
1272 handled = mKeyHandlerFunc ? mKeyHandlerFunc(key, true) : false;
1273
1274 return handled;
1275}
1276
1277void IGraphics::OnDrop(const char* str, float x, float y)
1278{
1279 IControl* pControl = GetMouseControl(x, y, false);
1280 if (pControl) pControl->OnDrop(str);
1281}
1282
1283void IGraphics::OnDropMultiple(const std::vector<const char*>& paths, float x, float y)
1284{
1285 IControl* pControl = GetMouseControl(x, y, false);
1286 if (pControl) pControl->OnDropMultiple(paths);
1287}
1288
1290{
1291 mCapturedMap.clear();
1292 if (mCursorHidden)
1293 HideMouseCursor(false);
1294}
1295
1296int IGraphics::GetMouseControlIdx(float x, float y, bool mouseOver)
1297{
1298 if (!mouseOver || mEnableMouseOver)
1299 {
1300 // Search from front to back
1301 for (auto c = NControls() - 1; c >= (mouseOver ? 1 : 0); --c)
1302 {
1303 IControl* pControl = GetControl(c);
1304
1305#ifndef NDEBUG
1306 if(!mLiveEdit)
1307 {
1308#endif
1309 if (!pControl->IsHidden() && !pControl->GetIgnoreMouse())
1310 {
1311 if ((!pControl->IsDisabled() || (mouseOver ? pControl->GetMouseOverWhenDisabled() : pControl->GetMouseEventsWhenDisabled())))
1312 {
1313 if (pControl->IsHit(x, y))
1314 {
1315 return c;
1316 }
1317 }
1318 }
1319#ifndef NDEBUG
1320 }
1321 else if (pControl->GetRECT().Contains(x, y) && pControl->GetParent() == nullptr)
1322 {
1323 return c;
1324 }
1325#endif
1326 }
1327 }
1328
1329 return -1;
1330}
1331
1332IControl* IGraphics::GetMouseControl(float x, float y, bool capture, bool mouseOver, ITouchID touchID)
1333{
1334 IControl* pControl = nullptr;
1335
1336 auto itr = mCapturedMap.find(touchID);
1337
1338 if(ControlIsCaptured() && itr != mCapturedMap.end())
1339 {
1340 pControl = itr->second;
1341
1342 if(pControl)
1343 return pControl;
1344 }
1345
1346 int controlIdx = -1;
1347
1348 if (!pControl && mPopupControl && mPopupControl->GetExpanded())
1349 pControl = mPopupControl.get();
1350
1351 if (!pControl && mTextEntryControl && mTextEntryControl->EditInProgress())
1352 pControl = mTextEntryControl.get();
1353
1354
1355#if !defined(NDEBUG)
1356 if (!pControl && mLiveEdit)
1357 pControl = mLiveEdit.get();
1358#endif
1359
1360 if (!pControl && mCornerResizer && mCornerResizer->GetRECT().Contains(x, y))
1361 pControl = mCornerResizer.get();
1362
1363 if (!pControl && mPerfDisplay && mPerfDisplay->GetRECT().Contains(x, y))
1364 pControl = mPerfDisplay.get();
1365
1366 if (!pControl)
1367 {
1368 controlIdx = GetMouseControlIdx(x, y, mouseOver);
1369 pControl = (controlIdx >= 0) ? GetControl(controlIdx) : nullptr;
1370 }
1371
1372 if (capture && pControl)
1373 {
1374 if(MultiTouchEnabled())
1375 {
1376 bool alreadyCaptured = ControlIsCaptured(pControl);
1377
1378 if (alreadyCaptured && !pControl->GetWantsMultiTouch())
1379 return nullptr;
1380 }
1381
1382 mCapturedMap.insert(std::make_pair(touchID, pControl));
1383
1384// DBGMSG("ADD - NCONTROLS captured = %lu\n", mCapturedMap.size());
1385 }
1386
1387 if (mouseOver)
1388 mMouseOverIdx = controlIdx;
1389
1390 return pControl;
1391}
1392
1394{
1395 IControl* pControl = GetMouseControl(x, y, false);
1396 int idx = mLastClickedParam = pControl ? pControl->GetParamIdx() : -1;
1397 return idx;
1398}
1399
1401{
1402 const int idx = mLastClickedParam;
1403 mLastClickedParam = kNoParameter;
1404 return idx;
1405}
1406
1407void IGraphics::SetPTParameterHighlight(int paramIdx, bool isHighlighted, int color)
1408{
1409 ForMatchingControls(&IControl::SetPTParameterHighlight, paramIdx, isHighlighted, color);
1410}
1411
1412void IGraphics::PopupHostContextMenuForParam(IControl* pControl, int paramIdx, float x, float y)
1413{
1414 IPopupMenu& contextMenu = mPromptPopupMenu;
1415 contextMenu.Clear();
1416
1417 if(pControl)
1418 {
1419 pControl->CreateContextMenu(contextMenu);
1420
1421#if defined VST3_API || defined VST3C_API
1422 VST3_API_BASE* pVST3 = dynamic_cast<VST3_API_BASE*>(GetDelegate());
1423
1424 if (!pVST3->GetComponentHandler() || !pVST3->GetView())
1425 return;
1426
1427 Steinberg::FUnknownPtr<Steinberg::Vst::IComponentHandler3>handler(pVST3->GetComponentHandler() );
1428
1429 if (handler == 0)
1430 return;
1431
1432 Steinberg::Vst::ParamID p = paramIdx;
1433
1434 Steinberg::Vst::IContextMenu* pVST3ContextMenu = handler->createContextMenu(pVST3->GetView(), &p);
1435
1436 if (pVST3ContextMenu)
1437 {
1438 std::function<void(IPopupMenu* pCurrentMenu)> populateFunc;
1439 Steinberg::int32 tag = 0;
1440
1441 populateFunc = [&populateFunc, &tag, pVST3ContextMenu, pControl](IPopupMenu* pCurrentMenu) {
1442 Steinberg::Vst::IContextMenu::Item item = {0};
1443
1444 for (int i = 0; i < pCurrentMenu->NItems(); i++)
1445 {
1446 Steinberg::UString128 (pCurrentMenu->GetItemText(i)).copyTo (item.name, 128);
1447 item.tag = tag++;
1448 item.flags = 0;
1449
1450 if (pCurrentMenu->GetItem(i)->GetIsSeparator())
1451 {
1452 item.flags = Steinberg::Vst::IContextMenu::Item::kIsSeparator;
1453 }
1454 else if (auto pSubMenu = pCurrentMenu->GetItem(i)->GetSubmenu())
1455 {
1456 item.flags = Steinberg::Vst::IContextMenu::Item::kIsGroupStart;
1457 pVST3ContextMenu->addItem(item, pControl);
1458 populateFunc(pSubMenu);
1459 item.tag = tag++;
1460 item.flags = Steinberg::Vst::IContextMenu::Item::kIsGroupEnd;
1461 pVST3ContextMenu->addItem(item, pControl);
1462 continue;
1463 }
1464 else
1465 {
1466 if (!pCurrentMenu->GetItem(i)->GetEnabled())
1467 item.flags |= Steinberg::Vst::IContextMenu::Item::kIsDisabled;
1468
1469 if (pCurrentMenu->GetItem(i)->GetChecked())
1470 item.flags |= Steinberg::Vst::IContextMenu::Item::kIsChecked;
1471 }
1472
1473 pVST3ContextMenu->addItem(item, pControl);
1474 }
1475 };
1476
1477 populateFunc(&contextMenu);
1478
1479#ifdef OS_WIN
1480 x *= GetTotalScale();
1481 y *= GetTotalScale();
1482#else
1483 x *= GetDrawScale();
1484 y *= GetDrawScale();
1485#endif
1486 pVST3ContextMenu->popup((Steinberg::UCoord) x, (Steinberg::UCoord) y);
1487 pVST3ContextMenu->release();
1488 }
1489
1490#else
1491 if(!contextMenu.NItems())
1492 return;
1493
1494 DoCreatePopupMenu(*pControl, contextMenu, IRECT(x, y, x, y), kNoValIdx, true);
1495#endif
1496 }
1497}
1498
1499void IGraphics::PopupHostContextMenuForParam(int controlIdx, int paramIdx, float x, float y)
1500{
1501 PopupHostContextMenuForParam(GetControl(controlIdx), paramIdx, x, y);
1502}
1503
1505{
1506 TRACE
1508}
1509
1510void IGraphics::OnDragResize(float x, float y)
1511{
1512 if(mGUISizeMode == EUIResizerMode::Scale)
1513 {
1514 float scaleX = (x * GetDrawScale()) / mMouseDownX;
1515 float scaleY = (y * GetDrawScale()) / mMouseDownY;
1516
1517 Resize(Width(), Height(), std::min(scaleX, scaleY));
1518 }
1519 else
1520 {
1521 Resize(static_cast<int>(x), static_cast<int>(y), GetDrawScale());
1522 }
1523}
1524
1525void IGraphics::OnAppearanceChanged(EUIAppearance appearance)
1526{
1527 if (mAppearanceChangedFunc)
1528 mAppearanceChangedFunc(appearance);
1529}
1530
1532{
1533 //TODO: bug with # frames!
1534// return LoadBitmap(src.GetResourceName().Get(), src.N(), src.GetFramesAreHorizontal(), (GetRoundedScreenScale() == 1 && GetDrawScale() > 1.) ? 2 : 0 /* ??? */);
1535 return LoadBitmap(src.GetResourceName().Get(), src.N(), src.GetFramesAreHorizontal(), GetRoundedScreenScale());
1536}
1537
1539{
1540 mEnableTooltips = enable;
1541 if (enable) mEnableMouseOver = true;
1542}
1543
1545{
1546#ifndef NDEBUG
1547 if (enable)
1548 {
1549 if (!mLiveEdit)
1550 {
1551 mLiveEdit = std::make_unique<IGraphicsLiveEdit>(mEnableMouseOver);
1552 mLiveEdit->SetDelegate(*GetDelegate());
1553 }
1554 }
1555 else
1556 {
1557 mLiveEdit = nullptr;
1558 }
1559
1560 ClearMouseOver();
1562 SetMouseCursor(ECursor::ARROW);
1564#endif
1565}
1566
1567// Skia has its own implementation for SVGs. On all other platforms we use NanoSVG, because it works.
1568#ifdef SVG_USE_SKIA
1569ISVG IGraphics::LoadSVG(const char* fileName, const char* units, float dpi)
1570{
1571 StaticStorage<SVGHolder>::Accessor storage(sSVGCache);
1572 SVGHolder* pHolder = storage.Find(fileName);
1573
1574 if(!pHolder)
1575 {
1576 WDL_TypedBuf<uint8_t> svgData = LoadResource(fileName, "svg");
1577 if (svgData.GetSize() == 0)
1578 {
1579 return ISVG(nullptr);
1580 }
1581 else
1582 {
1583 return LoadSVG(fileName, svgData.Get(), svgData.GetSize(), units, dpi);
1584 }
1585 }
1586
1587 return ISVG(pHolder->mSVGDom);
1588}
1589
1590ISVG IGraphics::LoadSVG(const char* name, const void* pData, int dataSize, const char* units, float dpi)
1591{
1592 StaticStorage<SVGHolder>::Accessor storage(sSVGCache);
1593 SVGHolder* pHolder = storage.Find(name);
1594
1595 if (!pHolder)
1596 {
1597 sk_sp<SkSVGDOM> svgDOM;
1598 SkDOM xmlDom;
1599
1600 SkMemoryStream svgStream(pData, dataSize);
1601 svgDOM = SkSVGDOM::MakeFromStream(svgStream);
1602
1603 if (!svgDOM)
1604 return ISVG(nullptr); // return invalid SVG
1605
1606 // If an SVG doesn't have a container size, SKIA doesn't seem to have access to any meaningful size info.
1607 // So use NanoSVG to get the size.
1608 if (svgDOM->containerSize().width() == 0)
1609 {
1610 NSVGimage* pImage = nullptr;
1611
1612 WDL_String svgStr;
1613 svgStr.Set((const char*)pData, dataSize);
1614 pImage = nsvgParse(svgStr.Get(), units, dpi);
1615
1616 assert(pImage);
1617
1618 svgDOM->setContainerSize(SkSize::Make(pImage->width, pImage->height));
1619
1620 nsvgDelete(pImage);
1621 }
1622
1623 pHolder = new SVGHolder(svgDOM);
1624 storage.Add(pHolder, name);
1625 }
1626
1627 return ISVG(pHolder->mSVGDom);
1628}
1629
1630#else
1631ISVG IGraphics::LoadSVG(const char* fileName, const char* units, float dpi)
1632{
1633 StaticStorage<SVGHolder>::Accessor storage(sSVGCache);
1634 SVGHolder* pHolder = storage.Find(fileName);
1635
1636 if(!pHolder)
1637 {
1638 WDL_TypedBuf<uint8_t> svgData = LoadResource(fileName, "svg");
1639 if (svgData.GetSize() == 0)
1640 {
1641 return ISVG(nullptr);
1642 }
1643 else
1644 {
1645 return LoadSVG(fileName, svgData.Get(), svgData.GetSize(), units, dpi);
1646 }
1647 }
1648
1649 return ISVG(pHolder->mImage);
1650}
1651
1652ISVG IGraphics::LoadSVG(const char* name, const void* pData, int dataSize, const char* units, float dpi)
1653{
1654 StaticStorage<SVGHolder>::Accessor storage(sSVGCache);
1655 SVGHolder* pHolder = storage.Find(name);
1656
1657 if (!pHolder)
1658 {
1659 NSVGimage* pImage = nullptr;
1660
1661 WDL_String svgStr;
1662 svgStr.Set(reinterpret_cast<const char*>(pData), dataSize);
1663 pImage = nsvgParse(svgStr.Get(), units, dpi);
1664
1665 if (!pImage)
1666 return ISVG(nullptr);
1667
1668 pHolder = new SVGHolder(pImage);
1669
1670 storage.Add(pHolder, name);
1671 }
1672
1673 return ISVG(pHolder->mImage);
1674}
1675#endif
1676
1677WDL_TypedBuf<uint8_t> IGraphics::LoadResource(const char* fileNameOrResID, const char* fileType)
1678{
1679 WDL_TypedBuf<uint8_t> result;
1680
1681 WDL_String path;
1682 EResourceLocation resourceFound = LocateResource(fileNameOrResID, fileType, path, GetBundleID(), GetWinModuleHandle(), GetSharedResourcesSubPath());
1683
1684 if (resourceFound == EResourceLocation::kNotFound)
1685 return result;
1686
1687#ifdef OS_WIN
1688 if (resourceFound == EResourceLocation::kWinBinary)
1689 {
1690 int size = 0;
1691 const void* pResData = LoadWinResource(path.Get(), fileType, size, GetWinModuleHandle());
1692 result.Resize(size);
1693 result.Set((const uint8_t*)pResData, size);
1694 }
1695#endif
1696 if (resourceFound == EResourceLocation::kAbsolutePath)
1697 {
1698 FILE* fd = fopenUTF8(path.Get(), "rb");
1699
1700 if (!fd)
1701 return result;
1702
1703 // First we determine the file size
1704 if (fseek(fd, 0, SEEK_END))
1705 {
1706 fclose(fd);
1707 return result;
1708 }
1709 long size = ftell(fd);
1710
1711 // Now reset to the start of the file so we can actually read it.
1712 if (fseek(fd, 0, SEEK_SET))
1713 {
1714 fclose(fd);
1715 return result;
1716 }
1717
1718 result.Resize((int)size);
1719 size_t bytesRead = fread(result.Get(), 1, (size_t)size, fd);
1720 if (bytesRead != (size_t)size)
1721 {
1722 fclose(fd);
1723 result.Resize(0, true);
1724 return result;
1725 }
1726 fclose(fd);
1727 }
1728
1729 return result;
1730}
1731
1732IBitmap IGraphics::LoadBitmap(const char* name, int nStates, bool framesAreHorizontal, int targetScale)
1733{
1734 if (targetScale == 0)
1735 targetScale = GetRoundedScreenScale();
1736
1737 StaticStorage<APIBitmap>::Accessor storage(sBitmapCache);
1738 APIBitmap* pAPIBitmap = storage.Find(name, targetScale);
1739
1740 // If the bitmap is not already cached at the targetScale
1741 if (!pAPIBitmap)
1742 {
1743 WDL_String fullPath;
1744 std::unique_ptr<APIBitmap> loadedBitmap;
1745 int sourceScale = 0;
1746
1747 const char* ext = name + strlen(name) - 1;
1748 while (ext >= name && *ext != '.') --ext;
1749 ++ext;
1750
1751 bool bitmapTypeSupported = BitmapExtSupported(ext);
1752
1753 if (!bitmapTypeSupported)
1754 return IBitmap(); // return invalid IBitmap
1755
1756 EResourceLocation resourceLocation = SearchImageResource(name, ext, fullPath, targetScale, sourceScale);
1757
1758 if (resourceLocation == EResourceLocation::kNotFound)
1759 {
1760 // If no resource exists then search the cache for a suitable match
1761 pAPIBitmap = SearchBitmapInCache(name, targetScale, sourceScale);
1762 }
1763 else
1764 {
1765 // Try in the cache for a mismatched bitmap
1766 if (sourceScale != targetScale)
1767 pAPIBitmap = storage.Find(name, sourceScale);
1768
1769 // Load the resource if no match found
1770 if (!pAPIBitmap)
1771 {
1772 loadedBitmap = std::unique_ptr<APIBitmap>(LoadAPIBitmap(fullPath.Get(), sourceScale, resourceLocation, ext));
1773 pAPIBitmap= loadedBitmap.get();
1774 }
1775 }
1776
1777 // Protection from searching for non-existent bitmaps (e.g. typos in config.h or .rc)
1778 assert(pAPIBitmap && "Bitmap not found");
1779
1780 // Scale or retain if needed (N.B. - scaling retains in the cache)
1781 if (pAPIBitmap->GetScale() != targetScale)
1782 {
1783 return ScaleBitmap(IBitmap(pAPIBitmap, nStates, framesAreHorizontal, name), name, targetScale);
1784 }
1785 else if (loadedBitmap)
1786 {
1787 RetainBitmap(IBitmap(loadedBitmap.release(), nStates, framesAreHorizontal, name), name);
1788 }
1789 }
1790
1791 return IBitmap(pAPIBitmap, nStates, framesAreHorizontal, name);
1792}
1793
1794IBitmap IGraphics::LoadBitmap(const char *name, const void *pData, int dataSize, int nStates, bool framesAreHorizontal, int targetScale)
1795{
1796 if (targetScale == 0)
1797 targetScale = GetRoundedScreenScale();
1798
1799 StaticStorage<APIBitmap>::Accessor storage(sBitmapCache);
1800 APIBitmap* pAPIBitmap = storage.Find(name, targetScale);
1801
1802 // If the bitmap is not already cached at the targetScale
1803 if (!pAPIBitmap)
1804 {
1805 WDL_String fullPath;
1806 std::unique_ptr<APIBitmap> loadedBitmap;
1807 int sourceScale = 0;
1808
1809 const char* ext = name + strlen(name) - 1;
1810 while (ext >= name && *ext != '.') --ext;
1811 ++ext;
1812
1813 bool bitmapTypeSupported = BitmapExtSupported(ext);
1814
1815 if (!bitmapTypeSupported)
1816 return IBitmap(); // return invalid IBitmap
1817
1818 // Seach the cache for an existing copy, maybe with a different scale
1819 pAPIBitmap = SearchBitmapInCache(name, targetScale, sourceScale);
1820 // It's definitely not loaded, so load it with scale = 1.
1821 if (!pAPIBitmap)
1822 {
1823 loadedBitmap = std::unique_ptr<APIBitmap>(LoadAPIBitmap(name, pData, dataSize, 1));
1824 pAPIBitmap= loadedBitmap.get();
1825 }
1826
1827 // Protection from searching for non-existent bitmaps (e.g. typos in config.h or .rc)
1828 // Also protects from invalid bitmap data.
1829 assert(pAPIBitmap && "Bitmap not found");
1830
1831 // Scale or retain if needed (N.B. - scaling retains in the cache)
1832 if (pAPIBitmap->GetScale() != targetScale)
1833 {
1834 return ScaleBitmap(IBitmap(pAPIBitmap, nStates, framesAreHorizontal, name), name, targetScale);
1835 }
1836 else if (loadedBitmap)
1837 {
1838 RetainBitmap(IBitmap(loadedBitmap.release(), nStates, framesAreHorizontal, name), name);
1839 }
1840 }
1841
1842 return IBitmap(pAPIBitmap, nStates, framesAreHorizontal, name);
1843}
1844
1846{
1847 StaticStorage<APIBitmap>::Accessor storage(sBitmapCache);
1848 storage.Remove(bitmap.GetAPIBitmap());
1849}
1850
1851void IGraphics::RetainBitmap(const IBitmap& bitmap, const char* cacheName)
1852{
1853 StaticStorage<APIBitmap>::Accessor storage(sBitmapCache);
1854 storage.Add(bitmap.GetAPIBitmap(), cacheName, bitmap.GetScale());
1855}
1856
1857IBitmap IGraphics::ScaleBitmap(const IBitmap& inBitmap, const char* name, int scale)
1858{
1859 int screenScale = GetRoundedScreenScale();
1860 float drawScale = GetDrawScale();
1861
1862 mScreenScale = scale;
1863 mDrawScale = inBitmap.GetDrawScale();
1864
1865 IRECT bounds = IRECT(0, 0, inBitmap.W() / inBitmap.GetDrawScale(), inBitmap.H() / inBitmap.GetDrawScale());
1866 StartLayer(nullptr, bounds, true);
1867 DrawBitmap(inBitmap, bounds, 0, 0, nullptr);
1868 ILayerPtr layer = EndLayer();
1869 IBitmap outBitmap = IBitmap(layer->mBitmap.release(), inBitmap.N(), inBitmap.GetFramesAreHorizontal(), name);
1870 RetainBitmap(outBitmap, name);
1871
1872 mScreenScale = screenScale;
1873 mDrawScale = drawScale;
1874
1875 return outBitmap;
1876}
1877
1878auto SearchNextScale = [](int& sourceScale, int targetScale) {
1879 if (sourceScale == targetScale && (targetScale != MAX_IMG_SCALE))
1880 sourceScale = MAX_IMG_SCALE;
1881 else if (sourceScale == targetScale + 1)
1882 sourceScale = targetScale - 1;
1883 else
1884 sourceScale--;
1885};
1886
1887EResourceLocation IGraphics::SearchImageResource(const char* name, const char* type, WDL_String& result, int targetScale, int& sourceScale)
1888{
1889 for (sourceScale = targetScale ; sourceScale > 0; SearchNextScale(sourceScale, targetScale))
1890 {
1891 WDL_String fullName(name);
1892
1893 if (sourceScale != 1)
1894 {
1895 WDL_String baseName(name); baseName.remove_fileext();
1896 WDL_String ext(fullName.get_fileext());
1897 fullName.SetFormatted((int) (strlen(name) + strlen("@2x")), "%s@%dx%s", baseName.Get(), sourceScale, ext.Get());
1898 }
1899
1900 EResourceLocation found = LocateResource(fullName.Get(), type, result, GetBundleID(), GetWinModuleHandle(), GetSharedResourcesSubPath());
1901
1902 if (found > EResourceLocation::kNotFound)
1903 return found;
1904 }
1905
1906 return EResourceLocation::kNotFound;
1907}
1908
1909APIBitmap* IGraphics::SearchBitmapInCache(const char* name, int targetScale, int& sourceScale)
1910{
1911 StaticStorage<APIBitmap>::Accessor storage(sBitmapCache);
1912
1913 for (sourceScale = targetScale; sourceScale > 0; SearchNextScale(sourceScale, targetScale))
1914 {
1915 APIBitmap* pBitmap = storage.Find(name, sourceScale);
1916
1917 if (pBitmap)
1918 return pBitmap;
1919 }
1920
1921 return nullptr;
1922}
1923
1925{
1926 for (auto c = 0; c < NControls(); c++)
1927 {
1928 IVectorBase* pVB = dynamic_cast<IVectorBase*>(GetControl(c));
1929 if (pVB)
1930 pVB->SetStyle(style);
1931 }
1932}
1933
1934void IGraphics::CreateTextEntry(IControl& control, const IText& text, const IRECT& bounds, const char* str, int valIdx)
1935{
1936 mInTextEntry = &control;
1937 mTextEntryValIdx = valIdx;
1938
1939 int paramIdx = valIdx > kNoValIdx ? control.GetParamIdx(valIdx) : kNoParameter;
1940
1941 if (mTextEntryControl)
1942 mTextEntryControl->CreateTextEntry(paramIdx, text, bounds, control.GetTextEntryLength(), str);
1943 else
1944 CreatePlatformTextEntry(paramIdx, text, bounds, control.GetTextEntryLength(), str);
1945
1946 mInTextEntry->SetDirty(false);
1947}
1948
1949void IGraphics::DoCreatePopupMenu(IControl& control, IPopupMenu& menu, const IRECT& bounds, int valIdx, bool isContext)
1950{
1952
1953 mInPopupMenu = &control;
1954 mPopupMenuValIdx = valIdx;
1955 mIsContextMenu = isContext;
1956
1957 if (mPopupControl) // if we are not using platform pop-up menus
1958 {
1959 mPopupControl->CreatePopupMenu(menu, bounds);
1960 }
1961 else
1962 {
1963 bool isAsync = false;
1964 IPopupMenu* pReturnMenu = CreatePlatformPopupMenu(menu, bounds, isAsync);
1965
1966 if (!isAsync)
1967 SetControlValueAfterPopupMenu(pReturnMenu);
1968 }
1969}
1970
1971void IGraphics::CreatePopupMenu(IControl& control, IPopupMenu& menu, const IRECT& bounds, int valIdx)
1972{
1973 DoCreatePopupMenu(control, menu, bounds, valIdx, false);
1974}
1975
1976void IGraphics::EndDragResize()
1977{
1978 mResizingInProcess = false;
1979
1980 if (GetResizerMode() == EUIResizerMode::Scale)
1981 {
1982 // If scaling up we may want to load in high DPI bitmaps if scale > 1.
1985 }
1986}
1987
1988void IGraphics::StartLayer(IControl* pControl, const IRECT& r, bool cacheable)
1989{
1990 auto pixelBackingScale = GetBackingPixelScale();
1991 IRECT alignedBounds = r.GetPixelAligned(pixelBackingScale);
1992 const int w = static_cast<int>(std::ceil(pixelBackingScale * std::ceil(alignedBounds.W())));
1993 const int h = static_cast<int>(std::ceil(pixelBackingScale * std::ceil(alignedBounds.H())));
1994
1995 PushLayer(new ILayer(CreateAPIBitmap(w, h, GetScreenScale(), GetDrawScale(), cacheable), alignedBounds, pControl, pControl ? pControl->GetRECT() : IRECT()));
1996}
1997
1999{
2000 ILayerPtr ownedLayer;
2001
2002 ownedLayer.swap(layer);
2003 ILayer* pOwnerlessLayer = ownedLayer.release();
2004
2005 if (pOwnerlessLayer)
2006 {
2007 PushLayer(pOwnerlessLayer);
2008 }
2009}
2010
2012{
2013 return ILayerPtr(PopLayer());
2014}
2015
2017{
2018 mLayers.push(pLayer);
2019 UpdateLayer();
2021 PathClipRegion(pLayer->Bounds());
2022 PathClear();
2023}
2024
2026{
2027 ILayer* pLayer = nullptr;
2028
2029 if (!mLayers.empty())
2030 {
2031 pLayer = mLayers.top();
2032 mLayers.pop();
2033 }
2034
2035 UpdateLayer();
2038 PathClear();
2039
2040 return pLayer;
2041}
2042
2044{
2045 const APIBitmap* pBitmap = layer ? layer->GetAPIBitmap() : nullptr;
2046
2047 if (pBitmap && layer->mControl && layer->mControlRECT != layer->mControl->GetRECT())
2048 {
2049 layer->mControlRECT = layer->mControl->GetRECT();
2050 layer->Invalidate();
2051 }
2052
2053 return pBitmap && !layer->mInvalid && pBitmap->GetDrawScale() == GetDrawScale() && pBitmap->GetScale() == GetScreenScale();
2054}
2055
2056void IGraphics::DrawLayer(const ILayerPtr& layer, const IBlend* pBlend)
2057{
2060 DrawBitmap(layer->GetBitmap(), layer->Bounds(), 0, 0, pBlend);
2062}
2063
2064void IGraphics::DrawFittedLayer(const ILayerPtr& layer, const IRECT& bounds, const IBlend* pBlend)
2065{
2066 IBitmap bitmap = layer->GetBitmap();
2067 IRECT layerBounds = layer->Bounds();
2069 PathTransformTranslate(bounds.L, bounds.T);
2070 IRECT newBounds(0., 0., layerBounds.W(), layerBounds.H());
2071 PathTransformScale(bounds.W() / layerBounds.W(), bounds.H() / layerBounds.H());
2072 DrawBitmap(bitmap, newBounds, 0, 0, pBlend);
2074}
2075
2076void IGraphics::DrawRotatedLayer(const ILayerPtr& layer, double angle)
2077{
2080 IBitmap bitmap = layer->GetBitmap();
2081 IRECT bounds = layer->Bounds();
2082 DrawRotatedBitmap(bitmap, bounds.MW(), bounds.MH(), angle);
2084}
2085
2087{
2088 auto GaussianBlurSwap = [](uint8_t* out, uint8_t* in, uint8_t* kernel, int width, int height,
2089 int outStride, int inStride, int kernelSize, uint32_t norm)
2090 {
2091 int repeats = 0;
2092 int fullKernelSize = kernelSize * 2 + 1;
2093 uint32_t last = 0;
2094
2095 auto RepeatCheck = [&](int idx)
2096 {
2097 repeats = last == in[idx * 4] ? std::min(repeats + 1, fullKernelSize) : 1;
2098 last = in[idx * 4];
2099
2100 return repeats == fullKernelSize;
2101 };
2102
2103 for (int i = 0; i < height; i++, in += inStride)
2104 {
2105 for (int j = 0; j < kernelSize - 1; j++)
2106 {
2107 uint32_t accum = in[j * 4] * kernel[0];
2108 for (int k = 1; k < j + 1; k++)
2109 accum += kernel[k] * in[(j - k) * 4];
2110 for (int k = 1; k < kernelSize; k++)
2111 accum += kernel[k] * in[(j + k) * 4];
2112 out[j * outStride + (i * 4)] = static_cast<uint8_t>(std::min(static_cast<uint32_t>(255), accum / norm));
2113 }
2114 for (int j = 0; j < kernelSize * 2 - 2; j++)
2115 RepeatCheck(j);
2116 for (int j = kernelSize - 1; j < (width - kernelSize) + 1; j++)
2117 {
2118 if (RepeatCheck(j + kernelSize - 1))
2119 {
2120 out[j * outStride + (i * 4)] = static_cast<uint8_t>(last);
2121 continue;
2122 }
2123
2124 uint32_t accum = in[j * 4] * kernel[0];
2125 for (int k = 1; k < kernelSize; k++)
2126 accum += kernel[k] * (in[(j - k) * 4] + in[(j + k) * 4]);
2127 out[j * outStride + (i * 4)] = static_cast<uint8_t>(std::min(static_cast<uint32_t>(255), accum / norm));
2128 }
2129 for (int j = (width - kernelSize) + 1; j < width; j++)
2130 {
2131 uint32_t accum = in[j * 4] * kernel[0];
2132 for (int k = 1; k < kernelSize; k++)
2133 accum += kernel[k] * in[(j - k) * 4];
2134 for (int k = 1; k < width - j; k++)
2135 accum += kernel[k] * in[(j + k) * 4];
2136 out[j * outStride + (i * 4)] = static_cast<uint8_t>(std::min(static_cast<uint32_t>(255), accum / norm));
2137 }
2138 }
2139 };
2140
2141 RawBitmapData temp1;
2142 RawBitmapData temp2;
2143 RawBitmapData kernel;
2144
2145 // Get bitmap in 32-bit form
2146 GetLayerBitmapData(layer, temp1);
2147
2148 if (!temp1.GetSize())
2149 return;
2150 temp2.Resize(temp1.GetSize());
2151
2152 // Form kernel (reference blurSize from zero (which will be no blur))
2153 bool flipped = FlippedBitmap();
2154 float scale = layer->GetAPIBitmap()->GetScale() * layer->GetAPIBitmap()->GetDrawScale();
2155 float blurSize = std::max(1.f, (shadow.mBlurSize * scale) + 1.f);
2156 float blurConst = 4.5f / (blurSize * blurSize);
2157 int iSize = static_cast<int>(ceil(blurSize));
2158 int width = layer->GetAPIBitmap()->GetWidth();
2159 int height = layer->GetAPIBitmap()->GetHeight();
2160 int stride1 = temp1.GetSize() / width;
2161 int stride2 = flipped ? -temp1.GetSize() / height : temp1.GetSize() / height;
2162 int stride3 = flipped ? -stride2 : stride2;
2163
2164 kernel.Resize(iSize);
2165
2166 for (int i = 0; i < iSize; i++)
2167 kernel.Get()[i] = static_cast<uint8_t>(std::round(255.f * std::expf(-(i * i) * blurConst)));
2168
2169 // Kernel normalisation
2170 int normFactor = kernel.Get()[0];
2171
2172 for (int i = 1; i < iSize; i++)
2173 normFactor += kernel.Get()[i] + kernel.Get()[i];
2174
2175 // Do blur
2176 uint8_t* asRows = temp1.Get() + AlphaChannel();
2177 uint8_t* inRows = flipped ? asRows + stride3 * (height - 1) : asRows;
2178 uint8_t* asCols = temp2.Get() + AlphaChannel();
2179
2180 GaussianBlurSwap(asCols, inRows, kernel.Get(), width, height, stride1, stride2, iSize, normFactor);
2181 GaussianBlurSwap(asRows, asCols, kernel.Get(), height, width, stride3, stride1, iSize, normFactor);
2182
2183 // Apply alphas to the pattern and recombine/replace the image
2184 ApplyShadowMask(layer, temp1, shadow);
2185}
2186
2187bool IGraphics::LoadFont(const char* fontID, const char* fileNameOrResID)
2188{
2189 PlatformFontPtr font = LoadPlatformFont(fontID, fileNameOrResID);
2190
2191 if (font)
2192 {
2193 if (LoadAPIFont(fontID, font))
2194 {
2195 CachePlatformFont(fontID, font);
2196 return true;
2197 }
2198 }
2199
2200 DBGMSG("Could not locate font %s\n", fileNameOrResID);
2201 return false;
2202}
2203
2204bool IGraphics::LoadFont(const char* fontID, void* pData, int dataSize)
2205{
2206 PlatformFontPtr font = LoadPlatformFont(fontID, pData, dataSize);
2207
2208 if (font)
2209 {
2210 if (LoadAPIFont(fontID, font))
2211 {
2212 CachePlatformFont(fontID, font);
2213 return true;
2214 }
2215 }
2216
2217 DBGMSG("Could not load font %s\n", fontID);
2218 return false;
2219}
2220
2221bool IGraphics::LoadFont(const char* fontID, const char* fontName, ETextStyle style)
2222{
2223 PlatformFontPtr font = LoadPlatformFont(fontID, fontName, style);
2224
2225 if (font)
2226 {
2227 if (LoadAPIFont(fontID, font))
2228 {
2229 CachePlatformFont(fontID, font);
2230 return true;
2231 }
2232 }
2233
2234 DBGMSG("Could not locate font %s\n", fontID);
2235 return false;
2236}
2237
2238void IGraphics::DoMeasureTextRotation(const IText& text, const IRECT& bounds, IRECT& rect) const
2239{
2240 double tx = 0.0, ty = 0.0;
2241
2242 CalculateTextRotation(text, bounds, rect, tx, ty);
2243 rect.Translate(static_cast<float>(tx), static_cast<float>(ty));
2244}
2245
2246void IGraphics::CalculateTextRotation(const IText& text, const IRECT& bounds, IRECT& rect, double& tx, double& ty) const
2247{
2248 if (!text.mAngle)
2249 return;
2250
2251 IMatrix m = IMatrix().Rotate(text.mAngle);
2252
2253 double x0 = rect.L;
2254 double y0 = rect.T;
2255 double x1 = rect.R;
2256 double y1 = rect.T;
2257 double x2 = rect.R;
2258 double y2 = rect.B;
2259 double x3 = rect.L;
2260 double y3 = rect.B;
2261
2262 m.TransformPoint(x0, y0);
2263 m.TransformPoint(x1, y1);
2264 m.TransformPoint(x2, y2);
2265 m.TransformPoint(x3, y3);
2266
2267 IRECT r1(static_cast<float>(std::min(x0, x3)), static_cast<float>(std::min(y0, y3)), static_cast<float>(std::max(x0, x3)), static_cast<float>(std::max(y0, y3)));
2268 IRECT r2(static_cast<float>(std::min(x1, x2)), static_cast<float>(std::min(y1, y2)), static_cast<float>(std::max(x1, x2)), static_cast<float>(std::max(y1, y2)));
2269 rect = r1.Union(r2);
2270
2271 switch (text.mAlign)
2272 {
2273 case EAlign::Near: tx = bounds.L - rect.L; break;
2274 case EAlign::Center: tx = bounds.MW() - rect.MW(); break;
2275 case EAlign::Far: tx = bounds.R - rect.R; break;
2276 }
2277
2278 switch (text.mVAlign)
2279 {
2280 case EVAlign::Top: ty = bounds.T - rect.T; break;
2281 case EVAlign::Middle: ty = bounds.MH() - rect.MH(); break;
2282 case EVAlign::Bottom: ty = bounds.B - rect.B; break;
2283 }
2284}
2285
2286void IGraphics::SetQwertyMidiKeyHandlerFunc(std::function<void(const IMidiMsg& msg)> func)
2287{
2288 SetKeyHandlerFunc([&, func](const IKeyPress& key, bool isUp) {
2289 IMidiMsg msg;
2290
2291 int note = 0;
2292 static int base = 48;
2293 static bool keysDown[128] = {};
2294
2295 auto onOctSwitch = [&]() {
2296 base = Clip(base, 24, 96);
2297
2298 for(auto i=0;i<128;i++) {
2299 if(keysDown[i]) {
2300 msg.MakeNoteOffMsg(i, 0);
2302 if(func)
2303 func(msg);
2304 }
2305 }
2306 };
2307
2308 switch (key.VK) {
2309 case kVK_A: note = 0; break;
2310 case kVK_W: note = 1; break;
2311 case kVK_S: note = 2; break;
2312 case kVK_E: note = 3; break;
2313 case kVK_D: note = 4; break;
2314 case kVK_F: note = 5; break;
2315 case kVK_T: note = 6; break;
2316 case kVK_G: note = 7; break;
2317 case kVK_Y: note = 8; break;
2318 case kVK_H: note = 9; break;
2319 case kVK_U: note = 10; break;
2320 case kVK_J: note = 11; break;
2321 case kVK_K: note = 12; break;
2322 case kVK_O: note = 13; break;
2323 case kVK_L: note = 14; break;
2324 case kVK_Z: if(!isUp) { base -= 12; onOctSwitch(); } return true;
2325 case kVK_X: if(!isUp) { base += 12; onOctSwitch(); } return true;
2326 default: return true; // don't beep, but don't do anything
2327 }
2328
2329 int pitch = base + note;
2330
2331 if(!isUp) {
2332 if(keysDown[pitch] == false) {
2333 msg.MakeNoteOnMsg(pitch, 127, 0);
2334 keysDown[pitch] = true;
2336 if(func)
2337 func(msg);
2338 }
2339 }
2340 else {
2341 if(keysDown[pitch] == true) {
2342 msg.MakeNoteOffMsg(pitch, 0);
2343 keysDown[pitch] = false;
2345 if(func)
2346 func(msg);
2347 }
2348 }
2349
2350 return true;
2351 });
2352}
2353
2354bool IGraphics::RespondsToGesture(float x, float y)
2355{
2356 IControl* pControl = GetMouseControl(x, y, false, false);
2357
2358 if(pControl && pControl->GetWantsGestures())
2359 return true;
2360
2361 if(mGestureRegions.Size() == 0)
2362 return false;
2363 else
2364 {
2365 int regionIdx = mGestureRegions.Find(x, y);
2366
2367 if(regionIdx > -1)
2368 return true;
2369 }
2370
2371 return false;
2372}
2373
2375{
2376 IControl* pControl = GetMouseControl(info.x, info.y, false, false);
2377
2378 if(pControl && pControl->GetWantsGestures())
2379 pControl->OnGesture(info);
2380 else
2381 {
2382 int regionIdx = mGestureRegions.Find(info.x, info.y);
2383
2384 if(regionIdx > -1)
2385 mGestureRegionFuncs.find(regionIdx)->second(nullptr, info);
2386 }
2387}
2388
2390{
2391 if (std::find(std::begin(mRegisteredGestures), std::end(mRegisteredGestures), type) != std::end(mRegisteredGestures))
2392 {
2393 mRegisteredGestures.push_back(type);
2394 }
2395}
2396
2397void IGraphics::AttachGestureRecognizerToRegion(const IRECT& bounds, EGestureType type, IGestureFunc func)
2398{
2399 mGestureRegions.Add(bounds);
2401 mGestureRegionFuncs.insert(std::make_pair(mGestureRegions.Size()-1, func));
2402}
2403
2405{
2406 mGestureRegions.Clear();
2407 mGestureRegionFuncs.clear();
2408}
2409
2410void IGraphics::DrawRotatedBitmap(const IBitmap& bitmap, float destCtrX, float destCtrY, double angle, const IBlend* pBlend)
2411{
2412 float width = bitmap.W() / bitmap.GetDrawScale();
2413 float height = bitmap.H() / bitmap.GetDrawScale();
2414
2416 PathTransformTranslate(destCtrX, destCtrY);
2417 PathTransformRotate((float) angle);
2418 DrawBitmap(bitmap, IRECT(-width * 0.5f, - height * 0.5f, width * 0.5f, height * 0.5f), 0, 0, pBlend);
2420}
2421
2422void IGraphics::DrawPoint(const IColor& color, float x, float y, const IBlend* pBlend)
2423{
2424 FillRect(color, IRECT(x, y, x+1.f, y+1.f), pBlend);
2425}
2426
2427void IGraphics::DrawLine(const IColor& color, float x1, float y1, float x2, float y2, const IBlend* pBlend, float thickness)
2428{
2429 PathClear();
2430 PathMoveTo(x1, y1);
2431 PathLineTo(x2, y2);
2432 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2433}
2434
2435void IGraphics::DrawGrid(const IColor& color, const IRECT& bounds, float gridSizeH, float gridSizeV, const IBlend* pBlend, float thickness)
2436{
2437 PathClear();
2438
2439 // Vertical Lines grid
2440 if (gridSizeH > 1.f)
2441 {
2442 for (float x = bounds.L + gridSizeH; x < bounds.R; x += gridSizeH)
2443 {
2444 PathMoveTo(x, bounds.T);
2445 PathLineTo(x, bounds.B);
2446 }
2447 }
2448 // Horizontal Lines grid
2449 if (gridSizeV > 1.f)
2450 {
2451 for (float y = bounds.T + gridSizeV; y < bounds.B; y += gridSizeV)
2452 {
2453 PathMoveTo(bounds.L, y);
2454 PathLineTo(bounds.R, y);
2455 }
2456 }
2457
2458 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2459}
2460
2461void IGraphics::DrawData(const IColor& color, const IRECT& bounds, float* normYPoints, int nPoints, float* normXPoints, const IBlend* pBlend, float thickness, const IColor* pFillColor)
2462{
2463 if (nPoints == 0)
2464 return;
2465
2466 PathClear();
2467
2468 float xPos = bounds.L;
2469
2470 PathMoveTo(xPos, bounds.B - (bounds.H() * normYPoints[0]));
2471
2472 for (auto i = 1; i < nPoints; i++)
2473 {
2474 if (normXPoints)
2475 xPos = bounds.L + (bounds.W() * normXPoints[i]);
2476 else
2477 xPos = bounds.L + ((bounds.W() / (float) (nPoints - 1) * i));
2478
2479 PathLineTo(xPos, bounds.B - (bounds.H() * normYPoints[i]));
2480 }
2481
2482 if (pFillColor)
2483 {
2484 PathFill(*pFillColor, IFillOptions(true), pBlend);
2485 }
2486
2487 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2488}
2489
2490void IGraphics::DrawDottedLine(const IColor& color, float x1, float y1, float x2, float y2, const IBlend* pBlend, float thickness, float dashLen)
2491{
2492 PathClear();
2493
2494 IStrokeOptions options;
2495 options.mDash.SetDash(&dashLen, 0.0, 1);
2496 PathMoveTo(x1, y1);
2497 PathLineTo(x2, y2);
2498 PathStroke(color, thickness, options, pBlend);
2499}
2500
2501void IGraphics::DrawTriangle(const IColor& color, float x1, float y1, float x2, float y2, float x3, float y3, const IBlend* pBlend, float thickness)
2502{
2503 PathClear();
2504 PathTriangle(x1, y1, x2, y2, x3, y3);
2505 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2506}
2507
2508void IGraphics::DrawRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness)
2509{
2510 PathClear();
2511 PathRect(bounds);
2512 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2513}
2514
2515void IGraphics::DrawRoundRect(const IColor& color, const IRECT& bounds, float cornerRadius, const IBlend* pBlend, float thickness)
2516{
2517 PathClear();
2518 PathRoundRect(bounds, cornerRadius);
2519 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2520}
2521
2522void IGraphics::DrawRoundRect(const IColor& color, const IRECT& bounds, float cRTL, float cRTR, float cRBR, float cRBL, const IBlend* pBlend, float thickness)
2523{
2524 PathClear();
2525 PathRoundRect(bounds, cRTL, cRTR, cRBR, cRBL);
2526 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2527}
2528
2529void IGraphics::DrawConvexPolygon(const IColor& color, float* x, float* y, int nPoints, const IBlend* pBlend, float thickness)
2530{
2531 PathClear();
2532 PathConvexPolygon(x, y, nPoints);
2533 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2534}
2535
2536void IGraphics::DrawArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend, float thickness)
2537{
2538 PathClear();
2539 PathArc(cx, cy, r, a1, a2);
2540 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2541}
2542
2543void IGraphics::DrawCircle(const IColor& color, float cx, float cy, float r, const IBlend* pBlend, float thickness)
2544{
2545 PathClear();
2546 PathCircle(cx, cy, r);
2547 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2548}
2549
2550void IGraphics::DrawDottedRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness, float dashLen)
2551{
2552 PathClear();
2553 IStrokeOptions options;
2554 options.mDash.SetDash(&dashLen, 0., 1);
2555 PathRect(bounds);
2556 PathStroke(color, thickness, options, pBlend);
2557}
2558
2559void IGraphics::DrawEllipse(const IColor& color, const IRECT& bounds, const IBlend* pBlend, float thickness)
2560{
2561 PathClear();
2562 PathEllipse(bounds);
2563 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2564}
2565
2566void IGraphics::DrawEllipse(const IColor& color, float x, float y, float r1, float r2, float angle, const IBlend* pBlend, float thickness)
2567{
2568 PathClear();
2569 PathEllipse(x, y, r1, r2, angle);
2570 PathStroke(color, thickness, IStrokeOptions(), pBlend);
2571}
2572
2573void IGraphics::FillTriangle(const IColor& color, float x1, float y1, float x2, float y2, float x3, float y3, const IBlend* pBlend)
2574{
2575 PathClear();
2576 PathTriangle(x1, y1, x2, y2, x3, y3);
2577 PathFill(color, IFillOptions(), pBlend);
2578}
2579
2580void IGraphics::FillRect(const IColor& color, const IRECT& bounds, const IBlend* pBlend)
2581{
2582 PathClear();
2583 PathRect(bounds);
2584 PathFill(color, IFillOptions(), pBlend);
2585}
2586
2587void IGraphics::FillRoundRect(const IColor& color, const IRECT& bounds, float cornerRadius, const IBlend* pBlend)
2588{
2589 PathClear();
2590 PathRoundRect(bounds, cornerRadius);
2591 PathFill(color, IFillOptions(), pBlend);
2592}
2593
2594void IGraphics::FillRoundRect(const IColor& color, const IRECT& bounds, float cRTL, float cRTR, float cRBR, float cRBL, const IBlend* pBlend)
2595{
2596 PathClear();
2597 PathRoundRect(bounds, cRTL, cRTR, cRBR, cRBL);
2598 PathFill(color, IFillOptions(), pBlend);
2599}
2600
2601void IGraphics::FillConvexPolygon(const IColor& color, float* x, float* y, int nPoints, const IBlend* pBlend)
2602{
2603 PathClear();
2604 PathConvexPolygon(x, y, nPoints);
2605 PathFill(color, IFillOptions(), pBlend);
2606}
2607
2608void IGraphics::FillArc(const IColor& color, float cx, float cy, float r, float a1, float a2, const IBlend* pBlend)
2609{
2610 PathClear();
2611 PathMoveTo(cx, cy);
2612 PathArc(cx, cy, r, a1, a2);
2613 PathClose();
2614 PathFill(color, IFillOptions(), pBlend);
2615}
2616
2617void IGraphics::FillCircle(const IColor& color, float cx, float cy, float r, const IBlend* pBlend)
2618{
2619 PathClear();
2620 PathCircle(cx, cy, r);
2621 PathFill(color, IFillOptions(), pBlend);
2622}
2623
2624void IGraphics::FillEllipse(const IColor& color, const IRECT& bounds, const IBlend* pBlend)
2625{
2626 PathClear();
2627 PathEllipse(bounds);
2628 PathFill(color, IFillOptions(), pBlend);
2629}
2630
2631void IGraphics::FillEllipse(const IColor& color, float x, float y, float r1, float r2, float angle, const IBlend* pBlend)
2632{
2633 PathClear();
2634 PathEllipse(x, y, r1, r2, angle);
2635 PathFill(color, IFillOptions(), pBlend);
2636}
2637
2638void IGraphics::PathTriangle(float x1, float y1, float x2, float y2, float x3, float y3)
2639{
2640 PathMoveTo(x1, y1);
2641 PathLineTo(x2, y2);
2642 PathLineTo(x3, y3);
2643 PathClose();
2644}
2645
2646void IGraphics::PathRect(const IRECT& bounds)
2647{
2648 PathMoveTo(bounds.L, bounds.T);
2649 PathLineTo(bounds.R, bounds.T);
2650 PathLineTo(bounds.R, bounds.B);
2651 PathLineTo(bounds.L, bounds.B);
2652 PathClose();
2653}
2654
2655void IGraphics::PathRoundRect(const IRECT& bounds, float ctl, float ctr, float cbl, float cbr)
2656{
2657 if (ctl <= 0.f && ctr <= 0.f && cbl <= 0.f && cbr <= 0.f)
2658 {
2659 PathRect(bounds);
2660 }
2661 else
2662 {
2663 const float y = bounds.B - bounds.H();
2664 PathMoveTo(bounds.L, y + ctl);
2665 PathArc(bounds.L + ctl, y + ctl, ctl, 270.f, 360.f);
2666 PathArc(bounds.L + bounds.W() - ctr, y + ctr, ctr, 0.f, 90.f);
2667 PathArc(bounds.L + bounds.W() - cbr, y + bounds.H() - cbr, cbr, 90.f, 180.f);
2668 PathArc(bounds.L + cbl, y + bounds.H() - cbl, cbl, 180.f, 270.f);
2669 PathClose();
2670 }
2671}
2672
2673void IGraphics::PathRoundRect(const IRECT& bounds, float cr)
2674{
2675 PathRoundRect(bounds, cr, cr, cr, cr);
2676}
2677
2678void IGraphics::PathEllipse(float x, float y, float r1, float r2, float angle)
2679{
2681
2682 if (r1 <= 0.0 || r2 <= 0.0)
2683 return;
2684
2686 PathTransformRotate(angle);
2687 PathTransformScale(r1, r2);
2688
2689 PathCircle(0.0, 0.0, 1.0);
2690
2692}
2693
2695{
2696 PathEllipse(bounds.MW(), bounds.MH(), bounds.W() / 2.f, bounds.H() / 2.f);
2697}
2698
2699void IGraphics::PathCircle(float cx, float cy, float r)
2700{
2701 PathMoveTo(cx, cy - r);
2702 PathArc(cx, cy, r, 0.f, 360.f);
2703 PathClose();
2704}
2705
2706void IGraphics::PathConvexPolygon(float* x, float* y, int nPoints)
2707{
2708 PathMoveTo(x[0], y[0]);
2709 for(int i = 1; i < nPoints; i++)
2710 PathLineTo(x[i], y[i]);
2711 PathClose();
2712}
2713
2715{
2716 mTransformStates.push(mTransform);
2717}
2718
2720{
2721 if (!mTransformStates.empty())
2722 {
2723 mTransform = mTransformStates.top();
2724 mTransformStates.pop();
2725 PathTransformSetMatrix(mTransform);
2726 }
2727}
2728
2729void IGraphics::PathTransformReset(bool clearStates)
2730{
2731 if (clearStates)
2732 {
2733 std::stack<IMatrix> newStack;
2734 mTransformStates.swap(newStack);
2735 }
2736
2737 mTransform = IMatrix();
2738 PathTransformSetMatrix(mTransform);
2739}
2740
2742{
2743 mTransform.Translate(x, y);
2744 PathTransformSetMatrix(mTransform);
2745}
2746
2747void IGraphics::PathTransformScale(float scaleX, float scaleY)
2748{
2749 mTransform.Scale(scaleX, scaleY);
2750 PathTransformSetMatrix(mTransform);
2751}
2752
2754{
2755 PathTransformScale(scale, scale);
2756}
2757
2759{
2760 mTransform.Rotate(angle);
2761 PathTransformSetMatrix(mTransform);
2762}
2763
2764void IGraphics::PathTransformSkew(float xAngle, float yAngle)
2765{
2766 mTransform.Skew(xAngle, yAngle);
2767 PathTransformSetMatrix(mTransform);
2768}
2769
2771{
2772 mTransform.Transform(matrix);
2773 PathTransformSetMatrix(mTransform);
2774}
2775
2777{
2778 IRECT drawArea = mLayers.empty() ? mClipRECT : mLayers.top()->Bounds();
2779 IRECT clip = r.Empty() ? drawArea : r.Intersect(drawArea);
2780 PathTransformSetMatrix(IMatrix());
2781 SetClipRegion(clip);
2782 PathTransformSetMatrix(mTransform);
2783}
2784
2785void IGraphics::DrawFittedBitmap(const IBitmap& bitmap, const IRECT& bounds, const IBlend* pBlend)
2786{
2788 PathTransformTranslate(bounds.L, bounds.T);
2789 IRECT newBounds(0., 0., static_cast<float>(bitmap.W()), static_cast<float>(bitmap.H()));
2790 PathTransformScale(bounds.W() / static_cast<float>(bitmap.W()), bounds.H() / static_cast<float>(bitmap.H()));
2791 DrawBitmap(bitmap, newBounds, 0, 0, pBlend);
2793}
2794
2795void IGraphics::DrawSVG(const ISVG& svg, const IRECT& dest, const IBlend* pBlend, const IColor* pStrokeColor, const IColor* pFillColor)
2796{
2797 float xScale = dest.W() / svg.W();
2798 float yScale = dest.H() / svg.H();
2799 float scale = xScale < yScale ? xScale : yScale;
2800
2802 PathTransformTranslate(dest.L, dest.T);
2803 PathTransformScale(scale);
2804 DoDrawSVG(svg, pBlend, pStrokeColor, pFillColor);
2806}
2807
2808void IGraphics::DrawRotatedSVG(const ISVG& svg, float destCtrX, float destCtrY, float width, float height, double angle, const IBlend* pBlend)
2809{
2811 PathTransformTranslate(destCtrX, destCtrY);
2812 PathTransformRotate((float) angle);
2813 DrawSVG(svg, IRECT(-width * 0.5f, - height * 0.5f, width * 0.5f, height * 0.5f), pBlend);
2815}
2816
2817IPattern IGraphics::GetSVGPattern(const NSVGpaint& paint, float opacity)
2818{
2819 int alpha = std::min(255, std::max(int(roundf(opacity * 255.f)), 0));
2820
2821 switch (paint.type)
2822 {
2823 case NSVG_PAINT_COLOR:
2824 return IColor(alpha, (paint.color >> 0) & 0xFF, (paint.color >> 8) & 0xFF, (paint.color >> 16) & 0xFF);
2825
2826 case NSVG_PAINT_LINEAR_GRADIENT:
2827 case NSVG_PAINT_RADIAL_GRADIENT:
2828 {
2829 NSVGgradient* pGrad = paint.gradient;
2830
2831 IPattern pattern(paint.type == NSVG_PAINT_LINEAR_GRADIENT ? EPatternType::Linear : EPatternType::Radial);
2832
2833 // Set Extend Rule
2834 switch (pGrad->spread)
2835 {
2836 case NSVG_SPREAD_PAD: pattern.mExtend = EPatternExtend::Pad; break;
2837 case NSVG_SPREAD_REFLECT: pattern.mExtend = EPatternExtend::Reflect; break;
2838 case NSVG_SPREAD_REPEAT: pattern.mExtend = EPatternExtend::Repeat; break;
2839 }
2840
2841 // Copy Stops
2842 for (int i = 0; i < pGrad->nstops; i++)
2843 {
2844 unsigned int color = pGrad->stops[i].color;
2845 pattern.AddStop(IColor(255, (color >> 0) & 0xFF, (color >> 8) & 0xFF, (color >> 16) & 0xFF), pGrad->stops[i].offset);
2846 }
2847
2848 // Copy transform
2849 pattern.SetTransform(pGrad->xform[0], pGrad->xform[1], pGrad->xform[2], pGrad->xform[3], pGrad->xform[4], pGrad->xform[5]);
2850
2851 return pattern;
2852 }
2853 default:
2854 return IColor(alpha, 0, 0, 0);
2855 }
2856}
2857
2858void IGraphics::DoDrawSVG(const ISVG& svg, const IBlend* pBlend, const IColor* pStrokeColor, const IColor* pFillColor)
2859{
2860#ifdef SVG_USE_SKIA
2861 SkCanvas* canvas = static_cast<SkCanvas*>(GetDrawContext());
2862 svg.mSVGDom->render(canvas); //TODO: blend
2863#else
2864 NSVGimage* pImage = svg.mImage;
2865
2866 assert(pImage != nullptr);
2867
2868 for (NSVGshape* pShape = pImage->shapes; pShape; pShape = pShape->next)
2869 {
2870 if (!(pShape->flags & NSVG_FLAGS_VISIBLE))
2871 continue;
2872
2873 // Build a new path for each shape
2874 PathClear();
2875
2876 // iterate subpaths in this shape
2877 for (NSVGpath* pPath = pShape->paths; pPath; pPath = pPath->next)
2878 {
2879 PathMoveTo(pPath->pts[0], pPath->pts[1]);
2880
2881 for (int i = 1; i < pPath->npts; i += 3)
2882 {
2883 float *p = &pPath->pts[i*2];
2884 PathCubicBezierTo(p[0], p[1], p[2], p[3], p[4], p[5]);
2885 }
2886
2887 if (pPath->closed)
2888 PathClose();
2889
2890 // Compute whether this path is a hole or a solid and set the winding direction accordingly.
2891 int crossings = 0;
2892 IVec2 p0{pPath->pts[0], pPath->pts[1]};
2893 IVec2 p1{pPath->bounds[0] - 1.0f, pPath->bounds[1] - 1.0f};
2894 // Iterate all other paths
2895 for (NSVGpath *pPath2 = pShape->paths; pPath2; pPath2 = pPath2->next)
2896 {
2897 if (pPath2 == pPath)
2898 continue;
2899 // Iterate all lines on the path
2900 if (pPath2->npts < 4)
2901 continue;
2902 for (int i = 1; i < pPath2->npts + 3; i += 3)
2903 {
2904 float *p = &pPath2->pts[2*i];
2905 // The previous point
2906 IVec2 p2 {p[-2], p[-1]};
2907 // The current point
2908 IVec2 p3 = (i < pPath2->npts) ? IVec2{p[4], p[5]} : IVec2{pPath2->pts[0], pPath2->pts[1]};
2909 float crossing = GetLineCrossing(p0, p1, p2, p3);
2910 float crossing2 = GetLineCrossing(p2, p3, p0, p1);
2911 if (0.0 <= crossing && crossing < 1.0 && 0.0 <= crossing2)
2912 {
2913 crossings++;
2914 }
2915 }
2916 }
2917 PathSetWinding(crossings % 2 != 0);
2918 }
2919
2920 // Fill combined path using windings set in subpaths
2921 if (pShape->fill.type != NSVG_PAINT_NONE)
2922 {
2923 IFillOptions options;
2924 options.mFillRule = EFillRule::Preserve;
2925
2926 options.mPreserve = pShape->stroke.type != NSVG_PAINT_NONE;
2927 PathFill(pFillColor ? IPattern(*pFillColor) : GetSVGPattern(pShape->fill, pShape->opacity), options, pBlend);
2928 }
2929
2930 // Stroke
2931 if (pShape->stroke.type != NSVG_PAINT_NONE)
2932 {
2933 IStrokeOptions options;
2934
2935 options.mMiterLimit = pShape->miterLimit;
2936
2937 switch (pShape->strokeLineCap)
2938 {
2939 case NSVG_CAP_BUTT: options.mCapOption = ELineCap::Butt; break;
2940 case NSVG_CAP_ROUND: options.mCapOption = ELineCap::Round; break;
2941 case NSVG_CAP_SQUARE: options.mCapOption = ELineCap::Square; break;
2942 }
2943
2944 switch (pShape->strokeLineJoin)
2945 {
2946 case NSVG_JOIN_MITER: options.mJoinOption = ELineJoin::Miter; break;
2947 case NSVG_JOIN_ROUND: options.mJoinOption = ELineJoin::Round; break;
2948 case NSVG_JOIN_BEVEL: options.mJoinOption = ELineJoin::Bevel; break;
2949 }
2950
2951 options.mDash.SetDash(pShape->strokeDashArray, pShape->strokeDashOffset, pShape->strokeDashCount);
2952
2953 PathStroke(pStrokeColor ? IPattern(*pStrokeColor) : GetSVGPattern(pShape->stroke, pShape->opacity), pShape->strokeWidth, options, pBlend);
2954 }
2955 }
2956#endif
2957 }
This file contains the base IControl implementation, along with some base classes for specific types ...
A collection of IControls for common UI widgets, such as knobs, sliders, switches.
const void * LoadWinResource(const char *resID, const char *type, int &sizeInBytes, void *pHInstance)
Load a resource from the binary (windows only).
EResourceLocation LocateResource(const char *fileNameOrResID, const char *type, WDL_String &result, const char *bundleID, void *pHInstance, const char *sharedResourcesSubPath)
Find the absolute path of a resource based on it's file name (e.g.
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
float GetDrawScale() const
A basic control to draw a bitmap, or one frame of a stacked bitmap depending on the current value.
Definition: IControl.h:2086
User-facing bitmap abstraction that you use to manage bitmap data, independant of draw class/platform...
int W() const
bool GetFramesAreHorizontal() const
const WDL_String & GetResourceName() const
int N() const
float GetDrawScale() const
float GetScale() const
int H() const
APIBitmap * GetAPIBitmap() const
A special control to draw contextual info as a slider etc is moved If used in the main IControl stack...
The lowest level base class of an IGraphics control.
Definition: IControl.h:49
virtual bool GetWantsGestures() const
Definition: IControl.h:446
virtual void OnResize()
Called when IControl is constructed or resized using SetRect().
Definition: IControl.h:153
const char * GetGroup() const
Get the group that the control belongs to, if any.
Definition: IControl.h:289
int GetTextEntryLength() const
Get the max number of characters that are allowed in text entry.
Definition: IControl.h:308
virtual void OnDropMultiple(const std::vector< const char * > &paths)
Implement to handle multiple items drag 'n dropped onto this control.
Definition: IControl.h:147
virtual bool OnKeyUp(float x, float y, const IKeyPress &key)
Implement this method to respond to a key up event on this control.
Definition: IControl.h:126
virtual void OnMouseOut()
Implement this method to respond to a mouseout event on this control.
Definition: IControl.cpp:275
bool GetWantsMultiTouch() const
Definition: IControl.h:438
virtual void OnGUIIdle()
This is an idle timer tick call on the GUI thread, only active if USE_IDLE_CALLS is defined.
Definition: IControl.h:423
bool GetMouseEventsWhenDisabled() const
Definition: IControl.h:381
bool GetMouseOverWhenDisabled() const
Definition: IControl.h:378
virtual void OnMouseOver(float x, float y, const IMouseMod &mod)
Implement this method to respond to a mouseover event on this control.
Definition: IControl.cpp:267
virtual void SetPosition(float x, float y)
Set the position of the control, preserving the width and height.
Definition: IControl.cpp:291
void SetGroup(const char *groupName)
Assign the control to a control group.
Definition: IControl.h:285
virtual void OnPopupMenuSelection(IPopupMenu *pSelectedMenu, int valIdx)
Implement this method to handle popup menu selection after IGraphics::CreatePopupMenu/IControlPromptU...
Definition: IControl.cpp:283
bool IsDisabled() const
Definition: IControl.h:367
virtual void OnMouseDown(float x, float y, const IMouseMod &mod)
Implement this method to respond to a mouse down event on this control.
Definition: IControl.cpp:252
virtual void SetValueFromUserInput(double value, int valIdx=0)
Set the control's value after user input.
Definition: IControl.cpp:174
virtual bool IsDirty()
Called at each display refresh by the IGraphics draw loop, after IControl::Animate(),...
Definition: IControl.cpp:231
virtual bool IsHit(float x, float y) const
Hit test the control.
Definition: IControl.h:400
virtual void Hide(bool hide)
Shows or hides the IControl.
Definition: IControl.cpp:239
virtual void OnRescale()
Implement to do something when graphics is scaled globally (e.g.
Definition: IControl.h:150
void SetDelegate(IGEditorDelegate &dlg)
Used internally to set the mDelegate (and mGraphics) variables.
Definition: IControl.h:457
virtual void OnDeleteFromPopupMenu(IPopupMenu *pMenu, int itemIdx)
Implement this method to handle popup menu deletion interactions (on IOS) after IGraphics::CreatePopu...
Definition: IControl.h:181
virtual void OnTouchCancelled(float x, float y, const IMouseMod &mod)
Implement this method to respond to a touch cancel event on this control.
Definition: IControl.h:141
virtual void OnDrop(const char *str)
Implement to do something when something was drag 'n dropped onto this control.
Definition: IControl.h:144
int GetParamIdx(int valIdx=0) const
Get the index of a parameter that the control is linked to Normaly controls are either linked to a si...
Definition: IControl.cpp:109
const IRECT & GetRECT() const
Get the rectangular draw area for this control, within the graphics context.
Definition: IControl.h:316
virtual bool OnKeyDown(float x, float y, const IKeyPress &key)
Implement this method to respond to a key down event on this control.
Definition: IControl.h:120
int LinkedToParam(int paramIdx) const
Check if the control is linked to a particular parameter.
Definition: IControl.cpp:132
bool GetIgnoreMouse() const
Definition: IControl.h:384
virtual void OnMouseDblClick(float x, float y, const IMouseMod &mod)
Implement this method to respond to a mouse double click event on this control.
Definition: IControl.cpp:258
int GetTag() const
Get the control's tag.
Definition: IControl.h:426
virtual void OnMouseUp(float x, float y, const IMouseMod &mod)
Implement this method to respond to a mouse up event on this control.
Definition: IControl.h:93
virtual void DrawPTHighlight(IGraphics &g)
Implement this to customise how a colored highlight is drawn on the control in ProTools (AAX format o...
Definition: IControl.cpp:389
virtual void CreateContextMenu(IPopupMenu &contextMenu)
Called by default when the user right clicks a control.
Definition: IControl.h:171
virtual void OnAttached()
Called after the control has been attached, and its delegate and graphics member variable set.
Definition: IControl.h:159
virtual bool OnGesture(const IGestureInfo &info)
Definition: IControl.cpp:320
virtual void OnMouseDrag(float x, float y, float dX, float dY, const IMouseMod &mod)
Implement this method to respond to a mouse drag event on this control.
Definition: IControl.h:101
virtual void OnMouseWheel(float x, float y, const IMouseMod &mod, float d)
Implement this method to respond to a mouse wheel event on this control.
Definition: IControl.h:114
virtual void Draw(IGraphics &g)=0
Draw the control to the graphics context.
const IParam * GetParam(int valIdx=0) const
Get a const pointer to the IParam object (owned by the editor delegate class), associated with this c...
Definition: IControl.cpp:122
const IText & GetText() const
Get the Text object for the control.
Definition: IControl.h:293
bool GetMouseDblAsSingleClick() const
Get double click as single click By default, mouse double click has its own handler.
Definition: IControl.h:353
virtual void SetSize(float w, float h)
Set the size of the control, preserving the current position.
Definition: IControl.cpp:303
void SetPTParameterHighlight(bool isHighlighted, int color)
Used internally by the AAX wrapper view interface to set the control parmeter highlight.
Definition: IControl.cpp:365
virtual void OnTextEntryCompletion(const char *str, int valIdx)
Implement this method to handle text input after IGraphics::CreateTextEntry/IControlPromptUserInput.
Definition: IControl.h:186
virtual void SetDisabled(bool disable)
Sets disabled mode for the control, the default implementation modifies the mBlend member.
Definition: IControl.cpp:245
double GetValue(int valIdx=0) const
Get the control's value.
Definition: IControl.cpp:153
void SetTargetAndDrawRECTs(const IRECT &bounds)
Set BOTH the draw rect and the target area, within the graphics context for this control.
Definition: IControl.h:332
virtual void OnContextSelection(int itemSelected)
Implement this to respond to a menu selection from CreateContextMenu();.
Definition: IControl.h:189
virtual int GetValIdxForPos(float x, float y) const
Check to see which of the control's values relates to this x and y coordinate.
Definition: IControl.h:250
int NVals() const
Definition: IControl.h:244
IContainerBase * GetParent() const
Definition: IControl.h:467
virtual void SetDirty(bool triggerAction=true, int valIdx=kNoValIdx)
Mark the control as dirty, i.e.
Definition: IControl.cpp:198
bool GetPromptShowsParamLabel() const
Definition: IControl.h:391
bool IsHidden() const
Definition: IControl.h:360
A control for resizing the plug-in window by clicking and dragging in the bottom right-hand corner Th...
virtual void BeginInformHostOfParamChangeFromUI(int paramIdx)=0
Called by the UI at the beginning of a parameter change gesture, in order to notify the host (via a c...
virtual void EndInformHostOfParamChangeFromUI(int paramIdx)=0
Called by the user interface at the end of a parameter change gesture, in order to notify the host (v...
virtual void SendMidiMsgFromUI(const IMidiMsg &msg)
SendMidiMsgFromUI (Abbreviation: SMMFUI) This method should be used when sending a MIDI message from ...
virtual bool ConstrainEditorResize(int &w, int &h) const
Constrain the incoming editor width and height values based on the minimum and maximum.
virtual bool EditorResizeFromUI(int viewWidth, int viewHeight, bool needsPlatformResize)
If the editor changes UI dimensions, e.g.
An editor delegate base class that uses IGraphics for the UI.
virtual void LayoutUI(IGraphics *pGraphics)
Called to layout controls when the GUI is initially opened and again if the UI size changes.
bool ControlIsCaptured() const
Check to see if any control is captured.
Definition: IGraphics.h:1400
void EnableTooltips(bool enable)
Definition: IGraphics.cpp:1538
virtual float DoMeasureText(const IText &text, const char *str, IRECT &bounds) const =0
void Resize(int w, int h, float scale, bool needsPlatformResize=true)
Definition: IGraphics.cpp:90
virtual void DrawRotatedSVG(const ISVG &svg, float destCentreX, float destCentreY, float width, float height, double angle, const IBlend *pBlend=0)
Draw an SVG image to the graphics context with rotation.
Definition: IGraphics.cpp:2808
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:2508
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:2624
ILayer * PopLayer()
Pop a layer off the stack.
Definition: IGraphics.cpp:2025
virtual void DrawConvexPolygon(const IColor &color, float *x, float *y, int nPoints, const IBlend *pBlend=0, float thickness=1.f)
Draw a convex polygon to the graphics context.
Definition: IGraphics.cpp:2529
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:2559
virtual void DrawFittedBitmap(const IBitmap &bitmap, const IRECT &bounds, const IBlend *pBlend=0)
Draw a bitmap (raster) image to the graphics context, scaling the image to fit the bounds.
Definition: IGraphics.cpp:2785
void DrawBitmapedText(const IBitmap &bitmap, const IRECT &bounds, IText &text, IBlend *pBlend, const char *str, bool vCenter=true, bool multiline=false, int charWidth=6, int charHeight=12, int charOffset=0)
Draws mono spaced bitmap text.
Definition: IGraphics.cpp:722
void AttachPopupMenuControl(const IText &text=DEFAULT_TEXT, const IRECT &bounds=IRECT())
Attach a control for pop-up menus, to override platform style menus.
Definition: IGraphics.cpp:364
virtual void DrawPoint(const IColor &color, float x, float y, const IBlend *pBlend=0)
Fill a rectangle corresponding to a pixel on a 1:1 screen with a color.
Definition: IGraphics.cpp:2422
void RemoveControl(int idx)
Remove a control at a particular index, (frees memory).
Definition: IGraphics.cpp:167
void DeleteFromPopupMenu(IPopupMenu *pMenu, int itemIdx)
Called by IOS platform (or other supported platforms) in order to update a control with a deletion in...
Definition: IGraphics.cpp:280
virtual const char * GetBundleID() const
Get the bundle ID on macOS and iOS, returns emtpy string on other OSs.
Definition: IGraphics.h:968
void PathConvexPolygon(float *x, float *y, int nPoints)
Add a convex polygon to the current path.
Definition: IGraphics.cpp:2706
virtual void PathFill(const IPattern &pattern, const IFillOptions &options=IFillOptions(), const IBlend *pBlend=0)=0
Fill the current current path.
virtual float GetPlatformWindowScale() const
Returns a scaling factor for resizing parent windows via the host/plugin API.
Definition: IGraphics.h:1225
void DrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend=0)
Draw some text to the graphics context in a specific rectangle.
Definition: IGraphics.cpp:678
float GetTotalScale() const
Gets the combined draw and screen/display scaling factor.
Definition: IGraphics.h:1118
int GetParamIdxForPTAutomation(float x, float y)
[AAX only] This can be called by the ProTools API class (e.g.
Definition: IGraphics.cpp:1393
void CreatePopupMenu(IControl &control, IPopupMenu &menu, const IRECT &bounds, int valIdx=0)
Shows a pop up/contextual menu in relation to a rectangular region of the graphics context.
Definition: IGraphics.cpp:1971
void CreateTextEntry(IControl &control, const IText &text, const IRECT &bounds, const char *str="", int valIdx=0)
Create a text entry box.
Definition: IGraphics.cpp:1934
virtual void PathClear()=0
Clear the stack of path drawing commands.
virtual void * GetDrawContext()=0
Gets a void pointer to underlying drawing context, for the IGraphics backend See draw class implement...
void PathRoundRect(const IRECT &bounds, float ctl, float ctr, float cbl, float cbr)
Add a rounded rectangle to the current path, with independent corner roundness.
Definition: IGraphics.cpp:2655
void SetPTParameterHighlight(int paramIdx, bool isHighlighted, int color)
[AAX only] See AAX_CEffectGUI::SetControlHighlightInfo()
Definition: IGraphics.cpp:1407
virtual void PathClose()=0
Close the path that is being specified.
void PathTransformTranslate(float x, float y)
Apply a translation transform to the current path.
Definition: IGraphics.cpp:2741
void ClearInTextEntryControl()
Called when the text entry is dismissed, to reset mInTextEntry.
Definition: IGraphics.h:1139
void DrawRadialLine(const IColor &color, float cx, float cy, float angle, float rMin, float rMax, const IBlend *pBlend=0, float thickness=1.f)
Draw a radial line to the graphics context, useful for pointers on dials.
Definition: IGraphics.cpp:819
void PathRect(const IRECT &bounds)
Add a rectangle to the current path.
Definition: IGraphics.cpp:2646
virtual void RetainBitmap(const IBitmap &bitmap, const char *cacheName)
Adds an IBitmap to the cache/static storage.
Definition: IGraphics.cpp:1851
void ForMatchingControls(T method, int paramIdx, Args... args)
For all standard controls in the main control stack that are linked to a specific parameter,...
Definition: IGraphics.cpp:585
virtual void CreatePlatformTextEntry(int paramIdx, const IText &text, const IRECT &bounds, int length, const char *str)=0
Creates a platform native text entry field.
void ResumeLayer(ILayerPtr &layer)
If a layer already exists, continue drawing to it.
Definition: IGraphics.cpp:1998
void PathTransformMatrix(const IMatrix &matrix)
Apply an arbitary affine transform matrix to the current path.
Definition: IGraphics.cpp:2770
void DrawRotatedLayer(const ILayerPtr &layer, double angle)
Draw a layer to the main IGraphics context, with rotation.
Definition: IGraphics.cpp:2076
void PathTransformScale(float x, float y)
Apply a scale transform to the current path, with independant x, y scales.
Definition: IGraphics.cpp:2747
virtual ECursor SetMouseCursor(ECursor cursorType=ECursor::ARROW)
Sets the mouse cursor to one of ECursor (implementations should return the result of the base impleme...
Definition: IGraphics.h:828
void PathLine(float x1, float y1, float x2, float y2)
Add a line to the current path.
Definition: IGraphics.h:601
virtual void DrawDottedRect(const IColor &color, const IRECT &bounds, const IBlend *pBlend=0, float thickness=1.f, float dashLen=2.f)
Draw a dotted rectangle to the graphics context.
Definition: IGraphics.cpp:2550
IControl * GetControlWithTag(int ctrlTag) const
Get the control with a particular tag.
Definition: IGraphics.cpp:456
virtual void PathSetWinding(bool clockwise)
NanoVG only.
Definition: IGraphics.h:685
void OnMouseDrag(const std::vector< IMouseInfo > &points)
Called when the platform class sends drag events.
Definition: IGraphics.cpp:1163
virtual void ReleaseBitmap(const IBitmap &bitmap)
Releases an IBitmap from the cache/static storage.
Definition: IGraphics.cpp:1845
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:2515
void OnMouseOut()
Called when the mouse leaves the graphics context.
Definition: IGraphics.cpp:1153
void UpdatePeers(IControl *pCaller, int callerValIdx)
This method is called after interacting with a control, so that any other controls linked to the same...
Definition: IGraphics.cpp:611
void RemovePopupMenuControl()
Remove the IGraphics popup menu, use platform popup menu if available.
Definition: IGraphics.cpp:373
virtual float GetBackingPixelScale() const
Definition: IGraphics.h:1784
virtual void DrawRotatedBitmap(const IBitmap &bitmap, float destCentreX, float destCentreY, double angle, const IBlend *pBlend=0)
Draw a bitmap (raster) image to the graphics context with rotation.
Definition: IGraphics.cpp:2410
bool OnKeyDown(float x, float y, const IKeyPress &key)
Definition: IGraphics.cpp:1241
virtual int AlphaChannel() const =0
void AttachGestureRecognizerToRegion(const IRECT &bounds, EGestureType type, IGestureFunc func)
Attach a gesture recognizer to a rectangular region of the GUI, i.e.
Definition: IGraphics.cpp:2397
bool MultiTouchEnabled() const
Definition: IGraphics.h:1166
int Width() const
Gets the width of the graphics context.
Definition: IGraphics.h:1086
virtual void EndFrame()
Called by some drawing API classes to finally blit the draw bitmap onto the screen or perform other c...
Definition: IGraphics.h:100
void DisableControl(int paramIdx, bool diable)
Disable or enable controls linked to a specific parameter.
Definition: IGraphics.cpp:491
void SetControlBounds(IControl *pControl, const IRECT &r)
Set a controls target and draw rect to r, redrawing the interface correctly.
Definition: IGraphics.cpp:228
void PathTriangle(float x1, float y1, float x2, float y2, float x3, float y3)
Add a triangle to the current path.
Definition: IGraphics.cpp:2638
void DoMeasureTextRotation(const IText &text, const IRECT &bounds, IRECT &rect) const
Definition: IGraphics.cpp:2238
void AttachTextEntryControl()
Attach a control for text entry, to override platform text entry.
Definition: IGraphics.cpp:378
void AttachCornerResizer(EUIResizerMode sizeMode=EUIResizerMode::Scale, bool layoutOnResize=false, const IColor &color=COLOR_TRANSLUCENT, const IColor &mouseOverColor=COLOR_BLACK, const IColor &dragColor=COLOR_BLACK, float size=20.f)
Attach the default control to scale or increase the UI size by dragging the plug-in bottom right-hand...
Definition: IGraphics.cpp:328
virtual void AttachGestureRecognizer(EGestureType type)
Registers a gesture recognizer with the graphics context.
Definition: IGraphics.cpp:2389
virtual APIBitmap * CreateAPIBitmap(int width, int height, float scale, double drawScale, bool cacheable=false)=0
Creates a new API bitmap, either in memory or as a GPU texture.
void OnMouseUp(const std::vector< IMouseInfo > &points)
Called when the platform class sends mouse up events.
Definition: IGraphics.cpp:1065
void PathTransformReset(bool clearStates=false)
Reset the affine transform of the current path, to the default state.
Definition: IGraphics.cpp:2729
void OnAppearanceChanged(EUIAppearance appearance)
Called by the platform class if the view changes to dark/light mode.
Definition: IGraphics.cpp:1525
void CalculateTextRotation(const IText &text, const IRECT &bounds, IRECT &rect, double &tx, double &ty) const
Definition: IGraphics.cpp:2246
bool IsInPlatformTextEntry()
Definition: IGraphics.h:1133
virtual void PathStroke(const IPattern &pattern, float thickness, const IStrokeOptions &options=IStrokeOptions(), const IBlend *pBlend=0)=0
Stroke the current current path.
bool CheckLayer(const ILayerPtr &layer)
Test to see if a layer needs drawing, for instance if the control's bounds were changed.
Definition: IGraphics.cpp:2043
int WindowWidth() const
Gets the width of the graphics context including draw scaling.
Definition: IGraphics.h:1094
void Draw(IRECTList &rects)
Called by the platform class indicating a number of rectangles in the UI that need to redraw.
Definition: IGraphics.cpp:946
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:1887
IControl * GetControlWithParamIdx(int paramIdx)
Get the first control with a parameter index that matches paramIdx.
Definition: IGraphics.cpp:471
virtual void ApplyShadowMask(ILayerPtr &layer, RawBitmapData &mask, const IShadow &shadow)=0
Implemented by a graphics backend to apply a calculated shadow mask to a layer, according to the shad...
void EnableLiveEdit(bool enable)
Live edit mode allows you to relocate controls at runtime in debug builds.
Definition: IGraphics.cpp:1544
void PathTransformRestore()
Restore the affine transform of the current path, to the previously saved state.
Definition: IGraphics.cpp:2719
void SetControlValueAfterPopupMenu(IPopupMenu *pMenu)
Called by PopupMenuControl in order to update a control with a new value after returning from the non...
Definition: IGraphics.cpp:255
void DrawLayer(const ILayerPtr &layer, const IBlend *pBlend=nullptr)
Draw a layer to the main IGraphics context.
Definition: IGraphics.cpp:2056
void PushLayer(ILayer *pLayer)
Push a layer on to the stack.
Definition: IGraphics.cpp:2016
void ReleaseMouseCapture()
Used to tell the graphics context to stop tracking mouse interaction with a control.
Definition: IGraphics.cpp:1289
int GetLastClickedParamForPTAutomation()
[AAX only]
Definition: IGraphics.cpp:1400
void ForControlWithParam(int paramIdx, IControlFunction func)
For all standard controls in the main control stack that are linked to a specific parameter,...
Definition: IGraphics.cpp:496
void AttachBubbleControl(const IText &text=DEFAULT_TEXT)
Attach the default control to show text as a control changes.
Definition: IGraphics.cpp:352
EUIResizerMode GetResizerMode() const
Definition: IGraphics.h:1145
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:2580
int GetRoundedScreenScale() const
Gets the screen/display scaling factor, rounded up.
Definition: IGraphics.h:1114
APIBitmap * SearchBitmapInCache(const char *fileName, int targetScale, int &sourceScale)
Search the static storage cache for a bitmap image resource matching the target scale.
Definition: IGraphics.cpp:1909
virtual void DrawLine(const IColor &color, float x1, float y1, float x2, float y2, const IBlend *pBlend=0, float thickness=1.f)
Draw a line to the graphics context.
Definition: IGraphics.cpp:2427
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:2536
void OnTouchCancelled(const std::vector< IMouseInfo > &points)
Called when the platform class sends touch cancel events.
Definition: IGraphics.cpp:1106
bool OnMouseOver(float x, float y, const IMouseMod &mod)
Definition: IGraphics.cpp:1131
void OnDragResize(float x, float y)
Called by ICornerResizerControl as the corner is dragged to resize.
Definition: IGraphics.cpp:1510
virtual PlatformFontPtr LoadPlatformFont(const char *fontID, const char *fileNameOrResID)=0
Load a font from disk or resource in a platform format.
void ForAllControlsFunc(IControlFunction func)
For all controls, including the "special controls" call a method.
Definition: IGraphics.cpp:548
void HideControl(int paramIdx, bool hide)
Hide controls linked to a specific parameter.
Definition: IGraphics.cpp:486
virtual bool LoadFont(const char *fontID, const char *fileNameOrResID)
Load a font to be used by the graphics context.
Definition: IGraphics.cpp:2187
virtual bool BitmapExtSupported(const char *ext)=0
Checks a file extension and reports whether this drawing API supports loading that extension.
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:2543
void OnMouseDown(const std::vector< IMouseInfo > &points)
Called when the platform class sends mouse down events.
Definition: IGraphics.cpp:979
int Height() const
Gets the height of the graphics context.
Definition: IGraphics.h:1090
void PathClipRegion(const IRECT r=IRECT())
Clip the current path to a particular region.
Definition: IGraphics.cpp:2776
void PathCircle(float cx, float cy, float r)
Add a circle to the current path.
Definition: IGraphics.cpp:2699
void PathRadialLine(float cx, float cy, float angle, float rMin, float rMax)
Add a radial line to the current path.
Definition: IGraphics.cpp:826
void PathTransformRotate(float angle)
Apply a rotation transform to the current path.
Definition: IGraphics.cpp:2758
bool OnKeyUp(float x, float y, const IKeyPress &key)
Definition: IGraphics.cpp:1259
bool IsDirty(IRECTList &rects)
Called repeatedly at frame rate by the platform class to check what the graphics context says is dirt...
Definition: IGraphics.cpp:833
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:2587
virtual void GetLayerBitmapData(const ILayerPtr &layer, RawBitmapData &data)=0
Get the contents of a layer as Raw RGBA bitmap data NOTE: you should only call this within IControl::...
bool RespondsToGesture(float x, float y)
Called by platform class to see if the point at x, y is linked to a gesture recognizer.
Definition: IGraphics.cpp:2354
void ForAllControls(T method, Args... args)
For all controls, including the "special controls" call a method.
Definition: IGraphics.cpp:579
void ClearGestureRegions()
Remove all gesture recognizers linked to regions.
Definition: IGraphics.cpp:2404
void SetControlValueAfterTextEdit(const char *str)
Called by the platform class after returning from a text entry in order to update a control with a ne...
Definition: IGraphics.cpp:235
virtual void * GetWinModuleHandle()
Definition: IGraphics.h:926
int NControls() const
Definition: IGraphics.h:1442
virtual void UpdateLayer()
Implemented by a graphics backend to prepare for drawing to the layer at the top of the stack.
Definition: IGraphics.h:578
virtual void DrawData(const IColor &color, const IRECT &bounds, float *normYPoints, int nPoints, float *normXPoints=nullptr, const IBlend *pBlend=0, float thickness=1.f, const IColor *pFillColor=nullptr)
Draw a line between a collection of normalized points.
Definition: IGraphics.cpp:2461
void SetQwertyMidiKeyHandlerFunc(std::function< void(const IMidiMsg &msg)> func=nullptr)
A helper to set the IGraphics KeyHandlerFunc in order to make an instrument playable via QWERTY keys.
Definition: IGraphics.cpp:2286
void AttachSVGBackground(const char *fileName)
Attach an ISVGControl as the lowest IControl in the control stack to be the background for the graphi...
Definition: IGraphics.cpp:295
virtual void FillTriangle(const IColor &color, float x1, float y1, float x2, float y2, float x3, float y3, const IBlend *pBlend=0)
Fill a triangle with a color.
Definition: IGraphics.cpp:2573
void ApplyLayerDropShadow(ILayerPtr &layer, const IShadow &shadow)
Applies a drop shadow directly onto a layer.
Definition: IGraphics.cpp:2086
virtual void DrawGrid(const IColor &color, const IRECT &bounds, float gridSizeH, float gridSizeV, const IBlend *pBlend=0, float thickness=1.f)
Draw a grid to the graphics context.
Definition: IGraphics.cpp:2435
void SetAllControlsDirty()
Calls SetDirty() on every control.
Definition: IGraphics.cpp:590
virtual IBitmap ScaleBitmap(const IBitmap &inBitmap, const char *cacheName, int targetScale)
Returns a new IBitmap, an integer scaled version of the input, and adds it to the cache.
Definition: IGraphics.cpp:1857
IControl * GetControlInTextEntry()
Definition: IGraphics.h:1136
IBitmap GetScaledBitmap(IBitmap &inBitmap)
Get a version of the input bitmap from the cache that corresponds to the current screen scale For exa...
Definition: IGraphics.cpp:1531
void AttachPanelBackground(const IPattern &color)
Attach an IPanelControl as the lowest IControl in the control stack to fill the background with a sol...
Definition: IGraphics.cpp:302
virtual void FillConvexPolygon(const IColor &color, float *x, float *y, int nPoints, const IBlend *pBlend=0)
Fill a convex polygon with a color.
Definition: IGraphics.cpp:2601
bool OnMouseWheel(float x, float y, const IMouseMod &mod, float delta)
Definition: IGraphics.cpp:1231
void SetAllControlsClean()
Calls SetClean() on every control.
Definition: IGraphics.cpp:595
void StyleAllVectorControls(const IVStyle &style)
Helper method to style all of the controls which inherit IVectorBase.
Definition: IGraphics.cpp:1924
virtual IPopupMenu * CreatePlatformPopupMenu(IPopupMenu &menu, const IRECT bounds, bool &isAsync)=0
Calls the platform backend to create the platform popup menu.
void RemoveControlWithTag(int ctrlTag)
Remove controls from the control list with a particular tag.
Definition: IGraphics.cpp:132
void GetTouches(IControl *pControl, std::vector< ITouchID > &touchesOnThisControl) const
Populate a vector with the touchIDs active on pControl.
Definition: IGraphics.h:1410
void DrawVerticalLine(const IColor &color, const IRECT &bounds, float x, const IBlend *pBlend=0, float thickness=1.f)
Draw a vertical line, within a rectangular region of the graphics context.
Definition: IGraphics.cpp:795
virtual void PathMoveTo(float x, float y)=0
Move the current point in the current path.
virtual void PathArc(float cx, float cy, float r, float a1, float a2, EWinding winding=EWinding::CW)=0
Add an arc to the current path.
void PopupHostContextMenuForParam(int controlIdx, int paramIdx, float x, float y)
[VST3 primarily] In VST3 plug-ins this enable support for the IContextMenu interface,...
Definition: IGraphics.cpp:1499
virtual bool FlippedBitmap() const =0
void PromptUserInput(IControl &control, const IRECT &bounds, int valIdx=0)
Prompt for user input either using a text entry or pop up menu.
Definition: IGraphics.cpp:630
void SetControlSize(IControl *pControl, float w, float h)
Resize a control, redrawing the interface correctly.
Definition: IGraphics.cpp:221
float GetScreenScale() const
Gets the screen/display scaling factor, e.g.
Definition: IGraphics.h:1110
virtual bool LoadAPIFont(const char *fontID, const PlatformFontPtr &font)=0
Drawing API method to load a font from a PlatformFontPtr, called internally.
void OnDropMultiple(const std::vector< const char * > &paths, float x, float y)
Definition: IGraphics.cpp:1283
void AssignParamNameToolTips()
Call this method in order to create tool tips for every IControl that show the associated parameter's...
Definition: IGraphics.cpp:600
virtual ISVG LoadSVG(const char *fileNameOrResID, const char *units="px", float dpi=72.f)
Load an SVG from disk or from windows resource.
Definition: IGraphics.cpp:1631
virtual APIBitmap * LoadAPIBitmap(const char *fileNameOrResID, int scale, EResourceLocation location, const char *ext)=0
Drawing API method to load a bitmap, called internally.
virtual void HideMouseCursor(bool hide=true, bool lock=true)=0
Call to hide/show the mouse cursor.
void SetScaleConstraints(float lo, float hi)
Sets the minimum and maximum (draw) scaling values.
Definition: IGraphics.cpp:126
void DrawHorizontalLine(const IColor &color, const IRECT &bounds, float y, const IBlend *pBlend=0, float thickness=1.f)
Draw a horizontal line, within a rectangular region of the graphics context.
Definition: IGraphics.cpp:802
void ForControlInGroup(const char *group, IControlFunction func)
For all standard controls in the main control stack that are linked to a group, execute a function.
Definition: IGraphics.cpp:527
virtual void DoDrawText(const IText &text, const char *str, const IRECT &bounds, const IBlend *pBlend=nullptr)=0
void PathTransformSave()
Save the current affine transform of the current path.
Definition: IGraphics.cpp:2714
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:2608
void SetKeyHandlerFunc(IKeyHandlerFunc func)
Set a function that is called when key presses are not intercepted by any controls.
Definition: IGraphics.h:1211
virtual void DrawDottedLine(const IColor &color, float x1, float y1, float x2, float y2, const IBlend *pBlend=0, float thickness=1.f, float dashLen=2.f)
Draw a dotted line to the graphics context.
Definition: IGraphics.cpp:2490
void RemoveTextEntryControl()
Remove the IGraphics text entry, use platform text entry if available.
Definition: IGraphics.cpp:387
void ShowFPSDisplay(bool enable)
Shows a control to display the frame rate of drawing.
Definition: IGraphics.cpp:437
virtual void PathLineTo(float x, float y)=0
Add a line to the current path from the current point to the specified location.
void DrawLineAcross(const IColor &color, const IRECT &bounds, EDirection dir, float pos, const IBlend *pBlend=0, float thickness=1.f)
Draw a horzional or vertical line, within a rectangular region of the graphics context.
Definition: IGraphics.cpp:787
const char * GetSharedResourcesSubPath() const
Gets the name of the shared resources subpath.
Definition: IGraphics.h:1642
void PathTransformSkew(float xAngle, float yAngle)
Apply a skew transform to the current path.
Definition: IGraphics.cpp:2764
float GetDrawScale() const
Gets the graphics context scaling factor.
Definition: IGraphics.h:1106
void DrawFittedLayer(const ILayerPtr &layer, const IRECT &bounds, const IBlend *pBlend)
Draw a layer to the main IGraphics context, fitting it to a rectangle that is different to the layer'...
Definition: IGraphics.cpp:2064
void RemoveControls(int fromIdx)
Remove controls from the control list above a particular index, (frees memory).
Definition: IGraphics.cpp:139
void AttachBackground(const char *fileName)
Attach an IBitmapControl as the lowest IControl in the control stack to be the background for the gra...
Definition: IGraphics.cpp:288
void SetStrictDrawing(bool strict)
Enables strict drawing mode.
Definition: IGraphics.cpp:973
void OnGUIIdle()
This is an idle timer tick call on the GUI thread, only active if USE_IDLE_CALLS is defined.
Definition: IGraphics.cpp:1504
IGEditorDelegate * GetDelegate()
Gets a pointer to the delegate class that handles communication to and from this graphics context.
Definition: IGraphics.h:1127
IControl * GetControl(int idx)
Get the control at a certain index in the control stack.
Definition: IGraphics.h:1359
void StartLayer(IControl *pOwner, const IRECT &r, bool cacheable=false)
Create an IGraphics layer.
Definition: IGraphics.cpp:1988
void OnGestureRecognized(const IGestureInfo &info)
Called by platform class when a gesture is recognized.
Definition: IGraphics.cpp:2374
virtual void CachePlatformFont(const char *fontID, const PlatformFontPtr &font)=0
Called to indicate that the platform should cache data about the platform font if needed.
bool OnMouseDblClick(float x, float y, const IMouseMod &mod)
Definition: IGraphics.cpp:1203
virtual void DrawSVG(const ISVG &svg, const IRECT &bounds, const IBlend *pBlend=0, const IColor *pStrokeColor=nullptr, const IColor *pFillColor=nullptr)
Draw an SVG image to the graphics context.
Definition: IGraphics.cpp:2795
void ForStandardControlsFunc(IControlFunction func)
For all standard controls in the main control stack perform a function.
Definition: IGraphics.cpp:542
virtual void DrawTriangle(const IColor &color, float x1, float y1, float x2, float y2, float x3, float y3, const IBlend *pBlend=0, float thickness=1.f)
Draw a triangle to the graphics context.
Definition: IGraphics.cpp:2501
virtual void PathCubicBezierTo(float c1x, float c1y, float c2x, float c2y, float x2, float y2)=0
Add a cubic bezier to the current path from the current point to the specified location.
int WindowHeight() const
Gets the height of the graphics context including draw scaling.
Definition: IGraphics.h:1098
void PathEllipse(const IRECT &bounds)
Add an ellipse to the current path, specifying the rectangular region.
Definition: IGraphics.cpp:2694
virtual IBitmap LoadBitmap(const char *fileNameOrResID, int nStates=1, bool framesAreHorizontal=false, int targetScale=0)
Load a bitmap image from disk or from windows resource.
Definition: IGraphics.cpp:1732
virtual void BeginFrame()
Called at the beginning of drawing.
Definition: IGraphics.cpp:875
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:2617
void SetControlPosition(IControl *pControl, float x, float y)
Reposition a control, redrawing the interface correctly.
Definition: IGraphics.cpp:214
void SetScreenScale(float scale)
Called by the platform IGraphics class when moving to a new screen to set DPI.
Definition: IGraphics.cpp:75
ILayerPtr EndLayer()
End an IGraphics layer.
Definition: IGraphics.cpp:2011
virtual WDL_TypedBuf< uint8_t > LoadResource(const char *fileNameOrResID, const char *fileType)
Load a resource from the file system, the bundle, or a Windows resource, and returns its data.
Definition: IGraphics.cpp:1677
void OnDrop(const char *str, float x, float y)
Definition: IGraphics.cpp:1277
IRECT GetBounds() const
Returns an IRECT that represents the entire UI bounds This is useful for programatically arranging UI...
Definition: IGraphics.h:1199
IControl * AttachControl(IControl *pControl, int ctrlTag=kNoTag, const char *group="")
Attach an IControl to the graphics context and add it to the top of the control stack.
Definition: IGraphics.cpp:309
virtual void DrawBitmap(const IBitmap &bitmap, const IRECT &bounds, int srcX, int srcY, const IBlend *pBlend=0)=0
Draw a bitmap (raster) image to the graphics context.
virtual float MeasureText(const IText &text, const char *str, IRECT &bounds) const
Measure the rectangular region that some text will occupy.
Definition: IGraphics.cpp:686
An abstraction that is used to store a temporary raster image/framebuffer.
const IRECT & Bounds() const
A basic control to fill a rectangle with a color or gradient.
Definition: IControl.h:1974
IPlug's parameter class.
EParamType Type() const
Get the parameter's type.
double ToNormalized(double nonNormalizedValue) const
Convert a real value to normalized value for this parameter.
void GetDisplay(WDL_String &display, bool withDisplayText=true) const
Get the current textual display for the current parameter value.
const char * GetLabel() const
Returns the parameter's label.
double StringToValue(const char *str) const
Convert a textual representation of the parameter value to a double (real value)
const char * GetName() const
Returns the parameter's name.
const char * GetDisplayText(double value) const
Get the display text for a particular value.
int NDisplayTexts() const
Get the number of display texts for the parameter.
EParamType
Defines types or parameter.
A class to specify an item of a pop up menu.
A class for setting the contents of a pop up menu.
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
void Clear()
Clear the list.
IRECT Bounds()
Get a union of all rectangles in the list.
int Size() const
void Add(const IRECT &rect)
Add a rectangle to the list.
void PixelAlign()
Align the rectangles to pixel boundaries.
void Optimize()
Remove rects that are contained by other rects and intersections and merge any rects that can be merg...
int Find(float x, float y) const
Find the first index of the rect that contains point x, y, if it exists.
const IRECT & Get(int idx) const
Get an IRECT from the list (will crash if idx is invalid)
A basic control to draw an SVG image to the screen.
Definition: IControl.h:2117
A base interface to be combined with IControl for vectorial controls "IVControls",...
Definition: IControl.h:762
virtual void SetStyle(const IVStyle &style)
Set the Style of this IVControl.
Definition: IControl.h:830
std::unique_ptr< ILayer > ILayerPtr
ILayerPtr is a managed pointer for transferring the ownership of layers.
Used to describe a particular gesture.
Used to group mouse coordinates with mouse modifier information.
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.
Used to manage composite/blend operations, independent of draw class/platform.
Used to manage color data, independent of draw class/platform.
void Randomise(int alpha=255)
Randomise the color parts, with optional alpha.
Used to manage fill behaviour.
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Definition: IPlugStructs.h:616
Used to store transformation matrices.
IMatrix & Rotate(float a)
Set the matrix for a rotation transform.
IMatrix & Scale(float x, float y)
Set the matrix for a scale transform.
IMatrix & Transform(const IRECT &before, const IRECT &after)
IMatrix & Translate(float x, float y)
Set the matrix for a translation transform.
IMatrix & Skew(float xa, float ya)
Set the matrix for a skew transform.
void TransformPoint(double &x, double &y, double x0, double y0) const
Transforms the point x, y.
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
void MakeNoteOnMsg(int noteNumber, int velocity, int offset, int channel=0)
Make a Note On message.
Definition: IPlugMidi.h:145
void MakeNoteOffMsg(int noteNumber, int offset, int channel=0)
Make a Note Off message.
Definition: IPlugMidi.h:158
Used to manage mouse modifiers i.e.
Used to store pattern information for gradients.
Used to manage a rectangular area, independent of draw class/platform.
IRECT Intersect(const IRECT &rhs) const
Create a new IRECT that is the intersection of this IRECT and rhs.
IRECT GetPixelAligned() const
Get a copy of this IRECT with PixelAlign() called.
void PixelAlign()
Pixel aligns the rect in an inclusive manner (moves all points outwards)
float MH() const
bool Empty() const
float W() const
void Clank(const IRECT &rhs)
Clank will limit this IRECT's bounds based on the boundaries of the IRECT passed in as an argument.
IRECT Union(const IRECT &rhs) const
Create a new IRECT that is a union of this IRECT and rhs.
bool Intersects(const IRECT &rhs) const
Returns true if this IRECT shares any common pixels with rhs, false otherwise.
float H() const
void Translate(float x, float y)
Translate this rectangle.
float MW() const
bool Contains(const IRECT &rhs) const
Returns true if this IRECT completely contains rhs.
IRECT GetPadded(float padding) const
Get a copy of this IRECT with each value padded by padding N.B.
User-facing SVG abstraction that you use to manage SVG data ISVG doesn't actually own the image data.
float H() const
float W() 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,...
A struct encapsulating a set of properties used to configure IVControls.
Encapsulate an xy point in one struct.