1 // [Blend2D]
2 // 2D Vector Graphics Powered by a JIT Compiler.
3 //
4 // [License]
5 // Zlib - See LICENSE.md file in the package.
6 
7 #ifndef BLEND2D_API_INTERNAL_P_H
8 #define BLEND2D_API_INTERNAL_P_H
9 
10 // ============================================================================
11 // [Dependencies]
12 // ============================================================================
13 
14 #include "./api.h"
15 #include "./api-impl.h"
16 #include "./variant.h"
17 
18 // C Headers
19 // ---------
20 
21 // NOTE: Some headers are already included by <blapi.h>. This should be useful
22 // for creating an overview of what Blend2D really needs globally to be included.
23 #include <math.h>
24 #include <stdarg.h>
25 #include <stddef.h>
26 #include <stdint.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 // C++ Headers
32 // -----------
33 
34 // We are just fine with <math.h>, however, there are some useful overloads in
35 // C++'s <cmath> that are nicer to use than those in <math.h>. Mostly low level
36 // functionality like blIsFinite() relies on <cmath> instead of <math.h>.
37 #include <new>
38 #include <cmath>
39 #include <limits>
40 
41 // Platform Specific Headers
42 // -------------------------
43 
44 #ifdef _WIN32
45   //! \cond NEVER
46   #ifndef WIN32_LEAN_AND_MEAN
47     #define WIN32_LEAN_AND_MEAN
48   #endif
49   #ifndef NOMINMAX
50     #define NOMINMAX
51   #endif
52   //! \endcond
53 
54   #include <windows.h>   // Required to build Blend2D on Windows platform.
55   #include <synchapi.h>  // Synchronization primitivess.
56 #else
57   #include <errno.h>     // Need to access it in some cases.
58   #include <pthread.h>   // Required to build Blend2D on POSIX compliant platforms.
59   #include <unistd.h>    // Filesystem, sysconf, etc...
60 #endif
61 
62 // Some intrinsics defined by MSVC compiler are useful. Most of them should be
63 // used by "blsupport_p.h" that provides a lot of support functions used across
64 // the library.
65 #ifdef _MSC_VER
66   #include <intrin.h>
67 #endif
68 
69 // ============================================================================
70 // [Compiler Macros]
71 // ============================================================================
72 
73 #if defined(__clang__)
74   #define BL_CLANG_AT_LEAST(MAJOR, MINOR) ((MAJOR > __clang_major__) || (MAJOR == __clang_major__ && MINOR >= __clang_minor__))
75 #else
76   #define BL_CLANG_AT_LEAST(MAJOR, MINOR) 0
77 #endif
78 
79 // Some compilers won't optimize our stuff if we won't tell them. However, we
80 // have to be careful as Blend2D doesn't use fast-math by default. So when
81 // applicable we turn some optimizations on and off locally, but not globally.
82 //
83 // This has also a lot of downsides. For example all wrappers in "std" namespace
84 // are compiled with default flags so even when we force the compiler to follow
85 // certain behavior it would only work if we use the "original" functions and
86 // not those wrapped in `std` namespace (so use floor and not std::floor, etc).
87 #if defined(_MSC_VER) && !defined(__clang__)
88   #define BL_PRAGMA_FAST_MATH_PUSH __pragma(float_control(precise, off, push))
89   #define BL_PRAGMA_FAST_MATH_POP __pragma(float_control(pop))
90 #else
91   #define BL_PRAGMA_FAST_MATH_PUSH
92   #define BL_PRAGMA_FAST_MATH_POP
93 #endif
94 
95 #if defined(__clang__) || defined(__has_attribute__)
96   #define BL_CC_HAS_ATTRIBUTE(x) __has_attribute(x)
97 #else
98   #define BL_CC_HAS_ATTRIBUTE(x) 0
99 #endif
100 
101 #if __cplusplus >= 201703L
102   #define BL_FALLTHROUGH [[fallthrough]];
103 #elif defined(__GNUC__) && __GNUC__ >= 7
104   #define BL_FALLTHROUGH __attribute__((fallthrough));
105 #else
106   #define BL_FALLTHROUGH /* fallthrough */
107 #endif
108 
109 // PROBLEM: On Linux the default C++ standard library is called `libstdc++` and
110 // comes with GCC. Clang can also use this standard library and in many cases
111 // it is configured to do so, however, the standard library can be older (and
112 // thus provide less features) than the C++ version reported by the compiler via
113 // `__cplusplus` macro. This means that the `__cplusplus` version doesn't
114 // correspond to the C++ standard library version!
115 //
116 // The problem is that `libstdc++` doesn't provide any version information, but
117 // a timestamp, which is unreliable if you use other compiler than GCC. Since we
118 // only use C++14 and higher optionally we don't have a problem to detect such
119 // case and to conditionally disable C++14 and higher features of the standard
120 // C++ library.
121 #if defined(__GLIBCXX__) && defined(__clang__)
122   #if __has_include(<string_view>) && __cplusplus >= 201703L
123     #define BL_STDCXX_VERSION 201703L
124   #elif __has_include(<shared_mutex>) && __cplusplus >= 201402L
125     #define BL_STDCXX_VERSION 201402L
126   #else
127     #define BL_STDCXX_VERSION 201103L
128   #endif
129 #else
130   #define BL_STDCXX_VERSION __cplusplus
131 #endif
132 
133 // ============================================================================
134 // [Internal Macros]
135 // ============================================================================
136 
137 //! \def BL_HIDDEN
138 //!
139 //! Decorates a function that is used across more than one source file, but
140 //! should never be exported. Expands to a compiler-specific code that affects
141 //! the visibility.
142 #if defined(__GNUC__) && !defined(__MINGW32__)
143   #define BL_HIDDEN __attribute__((__visibility__("hidden")))
144 #else
145   #define BL_HIDDEN
146 #endif
147 
148 //! \def BL_OPTIMIZE
149 //!
150 //! Decorates a function that should be highly optimized by C++ compiler. In
151 //! general Blend2D uses "-O2" optimization level on GCC and Clang, this macro
152 //! would change the optimization level to "-O3" for the decorated function.
153 #if !defined(BL_BUILD_DEBUG) && (BL_CC_HAS_ATTRIBUTE(__optimize__) || (!defined(__clang__) && defined(__GNUC__)))
154   #define BL_OPTIMIZE __attribute__((__optimize__("O3")))
155 #else
156   #define BL_OPTIMIZE
157 #endif
158 
159 //! \def BL_NOINLINE
160 //!
161 //! Decorates a function that should never be inlined. Sometimes used by Blend2D
162 //! to decorate functions that are either called rarely or that are called from
163 //! other code in corner cases - like buffer reallocation, etc...
164 #if defined(__GNUC__)
165   #define BL_NOINLINE __attribute__((noinline))
166 #elif defined(_MSC_VER)
167   #define BL_NOINLINE __declspec(noinline)
168 #else
169   #define BL_NOINLINE
170 #endif
171 
172 //! \def BL_STDCALL
173 //!
174 //! Calling convention used by Windows.
175 #if defined(__GNUC__) && defined(__i386__) && !defined(__x86_64__)
176   #define BL_STDCALL __attribute__((__stdcall__))
177 #elif defined(_MSC_VER)
178   #define BL_STDCALL __stdcall
179 #else
180   #define BL_STDCALL
181 #endif
182 
183 #define BL_NONCOPYABLE(...)                                                   \
184   __VA_ARGS__(const __VA_ARGS__& other) = delete;                             \
185   __VA_ARGS__& operator=(const __VA_ARGS__& other) = delete;
186 
187 #define BL_UNUSED(X) (void)(X)
188 
189 #define BL_ARRAY_SIZE(X) uint32_t(sizeof(X) / sizeof(X[0]))
190 #define BL_OFFSET_OF(STRUCT, MEMBER) ((int)(offsetof(STRUCT, MEMBER)))
191 
192 //! \def BL_NOT_REACHED()
193 //!
194 //! Run-time assertion used in code that should never be reached.
195 #ifdef BL_BUILD_DEBUG
196   #define BL_NOT_REACHED()                                                    \
197     do {                                                                      \
198       blRuntimeAssertionFailure(__FILE__, __LINE__,                           \
199                                 "Unreachable code-path reached");             \
200     } while (0)
201 #else
202   #define BL_NOT_REACHED() BL_ASSUME(0)
203 #endif
204 
205 #if defined(__INTEL_COMPILER)
206   #define BL_NOUNROLL __pragma(nounroll)
207 #elif defined(__clang__) && BL_CLANG_AT_LEAST(3, 6)
208   #define BL_NOUNROLL _Pragma("nounroll")
209 #elif defined(__GNUC__) && (__GNUC__ >= 8)
210   #define BL_NOUNROLL _Pragma("GCC unroll 1")
211 #else
212   #define BL_NOUNROLL
213 #endif
214 
215 #define for_nounroll BL_NOUNROLL for
216 #define while_nounroll BL_NOUNROLL while
217 
218 //! Decorates a base class that has virtual functions.
219 #define BL_OVERRIDE_NEW_DELETE(TYPE)                                          \
220   BL_INLINE void* operator new(size_t n) noexcept { return malloc(n); }       \
221   BL_INLINE void  operator delete(void* p) noexcept { if (p) free(p); }       \
222                                                                               \
223   BL_INLINE void* operator new(size_t, void* p) noexcept { return p; }        \
224   BL_INLINE void  operator delete(void*, void*) noexcept {}
225 
226 //! Like BL_PROPAGATE, but propagates everything except `BL_RESULT_NOTHING`.
227 #define BL_PROPAGATE_IF_NOT_NOTHING(...)                                      \
228   do {                                                                        \
229     BLResult resultToPropagate = (__VA_ARGS__);                               \
230     if (resultToPropagate != BL_RESULT_NOTHING)                               \
231       return resultToPropagate;                                               \
232   } while (0)
233 
234 // ============================================================================
235 // [Forward Declarations]
236 // ============================================================================
237 
238 struct BLRuntimeContext;
239 
240 // ============================================================================
241 // [Internal Constants]
242 // ============================================================================
243 
244 //! Internal constants and limits used across the library.
245 enum BLInternalConsts : uint32_t {
246   //! BLResult value that is used internally to signalize that the function
247   //! didn't succeed, but also didn't fail. This is not an error state.
248   //!
249   //! At the moment this is only used by `BLPixelConverter` when setting up
250   //! optimized conversion functions.
251   //!
252   //! \note This result code can be never propagated to the user code!
253   BL_RESULT_NOTHING = 0xFFFFFFFFu,
254 
255   // --------------------------------------------------------------------------
256   // System allocator properties and some limits used by Blend2D.
257   // --------------------------------------------------------------------------
258 
259   //! Host memory allocator overhead (estimated).
260   BL_ALLOC_OVERHEAD = uint32_t(sizeof(void*)) * 4,
261   //! Host memory allocator alignment (must match!).
262   BL_ALLOC_ALIGNMENT = 8,
263 
264   //! Limits a doubling of a container size after the limit size [in bytes] is
265   //! reached [8MB]. After the size is reached the container will grow in [8MB]
266   //! chunks.
267   BL_ALLOC_GROW_LIMIT = 1 << 23,
268 
269   // --------------------------------------------------------------------------
270   // Alloc hints are specified in bytes. Each container will be allocated to
271   // `BL_ALLOC_HINT_...` bytes initially when a first item is added
272   // to it.
273   // --------------------------------------------------------------------------
274 
275   //! Initial size of BLStringImpl of a newly allocated string [in bytes].
276   BL_ALLOC_HINT_STRING = 64,
277   //! Initial size of BLArrayImpl of a newly allocated array [in bytes].
278   BL_ALLOC_HINT_ARRAY = 128,
279   //! Initial size of BLRegionImpl of a newly allocated region [in bytes].
280   BL_ALLOC_HINT_REGION = 256,
281   //! Initial size of BLPathImpl of a newly allocated path [in bytes].
282   BL_ALLOC_HINT_PATH2D = 512,
283   //! Initial size of BLGradientImpl of a newly allocated gradient [in bytes].
284   BL_ALLOC_HINT_GRADIENT = 256,
285 
286   BL_CACHE_LINE_SIZE = 64,
287 
288   //! To make checks for APPEND operation easier.
289   BL_MODIFY_OP_APPEND_START = 2,
290   //! Mask that can be used to check whether `BLModifyOp` has a grow hint.
291   BL_MODIFY_OP_GROW_MASK = 0x1,
292 
293   //! Minimum vertices to amortize the check of a matrix type.
294   BL_MATRIX_TYPE_MINIMUM_SIZE = 16,
295 
296   //! Maximum number of faces per a single font collection.
297   BL_FONT_DATA_MAX_FACE_COUNT = 256
298 
299 };
300 
301 //! Analysis result that describes whether an unknown input is conforming.
302 enum BLDataAnalysis : uint32_t {
303   //! The input data is conforming (stored exactly as expected).
304   BL_DATA_ANALYSIS_CONFORMING = 0, // Must be 0
305   //! The input data is valid, but non-conforming (must be processed).
306   BL_DATA_ANALYSIS_NON_CONFORMING = 1, // Must be 1
307   //! The input data contains an invalid value.
308   BL_DATA_ANALYSIS_INVALID_VALUE = 2
309 };
310 
311 // ============================================================================
312 // [Internal Functions]
313 // ============================================================================
314 
315 template<typename T>
316 struct BLInternalCastImpl { T Type; };
317 
318 //! Casts a public `T` type into an internal implementation/data of that type.
319 //! For example `BLPathImpl` would be casted to `BLInternalPathImpl`. Used
320 //! by Blend2D as a shortcut to prevent using more verbose `static_cast<>` in
321 //! code that requires a lot of such casts (in most cases public API which is
322 //! then casting public interfaces into an internal ones).
323 template<typename T>
blInternalCast(T * something)324 constexpr typename BLInternalCastImpl<T>::Type* blInternalCast(T* something) noexcept {
325   return static_cast<typename BLInternalCastImpl<T>::Type*>(something);
326 }
327 
328 //! \overload
329 template<typename T>
blInternalCast(const T * something)330 constexpr const typename BLInternalCastImpl<T>::Type* blInternalCast(const T* something) noexcept {
331   return static_cast<const typename BLInternalCastImpl<T>::Type*>(something);
332 }
333 
334 //! Checks whether `dataAccessFlags` is valid.
blDataAccessFlagsIsValid(uint32_t dataAccessFlags)335 static BL_INLINE bool blDataAccessFlagsIsValid(uint32_t dataAccessFlags) noexcept {
336   return dataAccessFlags == BL_DATA_ACCESS_READ ||
337          dataAccessFlags == BL_DATA_ACCESS_RW;
338 }
339 
340 //! Initializes the built-in 'none' implementation.
341 template<typename T>
blInitBuiltInNull(T * impl,uint32_t implType,uint32_t implTraits)342 static BL_INLINE void blInitBuiltInNull(T* impl, uint32_t implType, uint32_t implTraits) noexcept {
343   impl->refCount = SIZE_MAX;
344   impl->implType = uint8_t(implType);
345   impl->implTraits = uint8_t(implTraits | BL_IMPL_TRAIT_NULL);
346 }
347 
348 //! Assigns a built-in none implementation `impl` to `blNone` - a built-in
349 //! array that contains all null instances provided by Blend2D. Any code that
350 //! assigns to `blNone` array must use this function as it's then easy to find.
351 template<typename T>
blAssignBuiltInNull(T * impl)352 static BL_INLINE void blAssignBuiltInNull(T* impl) noexcept {
353   *reinterpret_cast<T**>((void **)blNone + impl->implType) = impl;
354 }
355 
356 template<typename T>
blCallCtor(T & t)357 static BL_INLINE void blCallCtor(T& t) noexcept {
358   // Only needed by MSVC as otherwise it could generate null-pointer check
359   // before calling the constructor (as null pointer is allowed). GCC and
360   // clang can emit a "-Wtautological-undefined-compare" warning and that's
361   // the main reason it's only enabled for MSVC.
362   #if defined(_MSC_VER) && !defined(__clang__)
363   BL_ASSUME(&t != nullptr);
364   #endif
365 
366   new(&t) T();
367 }
368 
369 template<typename T, typename... Args>
blCallCtor(T & t,Args &&...args)370 static BL_INLINE void blCallCtor(T& t, Args&&... args) noexcept {
371   // Only needed by MSVC as otherwise it could generate null-pointer check
372   // before calling the constructor (as null pointer is allowed). GCC and
373   // clang can emit a "-Wtautological-undefined-compare" warning and that's
374   // the main reason it's only enabled for MSVC.
375   #if defined(_MSC_VER) && !defined(__clang__)
376   BL_ASSUME(&t != nullptr);
377   #endif
378 
379   new(&t) T(std::forward<Args>(args)...);
380 }
381 
382 template<typename T>
blCallDtor(T & t)383 static BL_INLINE void blCallDtor(T& t) noexcept {
384   t.~T();
385 }
386 
387 //! \}
388 //! \endcond
389 
390 #endif // BLEND2D_API_INTERNAL_P_H
391