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#include "./blapi-build_p.h"
8#include "./blformat_p.h"
9#include "./bltables_p.h"
10
11// ============================================================================
12// [BLFormatInfo - Globals]
13// ============================================================================
14
15const BLFormatInfo blFormatInfo[BL_FORMAT_RESERVED_COUNT] = {
16 #define U 0 // Used only to distinguish between zero and unused.
17 { 0 , blFormatFlagsStatic(0 ), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Public:NONE>
18 { 32, blFormatFlagsStatic(1 ), {{ { 8, 8, 8, 8 }, { 16, 8 , 0 , 24 } }} }, // <Public:PRGB32>
19 { 32, blFormatFlagsStatic(2 ), {{ { 8, 8, 8, U }, { 16, 8 , 0 , U } }} }, // <Public:XRGB32>
20 { 8 , blFormatFlagsStatic(3 ), {{ { U, U, U, 8 }, { U , U , U , 0 } }} }, // <Public:A8>
21 { 32, blFormatFlagsStatic(4 ), {{ { 8, 8, 8, U }, { 16, 8 , 0 , U } }} }, // <Internal:FRGB32>
22 { 32, blFormatFlagsStatic(5 ), {{ { 8, 8, 8, 8 }, { 16, 8 , 0 , 24 } }} }, // <Internal:ZERO32>
23 { 0 , blFormatFlagsStatic(6 ), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
24 { 0 , blFormatFlagsStatic(7 ), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
25 { 0 , blFormatFlagsStatic(8 ), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
26 { 0 , blFormatFlagsStatic(9 ), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
27 { 0 , blFormatFlagsStatic(10), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
28 { 0 , blFormatFlagsStatic(11), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
29 { 0 , blFormatFlagsStatic(12), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
30 { 0 , blFormatFlagsStatic(13), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
31 { 0 , blFormatFlagsStatic(14), {{ { U, U, U, U }, { U , U , U , U } }} }, // <Reserved>
32 { 0 , blFormatFlagsStatic(15), {{ { U, U, U, U }, { U , U , U , U } }} } // <Reserved>
33 #undef U
34};
35
36static_assert(BL_FORMAT_INTERNAL_COUNT == 6,
37 "New formats must be added to 'blFormatInfo' table");
38
39// ============================================================================
40// [BLFormatInfo - Tables]
41// ============================================================================
42
43// Indexes of components based on format flags that describe components. Each
44// bit in the mask describes RGBA components (in order). Thus 0x1 descrines red
45// component, 0x2 green 0x4 blue, and 0x8 alpha. Components can be combined so
46// 0x7 describes RGB and 0xF RGBA.
47struct BLPixelConverterComponentIndexesGen {
48 static constexpr uint8_t value(size_t i) noexcept {
49 return i == BL_FORMAT_FLAG_RGB ? uint8_t(0x7) :
50 i == BL_FORMAT_FLAG_ALPHA ? uint8_t(0x8) :
51 i == BL_FORMAT_FLAG_RGBA ? uint8_t(0xF) :
52 i == BL_FORMAT_FLAG_LUM ? uint8_t(0x7) :
53 i == BL_FORMAT_FLAG_LUMA ? uint8_t(0xF) : uint8_t(0);
54 }
55};
56
57static constexpr const auto blPixelConverterComponentIndexesTable =
58 blLookupTable<uint8_t, 16, BLPixelConverterComponentIndexesGen>();
59
60// ============================================================================
61// [BLFormatInfo - Sanitize]
62// ============================================================================
63
64static BL_INLINE bool blFormatInfoIsDepthValid(uint32_t depth) noexcept {
65 switch (depth) {
66 case 1:
67 case 2:
68 case 4:
69 case 8:
70 case 16:
71 case 24:
72 case 32:
73 return true;
74
75 default:
76 return false;
77 }
78}
79
80BLResult blFormatInfoSanitize(BLFormatInfo* self) noexcept {
81 BLFormatInfo& f = *self;
82
83 // Filter out all flags that will be computed.
84 f.flags &= BL_FORMAT_ALL_FLAGS;
85
86 uint32_t i;
87 bool masksOverlap = false;
88 bool notByteAligned = false;
89 bool crossesByteBoundary = false;
90
91 // Check depth.
92 if (!blFormatInfoIsDepthValid(f.depth))
93 return blTraceError(BL_ERROR_INVALID_VALUE);
94
95 if (f.flags & BL_FORMAT_FLAG_INDEXED) {
96 // In 32-bit mode shifts are not overlapping with `palette` so zero them.
97 if (sizeof(void*) == 4)
98 memset(f.shifts, 0, sizeof(f.shifts));
99
100 // Indexed formats are up to 8 bits-per-pixel and must have palette.
101 if (f.depth > 8 || !f.palette)
102 return blTraceError(BL_ERROR_INVALID_VALUE);
103 }
104 else {
105 // Check whether RGB|A components are correct.
106 uint64_t masksAsU64[4];
107 uint64_t masksCombined = 0;
108
109 // Check whether pixel components are specified correctly.
110 uint32_t componentIndexes = blPixelConverterComponentIndexesTable[f.flags & 0xF];
111 if (!componentIndexes)
112 return blTraceError(BL_ERROR_INVALID_VALUE);
113
114 for (i = 0; i < 4; i++) {
115 uint32_t size = f.sizes[i];
116 uint32_t shift = f.shifts[i];
117
118 if (size == 0) {
119 // Fail if this component must be provided.
120 if (componentIndexes & (1u << i))
121 return blTraceError(BL_ERROR_INVALID_VALUE);
122
123 // Undefined size (0) must have zero shift as well. As it's not
124 // used it doesn't make sense to assign it a value.
125 if (shift != 0)
126 return blTraceError(BL_ERROR_INVALID_VALUE);
127
128 masksAsU64[i] = 0;
129 }
130 else {
131 // Fail if this component must not be provided.
132 if (!(componentIndexes & (1u << i)))
133 return blTraceError(BL_ERROR_INVALID_VALUE);
134
135 // Fail if the size is too large.
136 if (size > 16)
137 return blTraceError(BL_ERROR_INVALID_VALUE);
138
139 // Shifted mask overflows the pixel depth?
140 if (shift + size > f.depth)
141 return blTraceError(BL_ERROR_INVALID_VALUE);
142
143 // Byte aligned means that shifts are [0, 8, 16, 24] and mask is 0xFF.
144 if (size != 8 || (shift & 0x7u) != 0)
145 notByteAligned = true;
146
147 // Does the mask cross a byte-boundary?
148 if ((shift / 8u) != ((shift + size - 1) / 8u))
149 crossesByteBoundary = true;
150
151 // Does the mask overlap with others?
152 uint64_t maskAsU64 = uint64_t(blTrailingBitMask<uint32_t>(size)) << shift;
153 if (masksCombined & maskAsU64) {
154 masksOverlap = true;
155 // Alpha channel cannot overlap.
156 if (i == 3)
157 return blTraceError(BL_ERROR_INVALID_VALUE);
158 }
159
160 masksAsU64[i] = maskAsU64;
161 masksCombined |= maskAsU64;
162 }
163 }
164
165 // Unset `BL_FORMAT_FLAG_PREMULTIPLIED` if the format doesn't have alpha.
166 if (!(f.flags & (BL_FORMAT_FLAG_ALPHA)))
167 f.flags &= ~BL_FORMAT_FLAG_PREMULTIPLIED;
168
169 // It's allowed that masks overlap only when the pixel format describes a grayscale (LUM).
170 bool isLUM = (f.flags & BL_FORMAT_FLAG_LUM) != 0;
171 if (isLUM != masksOverlap)
172 return blTraceError(BL_ERROR_INVALID_VALUE);
173
174 // RGB components must match in grayscale (LUM) mode.
175 if (isLUM && (f.rSize != f.gSize || f.rShift != f.gShift ||
176 f.gSize != f.bSize || f.gShift != f.bShift))
177 return blTraceError(BL_ERROR_INVALID_VALUE);
178 }
179
180 // Switch to a native byte-order if possible.
181 if (f.flags & BL_FORMAT_FLAG_BYTE_SWAP) {
182 if (f.depth <= 8) {
183 // Switch to native byte-order if the depth <= 8.
184 f.flags &= ~BL_FORMAT_FLAG_BYTE_SWAP;
185 }
186 else if (!crossesByteBoundary) {
187 // Switch to native byte-order if no mask crosses a byte boundary.
188 for (i = 0; i < 4; i++) {
189 uint32_t size = f.sizes[i];
190 if (!size)
191 continue;
192 f.shifts[i] = uint8_t(f.depth - f.shifts[i] - size);
193 }
194
195 f.flags &= ~BL_FORMAT_FLAG_BYTE_SWAP;
196 }
197 }
198
199 // Add computed flags.
200 if (!notByteAligned)
201 f.flags |= BL_FORMAT_FLAG_BYTE_ALIGNED;
202
203 return BL_SUCCESS;
204}
205