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