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