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
24static BLImageCodecVirt blNullImageCodecVirt;
25static BLImageDecoderVirt blNullImageDecoderVirt;
26static BLImageEncoderVirt blNullImageEncoderVirt;
27
28static BLWrap<BLInternalImageImpl> blNullImageImpl;
29static BLWrap<BLImageCodecImpl> blNullImageCodecImpl;
30static BLWrap<BLImageEncoderImpl> blNullImageEncoderImpl;
31static BLWrap<BLImageDecoderImpl> blNullImageDecoderImpl;
32
33static BLWrap<BLArray<BLImageCodec>> blImageCodecs;
34static BLWrap<BLMutex> blImageCodecsMutex;
35
36static const char blEmptyCString[] = "";
37
38// ============================================================================
39// [BLImage - Utilities]
40// ============================================================================
41
42static 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
47static 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
72static 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
117static 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.
148BLResult 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
181static 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
191BLResult blImageInit(BLImageCore* self) noexcept {
192 self->impl = &blNullImageImpl;
193 return BL_SUCCESS;
194}
195
196BLResult blImageInitAs(BLImageCore* self, int w, int h, uint32_t format) noexcept {
197 self->impl = &blNullImageImpl;
198 return blImageCreate(self, w, h, format);
199}
200
201BLResult 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
206BLResult 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
216BLResult 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
226BLResult 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
234BLResult 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
258BLResult 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
284BLResult 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
307BLResult 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
319BLResult 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
355bool 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
389BLResult 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
438BLResult 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
456BLResult 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
468BLResult 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
474BLResult 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
487BLResult blImageCodecInit(BLImageCodecCore* self) noexcept {
488 self->impl = &blNullImageCodecImpl;
489 return BL_SUCCESS;
490}
491
492BLResult 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
502BLResult 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
514static 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
522static 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
529static 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
551uint32_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
556BLResult 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
567BLResult 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
575BLResult blImageCodecCreateDecoder(const BLImageCodecCore* self, BLImageDecoderCore* dst) noexcept {
576 BLImageCodecImpl* selfI = self->impl;
577 return selfI->virt->createDecoder(selfI, dst);
578}
579
580BLResult 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
589BLResult blImageCodecArrayInitBuiltInCodecs(BLArrayCore* self) noexcept {
590 BLMutexGuard guard(blImageCodecsMutex());
591
592 self->impl = blImplIncRef(blImageCodecs->impl);
593 return BL_SUCCESS;
594}
595
596BLResult 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
604BLResult 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
614BLResult 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
628BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS)
629
630static BLResult BL_CDECL blImageCodecImplDestroy(BLImageCodecImpl* impl) noexcept { return BL_SUCCESS; }
631static uint32_t BL_CDECL blImageCodecImplInspectData(const BLImageCodecImpl* impl, const uint8_t* data, size_t size) noexcept { return 0; }
632static BLResult BL_CDECL blImageCodecImplCreateDecoder(const BLImageCodecImpl* impl, BLImageDecoderCore* dst) noexcept { return BL_ERROR_IMAGE_DECODER_NOT_PROVIDED; }
633static BLResult BL_CDECL blImageCodecImplCreateEncoder(const BLImageCodecImpl* impl, BLImageEncoderCore* dst) noexcept { return BL_ERROR_IMAGE_ENCODER_NOT_PROVIDED; }
634
635BL_DIAGNOSTIC_POP
636
637// ============================================================================
638// [BLImageDecoder - Init / Reset]
639// ============================================================================
640
641BLResult blImageDecoderInit(BLImageDecoderCore* self) noexcept {
642 self->impl = &blNullImageDecoderImpl;
643 return BL_SUCCESS;
644}
645
646BLResult 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
656BLResult 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
666BLResult 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
678BLResult blImageDecoderRestart(BLImageDecoderCore* self) noexcept {
679 BLImageDecoderImpl* impl = self->impl;
680 return impl->virt->restart(impl);
681}
682
683BLResult 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
688BLResult 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
697BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS)
698
699static BLResult BL_CDECL blImageDecoderImplDestroy(BLImageDecoderImpl* impl) noexcept { return BL_SUCCESS; }
700static uint32_t BL_CDECL blImageDecoderImplRestart(BLImageDecoderImpl* impl) noexcept { return BL_ERROR_INVALID_STATE; }
701static BLResult BL_CDECL blImageDecoderImplReadInfo(BLImageDecoderImpl* impl, BLImageInfo* infoOut, const uint8_t* data, size_t size) noexcept { return BL_ERROR_INVALID_STATE; }
702static BLResult BL_CDECL blImageDecoderImplReadFrame(BLImageDecoderImpl* impl, BLImageCore* imageOut, const uint8_t* data, size_t size) noexcept { return BL_ERROR_INVALID_STATE; }
703
704BL_DIAGNOSTIC_POP
705
706// ============================================================================
707// [BLImageEncoder - Init / Reset]
708// ============================================================================
709
710BLResult blImageEncoderInit(BLImageEncoderCore* self) noexcept {
711 self->impl = &blNullImageEncoderImpl;
712 return BL_SUCCESS;
713}
714
715BLResult 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
725BLResult 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
735BLResult 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
747BLResult blImageEncoderRestart(BLImageEncoderCore* self) noexcept {
748 BLImageEncoderImpl* impl = self->impl;
749 return impl->virt->restart(impl);
750}
751
752BLResult 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
761BL_DIAGNOSTIC_PUSH(BL_DIAGNOSTIC_NO_UNUSED_PARAMETERS)
762
763static BLResult BL_CDECL blImageEncoderImplDestroy(BLImageEncoderImpl* impl) noexcept { return BL_SUCCESS; }
764static uint32_t BL_CDECL blImageEncoderImplRestart(BLImageEncoderImpl* impl) noexcept { return BL_ERROR_INVALID_STATE; }
765static BLResult BL_CDECL blImageEncoderImplWriteFrame(BLImageEncoderImpl* impl, BLArrayCore* dst, const BLImageCore* image) noexcept { return BL_ERROR_INVALID_STATE; }
766
767BL_DIAGNOSTIC_POP
768
769// ============================================================================
770// [BLImage - Runtime Init]
771// ============================================================================
772
773static void BL_CDECL blImageRtShutdown(BLRuntimeContext* rt) noexcept {
774 BL_UNUSED(rt);
775 blImageCodecs.destroy();
776 blImageCodecsMutex.destroy();
777}
778
779void 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
846UNIT(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