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 "./blarray_p.h" |
9 | #include "./blfilesystem.h" |
10 | #include "./blformat.h" |
11 | #include "./blimage_p.h" |
12 | #include "./blimagescale_p.h" |
13 | #include "./blruntime_p.h" |
14 | #include "./blsupport_p.h" |
15 | #include "./blthreading_p.h" |
16 | #include "./codec/blbmpcodec_p.h" |
17 | #include "./codec/bljpegcodec_p.h" |
18 | #include "./codec/blpngcodec_p.h" |
19 | |
20 | // ============================================================================ |
21 | // [Global Variables] |
22 | // ============================================================================ |
23 | |
24 | static BLImageCodecVirt blNullImageCodecVirt; |
25 | static BLImageDecoderVirt blNullImageDecoderVirt; |
26 | static BLImageEncoderVirt blNullImageEncoderVirt; |
27 | |
28 | static BLWrap<BLInternalImageImpl> blNullImageImpl; |
29 | static BLWrap<BLImageCodecImpl> blNullImageCodecImpl; |
30 | static BLWrap<BLImageEncoderImpl> blNullImageEncoderImpl; |
31 | static BLWrap<BLImageDecoderImpl> blNullImageDecoderImpl; |
32 | |
33 | static BLWrap<BLArray<BLImageCodec>> blImageCodecs; |
34 | static BLWrap<BLMutex> blImageCodecsMutex; |
35 | |
36 | static const char blEmptyCString[] = "" ; |
37 | |
38 | // ============================================================================ |
39 | // [BLImage - Utilities] |
40 | // ============================================================================ |
41 | |
42 | static BL_INLINE void blZeroMemoryInline(uint8_t* dst, size_t n) noexcept { |
43 | for (size_t i = 0; i < n; i++) |
44 | dst[i] = 0; |
45 | } |
46 | |
47 | static void blImageCopy(uint8_t* dstData, intptr_t dstStride, const uint8_t* srcData, intptr_t srcStride, int w, int h, uint32_t format) noexcept { |
48 | size_t bytesPerLine = (size_t(unsigned(w)) * blFormatInfo[format].depth + 7u) / 8u; |
49 | |
50 | if (intptr_t(bytesPerLine) == dstStride && intptr_t(bytesPerLine) == srcStride) { |
51 | // Special case that happens offen - stride equals bytes-per-line (no gaps). |
52 | memcpy(dstData, srcData, bytesPerLine * unsigned(h)); |
53 | return; |
54 | } |
55 | else { |
56 | // Generic case - there are either gaps or source/destination is a subimage. |
57 | size_t gap = dstStride > 0 ? size_t(dstStride) - bytesPerLine : size_t(0); |
58 | for (unsigned y = unsigned(h); y; y--) { |
59 | memcpy(dstData, srcData, bytesPerLine); |
60 | blZeroMemoryInline(dstData + bytesPerLine, gap); |
61 | |
62 | dstData += dstStride; |
63 | srcData += srcStride; |
64 | } |
65 | } |
66 | } |
67 | |
68 | // ============================================================================ |
69 | // [BLImage - Internals] |
70 | // ============================================================================ |
71 | |
72 | static BLInternalImageImpl* blImageImplNewInternal(int w, int h, uint32_t format) noexcept { |
73 | BL_ASSERT(w > 0 && h > 0); |
74 | BL_ASSERT(format < BL_FORMAT_COUNT); |
75 | |
76 | uint32_t depth = blFormatInfo[format].depth; |
77 | size_t stride = blImageStrideForWidth(unsigned(w), depth); |
78 | |
79 | BL_ASSERT(stride != 0); |
80 | |
81 | size_t baseSize = sizeof(BLInternalImageImpl); |
82 | size_t implSize; |
83 | |
84 | if (BL_INTERNAL_IMAGE_DATA_ALIGNMENT > sizeof(void*)) |
85 | baseSize += BL_INTERNAL_IMAGE_DATA_ALIGNMENT - sizeof(void*); |
86 | |
87 | BLOverflowFlag of = 0; |
88 | implSize = blMulOverflow<size_t>(size_t(h), stride, &of); |
89 | implSize = blAddOverflow<size_t>(baseSize, implSize, &of); |
90 | |
91 | if (BL_UNLIKELY(of)) |
92 | return nullptr; |
93 | |
94 | uint16_t memPoolData; |
95 | BLInternalImageImpl* impl = blRuntimeAllocImplT<BLInternalImageImpl>(implSize, &memPoolData); |
96 | |
97 | if (BL_UNLIKELY(!impl)) |
98 | return impl; |
99 | |
100 | uint8_t* pixelData = reinterpret_cast<uint8_t*>(impl) + sizeof(BLInternalImageImpl); |
101 | if (BL_INTERNAL_IMAGE_DATA_ALIGNMENT > sizeof(void*)) |
102 | pixelData = blAlignUp(pixelData, BL_INTERNAL_IMAGE_DATA_ALIGNMENT); |
103 | |
104 | blImplInit(impl, BL_IMPL_TYPE_IMAGE, BL_IMPL_TRAIT_MUTABLE, memPoolData); |
105 | impl->pixelData = pixelData; |
106 | impl->stride = intptr_t(stride); |
107 | impl->writer = nullptr; |
108 | impl->format = uint8_t(format); |
109 | impl->flags = uint8_t(0); |
110 | impl->depth = uint16_t(depth); |
111 | impl->size.reset(w, h); |
112 | impl->writerCount = 0; |
113 | |
114 | return impl; |
115 | } |
116 | |
117 | static BLInternalImageImpl* blImageImplNewExternal(int w, int h, uint32_t format, void* pixelData, intptr_t stride, BLDestroyImplFunc destroyFunc, void* destroyData) noexcept { |
118 | BL_ASSERT(w > 0 && h > 0); |
119 | BL_ASSERT(format < BL_FORMAT_COUNT); |
120 | |
121 | size_t implSize = sizeof(BLExternalImplPreface) + sizeof(BLInternalImageImpl); |
122 | uint16_t memPoolData; |
123 | |
124 | void* p = blRuntimeAllocImpl(implSize, &memPoolData); |
125 | if (BL_UNLIKELY(!p)) |
126 | return nullptr; |
127 | |
128 | BLExternalImplPreface* preface = static_cast<BLExternalImplPreface*>(p); |
129 | BLInternalImageImpl* impl = blOffsetPtr<BLInternalImageImpl>(p, sizeof(BLExternalImplPreface)); |
130 | |
131 | preface->destroyFunc = destroyFunc ? destroyFunc : blRuntimeDummyDestroyImplFunc; |
132 | preface->destroyData = destroyFunc ? destroyData : nullptr; |
133 | |
134 | blImplInit(impl, BL_IMPL_TYPE_IMAGE, BL_IMPL_TRAIT_MUTABLE | BL_IMPL_TRAIT_EXTERNAL, memPoolData); |
135 | impl->pixelData = pixelData; |
136 | impl->stride = stride; |
137 | impl->writer = nullptr; |
138 | impl->format = uint8_t(format); |
139 | impl->flags = uint8_t(0); |
140 | impl->depth = uint16_t(blFormatInfo[format].depth); |
141 | impl->size.reset(w, h); |
142 | impl->writerCount = 0; |
143 | |
144 | return impl; |
145 | } |
146 | |
147 | // Cannot be static, called by `BLVariant` implementation. |
148 | BLResult blImageImplDelete(BLImageImpl* impl_) noexcept { |
149 | BLInternalImageImpl* impl = blInternalCast(impl_); |
150 | |
151 | // Postpone the deletion in case that the image still has writers attached. |
152 | // This is required as the rendering context doesn't manipulate the reference |
153 | // count of `BLImage` (otherwise it would not be possible to attach multiple |
154 | // rendering contexts, for example). |
155 | if (impl->writerCount != 0) |
156 | return BL_SUCCESS; |
157 | |
158 | uint8_t* implBase = reinterpret_cast<uint8_t*>(impl); |
159 | size_t implSize = 0; |
160 | uint32_t implTraits = impl->implTraits; |
161 | uint32_t memPoolData = impl->memPoolData; |
162 | |
163 | if (implTraits & BL_IMPL_TRAIT_EXTERNAL) { |
164 | // External does never allocate the image-data past `BLInternalImageImpl`. |
165 | implSize = sizeof(BLInternalImageImpl) + sizeof(BLExternalImplPreface); |
166 | implBase -= sizeof(BLExternalImplPreface); |
167 | blImplDestroyExternal(impl); |
168 | } |
169 | else { |
170 | implSize = sizeof(BLInternalImageImpl) + |
171 | BL_INTERNAL_IMAGE_DATA_ALIGNMENT + |
172 | unsigned(impl->size.h) * size_t(blAbs(impl->stride)); |
173 | } |
174 | |
175 | if (implTraits & BL_IMPL_TRAIT_FOREIGN) |
176 | return BL_SUCCESS; |
177 | else |
178 | return blRuntimeFreeImpl(implBase, implSize, memPoolData); |
179 | } |
180 | |
181 | static BL_INLINE BLResult blImageImplRelease(BLInternalImageImpl* impl) noexcept { |
182 | if (blImplDecRefAndTest(impl)) |
183 | return blImageImplDelete(impl); |
184 | return BL_SUCCESS; |
185 | } |
186 | |
187 | // ============================================================================ |
188 | // [BLImage - Init / Reset] |
189 | // ============================================================================ |
190 | |
191 | BLResult blImageInit(BLImageCore* self) noexcept { |
192 | self->impl = &blNullImageImpl; |
193 | return BL_SUCCESS; |
194 | } |
195 | |
196 | BLResult blImageInitAs(BLImageCore* self, int w, int h, uint32_t format) noexcept { |
197 | self->impl = &blNullImageImpl; |
198 | return blImageCreate(self, w, h, format); |
199 | } |
200 | |
201 | BLResult blImageInitAsFromData(BLImageCore* self, int w, int h, uint32_t format, void* pixelData, intptr_t stride, BLDestroyImplFunc destroyFunc, void* destroyData) noexcept { |
202 | self->impl = &blNullImageImpl; |
203 | return blImageCreateFromData(self, w, h, format, pixelData, stride, destroyFunc, destroyData); |
204 | } |
205 | |
206 | BLResult blImageReset(BLImageCore* self) noexcept { |
207 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
208 | self->impl = &blNullImageImpl; |
209 | return blImageImplRelease(selfI); |
210 | } |
211 | |
212 | // ============================================================================ |
213 | // [BLImage - Assign] |
214 | // ============================================================================ |
215 | |
216 | BLResult blImageAssignMove(BLImageCore* self, BLImageCore* other) noexcept { |
217 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
218 | BLInternalImageImpl* otherI = blInternalCast(other->impl); |
219 | |
220 | self->impl = otherI; |
221 | other->impl = &blNullImageImpl; |
222 | |
223 | return blImageImplRelease(selfI); |
224 | } |
225 | |
226 | BLResult blImageAssignWeak(BLImageCore* self, const BLImageCore* other) noexcept { |
227 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
228 | BLInternalImageImpl* otherI = blInternalCast(other->impl); |
229 | |
230 | self->impl = blImplIncRef(otherI); |
231 | return blImageImplRelease(selfI); |
232 | } |
233 | |
234 | BLResult blImageAssignDeep(BLImageCore* self, const BLImageCore* other) noexcept { |
235 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
236 | BLInternalImageImpl* otherI = blInternalCast(other->impl); |
237 | |
238 | int w = otherI->size.w; |
239 | int h = otherI->size.h; |
240 | uint32_t format = otherI->format; |
241 | |
242 | BLImageData dummyImageData; |
243 | if (selfI == otherI) |
244 | return blImageMakeMutable(self, &dummyImageData); |
245 | |
246 | BL_PROPAGATE(blImageCreate(self, w, h, format)); |
247 | selfI = blInternalCast(self->impl); |
248 | |
249 | blImageCopy(static_cast<uint8_t*>(selfI->pixelData), selfI->stride, |
250 | static_cast<uint8_t*>(otherI->pixelData), otherI->stride, w, h, format); |
251 | return BL_SUCCESS; |
252 | } |
253 | |
254 | // ============================================================================ |
255 | // [BLImage - Create] |
256 | // ============================================================================ |
257 | |
258 | BLResult blImageCreate(BLImageCore* self, int w, int h, uint32_t format) noexcept { |
259 | if (BL_UNLIKELY(w <= 0 || h <= 0 || |
260 | format == BL_FORMAT_NONE || |
261 | format >= BL_FORMAT_COUNT)) { |
262 | if (w == 0 && h == 0 && format == BL_FORMAT_NONE) |
263 | return blImageReset(self); |
264 | else |
265 | return blTraceError(BL_ERROR_INVALID_VALUE); |
266 | } |
267 | |
268 | if (BL_UNLIKELY(unsigned(w) >= BL_RUNTIME_MAX_IMAGE_SIZE || |
269 | unsigned(h) >= BL_RUNTIME_MAX_IMAGE_SIZE)) |
270 | return blTraceError(BL_ERROR_IMAGE_TOO_LARGE); |
271 | |
272 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
273 | if (selfI->size.w == w && selfI->size.h == h && selfI->format == format && !(selfI->implTraits & BL_IMPL_TRAIT_EXTERNAL) && blImplIsMutable(selfI)) |
274 | return BL_SUCCESS; |
275 | |
276 | BLInternalImageImpl* newI = blImageImplNewInternal(w, h, format); |
277 | if (newI == nullptr) |
278 | return blTraceError(BL_ERROR_OUT_OF_MEMORY); |
279 | |
280 | self->impl = newI; |
281 | return blImageImplRelease(selfI); |
282 | } |
283 | |
284 | BLResult blImageCreateFromData(BLImageCore* self, int w, int h, uint32_t format, void* pixelData, intptr_t stride, BLDestroyImplFunc destroyFunc, void* destroyData) noexcept { |
285 | if (BL_UNLIKELY(w <= 0 || h <= 0 || |
286 | format == BL_FORMAT_NONE || |
287 | format >= BL_FORMAT_COUNT)) |
288 | return blTraceError(BL_ERROR_INVALID_VALUE); |
289 | |
290 | if (BL_UNLIKELY(unsigned(w) >= BL_RUNTIME_MAX_IMAGE_SIZE || |
291 | unsigned(h) >= BL_RUNTIME_MAX_IMAGE_SIZE)) |
292 | return blTraceError(BL_ERROR_IMAGE_TOO_LARGE); |
293 | |
294 | BLInternalImageImpl* newI = blImageImplNewExternal(w, h, format, pixelData, stride, destroyFunc, destroyData); |
295 | if (newI == nullptr) |
296 | return blTraceError(BL_ERROR_OUT_OF_MEMORY); |
297 | |
298 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
299 | self->impl = newI; |
300 | return blImageImplRelease(selfI); |
301 | } |
302 | |
303 | // ============================================================================ |
304 | // [BLImage [GetData / MakeMutable] |
305 | // ============================================================================ |
306 | |
307 | BLResult blImageGetData(const BLImageCore* self, BLImageData* dataOut) noexcept { |
308 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
309 | |
310 | dataOut->pixelData = selfI->pixelData; |
311 | dataOut->stride = selfI->stride; |
312 | dataOut->size = selfI->size; |
313 | dataOut->format = selfI->format; |
314 | dataOut->flags = 0; |
315 | |
316 | return BL_SUCCESS; |
317 | } |
318 | |
319 | BLResult blImageMakeMutable(BLImageCore* self, BLImageData* dataOut) noexcept { |
320 | BLInternalImageImpl* selfI = blInternalCast(self->impl); |
321 | int w = selfI->size.w; |
322 | int h = selfI->size.h; |
323 | uint32_t format = selfI->format; |
324 | |
325 | if (format != BL_FORMAT_NONE && !blImplIsMutable(selfI)) { |
326 | BLInternalImageImpl* newI = blImageImplNewInternal(w, h, format); |
327 | if (BL_UNLIKELY(!newI)) |
328 | return blTraceError(BL_ERROR_OUT_OF_MEMORY); |
329 | |
330 | dataOut->pixelData = newI->pixelData; |
331 | dataOut->stride = newI->stride; |
332 | dataOut->size.reset(w, h); |
333 | dataOut->format = format; |
334 | dataOut->flags = 0; |
335 | |
336 | blImageCopy(static_cast<uint8_t*>(newI->pixelData), newI->stride, |
337 | static_cast<uint8_t*>(selfI->pixelData), selfI->stride, w, h, format); |
338 | self->impl = newI; |
339 | return blImageImplRelease(selfI); |
340 | } |
341 | else { |
342 | dataOut->pixelData = selfI->pixelData; |
343 | dataOut->stride = selfI->stride; |
344 | dataOut->size.reset(w, h); |
345 | dataOut->format = format; |
346 | dataOut->flags = 0; |
347 | return BL_SUCCESS; |
348 | } |
349 | } |
350 | |
351 | // ============================================================================ |
352 | // [BLImage - Equals] |
353 | // ============================================================================ |
354 | |
355 | bool blImageEquals(const BLImageCore* a, const BLImageCore* b) noexcept { |
356 | const BLInternalImageImpl* aImpl = blInternalCast(a->impl); |
357 | const BLInternalImageImpl* bImpl = blInternalCast(b->impl); |
358 | |
359 | if (aImpl == bImpl) |
360 | return true; |
361 | |
362 | if (aImpl->size != bImpl->size || aImpl->format != bImpl->format) |
363 | return false; |
364 | |
365 | uint32_t w = uint32_t(aImpl->size.w); |
366 | uint32_t h = uint32_t(aImpl->size.h); |
367 | |
368 | const uint8_t* aData = static_cast<const uint8_t*>(aImpl->pixelData); |
369 | const uint8_t* bData = static_cast<const uint8_t*>(bImpl->pixelData); |
370 | |
371 | intptr_t aStride = aImpl->stride; |
372 | intptr_t bStride = bImpl->stride; |
373 | |
374 | size_t bytesPerLine = (w * blFormatInfo[aImpl->format].depth + 7u) / 8u; |
375 | for (uint32_t y = 0; y < h; y++) { |
376 | if (memcmp(aData, bData, bytesPerLine) != 0) |
377 | return false; |
378 | aData += aStride; |
379 | bData += bStride; |
380 | } |
381 | |
382 | return true; |
383 | } |
384 | |
385 | // ============================================================================ |
386 | // [BLImage - Scale] |
387 | // ============================================================================ |
388 | |
389 | BLResult blImageScale(BLImageCore* dst, const BLImageCore* src, const BLSizeI* size, uint32_t filter, const BLImageScaleOptions* options) noexcept { |
390 | BLImageImpl* srcI = src->impl; |
391 | if (srcI->format == BL_FORMAT_NONE) |
392 | return blImageReset(dst); |
393 | |
394 | BLImageScaleContext scaleCtx; |
395 | BL_PROPAGATE(scaleCtx.create(*size, srcI->size, filter, options)); |
396 | |
397 | uint32_t format = srcI->format; |
398 | int tw = scaleCtx.dstWidth(); |
399 | int th = scaleCtx.srcHeight(); |
400 | |
401 | BLImage tmp; |
402 | BLImageData buf; |
403 | |
404 | if (th == scaleCtx.dstHeight() || tw == scaleCtx.srcWidth()) { |
405 | // Only horizontal or vertical scale. |
406 | |
407 | // Move to `tmp` so it's not destroyed by `dst->create()`. |
408 | if (dst == src) tmp = *blDownCast(src); |
409 | |
410 | BL_PROPAGATE(blImageCreate(dst, scaleCtx.dstWidth(), scaleCtx.dstHeight(), format)); |
411 | BL_PROPAGATE(blImageMakeMutable(dst, &buf)); |
412 | |
413 | if (th == scaleCtx.dstHeight()) |
414 | scaleCtx.processHorzData(static_cast<uint8_t*>(buf.pixelData), buf.stride, static_cast<const uint8_t*>(srcI->pixelData), srcI->stride, format); |
415 | else |
416 | scaleCtx.processVertData(static_cast<uint8_t*>(buf.pixelData), buf.stride, static_cast<const uint8_t*>(srcI->pixelData), srcI->stride, format); |
417 | } |
418 | else { |
419 | // Both horizontal and vertical scale. |
420 | BL_PROPAGATE(tmp.create(tw, th, format)); |
421 | BL_PROPAGATE(tmp.makeMutable(&buf)); |
422 | scaleCtx.processHorzData(static_cast<uint8_t*>(buf.pixelData), buf.stride, static_cast<const uint8_t*>(srcI->pixelData), srcI->stride, format); |
423 | |
424 | srcI = tmp.impl; |
425 | BL_PROPAGATE(blImageCreate(dst, scaleCtx.dstWidth(), scaleCtx.dstHeight(), format)); |
426 | BL_PROPAGATE(blImageMakeMutable(dst, &buf)); |
427 | |
428 | scaleCtx.processVertData(static_cast<uint8_t*>(buf.pixelData), buf.stride, static_cast<const uint8_t*>(srcI->pixelData), srcI->stride, format); |
429 | } |
430 | |
431 | return BL_SUCCESS; |
432 | } |
433 | |
434 | // ============================================================================ |
435 | // [BLImage - Read / Write] |
436 | // ============================================================================ |
437 | |
438 | BLResult blImageReadFromFile(BLImageCore* self, const char* fileName, const BLArrayCore* codecs) noexcept { |
439 | BLArray<uint8_t> buffer; |
440 | BL_PROPAGATE(BLFileSystem::readFile(fileName, buffer)); |
441 | |
442 | if (buffer.empty()) |
443 | return blTraceError(BL_ERROR_FILE_EMPTY); |
444 | |
445 | BLImageCodec codec; |
446 | BL_PROPAGATE(blImageCodecFindByData(&codec, buffer.data(), buffer.size(), codecs)); |
447 | |
448 | if (BL_UNLIKELY(!(codec.features() & BL_IMAGE_CODEC_FEATURE_READ))) |
449 | return blTraceError(BL_ERROR_IMAGE_DECODER_NOT_PROVIDED); |
450 | |
451 | BLImageDecoder decoder; |
452 | BL_PROPAGATE(codec.createDecoder(&decoder)); |
453 | return decoder.readFrame(*blDownCast(self), buffer); |
454 | } |
455 | |
456 | BLResult blImageReadFromData(BLImageCore* self, const void* data, size_t size, const BLArrayCore* codecs) noexcept { |
457 | BLImageCodec codec; |
458 | BL_PROPAGATE(blImageCodecFindByData(&codec, data, size, codecs)); |
459 | |
460 | if (BL_UNLIKELY(!(codec.features() & BL_IMAGE_CODEC_FEATURE_READ))) |
461 | return blTraceError(BL_ERROR_IMAGE_DECODER_NOT_PROVIDED); |
462 | |
463 | BLImageDecoder decoder; |
464 | BL_PROPAGATE(codec.createDecoder(&decoder)); |
465 | return decoder.readFrame(*blDownCast(self), data, size); |
466 | } |
467 | |
468 | BLResult blImageWriteToFile(const BLImageCore* self, const char* fileName, const BLImageCodecCore* codec) noexcept { |
469 | BLArray<uint8_t> buffer; |
470 | BL_PROPAGATE(blImageWriteToData(self, &buffer, codec)); |
471 | return BLFileSystem::writeFile(fileName, buffer); |
472 | } |
473 | |
474 | BLResult blImageWriteToData(const BLImageCore* self, BLArrayCore* dst, const BLImageCodecCore* codec) noexcept { |
475 | if (BL_UNLIKELY(!(blDownCast(codec)->features() & BL_IMAGE_CODEC_FEATURE_WRITE))) |
476 | return blTraceError(BL_ERROR_IMAGE_ENCODER_NOT_PROVIDED); |
477 | |
478 | BLImageEncoder encoder; |
479 | BL_PROPAGATE(blDownCast(codec)->createEncoder(&encoder)); |
480 | return encoder.writeFrame(dst->dcast<BLArray<uint8_t>>(), *blDownCast(self)); |
481 | } |
482 | |
483 | // ============================================================================ |
484 | // [BLImageCodec - Init / Reset] |
485 | // ============================================================================ |
486 | |
487 | BLResult blImageCodecInit(BLImageCodecCore* self) noexcept { |
488 | self->impl = &blNullImageCodecImpl; |
489 | return BL_SUCCESS; |
490 | } |
491 | |
492 | BLResult blImageCodecReset(BLImageCodecCore* self) noexcept { |
493 | BLImageCodecImpl* selfI = self->impl; |
494 | self->impl = &blNullImageCodecImpl; |
495 | return blImplReleaseVirt(selfI); |
496 | } |
497 | |
498 | // ============================================================================ |
499 | // [BLImageCodec - Assign] |
500 | // ============================================================================ |
501 | |
502 | BLResult blImageCodecAssignWeak(BLImageCodecCore* self, const BLImageCodecCore* other) noexcept { |
503 | BLImageCodecImpl* selfI = self->impl; |
504 | BLImageCodecImpl* otherI = other->impl; |
505 | |
506 | self->impl = blImplIncRef(otherI); |
507 | return blImplReleaseVirt(selfI); |
508 | } |
509 | |
510 | // ============================================================================ |
511 | // [BLImageCodec - Find Internal] |
512 | // ============================================================================ |
513 | |
514 | static BL_INLINE bool blStrEq(const char* a, const char* b, size_t bSize) noexcept { |
515 | size_t i; |
516 | for (i = 0; i < bSize; i++) |
517 | if (a[i] == 0 || a[i] != b[i]) |
518 | return false; |
519 | return a[i] == 0; |
520 | } |
521 | |
522 | static BLResult blImageCodecFindByNameInternal(BLImageCodecCore* self, const char* name, size_t size, const BLArrayCore* codecs) noexcept { |
523 | for (const auto& codec : codecs->dcast<BLArray<BLImageCodec>>().view()) |
524 | if (blStrEq(codec.name(), name, size)) |
525 | return blImageCodecAssignWeak(self, &codec); |
526 | return BL_ERROR_IMAGE_NO_MATCHING_CODEC; |
527 | } |
528 | |
529 | static BLResult blImageCodecFindByDataInternal(BLImageCodecCore* self, const void* data, size_t size, const BLArrayCore* codecs) noexcept { |
530 | uint32_t bestScore = 0; |
531 | const BLImageCodec* candidate = nullptr; |
532 | |
533 | for (const auto& codec : codecs->dcast<BLArray<BLImageCodec>>().view()) { |
534 | uint32_t score = codec.inspectData(data, size); |
535 | if (bestScore < score) { |
536 | bestScore = score; |
537 | candidate = &codec; |
538 | } |
539 | } |
540 | |
541 | if (candidate) |
542 | return blImageCodecAssignWeak(self, candidate); |
543 | |
544 | return BL_ERROR_IMAGE_NO_MATCHING_CODEC; |
545 | } |
546 | |
547 | // ============================================================================ |
548 | // [BLImageCodec - Interface] |
549 | // ============================================================================ |
550 | |
551 | uint32_t blImageCodecInspectData(const BLImageCodecCore* self, const void* data, size_t size) noexcept { |
552 | BLImageCodecImpl* selfI = self->impl; |
553 | return selfI->virt->inspectData(selfI, static_cast<const uint8_t*>(data), size); |
554 | } |
555 | |
556 | BLResult blImageCodecFindByName(BLImageCodecCore* self, const char* name, size_t size, const BLArrayCore* codecs) noexcept { |
557 | if (size == SIZE_MAX) |
558 | size = strlen(name); |
559 | |
560 | if (codecs) |
561 | return blImageCodecFindByNameInternal(self, name, size, codecs); |
562 | |
563 | BLMutexGuard guard(blImageCodecsMutex()); |
564 | return blImageCodecFindByNameInternal(self, name, size, &blImageCodecs); |
565 | } |
566 | |
567 | BLResult blImageCodecFindByData(BLImageCodecCore* self, const void* data, size_t size, const BLArrayCore* codecs) noexcept { |
568 | if (codecs) |
569 | return blImageCodecFindByDataInternal(self, data, size, codecs); |
570 | |
571 | BLMutexGuard guard(blImageCodecsMutex()); |
572 | return blImageCodecFindByDataInternal(self, data, size, &blImageCodecs); |
573 | } |
574 | |
575 | BLResult blImageCodecCreateDecoder(const BLImageCodecCore* self, BLImageDecoderCore* dst) noexcept { |
576 | BLImageCodecImpl* selfI = self->impl; |
577 | return selfI->virt->createDecoder(selfI, dst); |
578 | } |
579 | |
580 | BLResult blImageCodecCreateEncoder(const BLImageCodecCore* self, BLImageEncoderCore* dst) noexcept { |
581 | BLImageCodecImpl* selfI = self->impl; |
582 | return selfI->virt->createEncoder(selfI, dst); |
583 | } |
584 | |
585 | // ============================================================================ |
586 | // [BLImageCodec - BuiltIn] |
587 | // ============================================================================ |
588 | |
589 | BLResult blImageCodecArrayInitBuiltInCodecs(BLArrayCore* self) noexcept { |
590 | BLMutexGuard guard(blImageCodecsMutex()); |
591 | |
592 | self->impl = blImplIncRef(blImageCodecs->impl); |
593 | return BL_SUCCESS; |
594 | } |
595 | |
596 | BLResult blImageCodecArrayAssignBuiltInCodecs(BLArrayCore* self) noexcept { |
597 | BLMutexGuard guard(blImageCodecsMutex()); |
598 | BLArrayImpl* oldI = self->impl; |
599 | |
600 | self->impl = blImplIncRef(blImageCodecs->impl); |
601 | return blArrayImplRelease(oldI); |
602 | } |
603 | |
604 | BLResult blImageCodecAddToBuiltIn(const BLImageCodecCore* codec) noexcept { |
605 | BLMutexGuard guard(blImageCodecsMutex()); |
606 | size_t existingIndex = blImageCodecs->indexOf(*blDownCast(codec)); |
607 | |
608 | if (existingIndex != SIZE_MAX) |
609 | return blTraceError(BL_ERROR_ALREADY_EXISTS); |
610 | |
611 | return blImageCodecs->append(blDownCast(*codec)); |
612 | } |
613 | |
614 | BLResult blImageCodecRemoveFromBuiltIn(const BLImageCodecCore* codec) noexcept { |
615 | BLMutexGuard guard(blImageCodecsMutex()); |
616 | size_t existingIndex = blImageCodecs->indexOf(*blDownCast(codec)); |
617 | |
618 | if (existingIndex == SIZE_MAX) |
619 | return blTraceError(BL_ERROR_NO_ENTRY); |
620 | |
621 | return blImageCodecs->remove(existingIndex); |
622 | } |
623 | |
624 | // ============================================================================ |
625 | // [BLImageCodec - Virtual Functions] |
626 | // ============================================================================ |
627 | |
628 | BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS) |
629 | |
630 | static BLResult BL_CDECL blImageCodecImplDestroy(BLImageCodecImpl* impl) noexcept { return BL_SUCCESS; } |
631 | static uint32_t BL_CDECL blImageCodecImplInspectData(const BLImageCodecImpl* impl, const uint8_t* data, size_t size) noexcept { return 0; } |
632 | static BLResult BL_CDECL blImageCodecImplCreateDecoder(const BLImageCodecImpl* impl, BLImageDecoderCore* dst) noexcept { return BL_ERROR_IMAGE_DECODER_NOT_PROVIDED; } |
633 | static BLResult BL_CDECL blImageCodecImplCreateEncoder(const BLImageCodecImpl* impl, BLImageEncoderCore* dst) noexcept { return BL_ERROR_IMAGE_ENCODER_NOT_PROVIDED; } |
634 | |
635 | BL_DIAGNOSTIC_POP |
636 | |
637 | // ============================================================================ |
638 | // [BLImageDecoder - Init / Reset] |
639 | // ============================================================================ |
640 | |
641 | BLResult blImageDecoderInit(BLImageDecoderCore* self) noexcept { |
642 | self->impl = &blNullImageDecoderImpl; |
643 | return BL_SUCCESS; |
644 | } |
645 | |
646 | BLResult blImageDecoderReset(BLImageDecoderCore* self) noexcept { |
647 | BLImageDecoderImpl* selfI = self->impl; |
648 | self->impl = &blNullImageDecoderImpl; |
649 | return blImplReleaseVirt(selfI); |
650 | } |
651 | |
652 | // ============================================================================ |
653 | // [BLImageDecoder - Assign] |
654 | // ============================================================================ |
655 | |
656 | BLResult blImageDecoderAssignMove(BLImageDecoderCore* self, BLImageDecoderCore* other) noexcept { |
657 | BLImageDecoderImpl* selfI = self->impl; |
658 | BLImageDecoderImpl* otherI = other->impl; |
659 | |
660 | self->impl = otherI; |
661 | other->impl = &blNullImageDecoderImpl; |
662 | |
663 | return blImplReleaseVirt(selfI); |
664 | } |
665 | |
666 | BLResult blImageDecoderAssignWeak(BLImageDecoderCore* self, const BLImageDecoderCore* other) noexcept { |
667 | BLImageDecoderImpl* selfI = self->impl; |
668 | BLImageDecoderImpl* otherI = other->impl; |
669 | |
670 | self->impl = blImplIncRef(otherI); |
671 | return blImplReleaseVirt(selfI); |
672 | } |
673 | |
674 | // ============================================================================ |
675 | // [BLImageDecoder - Interface] |
676 | // ============================================================================ |
677 | |
678 | BLResult blImageDecoderRestart(BLImageDecoderCore* self) noexcept { |
679 | BLImageDecoderImpl* impl = self->impl; |
680 | return impl->virt->restart(impl); |
681 | } |
682 | |
683 | BLResult blImageDecoderReadInfo(BLImageDecoderCore* self, BLImageInfo* infoOut, const uint8_t* data, size_t size) noexcept { |
684 | BLImageDecoderImpl* impl = self->impl; |
685 | return impl->virt->readInfo(impl, infoOut, data, size); |
686 | } |
687 | |
688 | BLResult blImageDecoderReadFrame(BLImageDecoderCore* self, BLImageCore* imageOut, const uint8_t* data, size_t size) noexcept { |
689 | BLImageDecoderImpl* impl = self->impl; |
690 | return impl->virt->readFrame(impl, imageOut, data, size); |
691 | } |
692 | |
693 | // ============================================================================ |
694 | // [BLImageDecoder - Virtual Functions] |
695 | // ============================================================================ |
696 | |
697 | BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS) |
698 | |
699 | static BLResult BL_CDECL blImageDecoderImplDestroy(BLImageDecoderImpl* impl) noexcept { return BL_SUCCESS; } |
700 | static uint32_t BL_CDECL blImageDecoderImplRestart(BLImageDecoderImpl* impl) noexcept { return BL_ERROR_INVALID_STATE; } |
701 | static BLResult BL_CDECL blImageDecoderImplReadInfo(BLImageDecoderImpl* impl, BLImageInfo* infoOut, const uint8_t* data, size_t size) noexcept { return BL_ERROR_INVALID_STATE; } |
702 | static BLResult BL_CDECL blImageDecoderImplReadFrame(BLImageDecoderImpl* impl, BLImageCore* imageOut, const uint8_t* data, size_t size) noexcept { return BL_ERROR_INVALID_STATE; } |
703 | |
704 | BL_DIAGNOSTIC_POP |
705 | |
706 | // ============================================================================ |
707 | // [BLImageEncoder - Init / Reset] |
708 | // ============================================================================ |
709 | |
710 | BLResult blImageEncoderInit(BLImageEncoderCore* self) noexcept { |
711 | self->impl = &blNullImageEncoderImpl; |
712 | return BL_SUCCESS; |
713 | } |
714 | |
715 | BLResult blImageEncoderReset(BLImageEncoderCore* self) noexcept { |
716 | BLImageEncoderImpl* selfI = self->impl; |
717 | self->impl = &blNullImageEncoderImpl; |
718 | return blImplReleaseVirt(selfI); |
719 | } |
720 | |
721 | // ============================================================================ |
722 | // [BLImageEncoder - Assign] |
723 | // ============================================================================ |
724 | |
725 | BLResult blImageEncoderAssignMove(BLImageEncoderCore* self, BLImageEncoderCore* other) noexcept { |
726 | BLImageEncoderImpl* selfI = self->impl; |
727 | BLImageEncoderImpl* otherI = other->impl; |
728 | |
729 | self->impl = otherI; |
730 | other->impl = &blNullImageEncoderImpl; |
731 | |
732 | return blImplReleaseVirt(selfI); |
733 | } |
734 | |
735 | BLResult blImageEncoderAssignWeak(BLImageEncoderCore* self, const BLImageEncoderCore* other) noexcept { |
736 | BLImageEncoderImpl* selfI = self->impl; |
737 | BLImageEncoderImpl* otherI = other->impl; |
738 | |
739 | self->impl = blImplIncRef(otherI); |
740 | return blImplReleaseVirt(selfI); |
741 | } |
742 | |
743 | // ============================================================================ |
744 | // [BLImageEncoder - Interface] |
745 | // ============================================================================ |
746 | |
747 | BLResult blImageEncoderRestart(BLImageEncoderCore* self) noexcept { |
748 | BLImageEncoderImpl* impl = self->impl; |
749 | return impl->virt->restart(impl); |
750 | } |
751 | |
752 | BLResult blImageEncoderWriteFrame(BLImageEncoderCore* self, BLArrayCore* dst, const BLImageCore* src) noexcept { |
753 | BLImageEncoderImpl* impl = self->impl; |
754 | return impl->virt->writeFrame(impl, dst, src); |
755 | } |
756 | |
757 | // ============================================================================ |
758 | // [BLImageEncoder - Virtual Functions] |
759 | // ============================================================================ |
760 | |
761 | BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS) |
762 | |
763 | static BLResult BL_CDECL blImageEncoderImplDestroy(BLImageEncoderImpl* impl) noexcept { return BL_SUCCESS; } |
764 | static uint32_t BL_CDECL blImageEncoderImplRestart(BLImageEncoderImpl* impl) noexcept { return BL_ERROR_INVALID_STATE; } |
765 | static BLResult BL_CDECL blImageEncoderImplWriteFrame(BLImageEncoderImpl* impl, BLArrayCore* dst, const BLImageCore* image) noexcept { return BL_ERROR_INVALID_STATE; } |
766 | |
767 | BL_DIAGNOSTIC_POP |
768 | |
769 | // ============================================================================ |
770 | // [BLImage - Runtime Init] |
771 | // ============================================================================ |
772 | |
773 | static void BL_CDECL blImageRtShutdown(BLRuntimeContext* rt) noexcept { |
774 | BL_UNUSED(rt); |
775 | blImageCodecs.destroy(); |
776 | blImageCodecsMutex.destroy(); |
777 | } |
778 | |
779 | void blImageRtInit(BLRuntimeContext* rt) noexcept { |
780 | BLInternalImageImpl* imageI = &blNullImageImpl; |
781 | imageI->implType = uint8_t(BL_IMPL_TYPE_IMAGE); |
782 | imageI->implTraits = uint8_t(BL_IMPL_TRAIT_NULL); |
783 | blAssignBuiltInNull(imageI); |
784 | |
785 | BLImageCodecVirt* codecV = &blNullImageCodecVirt; |
786 | codecV->destroy = blImageCodecImplDestroy; |
787 | codecV->inspectData = blImageCodecImplInspectData; |
788 | codecV->createDecoder = blImageCodecImplCreateDecoder; |
789 | codecV->createEncoder = blImageCodecImplCreateEncoder; |
790 | |
791 | BLImageCodecImpl* codecI = &blNullImageCodecImpl; |
792 | codecI->virt = codecV; |
793 | codecI->implType = uint8_t(BL_IMPL_TYPE_IMAGE_CODEC); |
794 | codecI->implTraits = uint8_t(BL_IMPL_TRAIT_NULL | BL_IMPL_TRAIT_VIRT); |
795 | codecI->name = blEmptyCString; |
796 | codecI->vendor = blEmptyCString; |
797 | codecI->mimeType = blEmptyCString; |
798 | codecI->extensions = blEmptyCString; |
799 | blAssignBuiltInNull(codecI); |
800 | |
801 | BLImageDecoderVirt* decoderV = &blNullImageDecoderVirt; |
802 | decoderV->destroy = blImageDecoderImplDestroy; |
803 | decoderV->restart = blImageDecoderImplRestart; |
804 | decoderV->readInfo = blImageDecoderImplReadInfo; |
805 | decoderV->readFrame = blImageDecoderImplReadFrame; |
806 | |
807 | BLImageDecoderImpl* decoderI = &blNullImageDecoderImpl; |
808 | decoderI->virt = decoderV; |
809 | decoderI->implType = uint8_t(BL_IMPL_TYPE_IMAGE_DECODER); |
810 | decoderI->implTraits = uint8_t(BL_IMPL_TRAIT_NULL | BL_IMPL_TRAIT_VIRT); |
811 | decoderI->lastResult = BL_ERROR_INVALID_STATE; |
812 | blAssignBuiltInNull(decoderI); |
813 | |
814 | BLImageEncoderVirt* encoderV = &blNullImageEncoderVirt; |
815 | encoderV->destroy = blImageEncoderImplDestroy; |
816 | encoderV->restart = blImageEncoderImplRestart; |
817 | encoderV->writeFrame = blImageEncoderImplWriteFrame; |
818 | |
819 | BLImageEncoderImpl* encoderI = &blNullImageEncoderImpl; |
820 | encoderI->virt = encoderV; |
821 | encoderI->implType = uint8_t(BL_IMPL_TYPE_IMAGE_ENCODER); |
822 | encoderI->implTraits = uint8_t(BL_IMPL_TRAIT_NULL | BL_IMPL_TRAIT_VIRT); |
823 | encoderI->lastResult = BL_ERROR_INVALID_STATE; |
824 | blAssignBuiltInNull(encoderI); |
825 | |
826 | // Register built-in codecs. |
827 | BLArray<BLImageCodec>* codecs = blImageCodecs.init(); |
828 | blImageCodecsMutex.init(); |
829 | |
830 | BLImageCodecCore bmpCodec { blBmpCodecRtInit(rt) }; |
831 | BLImageCodecCore jpegCodec { blJpegCodecRtInit(rt) }; |
832 | BLImageCodecCore pngCodec { blPngCodecRtInit(rt) }; |
833 | |
834 | codecs->append(blDownCast(bmpCodec)); |
835 | codecs->append(blDownCast(jpegCodec)); |
836 | codecs->append(blDownCast(pngCodec)); |
837 | |
838 | rt->shutdownHandlers.add(blImageRtShutdown); |
839 | } |
840 | |
841 | // ============================================================================ |
842 | // [BLImage - Unit Tests] |
843 | // ============================================================================ |
844 | |
845 | #ifdef BL_TEST |
846 | UNIT(blend2d_image_codecs) { |
847 | INFO("Testing findByName and findByData" ); |
848 | { |
849 | static const uint8_t pngSignature[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; |
850 | static const uint8_t jpgSignature[3] = { 0xFF, 0xD8, 0xFF }; |
851 | |
852 | BLImageCodec codec; |
853 | BLImageCodec bmp; |
854 | BLImageCodec png; |
855 | BLImageCodec jpg; |
856 | |
857 | EXPECT(bmp.findByName("BMP" ) == BL_SUCCESS); |
858 | EXPECT(png.findByName("PNG" ) == BL_SUCCESS); |
859 | EXPECT(jpg.findByName("JPEG" ) == BL_SUCCESS); |
860 | |
861 | EXPECT(codec.findByData("BM" , 2) == BL_SUCCESS); |
862 | EXPECT(codec == bmp); |
863 | |
864 | EXPECT(codec.findByData(pngSignature, BL_ARRAY_SIZE(pngSignature)) == BL_SUCCESS); |
865 | EXPECT(codec == png); |
866 | |
867 | EXPECT(codec.findByData(jpgSignature, BL_ARRAY_SIZE(jpgSignature)) == BL_SUCCESS); |
868 | EXPECT(codec == jpg); |
869 | } |
870 | } |
871 | #endif |
872 | |