iPlug2 - C++ Audio Plug-in Framework
IGraphicsPrivate.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 
18 #include <codecvt>
19 #include <string>
20 #include <memory>
21 
22 #include "mutex.h"
23 #include "wdlstring.h"
24 #include "wdlendian.h"
25 #include "ptrlist.h"
26 #include "heapbuf.h"
27 
28 #ifdef IGRAPHICS_SKIA
29  #pragma warning( push )
30  #pragma warning( disable : 4244 )
31  #pragma warning( disable : 5030 )
32  #include "SkSVGDOM.h"
33  #include "include/core/SkCanvas.h"
34  #include "include/core/SkStream.h"
35  #include "src/xml/SkDOM.h"
36  #pragma warning( pop )
37 #else
38  #include "nanosvg.h"
39 #endif
40 
41 #include "IPlugPlatform.h"
42 
43 #if defined IGRAPHICS_NANOVG
44  #define BITMAP_DATA_TYPE int;
45 #elif defined IGRAPHICS_SKIA
46  #pragma warning( push )
47  #pragma warning( disable : 4244 )
48  #include "SkImage.h"
49  #include "SkSurface.h"
50  #pragma warning( pop )
51  struct SkiaDrawable
52  {
53  bool mIsSurface;
54  sk_sp<SkImage> mImage;
55  sk_sp<SkSurface> mSurface;
56  };
57  #define BITMAP_DATA_TYPE SkiaDrawable*
58 #elif defined IGRAPHICS_CANVAS
59  #include <emscripten.h>
60  #include <emscripten/val.h>
61  #define BITMAP_DATA_TYPE emscripten::val*
62 #else // NO_IGRAPHICS
63  #define BITMAP_DATA_TYPE void*;
64 #endif
65 
66 #if defined OS_MAC || defined OS_IOS
67  #include <CoreText/CoreText.h>
68  #define FONT_DESCRIPTOR_TYPE CTFontDescriptorRef
69 #elif defined OS_WIN
70  #include "wingdi.h"
71  #define FONT_DESCRIPTOR_TYPE HFONT
72 #elif defined OS_WEB
73  #define FONT_DESCRIPTOR_TYPE std::pair<WDL_String, WDL_String>*
74 #else
75  // NO_IGRAPHICS
76 #endif
77 
78 BEGIN_IPLUG_NAMESPACE
79 BEGIN_IGRAPHICS_NAMESPACE
80 using BitmapData = BITMAP_DATA_TYPE;
81 using FontDescriptor = FONT_DESCRIPTOR_TYPE;
82 using RawBitmapData = WDL_TypedBuf<uint8_t>;
83 
87 class APIBitmap
88 {
89 public:
90 
97  APIBitmap(BitmapData pBitmap, int w, int h, int scale, float drawScale)
98  : mBitmap(pBitmap)
99  , mWidth(w)
100  , mHeight(h)
101  , mScale(scale)
102  , mDrawScale(drawScale)
103  {}
104 
105  APIBitmap()
106  : mBitmap(0)
107  , mWidth(0)
108  , mHeight(0)
109  , mScale(0)
110  , mDrawScale(1.f)
111  {}
112 
113  virtual ~APIBitmap() {}
114 
115  APIBitmap(const APIBitmap&) = delete;
116  APIBitmap& operator=(const APIBitmap&) = delete;
117 
124  void SetBitmap(BitmapData pBitmap, int w, int h, int scale, float drawScale)
125  {
126  mBitmap = pBitmap;
127  mWidth = w;
128  mHeight = h;
129  mScale = scale;
130  mDrawScale = drawScale;
131  }
132 
134  BitmapData GetBitmap() const { return mBitmap; }
135 
137  int GetWidth() const { return mWidth; }
138 
140  int GetHeight() const { return mHeight; }
141 
143  int GetScale() const { return mScale; }
144 
146  float GetDrawScale() const { return mDrawScale; }
147 
148 private:
149  BitmapData mBitmap; // for most drawing APIs BitmapData is a pointer. For Nanovg it is an integer index
150  int mWidth;
151  int mHeight;
152  int mScale;
153  float mDrawScale;
154 };
155 
157 class IFontInfo
158 {
159 public:
160  IFontInfo(const void* data, uint32_t dataSize, uint32_t faceIdx)
161  : mData(reinterpret_cast<const unsigned char*>(data))
162  {
163  if (mData)
164  FindFace(faceIdx);
165 
166  if (mData)
167  {
168  mHeadLocation = LocateTable("head");
169  mNameLocation = LocateTable("name");
170  mHheaLocation = LocateTable("hhea");
171  mFDscLocation = LocateTable("fdsc");
172 
173  if (IsValid())
174  {
175  mUnitsPerEM = GetUInt16(mHeadLocation + 18);
176  mMacStyle = GetUInt16(mHeadLocation + 44);
177  mFamily = SearchFontString(1);
178  mStyle = SearchFontString(2);
179  mAscender = GetSInt16(mHheaLocation + 4);
180  mDescender = GetSInt16(mHheaLocation + 6);
181  mLineGap = GetSInt16(mHheaLocation + 8);
182  }
183  }
184  }
185 
186  bool IsValid() const { return mData && mHeadLocation && mNameLocation && mHheaLocation; }
187 
188  const WDL_String& GetFamily() const { return mFamily; }
189  const WDL_String& GetStyle() const { return mStyle; }
190 
191  bool IsBold() const { return mMacStyle & (1 << 0); }
192  bool IsItalic() const { return mMacStyle & (1 << 1); }
193  bool IsUnderline() const { return mMacStyle & (1 << 2); }
194  bool IsOutline() const { return mMacStyle & (1 << 3); }
195  bool IsShadow() const { return mMacStyle & (1 << 4); }
196  bool IsCondensed() const { return mMacStyle & (1 << 5); }
197  bool IsExpanded() const { return mMacStyle & (1 << 6); }
198 
199  double GetHeightEMRatio() const { return mUnitsPerEM / static_cast<double>(mAscender - mDescender); }
200 
201  uint16_t GetUnitsPerEM() const { return mUnitsPerEM; }
202  int16_t GetAscender() const { return mAscender; }
203  int16_t GetDescender() const { return mDescender; }
204  int16_t GetLineGap() const { return mLineGap; }
205  int16_t GetLineHeight() const { return (mAscender - mDescender) + mLineGap; }
206 
207 private:
208 
209  enum class EStringID { Mac, Windows };
210 
211  bool MatchTag(uint32_t loc, const char* tag)
212  {
213  return mData[loc+0] == tag[0] && mData[loc+1] == tag[1] && mData[loc+2] == tag[2] && mData[loc+3] == tag[3];
214  }
215 
216  uint32_t LocateTable(const char *tag)
217  {
218  uint16_t numTables = GetUInt16(4);
219 
220  for (uint16_t i = 0; i < numTables; ++i)
221  {
222  uint32_t tableLocation = 12 + (16 * i);
223  if (MatchTag(tableLocation, tag))
224  return GetUInt32(tableLocation + 8);
225  }
226 
227  return 0;
228  }
229 
230  WDL_String SearchFontString(int nameID)
231  {
232  WDL_String str = GetFontString(nameID, EStringID::Windows);
233 
234  if (str.GetLength())
235  return str;
236 
237  return GetFontString(nameID, EStringID::Mac);
238  }
239 
240  WDL_String GetFontString(int nameID, EStringID stringID)
241  {
242  // Default to windows values
243 
244  int platformID = 3;
245  int encodingID = 1;
246  int languageID = 0x409;
247 
248  switch (stringID)
249  {
250  case EStringID::Mac:
251  platformID = 1;
252  encodingID = 0;
253  languageID = 0;
254  break;
255 
256  case EStringID::Windows:
257  break;
258  }
259 
260  for (uint16_t i = 0; i < GetUInt16(mNameLocation + 2); ++i)
261  {
262  uint32_t loc = mNameLocation + 6 + (12 * i);
263 
264  if (platformID == GetUInt16(loc + 0) && encodingID == GetUInt16(loc + 2)
265  && languageID == GetUInt16(loc + 4) && nameID == GetUInt16(loc + 6))
266  {
267  uint32_t stringLocation = GetUInt16(mNameLocation + 4) + GetUInt16(loc + 10);
268  uint16_t length = GetUInt16(loc + 8);
269 
270  switch (stringID)
271  {
272  case EStringID::Windows:
273  {
274  WDL_TypedBuf<char> utf8;
275  WDL_TypedBuf<char16_t> utf16;
276  utf8.Resize((length * 3) / 2);
277  utf16.Resize(length / sizeof(char16_t));
278 
279  for (int j = 0; j < length; j++)
280  utf16.Get()[j] = GetUInt16(mNameLocation + stringLocation + j * 2);
281 
282  std::codecvt_utf8_utf16<char16_t> conv;
283  const char16_t *a;
284  char *b;
285  mbstate_t mbs;
286  memset(&mbs, 0, sizeof(mbs));
287  conv.out(mbs, utf16.Get(), utf16.Get() + utf16.GetSize(), a, utf8.Get(), utf8.Get() + utf8.GetSize(), b);
288 
289  return WDL_String(utf8.Get(), (int) (b - utf8.Get()));
290  }
291 
292  case EStringID::Mac:
293  return WDL_String((const char*)(mData + mNameLocation + stringLocation), length);
294  }
295  }
296  }
297 
298  return WDL_String();
299  }
300 
301  void FindFace(uint32_t faceIdx)
302  {
303  bool singleFont = IsSingleFont();
304 
305  if (singleFont && faceIdx == 0 )
306  return;
307 
308  // Check if it's a TTC file
309  if (!singleFont && MatchTag(0, "ttcf"))
310  {
311  // Check version
312  if (GetUInt32(4) == 0x00010000 || GetUInt32(4) == 0x00020000)
313  {
314  if (faceIdx < GetSInt32(8))
315  {
316  mData += GetUInt32(12 + faceIdx * 4);
317  return;
318  }
319  }
320  }
321  mData = nullptr;
322  }
323 
324  bool IsSingleFont()
325  {
326  char TTV1[4] = { '1', 0, 0, 0 };
327  char OTV1[4] = { 0, 1, 0, 0 };
328 
329  // Check the version number
330  if (MatchTag(0, TTV1)) return true; // TrueType 1
331  if (MatchTag(0, "typ1")) return true; // TrueType with type 1 font -- we don't support this!
332  if (MatchTag(0, "OTTO")) return true; // OpenType with CFF
333  if (MatchTag(0, OTV1)) return true; // OpenType 1.0
334 
335  return false;
336  }
337 
338 #if defined WDL_LITTLE_ENDIAN
339  uint16_t GetUInt16(uint32_t loc) { return (((uint16_t)mData[loc + 0]) << 8) | (uint16_t)mData[loc + 1]; }
340  int16_t GetSInt16(uint32_t loc) { return (((uint16_t)mData[loc + 0]) << 8) | (uint16_t)mData[loc + 1]; }
341  uint32_t GetUInt32(uint32_t loc) { return (((uint32_t)GetUInt16(loc + 0)) << 16) | (uint32_t)GetUInt16(loc + 2); }
342  int32_t GetSInt32(uint32_t loc) { return (((uint32_t)GetUInt16(loc + 0)) << 16) | (uint32_t)GetUInt16(loc + 2); }
343 #else
344  uint16_t GetUInt16(uint32_t loc) { return (((uint16_t)mData[loc + 1]) << 8) | (uint16_t)mData[loc + 0]; }
345  int16_t GetSInt16(uint32_t loc) { return (((uint16_t)mData[loc + 1]) << 8) | (uint16_t)mData[loc + 0]; }
346  uint32_t GetUInt32(uint32_t loc) { return (((uint32_t)GetUInt16(loc + 2)) << 16) | (uint32_t)GetUInt16(loc + 0); }
347  int32_t GetSInt32(uint32_t loc) { return (((uint32_t)GetUInt16(loc + 2)) << 16) | (uint32_t)GetUInt16(loc + 0); }
348 #endif
349 
350 private:
351  const unsigned char* mData;
352 
353  uint32_t mHeadLocation = 0;
354  uint32_t mNameLocation = 0;
355  uint32_t mHheaLocation = 0;
356  uint32_t mFDscLocation = 0;
357 
358  // Font Identifiers
359  WDL_String mFamily;
360  WDL_String mStyle;
361  uint16_t mMacStyle = 0;
362 
363  // Metrics
364  uint16_t mUnitsPerEM = 0;
365  int16_t mAscender = 0;
366  int16_t mDescender = 0;
367  int16_t mLineGap = 0;
368 };
369 
371 class IFontData : public IFontInfo, private WDL_TypedBuf<unsigned char>
372 {
373 public:
374  IFontData() : IFontInfo(nullptr, 0, -1), mFaceIdx(-1) {}
375 
376  IFontData(const void* data, int size, int faceIdx) : IFontInfo(data, size, faceIdx), mFaceIdx(faceIdx)
377  {
378  const unsigned char* src = reinterpret_cast<const unsigned char*>(data);
379  unsigned char* dest = ResizeOK(size);
380 
381  if (dest)
382  std::copy(src, src + size, dest);
383  }
384 
385  IFontData(int size) : IFontInfo(nullptr, 0, -1), mFaceIdx(-1)
386  {
387  Resize(size);
388  }
389 
390  void SetFaceIdx(int faceIdx)
391  {
392  mFaceIdx = faceIdx;
393  static_cast<IFontData&>(*this) = IFontData(Get(), GetSize(), mFaceIdx);
394  }
395 
396  bool IsValid() const { return GetSize() && mFaceIdx >= 0 && IFontInfo::IsValid(); }
397 
398  unsigned char* Get() { return WDL_TypedBuf<unsigned char>::Get(); }
399  int GetSize() const { return WDL_TypedBuf<unsigned char>::GetSize(); }
400  int GetFaceIdx() const { return mFaceIdx; }
401 
402 private:
403  int mFaceIdx;
404 };
405 
407 using IFontDataPtr = std::unique_ptr<IFontData>;
408 
410 class PlatformFont
411 {
412 public:
413  PlatformFont(bool system) : mSystem(system) {}
414  virtual ~PlatformFont() {}
415 
416  PlatformFont(const PlatformFont&) = delete;
417  PlatformFont& operator=(const PlatformFont&) = delete;
418 
419  virtual FontDescriptor GetDescriptor() { return nullptr; }
420  virtual IFontDataPtr GetFontData() { return IFontDataPtr(new IFontData()); }
421  bool IsSystem() { return mSystem; }
422 
423 protected:
424  int GetFaceIdx(const void* data, int dataSize, const char* styleName)
425  {
426  for (int idx = 0; ; idx++)
427  {
428  IFontInfo fontInfo(data, dataSize, idx);
429 
430  if (!fontInfo.IsValid())
431  return -1;
432 
433  const WDL_String& style = fontInfo.GetStyle();
434 
435  if (style.GetLength() && (!styleName[0] || !strcmp(style.Get(), styleName)))
436  return idx;
437  }
438  }
439 
440  bool mSystem;
441 };
442 
443 using PlatformFontPtr = std::unique_ptr<PlatformFont>;
444 
445 #ifdef IGRAPHICS_SKIA
446 struct SVGHolder
447 {
448  SVGHolder(sk_sp<SkSVGDOM> svgDom)
449  : mSVGDom(svgDom)
450  {
451  }
452 
453  ~SVGHolder()
454  {
455  mSVGDom = nullptr;
456  }
457 
458  SVGHolder(const SVGHolder&) = delete;
459  SVGHolder& operator=(const SVGHolder&) = delete;
460 
461  sk_sp<SkSVGDOM> mSVGDom;
462 };
463 #else
464 
465 struct SVGHolder
466 {
467  SVGHolder(NSVGimage* pImage)
468  : mImage(pImage)
469  {
470  }
471 
472  ~SVGHolder()
473  {
474  if(mImage)
475  nsvgDelete(mImage);
476 
477  mImage = nullptr;
478  }
479 
480  SVGHolder(const SVGHolder&) = delete;
481  SVGHolder& operator=(const SVGHolder&) = delete;
482 
483  NSVGimage* mImage = nullptr;
484 };
485 #endif
486 
488 template <class T>
489 class StaticStorage
490 {
491 public:
493  class Accessor : private WDL_MutexLock
494  {
495  public:
496  Accessor(StaticStorage& storage)
497  : WDL_MutexLock(&storage.mMutex)
498  , mStorage(storage)
499  {}
500 
501  T* Find(const char* str, double scale = 1.) { return mStorage.Find(str, scale); }
502  void Add(T* pData, const char* str, double scale = 1.) { return mStorage.Add(pData, str, scale); }
503  void Remove(T* pData) { return mStorage.Remove(pData); }
504  void Clear() { return mStorage.Clear(); }
505  void Retain() { return mStorage.Retain(); }
506  void Release() { return mStorage.Release(); }
507 
508  private:
509  StaticStorage& mStorage;
510  };
511 
512  StaticStorage() {}
513 
514  ~StaticStorage()
515  {
516  Clear();
517  }
518 
519  StaticStorage(const StaticStorage&) = delete;
520  StaticStorage& operator=(const StaticStorage&) = delete;
521 
522 private:
524  struct DataKey
525  {
526  // N.B. - hashID is not guaranteed to be unique
527  size_t hashID;
528  WDL_String name;
529  double scale;
530  std::unique_ptr<T> data;
531  };
532 
536  size_t Hash(const char* str)
537  {
538  std::string string(str);
539  return std::hash<std::string>()(string);
540  }
541 
546  T* Find(const char* str, double scale = 1.)
547  {
548  WDL_String cacheName(str);
549  cacheName.AppendFormatted((int) strlen(str) + 6, "-%.1fx", scale);
550 
551  size_t hashID = Hash(cacheName.Get());
552 
553  int i, n = mDatas.GetSize();
554  for (i = 0; i < n; ++i)
555  {
556  DataKey* pKey = mDatas.Get(i);
557 
558  // Use the hash id for a quick search and then confirm with the scale and identifier to ensure uniqueness
559  if (pKey->hashID == hashID && scale == pKey->scale && !strcmp(str, pKey->name.Get()))
560  return pKey->data.get();
561  }
562  return nullptr;
563  }
564 
569  void Add(T* pData, const char* str, double scale = 1.)
570  {
571  DataKey* pKey = mDatas.Add(new DataKey);
572 
573  WDL_String cacheName(str);
574  cacheName.AppendFormatted((int) strlen(str) + 6, "-%.1fx", scale);
575 
576  pKey->hashID = Hash(cacheName.Get());
577  pKey->data = std::unique_ptr<T>(pData);
578  pKey->scale = scale;
579  pKey->name.Set(str);
580 
581  //DBGMSG("adding %s to the static storage at %.1fx the original scale\n", str, scale);
582  }
583 
585  void Remove(T* pData)
586  {
587  for (int i = 0; i < mDatas.GetSize(); ++i)
588  {
589  if (mDatas.Get(i)->data.get() == pData)
590  {
591  mDatas.Delete(i, true);
592  break;
593  }
594  }
595  }
596 
598  void Clear()
599  {
600  mDatas.Empty(true);
601  };
602 
604  void Retain()
605  {
606  mCount++;
607  }
608 
610  void Release()
611  {
612  if (--mCount == 0)
613  Clear();
614  }
615 
616  int mCount = 0;
617  WDL_Mutex mMutex;
618  WDL_PtrList<DataKey> mDatas;
619 };
620 
622 struct IVec2
623 {
624  float x, y;
625  IVec2() = default;
626  IVec2(float x, float y) : x(x), y(y) {}
627 
628  IVec2 operator-(const IVec2 b) { return IVec2{x-b.x, y-b.y}; }
629  IVec2 operator+(const IVec2 b) { return IVec2{x+b.x, y+b.y}; }
630 };
631 
632 
633 END_IGRAPHICS_NAMESPACE
634 END_IPLUG_NAMESPACE
635 
Encapsulate an xy point in one struct.
void SetBitmap(BitmapData pBitmap, int w, int h, int scale, float drawScale)
Used to initialise the members after construction.
int GetWidth() const
Include to get consistently named preprocessor macros for different platforms and logging functionali...
int GetScale() const
BitmapData GetBitmap() const
float GetDrawScale() const
int GetHeight() const
A base class interface for a bitmap abstraction around the different drawing back end bitmap represen...
APIBitmap(BitmapData pBitmap, int w, int h, int scale, float drawScale)
APIBitmap constructor.