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