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