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