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