iPlug2 - C++ Audio Plug-in Framework
IGraphicsStructs.h
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 #pragma once
12 
19 #include <functional>
20 #include <chrono>
21 #include <numeric>
22 
23 #include "IPlugUtilities.h"
24 #include "IPlugLogger.h"
25 #include "IPlugStructs.h"
26 
27 #include "IGraphicsPrivate.h"
28 #include "IGraphicsUtilities.h"
29 #include "IGraphicsConstants.h"
30 
31 BEGIN_IPLUG_NAMESPACE
32 BEGIN_IGRAPHICS_NAMESPACE
33 
34 class IGraphics;
35 class IControl;
36 class ILambdaControl;
37 class IPopupMenu;
38 struct IRECT;
39 struct IVec2;
40 struct IMouseInfo;
41 struct IColor;
42 struct IGestureInfo;
43 
44 using IActionFunction = std::function<void(IControl*)>;
45 using IAnimationFunction = std::function<void(IControl*)>;
46 using ILambdaDrawFunction = std::function<void(ILambdaControl*, IGraphics&, IRECT&)>;
47 using IKeyHandlerFunc = std::function<bool(const IKeyPress& key, bool isUp)>;
48 using IMsgBoxCompletionHanderFunc = std::function<void(EMsgBoxResult result)>;
49 using IColorPickerHandlerFunc = std::function<void(const IColor& result)>;
50 using IGestureFunc = std::function<void(IControl*, const IGestureInfo&)>;
51 using IPopupFunction = std::function<void(IPopupMenu* pMenu)>;
52 using IDisplayTickFunc = std::function<void()>;
53 using ITouchID = uintptr_t;
54 
56 void EmptyClickActionFunc(IControl* pCaller);
57 
59 void DefaultClickActionFunc(IControl* pCaller);
60 
62 void DefaultAnimationFunc(IControl* pCaller);
63 
65 void SplashClickActionFunc(IControl* pCaller);
66 
68 void SplashAnimationFunc(IControl* pCaller);
69 
72 
75 
76 using MTLTexturePtr = void*;
77 
78 using Milliseconds = std::chrono::duration<double, std::chrono::milliseconds::period>;
79 using TimePoint = std::chrono::time_point<std::chrono::high_resolution_clock, Milliseconds>;
80 
84 class IBitmap
85 {
86 public:
92  IBitmap(APIBitmap* pAPIBitmap, int n, bool framesAreHorizontal, const char* name = "")
93  : mAPIBitmap(pAPIBitmap)
94  , mW(pAPIBitmap->GetWidth() / pAPIBitmap->GetScale())
95  , mH(pAPIBitmap->GetHeight() / pAPIBitmap->GetScale())
96  , mN(n)
97  , mFramesAreHorizontal(framesAreHorizontal)
98  , mResourceName(name, static_cast<int>(strlen(name)))
99  {
100  }
101 
102  IBitmap()
103  : mAPIBitmap(nullptr)
104  , mW(0)
105  , mH(0)
106  , mN(0)
107  , mFramesAreHorizontal(false)
108  {
109  }
110 
112  int W() const { return mW; }
113 
115  int H() const { return mH; }
116 
118  int FW() const { return (mFramesAreHorizontal ? mW / mN : mW); }
119 
121  int FH() const { return (mFramesAreHorizontal ? mH : mH / mN); }
122 
124  int N() const { return mN; }
125 
127  int GetScale() const { return mAPIBitmap->GetScale(); }
128 
130  float GetDrawScale() const { return mAPIBitmap->GetDrawScale(); }
131 
133  APIBitmap* GetAPIBitmap() const { return mAPIBitmap; }
134 
136  bool GetFramesAreHorizontal() const { return mFramesAreHorizontal; }
137 
139  const WDL_String& GetResourceName() const { return mResourceName; }
140 
142  inline bool IsValid() const { return mAPIBitmap != nullptr; }
143 
144 private:
146  APIBitmap* mAPIBitmap;
148  int mW;
150  int mH;
152  int mN;
154  bool mFramesAreHorizontal;
156  WDL_String mResourceName;
157 };
158 
162 #ifdef IGRAPHICS_SKIA
163 struct ISVG
164 {
165  ISVG(sk_sp<SkSVGDOM> svgDom)
166  : mSVGDom(svgDom)
167  {
168  }
169 
171  float W() const
172  {
173  if (mSVGDom)
174  return mSVGDom->containerSize().width();
175  else
176  return 0;
177  }
178 
180  float H() const
181  {
182  if (mSVGDom)
183  return mSVGDom->containerSize().height();
184  else
185  return 0;
186  }
187 
189  inline bool IsValid() const { return mSVGDom != nullptr; }
190 
191  sk_sp<SkSVGDOM> mSVGDom;
192 };
193 #else
194 struct ISVG
195 {
196  ISVG(NSVGimage* pImage)
197  {
198  mImage = pImage;
199  }
200 
202  float W() const
203  {
204  if (mImage)
205  return mImage->width;
206  else
207  return 0;
208  }
209 
211  float H() const
212  {
213  if (mImage)
214  return mImage->height;
215  else
216  return 0;
217  }
218 
220  inline bool IsValid() const { return mImage != nullptr; }
221 
222  NSVGimage* mImage = nullptr;
223 };
224 #endif
225 
227 struct IColor
228 {
229  int A, R, G, B;
230 
236  IColor(int a = 255, int r = 0, int g = 0, int b = 0) : A(a), R(r), G(g), B(b) {}
237 
238  bool operator==(const IColor& rhs) { return (rhs.A == A && rhs.R == R && rhs.G == G && rhs.B == B); }
239  bool operator!=(const IColor& rhs) { return !operator==(rhs); }
240 
246  void Set(int a = 255, int r = 0, int g = 0, int b = 0) { A = a; R = r; G = g; B = b; }
247 
249  bool Empty() const { return A == 0 && R == 0 && G == 0 && B == 0; }
250 
252  void Clamp() { A = Clip(A, 0, 255); R = Clip(R, 0, 255); G = Clip(G, 0, 255); B = Clip(B, 0, 255); }
253 
256  void Randomise(int alpha = 255) { A = alpha; R = std::rand() % 255; G = std::rand() % 255; B = std::rand() % 255; }
257 
260  void SetOpacity(float alpha)
261  {
262  A = static_cast<int>(Clip(alpha, 0.f, 1.f) * 255.f);
263  }
264 
268  IColor WithOpacity(float alpha) const
269  {
270  IColor n = *this;
271  n.SetOpacity(alpha);
272  return n;
273  }
274 
277  void Contrast(float c)
278  {
279  const int mod = static_cast<int>(c * 255.f);
280  R = Clip(R += mod, 0, 255);
281  G = Clip(G += mod, 0, 255);
282  B = Clip(B += mod, 0, 255);
283  }
284 
288  IColor WithContrast(float c) const
289  {
290  IColor n = *this;
291  n.Contrast(c);
292  return n;
293  }
294 
297  void GetRGBf(float* rgbf) const
298  {
299  rgbf[0] = R / 255.f;
300  rgbf[1] = G / 255.f;
301  rgbf[2] = B / 255.f;
302  }
303 
306  void GetRGBAf(float* rgbaf) const
307  {
308  rgbaf[0] = R / 255.f;
309  rgbaf[1] = G / 255.f;
310  rgbaf[2] = B / 255.f;
311  rgbaf[3] = A / 255.f;
312  }
313 
319  void GetHSLA(float& h, float& s, float& l, float& a) const
320  {
321  const float fR = R / 255.f;
322  const float fG = G / 255.f;
323  const float fB = B / 255.f;
324  a = A / 255.f;
325 
326  const float fMin = std::min(fR, std::min(fG, fB));
327  const float fMax = std::max(fR, std::max(fG, fB));
328  const float fDiff = fMax - fMin;
329  const float fSum = fMax + fMin;
330 
331  l = 50.f * fSum;
332 
333  if (fMin == fMax) { s = 0.f; h = 0.f; l /= 100.f; return; }
334  else if (l < 50.f) { s = 100.f * fDiff / fSum; }
335  else { s = 100.f * fDiff / (2.f - fDiff); }
336 
337  if (fMax == fR) { h = 60.f * (fG - fB) / fDiff; }
338  if (fMax == fG) { h = 60.f * (fB - fR) / fDiff + 120.f; }
339  if (fMax == fB) { h = 60.f * (fR - fG) / fDiff + 240.f; }
340 
341  if (h < 0.f) { h = h + 360.f; }
342 
343  h /= 360.f;
344  s /= 100.f;
345  l /= 100.f;
346  }
347 
350  int GetLuminosity() const
351  {
352  int min = R < G ? (R < B ? R : B) : (G < B ? G : B);
353  int max = R > G ? (R > B ? R : B) : (G > B ? G : B);
354  return (min + max) / 2;
355  };
356 
360  static IColor GetRandomColor(bool randomAlpha = false)
361  {
362  int A = randomAlpha ? std::rand() & 0xFF : 255;
363  int R = std::rand() & 0xFF;
364  int G = std::rand() & 0xFF;
365  int B = std::rand() & 0xFF;
366 
367  return IColor(A, R, G, B);
368  }
369 
373  static IColor FromRGBf(float* rgbf)
374  {
375  int A = 255;
376  int R = static_cast<int>(rgbf[0] * 255.f);
377  int G = static_cast<int>(rgbf[1] * 255.f);
378  int B = static_cast<int>(rgbf[2] * 255.f);
379 
380  return IColor(A, R, G, B);
381  }
382 
386  static IColor FromRGBAf(float* rgbaf)
387  {
388  int R = static_cast<int>(rgbaf[0] * 255.f);
389  int G = static_cast<int>(rgbaf[1] * 255.f);
390  int B = static_cast<int>(rgbaf[2] * 255.f);
391  int A = static_cast<int>(rgbaf[3] * 255.f);
392 
393  return IColor(A, R, G, B);
394  }
395 
405  static IColor FromColorCode(int colorCode, int A = 0xFF)
406  {
407  int R = (colorCode >> 16) & 0xFF;
408  int G = (colorCode >> 8) & 0xFF;
409  int B = colorCode & 0xFF;
410 
411  return IColor(A, R, G, B);
412  }
413 
417  static IColor FromColorCodeStr(const char* hexStr)
418  {
419  WDL_String str(hexStr);
420 
421  if(str.GetLength() == 7 && str.Get()[0] == '#')
422  {
423  str.DeleteSub(0, 1);
424 
425  return FromColorCode(static_cast<int>(std::stoul(str.Get(), nullptr, 16)));
426  }
427  else
428  {
429  assert(0 && "Invalid color code str, returning black");
430  return IColor();
431  }
432  }
433 
435  int ToColorCode() const
436  {
437  return (R << 16) | (G << 8) | B;
438  }
439 
441  void ToColorCodeStr(WDL_String& str) const
442  {
443  str.SetFormatted(32, "#%02x%02x%02x%02x", R, G, B, A);
444  }
445 
452  static IColor FromHSLA(float h, float s, float l, float a = 1.f)
453  {
454  auto hue = [](float h, float m1, float m2) {
455  if (h < 0) h += 1;
456  if (h > 1) h -= 1;
457  if (h < 1.0f / 6.0f)
458  return m1 + (m2 - m1) * h * 6.0f;
459  else if (h < 3.0f / 6.0f)
460  return m2;
461  else if (h < 4.0f / 6.0f)
462  return m1 + (m2 - m1) * (2.0f / 3.0f - h) * 6.0f;
463  return m1;
464  };
465 
466  IColor col;
467  h = std::fmodf(h, 1.0f);
468  if (h < 0.0f) h += 1.0f;
469  s = Clip(s, 0.0f, 1.0f);
470  l = Clip(l, 0.0f, 1.0f);
471  float m2 = l <= 0.5f ? (l * (1 + s)) : (l + s - l * s);
472  float m1 = 2 * l - m2;
473  col.R = static_cast<int>(Clip(hue(h + 1.0f / 3.0f, m1, m2), 0.0f, 1.0f) * 255.f);
474  col.G = static_cast<int>(Clip(hue(h, m1, m2), 0.0f, 1.0f) * 255.f);
475  col.B = static_cast<int>(Clip(hue(h - 1.0f / 3.0f, m1, m2), 0.0f, 1.0f) * 255.f);
476  col.A = static_cast<int>(a * 255.f);
477  return col;
478  }
479 
484  static IColor LinearInterpolateBetween(const IColor& start, const IColor& dest, float progress)
485  {
486  IColor result;
487  result.A = start.A + static_cast<int>(progress * static_cast<float>(dest.A - start.A));
488  result.R = start.R + static_cast<int>(progress * static_cast<float>(dest.R - start.R));
489  result.G = start.G + static_cast<int>(progress * static_cast<float>(dest.G - start.G));
490  result.B = start.B + static_cast<int>(progress * static_cast<float>(dest.B - start.B));
491  return result;
492  }
493 };
494 
495 const IColor COLOR_TRANSPARENT(0, 0, 0, 0);
496 const IColor COLOR_TRANSLUCENT(10, 0, 0, 0);
497 const IColor COLOR_BLACK(255, 0, 0, 0);
498 const IColor COLOR_BLACK_DROP_SHADOW(128, 0, 0, 0);
499 const IColor COLOR_GRAY(255, 127, 127, 127);
500 const IColor COLOR_LIGHT_GRAY(255, 240, 240, 240);
501 const IColor COLOR_MID_GRAY(255, 200, 200, 200);
502 const IColor COLOR_DARK_GRAY(255, 70, 70, 70);
503 const IColor COLOR_WHITE(255, 255, 255, 255);
504 const IColor COLOR_RED(255, 255, 0, 0);
505 const IColor COLOR_GREEN(255, 0, 255, 0);
506 const IColor COLOR_BLUE(255, 0, 0, 255);
507 const IColor COLOR_YELLOW(255, 255, 255, 0);
508 const IColor COLOR_ORANGE(255, 255, 127, 0);
509 const IColor COLOR_INDIGO(255, 75, 0, 130);
510 const IColor COLOR_VIOLET(255, 148, 0, 211);
511 
512 static IColor GetRainbow(int colorIdx)
513 {
514  switch (colorIdx) {
515  case 0: return COLOR_RED;
516  case 1: return COLOR_ORANGE;
517  case 2: return COLOR_YELLOW;
518  case 3: return COLOR_GREEN;
519  case 4: return COLOR_BLUE;
520  case 5: return COLOR_INDIGO;
521  case 6: return COLOR_VIOLET;
522  default:
523  assert(0);
524  return COLOR_WHITE;
525  }
526 }
527 
528 const IColor DEFAULT_GRAPHICS_BGCOLOR = COLOR_GRAY;
529 const IColor DEFAULT_BGCOLOR = COLOR_TRANSPARENT;
530 const IColor DEFAULT_FGCOLOR = COLOR_MID_GRAY;
531 const IColor DEFAULT_PRCOLOR = COLOR_LIGHT_GRAY;
532 
533 const IColor DEFAULT_FRCOLOR = COLOR_DARK_GRAY;
534 const IColor DEFAULT_HLCOLOR = COLOR_TRANSLUCENT;
535 const IColor DEFAULT_SHCOLOR = IColor(60, 0, 0, 0);
536 const IColor DEFAULT_X1COLOR = COLOR_BLACK;
537 const IColor DEFAULT_X2COLOR = COLOR_GREEN;
538 const IColor DEFAULT_X3COLOR = COLOR_BLUE;
539 
540 const IColor DEFAULT_TEXT_FGCOLOR = COLOR_BLACK;
541 const IColor DEFAULT_TEXTENTRY_BGCOLOR = COLOR_WHITE;
542 const IColor DEFAULT_TEXTENTRY_FGCOLOR = COLOR_BLACK;
543 
545 struct IBlend
546 {
547  EBlend mMethod;
548  float mWeight;
549 
553  IBlend(EBlend type = EBlend::Default, float weight = 1.0f)
554  : mMethod(type)
555  , mWeight(Clip(weight, 0.f, 1.f))
556  {}
557 };
558 
562 inline float BlendWeight(const IBlend* pBlend)
563 {
564  return (pBlend ? pBlend->mWeight : 1.0f);
565 }
566 
567 const IBlend BLEND_75 = IBlend(EBlend::Default, 0.75f);
568 const IBlend BLEND_50 = IBlend(EBlend::Default, 0.5f);
569 const IBlend BLEND_25 = IBlend(EBlend::Default, 0.25f);
570 const IBlend BLEND_10 = IBlend(EBlend::Default, 0.1f);
571 const IBlend BLEND_05 = IBlend(EBlend::Default, 0.05f);
572 const IBlend BLEND_01 = IBlend(EBlend::Default, 0.01f);
573 const IBlend BLEND_DST_IN = IBlend(EBlend::DstIn, 1.f);
574 const IBlend BLEND_DST_OVER = IBlend(EBlend::DstOver, 1.f);
575 
578 {
579  EFillRule mFillRule { EFillRule::Winding };
580  bool mPreserve { false };
581 
582  IFillOptions(bool preserve = false, EFillRule fillRule = EFillRule::Winding)
583  : mFillRule(fillRule)
584  , mPreserve(preserve)
585  {
586  }
587 
588 };
589 
592 {
594  class DashOptions
595  {
596  public:
598  DashOptions()
599  : mOffset(0)
600  , mCount(0)
601  {}
602 
607  DashOptions(float* array, float offset, int count)
608  {
609  SetDash(array, offset, count);
610  }
611 
613  int GetCount() const { return mCount; }
614 
616  float GetOffset() const { return mOffset; }
617 
619  const float* GetArray() const { return mArray; }
620 
625  void SetDash(float* pArray, float offset, int count)
626  {
627  assert(count >= 0 && count <= 8);
628 
629  mCount = count;
630  mOffset = offset;
631 
632  for (int i = 0; i < count; i++)
633  mArray[i] = pArray[i];
634  }
635 
636  private:
637  float mArray[8] = {};
638  float mOffset;
639  int mCount;
640  };
641 
642  float mMiterLimit = 10.f;
643  bool mPreserve = false;
644  ELineCap mCapOption = ELineCap::Butt;
645  ELineJoin mJoinOption = ELineJoin::Miter;
646  DashOptions mDash;
647 };
648 
650 static const char* TextStyleString(ETextStyle style)
651 {
652  switch (style)
653  {
654  case ETextStyle::Bold: return "Bold";
655  case ETextStyle::Italic: return "Italic";
656  case ETextStyle::Normal:
657  default:
658  return "Regular";
659  }
660 }
661 
663 struct IText
664 {
674  IText(float size = DEFAULT_TEXT_SIZE,
675  const IColor& color = DEFAULT_TEXT_FGCOLOR,
676  const char* fontID = nullptr,
677  EAlign align = EAlign::Center,
678  EVAlign valign = EVAlign::Middle,
679  float angle = 0,
680  const IColor& TEBGColor = DEFAULT_TEXTENTRY_BGCOLOR,
681  const IColor& TEFGColor = DEFAULT_TEXTENTRY_FGCOLOR)
682  : mSize(size)
683  , mFGColor(color)
684  , mTextEntryBGColor(TEBGColor)
685  , mTextEntryFGColor(TEFGColor)
686  , mAngle(angle)
687  , mAlign(align)
688  , mVAlign(valign)
689  {
690  strcpy(mFont, (fontID ? fontID : DEFAULT_FONT));
691  }
692 
697  IText(float size, EVAlign valign, const IColor& color = DEFAULT_TEXT_FGCOLOR)
698  : IText()
699  {
700  mSize = size;
701  mVAlign = valign;
702  mFGColor = color;
703  }
704 
709  IText(float size, EAlign align, const IColor& color = DEFAULT_TEXT_FGCOLOR)
710  : IText()
711  {
712  mSize = size;
713  mAlign = align;
714  mFGColor = color;
715  }
716 
720  IText(float size, const char* fontID)
721  : IText()
722  {
723  mSize = size;
724  strcpy(mFont, (fontID ? fontID : DEFAULT_FONT));
725  }
726 
727  IText WithFGColor(const IColor& fgColor) const { IText newText = *this; newText.mFGColor = fgColor; return newText; }
728  IText WithTEColors(const IColor& teBgColor, const IColor& teFgColor) const { IText newText = *this; newText.mTextEntryBGColor = teBgColor; newText.mTextEntryFGColor = teFgColor; return newText; }
729  IText WithAlign(EAlign align) const { IText newText = *this; newText.mAlign = align; return newText; }
730  IText WithVAlign(EVAlign valign) const { IText newText = *this; newText.mVAlign = valign; return newText; }
731  IText WithSize(float size) const { IText newText = *this; newText.mSize = size; return newText; }
732  IText WithAngle(float v) const { IText newText = *this; newText.mAngle = v; return newText; }
733  IText WithFont(const char* fontID) const { IText newText = *this; strcpy(newText.mFont, (fontID ? fontID : DEFAULT_FONT));; return newText; }
734 
735  char mFont[FONT_LEN];
736  float mSize;
737  IColor mFGColor;
738  IColor mTextEntryBGColor;
739  IColor mTextEntryFGColor;
740  float mAngle = 0.f; // Degrees ccwise from normal.
741  EAlign mAlign = EAlign::Near;
742  EVAlign mVAlign = EVAlign::Middle;
743 };
744 
745 const IText DEFAULT_TEXT = IText();
746 
750 struct IRECT
751 {
753  float L;
755  float T;
757  float R;
759  float B;
760 
763  {
764  L = T = R = B = 0.f;
765  }
766 
772  IRECT(float l, float t, float r, float b)
773  : L(l), T(t), R(r), B(b)
774  {}
775 
780  IRECT(float x, float y, const IBitmap& bitmap)
781  {
782  L = x;
783  T = y;
784  R = L + (float) bitmap.FW();
785  B = T + (float) bitmap.FH();
786  }
787 
794  static IRECT MakeXYWH(float l, float t, float w, float h)
795  {
796  return IRECT(l, t, l+w, t+h);
797  }
798 
800  bool Empty() const
801  {
802  return (L == 0.f && T == 0.f && R == 0.f && B == 0.f);
803  }
804 
806  void Clear()
807  {
808  L = T = R = B = 0.f;
809  }
810 
811  bool operator==(const IRECT& rhs) const
812  {
813  return (L == rhs.L && T == rhs.T && R == rhs.R && B == rhs.B);
814  }
815 
816  bool operator!=(const IRECT& rhs) const
817  {
818  return !(*this == rhs);
819  }
820 
822  inline float W() const { return R - L; }
823 
825  inline float H() const { return B - T; }
826 
828  inline float MW() const { return 0.5f * (L + R); }
829 
831  inline float MH() const { return 0.5f * (T + B); }
832 
834  inline float Area() const { return W() * H(); }
835 
840  inline IRECT Union(const IRECT& rhs) const
841  {
842  if (Empty()) { return rhs; }
843  if (rhs.Empty()) { return *this; }
844  return IRECT(std::min(L, rhs.L), std::min(T, rhs.T), std::max(R, rhs.R), std::max(B, rhs.B));
845  }
846 
851  inline IRECT Intersect(const IRECT& rhs) const
852  {
853  if (Intersects(rhs))
854  return IRECT(std::max(L, rhs.L), std::max(T, rhs.T), std::min(R, rhs.R), std::min(B, rhs.B));
855 
856  return IRECT();
857  }
858 
863  inline bool Intersects(const IRECT& rhs) const
864  {
865  return (!Empty() && !rhs.Empty() && R >= rhs.L && L < rhs.R && B >= rhs.T && T < rhs.B);
866  }
867 
872  inline bool Contains(const IRECT& rhs) const
873  {
874  return (!Empty() && !rhs.Empty() && rhs.L >= L && rhs.R <= R && rhs.T >= T && rhs.B <= B);
875  }
876 
882  inline bool Contains(float x, float y) const
883  {
884  return (!Empty() && x >= L && x < R && y >= T && y < B);
885  }
886 
893  inline bool ContainsEdge(float x, float y) const
894  {
895  return (!Empty() && x >= L && x <= R && y >= T && y <= B);
896  }
897 
901  inline void Constrain(float& x, float& y) const
902  {
903  if (x < L) x = L;
904  else if (x > R) x = R;
905 
906  if (y < T) y = T;
907  else if (y > B) y = B;
908  }
909 
912  IRECT Inset(const IRECT& rhs) const
913  {
914  return IRECT(L + rhs.L, T + rhs.T, L + rhs.R, T + rhs.B);
915  }
916 
922  bool Mergeable(const IRECT& rhs) const
923  {
924  if (Empty() || rhs.Empty())
925  return true;
926  if (L == rhs.L && R == rhs.R && ((T >= rhs.T && T <= rhs.B) || (rhs.T >= T && rhs.T <= B)))
927  return true;
928  return T == rhs.T && B == rhs.B && ((L >= rhs.L && L <= rhs.R) || (rhs.L >= L && rhs.L <= R));
929  }
930 
937  inline IRECT FracRect(EDirection layoutDir, float frac, bool fromTopOrRight = false) const
938  {
939  if(layoutDir == EDirection::Vertical)
940  return FracRectVertical(frac, fromTopOrRight);
941  else
942  return FracRectHorizontal(frac, fromTopOrRight);
943  }
944 
949  inline IRECT FracRectHorizontal(float frac, bool rhs = false) const
950  {
951  float widthOfSubRect = W() * frac;
952 
953  if(rhs)
954  return IRECT(R - widthOfSubRect, T, R, B);
955  else
956  return IRECT(L, T, L + widthOfSubRect, B);
957  }
958 
963  inline IRECT FracRectVertical(float frac, bool fromTop = false) const
964  {
965  float heightOfSubRect = H() * frac;
966 
967  if(fromTop)
968  return IRECT(L, T, R, T + heightOfSubRect);
969  else
970  return IRECT(L, B - heightOfSubRect, R, B);
971  }
972 
980  inline IRECT SubRectVertical(int numSlices, int sliceIdx) const
981  {
982  float heightOfSubRect = H() / (float) numSlices;
983  float t = heightOfSubRect * (float) sliceIdx;
984 
985  return IRECT(L, T + t, R, T + t + heightOfSubRect);
986  }
987 
995  inline IRECT SubRectHorizontal(int numSlices, int sliceIdx) const
996  {
997  float widthOfSubRect = W() / (float) numSlices;
998  float l = widthOfSubRect * (float) sliceIdx;
999 
1000  return IRECT(L + l, T, L + l + widthOfSubRect, B);
1001  }
1002 
1008  inline IRECT SubRect(EDirection layoutDir, int numSlices, int sliceIdx) const
1009  {
1010  if(layoutDir == EDirection::Vertical)
1011  return SubRectVertical(numSlices, sliceIdx);
1012  else
1013  return SubRectHorizontal(numSlices, sliceIdx);
1014  }
1015 
1020  inline IRECT GetFromTLHC(float w, float h) const { return IRECT(L, T, L+w, T+h); }
1021 
1026  inline IRECT GetFromBLHC(float w, float h) const { return IRECT(L, B-h, L+w, B); }
1027 
1032  inline IRECT GetFromTRHC(float w, float h) const { return IRECT(R-w, T, R, T+h); }
1033 
1038  inline IRECT GetFromBRHC(float w, float h) const { return IRECT(R-w, B-h, R, B); }
1039 
1043  inline IRECT GetFromTop(float amount) const { return IRECT(L, T, R, T+amount); }
1044 
1048  inline IRECT GetFromBottom(float amount) const { return IRECT(L, B-amount, R, B); }
1049 
1053  inline IRECT GetFromLeft(float amount) const { return IRECT(L, T, L+amount, B); }
1054 
1058  inline IRECT GetFromRight(float amount) const { return IRECT(R-amount, T, R, B); }
1059 
1063  inline IRECT GetReducedFromTop(float amount) const { return IRECT(L, T+amount, R, B); }
1064 
1068  inline IRECT GetReducedFromBottom(float amount) const { return IRECT(L, T, R, B-amount); }
1069 
1073  inline IRECT GetReducedFromLeft(float amount) const { return IRECT(L+amount, T, R, B); }
1074 
1078  inline IRECT GetReducedFromRight(float amount) const { return IRECT(L, T, R-amount, B); }
1079 
1083  inline IRECT ReduceFromTop(float amount) { IRECT r = GetFromTop(amount); T+=amount; return r; }
1084 
1088  inline IRECT ReduceFromBottom(float amount) { IRECT r = GetFromBottom(amount); B-=amount; return r; }
1089 
1093  inline IRECT ReduceFromLeft(float amount) { IRECT r = GetFromLeft(amount); L+=amount; return r; }
1094 
1098  inline IRECT ReduceFromRight(float amount) { IRECT r = GetFromRight(amount); R-=amount; return r; }
1099 
1106  inline IRECT GetGridCell(int row, int col, int nRows, int nColumns/*, EDirection = EDirection::Horizontal*/) const
1107  {
1108  assert(row * col <= nRows * nColumns); // not enough cells !
1109 
1110  const IRECT vrect = SubRectVertical(nRows, row);
1111  return vrect.SubRectHorizontal(nColumns, col);
1112  }
1113 
1121  inline IRECT GetGridCell(int cellIndex, int nRows, int nColumns, EDirection dir = EDirection::Horizontal, int nCells = 1) const
1122  {
1123  assert(cellIndex <= nRows * nColumns); // not enough cells !
1124 
1125  int cell = 0;
1126 
1127  if(dir == EDirection::Horizontal)
1128  {
1129  for(int row = 0; row < nRows; row++)
1130  {
1131  for(int col = 0; col < nColumns; col++)
1132  {
1133  if(cell == cellIndex)
1134  {
1135  const IRECT vrect = SubRectVertical(nRows, row);
1136  IRECT rect = vrect.SubRectHorizontal(nColumns, col);
1137 
1138  for (int n = 1; n < nCells && (col + n) < nColumns; n++)
1139  {
1140  rect = rect.Union(vrect.SubRectHorizontal(nColumns, col + n));
1141  }
1142  return rect;
1143  }
1144 
1145  cell++;
1146  }
1147  }
1148  }
1149  else
1150  {
1151  for(int col = 0; col < nColumns; col++)
1152  {
1153  for(int row = 0; row < nRows; row++)
1154  {
1155  if(cell == cellIndex)
1156  {
1157  const IRECT hrect = SubRectHorizontal(nColumns, col);
1158  IRECT rect = hrect.SubRectVertical(nRows, row);;
1159 
1160  for (int n = 1; n < nCells && (row + n) < nRows; n++)
1161  {
1162  rect = rect.Union(hrect.SubRectVertical(nRows, row + n));
1163  }
1164  return rect;
1165  }
1166 
1167  cell++;
1168  }
1169  }
1170  }
1171 
1172  return *this;
1173  }
1174 
1176  bool IsPixelAligned() const
1177  {
1178  // If all values are within 1/1000th of a pixel of an integer the IRECT is considered pixel aligned
1179 
1180  auto isInteger = [](float x){ return std::fabs(x - std::round(x)) <= static_cast<float>(1e-3); };
1181 
1182  return isInteger(L) && isInteger(T) && isInteger(R) && isInteger(B);
1183  }
1184 
1189  bool IsPixelAligned(float scale) const
1190  {
1191  IRECT r = *this;
1192  r.Scale(scale);
1193  return r.IsPixelAligned();
1194  }
1195 
1197  inline void PixelAlign()
1198  {
1199  L = std::floor(L);
1200  T = std::floor(T);
1201  R = std::ceil(R);
1202  B = std::ceil(B);
1203  }
1204 
1208  inline void PixelAlign(float scale)
1209  {
1210  // N.B. - double precision is *required* for accuracy of the reciprocal
1211  Scale(scale);
1212  PixelAlign();
1213  Scale(static_cast<float>(1.0/static_cast<double>(scale)));
1214  }
1215 
1218  inline IRECT GetPixelAligned() const
1219  {
1220  IRECT r = *this;
1221  r.PixelAlign();
1222  return r;
1223  }
1224 
1228  inline IRECT GetPixelAligned(float scale) const
1229  {
1230  IRECT r = *this;
1231  r.PixelAlign(scale);
1232  return r;
1233  }
1234 
1237  inline void PixelSnap()
1238  {
1239  L = std::round(L);
1240  T = std::round(T);
1241  R = std::round(R);
1242  B = std::round(B);
1243  }
1244 
1247  inline void PixelSnap(float scale)
1248  {
1249  // N.B. - double precision is *required* for accuracy of the reciprocal
1250  Scale(scale);
1251  PixelSnap();
1252  Scale(static_cast<float>(1.0/static_cast<double>(scale)));
1253  }
1254 
1256  inline IRECT GetPixelSnapped() const
1257  {
1258  IRECT r = *this;
1259  r.PixelSnap();
1260  return r;
1261  }
1262 
1266  inline IRECT GetPixelSnapped(float scale) const
1267  {
1268  IRECT r = *this;
1269  r.PixelSnap(scale);
1270  return r;
1271  }
1272 
1276  inline void Pad(float padding)
1277  {
1278  L -= padding;
1279  T -= padding;
1280  R += padding;
1281  B += padding;
1282  }
1283 
1290  inline void Pad(float padL, float padT, float padR, float padB)
1291  {
1292  L -= padL;
1293  T -= padT;
1294  R += padR;
1295  B += padB;
1296  }
1297 
1301  inline void HPad(float padding)
1302  {
1303  L -= padding;
1304  R += padding;
1305  }
1306 
1310  inline void VPad(float padding)
1311  {
1312  T -= padding;
1313  B += padding;
1314  }
1315 
1318  inline void MidHPad(float padding)
1319  {
1320  const float mw = MW();
1321  L = mw - padding;
1322  R = mw + padding;
1323  }
1324 
1327  inline void MidVPad(float padding)
1328  {
1329  const float mh = MH();
1330  T = mh - padding;
1331  B = mh + padding;
1332  }
1333 
1338  inline IRECT GetPadded(float padding) const
1339  {
1340  return IRECT(L-padding, T-padding, R+padding, B+padding);
1341  }
1342 
1350  inline IRECT GetPadded(float padL, float padT, float padR, float padB) const
1351  {
1352  return IRECT(L-padL, T-padT, R+padR, B+padB);
1353  }
1354 
1359  inline IRECT GetHPadded(float padding) const
1360  {
1361  return IRECT(L-padding, T, R+padding, B);
1362  }
1363 
1368  inline IRECT GetVPadded(float padding) const
1369  {
1370  return IRECT(L, T-padding, R, B+padding);
1371  }
1372 
1376  inline IRECT GetMidHPadded(float padding) const
1377  {
1378  return IRECT(MW()-padding, T, MW()+padding, B);
1379  }
1380 
1384  inline IRECT GetMidVPadded(float padding) const
1385  {
1386  return IRECT(L, MH()-padding, R, MH()+padding);
1387  }
1388 
1393  inline IRECT GetHSliced(float w, bool rhs = false) const
1394  {
1395  if(rhs)
1396  return IRECT(R - w, T, R, B);
1397  else
1398  return IRECT(L, T, L + w, B);
1399  }
1400 
1405  inline IRECT GetVSliced(float h, bool bot = false) const
1406  {
1407  if(bot)
1408  return IRECT(L, B - h, R, B);
1409  else
1410  return IRECT(L, T, R, T + h);
1411  }
1412 
1415  void Clank(const IRECT& rhs)
1416  {
1417  if (L < rhs.L)
1418  {
1419  R = std::min(rhs.R - 1, R + rhs.L - L);
1420  L = rhs.L;
1421  }
1422  if (T < rhs.T)
1423  {
1424  B = std::min(rhs.B - 1, B + rhs.T - T);
1425  T = rhs.T;
1426  }
1427  if (R >= rhs.R)
1428  {
1429  L = std::max(rhs.L, L - (R - rhs.R + 1));
1430  R = rhs.R - 1;
1431  }
1432  if (B >= rhs.B)
1433  {
1434  T = std::max(rhs.T, T - (B - rhs.B + 1));
1435  B = rhs.B - 1;
1436  }
1437  }
1438 
1441  void Scale(float scale)
1442  {
1443  L *= scale;
1444  T *= scale;
1445  R *= scale;
1446  B *= scale;
1447  }
1448 
1451  void ScaleAboutCentre(float scale)
1452  {
1453  float x = MW();
1454  float y = MH();
1455  float hw = W() / 2.f;
1456  float hh = H() / 2.f;
1457  L = x - (hw * scale);
1458  T = y - (hh * scale);
1459  R = x + (hw * scale);
1460  B = y + (hh * scale);
1461  }
1462 
1466  IRECT GetScaled(float scale) const
1467  {
1468  IRECT r = *this;
1469  r.Scale(scale);
1470  return r;
1471  }
1472 
1476  IRECT GetScaledAboutCentre(float scale) const
1477  {
1478  IRECT r = *this;
1479  r.ScaleAboutCentre(scale);
1480  return r;
1481  }
1482 
1488  static IRECT LinearInterpolateBetween(const IRECT& start, const IRECT& dest, float progress)
1489  {
1490  IRECT result;
1491  result.L = start.L + progress * (dest.L - start.L);
1492  result.T = start.T + progress * (dest.T - start.T);
1493  result.R = start.R + progress * (dest.R - start.R);
1494  result.B = start.B + progress * (dest.B - start.B);
1495  return result;
1496  }
1497 
1501  void GetRandomPoint(float& x, float& y) const
1502  {
1503  const float r1 = static_cast<float>(std::rand()/(static_cast<float>(RAND_MAX)+1.f));
1504  const float r2 = static_cast<float>(std::rand()/(static_cast<float>(RAND_MAX)+1.f));
1505 
1506  x = L + r1 * W();
1507  y = T + r2 * H();
1508  }
1509 
1512  {
1513  float l, t, r, b;
1514  GetRandomPoint(l, t);
1515  IRECT tmp = IRECT(l, t, R, B);
1516  tmp.GetRandomPoint(r, b);
1517 
1518  return IRECT(l, t, r, b);
1519  }
1520 
1526  void Offset(float l, float t, float r, float b)
1527  {
1528  L += l;
1529  T += t;
1530  R += r;
1531  B += b;
1532  }
1533 
1540  IRECT GetOffset(float l, float t, float r, float b) const
1541  {
1542  return IRECT(L + l, T + t, R + r, B + b);
1543  }
1544 
1548  void Translate(float x, float y)
1549  {
1550  L += x;
1551  T += y;
1552  R += x;
1553  B += y;
1554  }
1555 
1560  IRECT GetTranslated(float x, float y) const
1561  {
1562  return IRECT(L + x, T + y, R + x, B + y);
1563  }
1564 
1568  IRECT GetHShifted(float x) const
1569  {
1570  return GetTranslated(x, 0.f);
1571  }
1572 
1576  IRECT GetVShifted(float y) const
1577  {
1578  return GetTranslated(0.f, y);
1579  }
1580 
1584  IRECT GetCentredInside(const IRECT& sr) const
1585  {
1586  IRECT r;
1587  r.L = MW() - sr.W() / 2.f;
1588  r.T = MH() - sr.H() / 2.f;
1589  r.R = r.L + sr.W();
1590  r.B = r.T + sr.H();
1591 
1592  return r;
1593  }
1594 
1599  IRECT GetCentredInside(float w, float h = 0.f) const
1600  {
1601  w = std::max(w, 1.f);
1602 
1603  if(h <= 0.f)
1604  h = w;
1605 
1606  IRECT r;
1607  r.L = MW() - w / 2.f;
1608  r.T = MH() - h / 2.f;
1609  r.R = r.L + w;
1610  r.B = r.T + h;
1611 
1612  return r;
1613  }
1614 
1618  IRECT GetCentredInside(const IBitmap& bitmap) const
1619  {
1620  IRECT r;
1621  r.L = MW() - bitmap.FW() / 2.f;
1622  r.T = MH() - bitmap.FH() / 2.f;
1623  r.R = r.L + (float) bitmap.FW();
1624  r.B = r.T + (float) bitmap.FH();
1625 
1626  return r;
1627  }
1628 
1632  void VAlignTo(const IRECT& sr, EVAlign align)
1633  {
1634  const float height = H();
1635  switch (align)
1636  {
1637  case EVAlign::Top: T = sr.T; B = sr.T + height; break;
1638  case EVAlign::Bottom: T = sr.B - height; B = sr.B; break;
1639  case EVAlign::Middle: T = sr.T + (sr.H() * 0.5f) - (height * 0.5f); B = sr.T + (sr.H() * 0.5f) - (height * 0.5f) + height; break;
1640  }
1641  }
1642 
1646  void HAlignTo(const IRECT& sr, EAlign align)
1647  {
1648  const float width = W();
1649  switch (align)
1650  {
1651  case EAlign::Near: L = sr.L; R = sr.L + width; break;
1652  case EAlign::Far: L = sr.R - width; R = sr.R; break;
1653  case EAlign::Center: L = sr.L + (sr.W() * 0.5f) - (width * 0.5f); R = sr.L + (sr.W() * 0.5f) - (width * 0.5f) + width; break;
1654  }
1655  }
1656 
1661  IRECT GetVAlignedTo(const IRECT& sr, EVAlign align) const
1662  {
1663  IRECT result = *this;
1664  result.VAlignTo(sr, align);
1665  return result;
1666  }
1667 
1670  {
1671  if(W() < H())
1672  return W();
1673  else
1674  return H();
1675  }
1676 
1681  IRECT GetHAlignedTo(const IRECT& sr, EAlign align) const
1682  {
1683  IRECT result = *this;
1684  result.HAlignTo(sr, align);
1685  return result;
1686  }
1687 
1689  void DBGPrint() { DBGMSG("L: %f, T: %f, R: %f, B: %f,: W: %f, H: %f\n", L, T, R, B, W(), H()); }
1690 };
1691 
1694 {
1695  bool L, R, S, C, A;
1696  ITouchID touchID = 0;
1697  float touchRadius = 0.f;
1698 
1706  IMouseMod(bool l = false, bool r = false, bool s = false, bool c = false, bool a = false, ITouchID touchID = 0)
1707  : L(l), R(r), S(s), C(c), A(a), touchID(touchID)
1708  {}
1709 
1711  bool IsTouch() const { return touchID > 0; }
1712 
1714  void DBGPrint() { DBGMSG("L: %i, R: %i, S: %i, C: %i,: A: %i\n", L, R, S, C, A); }
1715 };
1716 
1719 {
1720  float x, y;
1721  float dX, dY;
1722  IMouseMod ms;
1723 };
1724 
1727 {
1728  float x = 0.f;
1729  float y = 0.f;
1730  float scale = 0.f; // pinch,
1731  float velocity = 0.f; // pinch, rotate
1732  float angle = 0.f; // rotate,
1733  EGestureState state = EGestureState::Unknown;
1734  EGestureType type = EGestureType::Unknown;
1735 };
1736 
1739 {
1740 public:
1741  IRECTList()
1742  {}
1743 
1744  IRECTList(const IRECTList&) = delete;
1745  IRECTList& operator=(const IRECTList&) = delete;
1746 
1748  int Size() const { return mRects.GetSize(); }
1749 
1752  void Add(const IRECT& rect)
1753  {
1754  mRects.Add(rect);
1755  }
1756 
1760  void Set(int idx, const IRECT& rect)
1761  {
1762  *(mRects.GetFast() + idx) = rect;
1763  }
1764 
1768  const IRECT& Get(int idx) const
1769  {
1770  return *(mRects.GetFast() + idx);
1771  }
1772 
1774  void Clear()
1775  {
1776  mRects.Resize(0);
1777  }
1778 
1782  {
1783  IRECT r = Get(0);
1784  for (auto i = 1; i < mRects.GetSize(); i++)
1785  r = r.Union(Get(i));
1786  return r;
1787  }
1788 
1790  void PixelAlign()
1791  {
1792  for (auto i = 0; i < Size(); i++)
1793  {
1794  IRECT r = Get(i);
1795  r.PixelAlign();
1796  Set(i, r);
1797  }
1798  }
1799 
1802  void PixelAlign(float scale)
1803  {
1804  for (auto i = 0; i < Size(); i++)
1805  {
1806  IRECT r = Get(i);
1807  r.PixelAlign(scale);
1808  Set(i, r);
1809  }
1810  }
1811 
1816  int Find(float x, float y) const
1817  {
1818  for (auto i = 0; i < Size(); i++)
1819  {
1820  if(Get(i).Contains(x, y))
1821  return i;
1822  }
1823 
1824  return -1;
1825  }
1826 
1834  static bool GetFracGrid(const IRECT& input, IRECTList& rects, const std::initializer_list<float>& rowFractions, const std::initializer_list<float>& colFractions, EDirection layoutDir = EDirection::Horizontal)
1835  {
1836  IRECT spaceLeft = input;
1837  float y = 0.;
1838  float x = 0.;
1839 
1840  if(std::accumulate(rowFractions.begin(), rowFractions.end(), 0.f) != 1.)
1841  return false;
1842 
1843  if(std::accumulate(colFractions.begin(), colFractions.end(), 0.f) != 1.)
1844  return false;
1845 
1846  if (layoutDir == EDirection::Horizontal)
1847  {
1848  for (auto& rowFrac : rowFractions)
1849  {
1850  IRECT thisRow = input.FracRectVertical(rowFrac, true).GetTranslated(0, y);
1851 
1852  x = 0.;
1853 
1854  for (auto& colFrac : colFractions)
1855  {
1856  IRECT thisCell = thisRow.FracRectHorizontal(colFrac).GetTranslated(x, 0);
1857 
1858  rects.Add(thisCell);
1859 
1860  x += thisCell.W();
1861  }
1862 
1863  spaceLeft.Intersect(thisRow);
1864 
1865  y = rects.Bounds().H();
1866  }
1867  }
1868 
1869  if (layoutDir == EDirection::Vertical)
1870  {
1871  for (auto& colFrac : colFractions)
1872  {
1873  IRECT thisCol = input.FracRectHorizontal(colFrac).GetTranslated(x, 0);
1874 
1875  y = 0.;
1876 
1877  for (auto& rowFrac : rowFractions)
1878  {
1879  IRECT thisCell = thisCol.FracRectVertical(rowFrac, true).GetTranslated(0, y);
1880 
1881  rects.Add(thisCell);
1882 
1883  y += thisCell.H();
1884  }
1885 
1886  spaceLeft.Intersect(thisCol);
1887 
1888  x = rects.Bounds().W();
1889  }
1890  }
1891 
1892  return true;
1893  }
1894 
1896  void Optimize()
1897  {
1898  for (int i = 0; i < Size(); i++)
1899  {
1900  for (int j = i + 1; j < Size(); j++)
1901  {
1902  if (Get(i).Contains(Get(j)))
1903  {
1904  mRects.Delete(j);
1905  j--;
1906  }
1907  else if (Get(j).Contains(Get(i)))
1908  {
1909  mRects.Delete(i);
1910  i--;
1911  break;
1912  }
1913  else if (Get(i).Intersects(Get(j)))
1914  {
1915  IRECT intersection = Get(i).Intersect(Get(j));
1916 
1917  if (Get(i).Mergeable(intersection))
1918  Set(i, Shrink(Get(i), intersection));
1919  else if (Get(j).Mergeable(intersection))
1920  Set(j, Shrink(Get(j), intersection));
1921  else if (Get(i).Area() < Get(j).Area())
1922  Set(i, Split(Get(i), intersection));
1923  else
1924  Set(j, Split(Get(j), intersection));
1925  }
1926  }
1927  }
1928 
1929  for (int i = 0; i < Size(); i++)
1930  {
1931  for (int j = i + 1; j < Size(); j++)
1932  {
1933  if (Get(i).Mergeable(Get(j)))
1934  {
1935  Set(j, Get(i).Union(Get(j)));
1936  mRects.Delete(i);
1937  i = -1;
1938  break;
1939  }
1940  }
1941  }
1942  }
1943 
1944 private:
1949  IRECT Shrink(const IRECT &r, const IRECT &i)
1950  {
1951  if (i.L != r.L)
1952  return IRECT(r.L, r.T, i.L, r.B);
1953  if (i.T != r.T)
1954  return IRECT(r.L, r.T, r.R, i.T);
1955  if (i.R != r.R)
1956  return IRECT(i.R, r.T, r.R, r.B);
1957  return IRECT(r.L, i.B, r.R, r.B);
1958  }
1959 
1964  IRECT Split(const IRECT r, const IRECT &i)
1965  {
1966  if (r.L == i.L)
1967  {
1968  if (r.T == i.T)
1969  {
1970  Add(IRECT(i.R, r.T, r.R, i.B));
1971  return IRECT(r.L, i.B, r.R, r.B);
1972  }
1973  else
1974  {
1975  Add(IRECT(r.L, r.T, r.R, i.T));
1976  return IRECT(i.R, i.T, r.R, r.B);
1977  }
1978  }
1979 
1980  if (r.T == i.T)
1981  {
1982  Add(IRECT(r.L, r.T, i.L, i.B));
1983  return IRECT(r.L, i.B, r.R, r.B);
1984  }
1985  else
1986  {
1987  Add(IRECT(r.L, r.T, r.R, i.T));
1988  return IRECT(r.L, i.T, i.L, r.B);
1989  }
1990  }
1991 
1992  WDL_TypedBuf<IRECT> mRects;
1993 };
1994 
1996 struct IMatrix
1997 {
2005  IMatrix(double xx, double yx, double xy, double yy, double tx, double ty)
2006  : mXX(xx), mYX(yx), mXY(xy), mYY(yy), mTX(tx), mTY(ty)
2007  {}
2008 
2010  IMatrix() : IMatrix(1.0, 0.0, 0.0, 1.0, 0.0, 0.0)
2011  {}
2012 
2017  IMatrix& Translate(float x, float y)
2018  {
2019  return Transform(IMatrix(1.0, 0.0, 0.0, 1.0, x, y));
2020  }
2021 
2026  IMatrix& Scale(float x, float y)
2027  {
2028  return Transform(IMatrix(x, 0.0, 0.0, y, 0.0, 0.0));
2029  }
2030 
2034  IMatrix& Rotate(float a)
2035  {
2036  const double rad = DegToRad(a);
2037  const double c = std::cos(rad);
2038  const double s = std::sin(rad);
2039 
2040  return Transform(IMatrix(c, s, -s, c, 0.0, 0.0));
2041  }
2042 
2047  IMatrix& Skew(float xa, float ya)
2048  {
2049  return Transform(IMatrix(1.0, std::tan(DegToRad(ya)), std::tan(DegToRad(xa)), 1.0, 0.0, 0.0));
2050  }
2051 
2057  void TransformPoint(double& x, double& y, double x0, double y0) const
2058  {
2059  x = x0 * mXX + y0 * mXY + mTX;
2060  y = x0 * mYX + y0 * mYY + mTY;
2061  };
2062 
2066  void TransformPoint(double& x, double& y) const
2067  {
2068  TransformPoint(x, y, x, y);
2069  };
2070 
2075  IMatrix& Transform(const IRECT& before, const IRECT& after)
2076  {
2077  const double sx = after.W() / before.W();
2078  const double sy = after.H() / before.H();
2079  const double tx = after.L - before.L * sx;
2080  const double ty = after.T - before.T * sy;
2081 
2082  return *this = IMatrix(sx, 0.0, 0.0, sy, tx, ty);
2083  }
2084 
2089  {
2090  IMatrix p = *this;
2091 
2092  mXX = m.mXX * p.mXX + m.mYX * p.mXY;
2093  mYX = m.mXX * p.mYX + m.mYX * p.mYY;
2094  mXY = m.mXY * p.mXX + m.mYY * p.mXY;
2095  mYY = m.mXY * p.mYX + m.mYY * p.mYY;
2096  mTX = m.mTX * p.mXX + m.mTY * p.mXY + p.mTX;
2097  mTY = m.mTX * p.mYX + m.mTY * p.mYY + p.mTY;
2098 
2099  return *this;
2100  }
2101 
2105  {
2106  IMatrix m = *this;
2107 
2108  double d = 1.0 / (m.mXX * m.mYY - m.mYX * m.mXY);
2109 
2110  mXX = m.mYY * d;
2111  mYX = -m.mYX * d;
2112  mXY = -m.mXY * d;
2113  mYY = m.mXX * d;
2114  mTX = (-(m.mTX * mXX) - (m.mTY * mXY));
2115  mTY = (-(m.mTX * mYX) - (m.mTY * mYY));
2116 
2117  return *this;
2118  }
2119 
2120  double mXX, mYX, mXY, mYY, mTX, mTY;
2121 };
2122 
2125 {
2126  IColorStop()
2127  : mOffset(0.f)
2128  {}
2129 
2133  IColorStop(IColor color, float offset)
2134  : mColor(color)
2135  , mOffset(offset)
2136  {
2137  assert(offset >= 0.0 && offset <= 1.0);
2138  }
2139 
2140  IColor mColor;
2141  float mOffset;
2142 };
2143 
2145 struct IPattern
2146 {
2147  EPatternType mType;
2148  EPatternExtend mExtend;
2149  IColorStop mStops[16];
2150  int mNStops;
2151  IMatrix mTransform;
2152 
2155  IPattern(EPatternType type)
2156  : mType(type), mExtend(EPatternExtend::Pad), mNStops(0)
2157  {}
2158 
2161  IPattern(const IColor& color)
2162  : mType(EPatternType::Solid), mExtend(EPatternExtend::Pad), mNStops(1)
2163  {
2164  mStops[0] = IColorStop(color, 0.0);
2165  }
2166 
2174  static IPattern CreateLinearGradient(float x1, float y1, float x2, float y2, const std::initializer_list<IColorStop>& stops = {})
2175  {
2176  IPattern pattern(EPatternType::Linear);
2177 
2178  // Calculate the affine transform from one line segment to another!
2179  const double xd = x2 - x1;
2180  const double yd = y2 - y1;
2181  const double d = sqrt(xd * xd + yd * yd);
2182  const double a = atan2(xd, yd);
2183  const double s = std::sin(a) / d;
2184  const double c = std::cos(a) / d;
2185 
2186  const double x0 = -(x1 * c - y1 * s);
2187  const double y0 = -(x1 * s + y1 * c);
2188 
2189  pattern.SetTransform(static_cast<float>(c),
2190  static_cast<float>(s),
2191  static_cast<float>(-s),
2192  static_cast<float>(c),
2193  static_cast<float>(x0),
2194  static_cast<float>(y0));
2195 
2196  for (auto& stop : stops)
2197  pattern.AddStop(stop.mColor, stop.mOffset);
2198 
2199  return pattern;
2200  }
2201 
2207  static IPattern CreateLinearGradient(const IRECT& bounds, EDirection direction, const std::initializer_list<IColorStop>& stops = {})
2208  {
2209  float x1, y1, x2, y2;
2210 
2211  if(direction == EDirection::Horizontal)
2212  {
2213  y1 = bounds.MH(); y2 = y1;
2214  x1 = bounds.L;
2215  x2 = bounds.R;
2216  }
2217  else//(direction == EDirection::Vertical)
2218  {
2219  x1 = bounds.MW(); x2 = x1;
2220  y1 = bounds.T;
2221  y2 = bounds.B;
2222  }
2223 
2224  return CreateLinearGradient(x1, y1, x2, y2, stops);
2225  }
2226 
2233  static IPattern CreateRadialGradient(float x1, float y1, float r, const std::initializer_list<IColorStop>& stops = {})
2234  {
2235  IPattern pattern(EPatternType::Radial);
2236 
2237  const float s = 1.f / r;
2238 
2239  pattern.SetTransform(s, 0, 0, s, -(x1 * s), -(y1 * s));
2240 
2241  for (auto& stop : stops)
2242  pattern.AddStop(stop.mColor, stop.mOffset);
2243 
2244  return pattern;
2245  }
2246 
2254  static IPattern CreateSweepGradient(float x1, float y1, const std::initializer_list<IColorStop>& stops = {},
2255  float angleStart = 0.f, float angleEnd = 360.f)
2256  {
2257  IPattern pattern(EPatternType::Sweep);
2258 
2259  #ifdef IGRAPHICS_SKIA
2260  angleStart -= 90;
2261  angleEnd -= 90;
2262  #endif
2263 
2264  float rad = DegToRad(angleStart);
2265  float c = std::cos(rad);
2266  float s = std::sin(rad);
2267 
2268  pattern.SetTransform(c, s, -s, c, -x1, -y1);
2269 
2270  for (auto& stop : stops)
2271  {
2272  pattern.AddStop(stop.mColor, stop.mOffset * (angleEnd - angleStart) / 360.f);
2273  }
2274  return pattern;
2275  }
2276 
2278  int NStops() const
2279  {
2280  return mNStops;
2281  }
2282 
2286  const IColorStop& GetStop(int idx) const
2287  {
2288  return mStops[idx];
2289  }
2290 
2294  void AddStop(IColor color, float offset)
2295  {
2296  assert(mType != EPatternType::Solid && mNStops < 16);
2297  assert(!mNStops || GetStop(mNStops - 1).mOffset < offset);
2298  if (mNStops < 16)
2299  mStops[mNStops++] = IColorStop(color, offset);
2300  }
2301 
2309  void SetTransform(float xx, float yx, float xy, float yy, float tx, float ty)
2310  {
2311  mTransform = IMatrix(xx, yx, xy, yy, tx, ty);
2312  }
2313 
2316  void SetTransform(const IMatrix& transform)
2317  {
2318  mTransform = transform;
2319  }
2320 };
2321 
2326 class ILayer
2327 {
2328  friend IGraphics;
2329 
2330 public:
2336  ILayer(APIBitmap* pBitmap, const IRECT& layerRect, IControl* pControl, const IRECT& controlRect)
2337  : mBitmap(pBitmap)
2338  , mControl(pControl)
2339  , mControlRECT(controlRect)
2340  , mRECT(layerRect)
2341  , mInvalid(false)
2342  {}
2343 
2344  ILayer(const ILayer&) = delete;
2345  ILayer operator=(const ILayer&) = delete;
2346 
2348  void Invalidate() { mInvalid = true; }
2349 
2351  const APIBitmap* GetAPIBitmap() const { return mBitmap.get(); }
2352 
2354  IBitmap GetBitmap() const { return IBitmap(mBitmap.get(), 1, false); }
2355 
2357  const IRECT& Bounds() const { return mRECT; }
2358 
2359 private:
2360  std::unique_ptr<APIBitmap> mBitmap;
2361  IControl* mControl;
2362  IRECT mControlRECT;
2363  IRECT mRECT;
2364  bool mInvalid;
2365 };
2366 
2368 using ILayerPtr = std::unique_ptr<ILayer>;
2369 
2371 struct IShadow
2372 {
2373  IShadow() {}
2374 
2382  IShadow(const IPattern& pattern, float blurSize, float xOffset, float yOffset, float opacity, bool drawForeground = true)
2383  : mPattern(pattern)
2384  , mBlurSize(blurSize)
2385  , mXOffset(xOffset)
2386  , mYOffset(yOffset)
2387  , mOpacity(opacity)
2388  , mDrawForeground(drawForeground)
2389  {}
2390 
2391  IPattern mPattern = COLOR_BLACK;
2392  float mBlurSize = 0.f;
2393  float mXOffset = 0.f;
2394  float mYOffset = 0.f;
2395  float mOpacity = 1.f;
2396  bool mDrawForeground = true;
2397 };
2398 
2401 {
2402  IColor mColors[kNumVColors];
2403 
2405  const IColor& GetColor(EVColor color) const
2406  {
2407  return mColors[(int) color];
2408  }
2409 
2411  static const IColor& GetDefaultColor(EVColor idx)
2412  {
2413  switch(idx)
2414  {
2415  case kBG: return DEFAULT_BGCOLOR; // Background
2416  case kFG: return DEFAULT_FGCOLOR; // OFF/Foreground
2417  case kPR: return DEFAULT_PRCOLOR; // ON/Pressed
2418  case kFR: return DEFAULT_FRCOLOR; // Frame
2419  case kHL: return DEFAULT_HLCOLOR; // Highlight
2420  case kSH: return DEFAULT_SHCOLOR; // Shadow
2421  case kX1: return DEFAULT_X1COLOR; // Extra 1
2422  case kX2: return DEFAULT_X2COLOR; // Extra 2
2423  case kX3: return DEFAULT_X3COLOR; // Extra 3
2424  default:
2425  return COLOR_TRANSPARENT;
2426  };
2427  }
2428 
2431  {
2432  ResetColors();
2433  }
2434 
2437  IVColorSpec(const std::initializer_list<IColor>& colors)
2438  {
2439  assert(colors.size() <= kNumVColors);
2440 
2441  int i = 0;
2442 
2443  for(auto& c : colors)
2444  {
2445  mColors[i++] = c;
2446  }
2447 
2448  for(; i<kNumVColors; i++)
2449  {
2450  mColors[i] = GetDefaultColor((EVColor) i);
2451  }
2452  }
2453 
2456  {
2457  for (int i=0; i<kNumVColors; i++)
2458  {
2459  mColors[i] = GetDefaultColor((EVColor) i);
2460  }
2461  }
2462 };
2463 
2464 const IVColorSpec DEFAULT_COLOR_SPEC = IVColorSpec();
2465 
2466 static constexpr bool DEFAULT_HIDE_CURSOR = true;
2467 static constexpr bool DEFAULT_SHOW_VALUE = true;
2468 static constexpr bool DEFAULT_SHOW_LABEL = true;
2469 static constexpr bool DEFAULT_DRAW_FRAME = true;
2470 static constexpr bool DEFAULT_DRAW_SHADOWS = true;
2471 static constexpr bool DEFAULT_EMBOSS = false;
2472 static constexpr float DEFAULT_ROUNDNESS = 0.f;
2473 static constexpr float DEFAULT_FRAME_THICKNESS = 1.f;
2474 static constexpr float DEFAULT_SHADOW_OFFSET = 3.f;
2475 static constexpr float DEFAULT_WIDGET_FRAC = 1.f;
2476 static constexpr float DEFAULT_WIDGET_ANGLE = 0.f;
2477 const IText DEFAULT_LABEL_TEXT {DEFAULT_TEXT_SIZE + 5.f, EVAlign::Top};
2478 const IText DEFAULT_VALUE_TEXT {DEFAULT_TEXT_SIZE, EVAlign::Bottom};
2479 
2481 struct IVStyle
2482 {
2483  bool hideCursor = DEFAULT_HIDE_CURSOR;
2484  bool showLabel = DEFAULT_SHOW_LABEL;
2485  bool showValue = DEFAULT_SHOW_VALUE;
2486  bool drawFrame = DEFAULT_DRAW_FRAME;
2487  bool drawShadows = DEFAULT_DRAW_SHADOWS;
2488  bool emboss = DEFAULT_EMBOSS;
2489  float roundness = DEFAULT_ROUNDNESS;
2490  float frameThickness = DEFAULT_FRAME_THICKNESS;
2491  float shadowOffset = DEFAULT_SHADOW_OFFSET;
2492  float widgetFrac = DEFAULT_WIDGET_FRAC;
2493  float angle = DEFAULT_WIDGET_ANGLE;
2494  IVColorSpec colorSpec = DEFAULT_COLOR_SPEC;
2495  IText labelText = DEFAULT_LABEL_TEXT;
2496  IText valueText = DEFAULT_VALUE_TEXT;
2497 
2513  IVStyle(bool showLabel = DEFAULT_SHOW_LABEL,
2514  bool showValue = DEFAULT_SHOW_VALUE,
2515  const IVColorSpec& colors = {DEFAULT_BGCOLOR, DEFAULT_FGCOLOR, DEFAULT_PRCOLOR, DEFAULT_FRCOLOR, DEFAULT_HLCOLOR, DEFAULT_SHCOLOR, DEFAULT_X1COLOR, DEFAULT_X2COLOR, DEFAULT_X3COLOR},
2516  const IText& labelText = DEFAULT_LABEL_TEXT,
2517  const IText& valueText = DEFAULT_VALUE_TEXT,
2518  bool hideCursor = DEFAULT_HIDE_CURSOR,
2519  bool drawFrame = DEFAULT_DRAW_FRAME,
2520  bool drawShadows = DEFAULT_DRAW_SHADOWS,
2521  bool emboss = DEFAULT_EMBOSS,
2522  float roundness = DEFAULT_ROUNDNESS,
2523  float frameThickness = DEFAULT_FRAME_THICKNESS,
2524  float shadowOffset = DEFAULT_SHADOW_OFFSET,
2525  float widgetFrac = DEFAULT_WIDGET_FRAC,
2526  float angle = DEFAULT_WIDGET_ANGLE)
2527  : hideCursor(hideCursor)
2528  , showLabel(showLabel)
2529  , showValue(showValue)
2530  , drawFrame(drawFrame)
2531  , drawShadows(drawShadows)
2532  , emboss(emboss)
2533  , roundness(roundness)
2534  , frameThickness(frameThickness)
2535  , shadowOffset(shadowOffset)
2536  , widgetFrac(widgetFrac)
2537  , angle(angle)
2538  , colorSpec(colors)
2539  , labelText(labelText)
2540  , valueText(valueText)
2541  {
2542  }
2543 
2546  IVStyle(const std::initializer_list<IColor>& colors)
2547  : colorSpec(colors)
2548  {
2549  }
2550 
2551  IVStyle WithShowLabel(bool show = true) const { IVStyle newStyle = *this; newStyle.showLabel = show; return newStyle; }
2552  IVStyle WithShowValue(bool show = true) const { IVStyle newStyle = *this; newStyle.showValue = show; return newStyle; }
2553  IVStyle WithLabelText(const IText& text) const { IVStyle newStyle = *this; newStyle.labelText = text; return newStyle;}
2554  IVStyle WithValueText(const IText& text) const { IVStyle newStyle = *this; newStyle.valueText = text; return newStyle; }
2555  IVStyle WithHideCursor(bool hide = true) const { IVStyle newStyle = *this; newStyle.hideCursor = hide; return newStyle; }
2556  IVStyle WithColor(EVColor idx, IColor color) const { IVStyle newStyle = *this; newStyle.colorSpec.mColors[idx] = color; return newStyle; }
2557  IVStyle WithColors(IVColorSpec spec) const { IVStyle newStyle = *this; newStyle.colorSpec = spec; return newStyle; }
2558  IVStyle WithRoundness(float v) const { IVStyle newStyle = *this; newStyle.roundness = Clip(v, 0.f, 1.f); return newStyle; }
2559  IVStyle WithFrameThickness(float v) const { IVStyle newStyle = *this; newStyle.frameThickness = v; return newStyle; }
2560  IVStyle WithShadowOffset(float v) const { IVStyle newStyle = *this; newStyle.shadowOffset = v; return newStyle; }
2561  IVStyle WithDrawShadows(bool v = true) const { IVStyle newStyle = *this; newStyle.drawShadows = v; return newStyle; }
2562  IVStyle WithDrawFrame(bool v = true) const { IVStyle newStyle = *this; newStyle.drawFrame = v; return newStyle; }
2563  IVStyle WithWidgetFrac(float v) const { IVStyle newStyle = *this; newStyle.widgetFrac = Clip(v, 0.f, 1.f); return newStyle; }
2564  IVStyle WithAngle(float v) const { IVStyle newStyle = *this; newStyle.angle = Clip(v, 0.f, 360.f); return newStyle; }
2565  IVStyle WithEmboss(bool v = true) const { IVStyle newStyle = *this; newStyle.emboss = v; return newStyle; }
2566 };
2567 
2568 const IVStyle DEFAULT_STYLE = IVStyle();
2569 
2570 END_IGRAPHICS_NAMESPACE
2571 END_IPLUG_NAMESPACE
2572 
Encapsulate an xy point in one struct.
static IColor FromColorCodeStr(const char *hexStr)
Create an IColor from a color code in a CString.
static IPattern CreateSweepGradient(float x1, float y1, const std::initializer_list< IColorStop > &stops={}, float angleStart=0.f, float angleEnd=360.f)
Create a sweep gradient IPattern (SKIA only)
void Scale(float scale)
Multiply each field of this IRECT by scale.
Contains a set of 9 colors used to theme IVControls.
IRECT Inset(const IRECT &rhs) const
Offsets the input IRECT based on the parent.
IRECT GetPixelSnapped() const
bool Contains(const IRECT &rhs) const
Returns true if this IRECT completely contains rhs.
const IRECT & Get(int idx) const
Get an IRECT from the list (will crash if idx is invalid)
Utility functions and macros.
float MW() const
void Optimize()
Remove rects that are contained by other rects and intersections and merge any rects that can be merg...
IRECT(float x, float y, const IBitmap &bitmap)
Construct a new IRECT at the given position and with the same size as the bitmap. ...
static IColor FromColorCode(int colorCode, int A=0xFF)
Create an IColor from a color code.
IRECT SubRect(EDirection layoutDir, int numSlices, int sliceIdx) const
Get a new rectangle which is a "slice" of this rectangle.
float MH() const
Used to manage a list of rectangular areas and optimize them for drawing to the screen.
bool IsValid() const
The lowest level base class of an IGraphics control.
Definition: IControl.h:42
Used to manage a rectangular area, independent of draw class/platform.
IRECT GetFromBLHC(float w, float h) const
Get a subrect of this IRECT expanding from the bottom-left corner.
void PixelSnap(float scale)
Pixel align a scaled version of this IRECT.
IVColorSpec(const std::initializer_list< IColor > &colors)
Create a new IVColorSpec object specifying the colors.
Used to manage composite/blend operations, independent of draw class/platform.
IRECT GetOffset(float l, float t, float r, float b) const
Get a copy of this rectangle where each field is offset by a specified amount.
User-facing bitmap abstraction that you use to manage bitmap data, independant of draw class/platform...
void VPad(float padding)
Pad this IRECT in the Y-axis N.B.
const APIBitmap * GetAPIBitmap() const
IRECT Bounds()
Get a union of all rectangles in the list.
Used to manage fill behaviour for path based drawing back ends.
IMatrix & Invert()
Changes the matrix to be the inverse of its original value.
IShadow(const IPattern &pattern, float blurSize, float xOffset, float yOffset, float opacity, bool drawForeground=true)
Create an IShadow.
IBitmap(APIBitmap *pAPIBitmap, int n, bool framesAreHorizontal, const char *name="")
IBitmap Constructor.
Used to manage mouse modifiers i.e.
void PixelAlign(float scale)
Pixel-align the IRECTs at the given scale factor then scale them back down.
IRECT GetHAlignedTo(const IRECT &sr, EAlign align) const
Get a rectangle the same dimensions as this one, horizontally aligned to the reference IRECT...
IRECT FracRectHorizontal(float frac, bool rhs=false) const
Returns a new IRECT with a width that is multiplied by frac.
IRECT GetVSliced(float h, bool bot=false) const
Get a copy of this IRECT with a new height.
float GetLengthOfShortestSide() const
void Pad(float padL, float padT, float padR, float padB)
Pad this IRECT N.B.
void EmptyClickActionFunc(IControl *pCaller)
A click action function that does nothing.
Definition: IControl.cpp:43
void Clank(const IRECT &rhs)
void Translate(float x, float y)
Translate this rectangle.
IRECT FracRect(EDirection layoutDir, float frac, bool fromTopOrRight=false) const
Get a new rectangle which is a fraction of this rectangle.
IRECT ReduceFromTop(float amount)
Reduce in height from the top edge by &#39;amount&#39; and return the removed region.
static IColor FromRGBf(float *rgbf)
Create an IColor from a 3 float RGB array.
static IRECT MakeXYWH(float l, float t, float w, float h)
Create a new IRECT with the given position and size.
void PixelAlign()
Pixel aligns the rect in an inclusive manner (moves all points outwards)
IRECT GetFromTLHC(float w, float h) const
Get a subrect of this IRECT expanding from the top-left corner.
IBitmap GetBitmap() const
IRECT GetVPadded(float padding) const
Get a copy of this IRECT padded in the Y-axis N.B.
IColor WithContrast(float c) const
Returns a new contrasted IColor based on this one.
IMouseMod(bool l=false, bool r=false, bool s=false, bool c=false, bool a=false, ITouchID touchID=0)
Create an IMouseMod.
Used to manage color data, independent of draw class/platform.
IRECT GetTranslated(float x, float y) const
Get a translated copy of this rectangle.
bool IsPixelAligned(float scale) const
Return true if, when scaled by scale, this IRECT is pixel aligned When scaling this mutliples each va...
void GetRGBAf(float *rgbaf) const
Get the color as a 4 float array.
void HPad(float padding)
Pad this IRECT in the X-axis N.B.
Used to describe a particular gesture.
static bool GetFracGrid(const IRECT &input, IRECTList &rects, const std::initializer_list< float > &rowFractions, const std::initializer_list< float > &colFractions, EDirection layoutDir=EDirection::Horizontal)
Fill an IRECTList with divisons of an input IRECT.
IRECT FracRectVertical(float frac, bool fromTop=false) const
Returns a new IRECT with a height that is multiplied by frac.
void ResetColors()
Reset the colors to the defaults.
void DefaultClickActionFunc(IControl *pCaller)
A click action function that triggers the default animation function for DEFAULT_ANIMATION_DURATION.
Definition: IControl.cpp:45
IRECT ReduceFromBottom(float amount)
Reduce in height from the bottom edge by &#39;amount&#39; and return the removed region.
Used to manage stroke behaviour for path based drawing back ends.
bool Empty() const
IRECT ReduceFromRight(float amount)
Reduce in width from the right edge by &#39;amount&#39; and return the removed region.
User-facing SVG abstraction that you use to manage SVG data ISVG doesn&#39;t actually own the image data...
int GetScale() const
IText(float size=DEFAULT_TEXT_SIZE, const IColor &color=DEFAULT_TEXT_FGCOLOR, const char *fontID=nullptr, EAlign align=EAlign::Center, EVAlign valign=EVAlign::Middle, float angle=0, const IColor &TEBGColor=DEFAULT_TEXTENTRY_BGCOLOR, const IColor &TEFGColor=DEFAULT_TEXTENTRY_FGCOLOR)
Create a new IText with size, color, fontID ...
IRECT SubRectVertical(int numSlices, int sliceIdx) const
Returns a new IRECT which is a horizontal "slice" of this IRECT.
int FW() const
IRECT GetPadded(float padL, float padT, float padR, float padB) const
Get a copy of this IRECT with the values padded N.B.
void VAlignTo(const IRECT &sr, EVAlign align)
Vertically align this rect to the reference IRECT.
void GetHSLA(float &h, float &s, float &l, float &a) const
Get the Hue, Saturation and Luminance of the color.
int GetScale() const
IMatrix()
Create an identity matrix.
IRECT GetFromLeft(float amount) const
Get a subrect of this IRECT bounded in X by the left edge and &#39;amount&#39;.
float R
Right side of the rectangle (X + W)
void Randomise(int alpha=255)
Randomise the color parts, with optional alpha.
IRECT GetScaled(float scale) const
Get a copy of this IRECT with all values multiplied by scale.
IPlug logging a.k.a tracing functionality.
void SetTransform(float xx, float yx, float xy, float yy, float tx, float ty)
Set the affine transform for the IPattern with values.
IRECT GetVAlignedTo(const IRECT &sr, EVAlign align) const
Get a rectangle the same dimensions as this one, vertically aligned to the reference IRECT...
static IPattern CreateRadialGradient(float x1, float y1, float r, const std::initializer_list< IColorStop > &stops={})
Create a radial gradient IPattern.
float H() const
IRECT GetGridCell(int cellIndex, int nRows, int nColumns, EDirection dir=EDirection::Horizontal, int nCells=1) const
Get a subrect (by index) of this IRECT which is a cell (or union of nCells sequential cells on same r...
IBlend(EBlend type=EBlend::Default, float weight=1.0f)
Creates a new IBlend.
IRECT Intersect(const IRECT &rhs) const
Create a new IRECT that is the intersection of this IRECT and rhs.
void ShowBubbleHorizontalActionFunc(IControl *pCaller)
Use with a param-linked control to popup the bubble control horizontally.
Definition: IControl.cpp:55
void ShowBubbleVerticalActionFunc(IControl *pCaller)
Use with a param-linked control to popup the bubble control vertically.
Definition: IControl.cpp:65
bool Intersects(const IRECT &rhs) const
Returns true if this IRECT shares any common pixels with rhs, false otherwise.
IRECT GetVShifted(float y) const
Get a copy of this rectangle translated on the Y axis.
float W() const
IColor WithOpacity(float alpha) const
Returns a new IColor with a different opacity.
IRECT GetMidHPadded(float padding) const
Get a copy of this IRECT where its width = 2 * padding but the center point on the X-axis has not cha...
int NStops() const
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...
IRECT GetScaledAboutCentre(float scale) const
Get a copy of this IRECT where the width and height are multiplied by scale without changing the cent...
void Offset(float l, float t, float r, float b)
Offset each field of the rectangle.
IMatrix & Translate(float x, float y)
Set the matrix for a translation transform.
const IColor & GetColor(EVColor color) const
int W() const
void DBGPrint()
Print the IRECT&#39;s detailes to the console in Debug builds.
void Clamp()
Keep the member int variables within the range 0-255.
ILayer(APIBitmap *pBitmap, const IRECT &layerRect, IControl *pControl, const IRECT &controlRect)
Create a layer/offscreen context (used internally)
IRECT GetReducedFromBottom(float amount) const
Get a subrect of this IRECT reduced in height from the bottom edge by &#39;amount&#39;.
Used to group mouse coordinates with mouse modifier information.
void SetOpacity(float alpha)
Set the color&#39;s opacity/alpha component with a float.
const IColorStop & GetStop(int idx) const
Get the IColorStop at a particular index (will crash if out of bounds)
void ScaleAboutCentre(float scale)
Scale the width and height of this IRECT by scale without changing the center point.
void MidVPad(float padding)
Set the height of this IRECT to 2*padding without changing it&#39;s center point on the Y-axis...
static IRECT LinearInterpolateBetween(const IRECT &start, const IRECT &dest, float progress)
Get a rectangle that is a linear interpolation between start and dest
bool Empty() const
int H() const
float GetDrawScale() const
const WDL_String & GetResourceName() const
IMatrix & Scale(float x, float y)
Set the matrix for a scale transform.
IRECT(float l, float t, float r, float b)
Construct a new IRECT with dimensions.
The lowest level base class of an IGraphics context.
Definition: IGraphics.h:86
float BlendWeight(const IBlend *pBlend)
Helper function to extract the blend weight value from an IBlend ptr if it is valid.
float Area() const
Used to specify properties of a drop-shadow to a layer.
void PixelAlign()
Align the rectangles to pixel boundaries.
void SetTransform(const IMatrix &transform)
Set the affine transform for the IPattern with an IMatrix.
IMatrix & Transform(const IRECT &before, const IRECT &after)
static IColor LinearInterpolateBetween(const IColor &start, const IColor &dest, float progress)
Helper function to linear interpolate between two IColors.
IText(float size, const char *fontID)
Create a new IText with size and fontID.
int ToColorCode() const
Convert the IColor to a single int (no alpha)
void DBGPrint()
Print the mouse modifier values to the console in Debug builds.
static const char * TextStyleString(ETextStyle style)
Helper to get a CString based on ETextStyle.
IRECT ReduceFromLeft(float amount)
Reduce in width from the left edge by &#39;amount&#39; and return the removed region.
Used to represent a point/stop in a gradient.
IRECT GetPixelAligned() const
Get a copy of this IRECT with PixelAlign() called.
int Size() const
void Clear()
Set all fields of this IRECT to 0.
BEGIN_IPLUG_NAMESPACE T Clip(T x, T lo, T hi)
Clips the value x between lo and hi.
const IRECT & Bounds() const
int FH() const
void TransformPoint(double &x, double &y, double x0, double y0) const
Transforms the point x, y.
A control that can be specialised with a lambda function, for quick experiments without making a cust...
Definition: IControl.h:1806
void DefaultAnimationFunc(IControl *pCaller)
An animation function that just calls the caller control&#39;s OnEndAnimation() method at the end of the ...
Definition: IControl.cpp:21
void Constrain(float &x, float &y) const
Ensure the point (x,y) is inside this IRECT.
void SplashAnimationFunc(IControl *pCaller)
The splash animation function is used by IVControls to animate the splash.
Definition: IControl.cpp:30
IRECT GetHSliced(float w, bool rhs=false) const
Get a copy of this IRECT with a new width.
void SplashClickActionFunc(IControl *pCaller)
The splash click action function is used by IVControls to start SplashAnimationFunc.
Definition: IControl.cpp:47
IColor(int a=255, int r=0, int g=0, int b=0)
Create an IColor.
IRECT Union(const IRECT &rhs) const
Create a new IRECT that is a union of this IRECT and rhs.
float W() const
IVStyle(bool showLabel=DEFAULT_SHOW_LABEL, bool showValue=DEFAULT_SHOW_VALUE, const IVColorSpec &colors={DEFAULT_BGCOLOR, DEFAULT_FGCOLOR, DEFAULT_PRCOLOR, DEFAULT_FRCOLOR, DEFAULT_HLCOLOR, DEFAULT_SHCOLOR, DEFAULT_X1COLOR, DEFAULT_X2COLOR, DEFAULT_X3COLOR}, const IText &labelText=DEFAULT_LABEL_TEXT, const IText &valueText=DEFAULT_VALUE_TEXT, bool hideCursor=DEFAULT_HIDE_CURSOR, bool drawFrame=DEFAULT_DRAW_FRAME, bool drawShadows=DEFAULT_DRAW_SHADOWS, bool emboss=DEFAULT_EMBOSS, float roundness=DEFAULT_ROUNDNESS, float frameThickness=DEFAULT_FRAME_THICKNESS, float shadowOffset=DEFAULT_SHADOW_OFFSET, float widgetFrac=DEFAULT_WIDGET_FRAC, float angle=DEFAULT_WIDGET_ANGLE)
Create a new IVStyle to configure common styling for IVControls.
IMatrix & Skew(float xa, float ya)
Set the matrix for a skew transform.
IRECT GetFromRight(float amount) const
Get a subrect of this IRECT bounded in X by &#39;amount&#39; and the right edge.
bool Mergeable(const IRECT &rhs) const
Return if this IRECT and rhs may be merged.
void Pad(float padding)
Pad this IRECT N.B.
bool Contains(float x, float y) const
Returns true if this IRECT completely contains the point (x,y).
void TransformPoint(double &x, double &y) const
Transforms the point x, y with the matrix.
void Set(int a=255, int r=0, int g=0, int b=0)
Set the color parts.
IRECT GetGridCell(int row, int col, int nRows, int nColumns) const
Get a subrect (by row, column) of this IRECT which is a cell in a grid of size (nRows * nColumns) ...
IRECT GetCentredInside(const IBitmap &bitmap) const
Get a rectangle with the same center point as this rectangle and the size of the bitmap.
A base class interface for a bitmap abstraction around the different drawing back end bitmap represen...
bool IsValid() const
static IColor GetRandomColor(bool randomAlpha=false)
Get a random IColor.
float GetDrawScale() const
void Contrast(float c)
Contrast the color.
IText(float size, EVAlign valign, const IColor &color=DEFAULT_TEXT_FGCOLOR)
Create a new IText with size, vertical align, color.
static IColor FromHSLA(float h, float s, float l, float a=1.f)
Create an IColor from Hue Saturation and Luminance values.
float L
Left side of the rectangle (X)
IRECT GetPixelAligned(float scale) const
Get a copy of this IRECT with PixelAlign(scale) called.
IRECT GetFromTRHC(float w, float h) const
Get a subrect of this IRECT expanding from the top-right corner.
IRECT()
Construct an empty IRECT.
IRECT GetPixelSnapped(float scale) const
Get a copy of this IRECT with PixelSnap(scale) called.
IRECT GetHPadded(float padding) const
Get a copy of this IRECT padded in the X-axis N.B.
IRECT GetReducedFromLeft(float amount) const
Get a subrect of this IRECT reduced in width from the left edge by &#39;amount&#39;.
IRECT GetCentredInside(const IRECT &sr) const
Get a rectangle the size of sr but with the same center point as this rectangle.
void Set(int idx, const IRECT &rect)
Set a specific rectangle in the list (will crash if idx is invalid)
IMatrix & Transform(const IMatrix &m)
Transform this matrix with another.
static const IColor & GetDefaultColor(EVColor idx)
void AddStop(IColor color, float offset)
Add an IColorStop to the IPattern.
IPattern(EPatternType type)
Create an IPattern.
static IPattern CreateLinearGradient(const IRECT &bounds, EDirection direction, const std::initializer_list< IColorStop > &stops={})
Create a linear gradient IPattern across a rectangular area.
void HAlignTo(const IRECT &sr, EAlign align)
Horizontally align this rect to the reference IRECT.
IRECT GetRandomSubRect() const
static IPattern CreateLinearGradient(float x1, float y1, float x2, float y2, const std::initializer_list< IColorStop > &stops={})
Create a linear gradient IPattern.
IRECT GetPadded(float padding) const
Get a copy of this IRECT with each value padded by padding N.B.
IRECT GetFromBRHC(float w, float h) const
Get a subrect of this IRECT expanding from the bottom-right corner.
IRECT GetCentredInside(float w, float h=0.f) const
Get a rectangle with the same center point as this rectangle and the given size.
void MidHPad(float padding)
Set the width of this IRECT to 2*padding without changing it&#39;s center point on the X-axis...
IVColorSpec()
Create a new IVColorSpec object with default colors.
IColorStop(IColor color, float offset)
Create an IColor stop.
int N() const
void GetRGBf(float *rgbf) const
Get the color as a 3 float array.
int Find(float x, float y) const
Find the first index of the rect that contains point x, y, if it exists.
APIBitmap * GetAPIBitmap() const
IText(float size, EAlign align, const IColor &color=DEFAULT_TEXT_FGCOLOR)
Create a new IText with size, horizontal align, color.
int GetLuminosity() const
IMatrix & Rotate(float a)
Set the matrix for a rotation transform.
bool IsPixelAligned() const
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.
IPattern(const IColor &color)
Create an IPattern with a solid color fill.
void ToColorCodeStr(WDL_String &str) const
Convert the IColor to a hex string e.g.
Used to store pattern information for gradients.
IRECT GetFromBottom(float amount) const
Get a subrect of this IRECT bounded in Y by &#39;amount&#39; and the bottom edge.
void Clear()
Clear the list.
IVStyle(const std::initializer_list< IColor > &colors)
Create a new IVStyle based on a list of colors, and defaults for the other elements.
static IColor FromRGBAf(float *rgbaf)
Create an IColor from a 4 float RGBA array.
IRECT GetFromTop(float amount) const
Get a subrect of this IRECT bounded in Y by the top edge and &#39;amount&#39;.
Used to store transformation matrices.
bool IsTouch() const
true if this IMouseMod is linked to a touch event
IRECT SubRectHorizontal(int numSlices, int sliceIdx) const
Returns a new IRECT which is a vertical "slice" of this IRECT.
IMatrix(double xx, double yx, double xy, double yy, double tx, double ty)
Create an IMatrix, specifying the values.
bool GetFramesAreHorizontal() const
bool ContainsEdge(float x, float y) const
Returns true if the point (x,y) is either contained in this IRECT or on an edge.
void Invalidate()
Mark the layer as needing its contents redrawn.
float T
Top of the rectangle (Y)
void PixelAlign(float scale)
Pixel-align this IRECT at the given scale factor then scale it back down When scaling this mutliples ...
void PixelSnap()
Pixel aligns to nearest pixels This may make the IRECT smaller, unlike PixelAlign().
A struct encapsulating a set of properties used to configure IVControls.
IRECT GetReducedFromRight(float amount) const
Get a subrect of this IRECT reduced in width from the right edge by &#39;amount&#39;.
void GetRandomPoint(float &x, float &y) const
Get a random point within this rectangle.
IRECT GetReducedFromTop(float amount) const
Get a subrect of this IRECT reduced in height from the top edge by &#39;amount&#39;.
float H() const
IRECT GetHShifted(float x) const
Get a copy of this rectangle translated on the X axis.
float B
Bottom of the rectangle (Y + H)
An abstraction that is used to store a temporary raster image/framebuffer.
IRECT GetMidVPadded(float padding) const
Get a copy of this IRECT where its height = 2 * padding but the center point on the Y-axis has not ch...