odin-blend2d

Odin bindings to Blend2D
Log | Files | Refs | README | LICENSE

pixelconverter.cpp (86934B)


      1 // This file is part of Blend2D project <https://blend2d.com>
      2 //
      3 // See blend2d.h or LICENSE.md for license and copyright information
      4 // SPDX-License-Identifier: Zlib
      5 
      6 #include "api-build_p.h"
      7 #include "api-impl.h"
      8 #include "format_p.h"
      9 #include "image.h"
     10 #include "pixelconverter_p.h"
     11 #include "runtime_p.h"
     12 #include "pixelops/scalar_p.h"
     13 #include "support/intops_p.h"
     14 #include "support/memops_p.h"
     15 #include "support/ptrops_p.h"
     16 #include "tables/tables_p.h"
     17 
     18 // bl::PixelConverter - Globals
     19 // ============================
     20 
     21 const BLPixelConverterOptions bl_pixel_converter_default_options {};
     22 
     23 // bl::PixelConverter - Tables
     24 // ===========================
     25 
     26 // A table that contains shifts of native 32-bit pixel format. The only reason to have this in a table is a fact that
     27 // a blue component is shifted by 8 (the same as green) to be at the right place, because there is no way to calculate
     28 // the constants of component that has to stay within the low 8 bits as `scale` value is calculated by doubling the
     29 // size until it reaches the required depth, so for example depth of 5 would scale to 10, depth 3 would scale to 9,
     30 // and depths 1-2 would scale to 8.
     31 static constexpr const uint8_t bl_pixel_converter_native32_from_foreign_shift_table[] = {
     32   16, // [0x00FF0000] R.
     33   8 , // [0x0000FF00] G.
     34   8 , // [0x0000FF00] B (shift to right by 8 to get the desired result).
     35   24  // [0xFF000000] A.
     36 };
     37 
     38 // bl::PixelConverter - Uninitialized
     39 // ==================================
     40 
     41 static BLResult BL_CDECL bl_convert_func_not_initialized(
     42   const BLPixelConverterCore* self,
     43   uint8_t* dst_data, intptr_t dst_stride,
     44   const uint8_t* src_line, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
     45 
     46   bl_unused(self, dst_data, dst_stride, src_line, src_stride, w, h, options);
     47   return bl_make_error(BL_ERROR_NOT_INITIALIZED);
     48 }
     49 
     50 // bl::PixelConverter - Utilities
     51 // ==============================
     52 
     53 static BL_INLINE bool bl_pixel_converter_is_indexed_depth(uint32_t depth) noexcept {
     54   return depth == 1 || depth == 2 || depth == 4 || depth == 8;
     55 }
     56 
     57 static bool bl_pixel_converter_palette_format_from_format_flags(BLFormatInfo& fi, BLFormatFlags flags) noexcept {
     58   // `fi` is now ARGB32 (non-premultiplied).
     59   fi = bl_format_info[BL_FORMAT_PRGB32];
     60   fi.clear_flags(BL_FORMAT_FLAG_PREMULTIPLIED);
     61 
     62   switch (flags & BL_FORMAT_FLAG_RGBA) {
     63     case BL_FORMAT_FLAG_ALPHA:
     64       return true;
     65 
     66     case BL_FORMAT_FLAG_RGB:
     67       fi.clear_flags(BL_FORMAT_FLAG_ALPHA);
     68       fi.sizes[3] = 0;
     69       fi.shifts[3] = 0;
     70       return true;
     71 
     72     case BL_FORMAT_FLAG_RGBA:
     73       fi.add_flags(BLFormatFlags(flags & BL_FORMAT_FLAG_PREMULTIPLIED));
     74       return true;
     75 
     76     default:
     77       return false;
     78   }
     79 }
     80 
     81 // bl::PixelConverter - Memory Management
     82 // ======================================
     83 
     84 static BL_INLINE void bl_pixel_converter_zero_initialize(BLPixelConverterCore* self) noexcept {
     85   memset(self, 0, sizeof(BLPixelConverterCore));
     86   self->convert_func = bl_convert_func_not_initialized;
     87 }
     88 
     89 static BL_INLINE void bl_pixel_converter_add_ref(BLPixelConverterCore* self) noexcept {
     90   BLPixelConverterData* d = bl_pixel_converter_get_data(self);
     91   if (!(d->internal_flags & BL_PIXEL_CONVERTER_INTERNAL_FLAG_DYNAMIC_DATA))
     92     return;
     93 
     94   bl_atomic_fetch_add_relaxed(d->ref_count);
     95 }
     96 
     97 static void bl_pixel_converter_release(BLPixelConverterCore* self) noexcept {
     98   BLPixelConverterData* d = bl_pixel_converter_get_data(self);
     99 
    100   uint32_t flags = d->internal_flags;
    101   if (!(flags & BL_PIXEL_CONVERTER_INTERNAL_FLAG_DYNAMIC_DATA))
    102     return;
    103 
    104   void* data_ptr = d->data_ptr;
    105   if (bl_atomic_fetch_sub_strong(d->ref_count) == 1) {
    106     if (flags & BL_PIXEL_CONVERTER_INTERNAL_FLAG_MULTI_STEP) {
    107       BLPixelConverterMultiStepContext* ctx = static_cast<BLPixelConverterMultiStepContext*>(data_ptr);
    108       bl_pixel_converter_reset(&ctx->first);
    109       bl_pixel_converter_reset(&ctx->second);
    110     }
    111     free(data_ptr);
    112   }
    113 }
    114 
    115 static BL_INLINE void bl_pixel_converter_copy_ref(BLPixelConverterCore* self, const BLPixelConverterCore* other) noexcept {
    116   memcpy(self, other, sizeof(BLPixelConverterCore));
    117   bl_pixel_converter_add_ref(self);
    118 }
    119 
    120 // bl::PixelConverter - Init & Destroy
    121 // ===================================
    122 
    123 BLResult bl_pixel_converter_init(BLPixelConverterCore* self) noexcept {
    124   bl_pixel_converter_zero_initialize(self);
    125   return BL_SUCCESS;
    126 }
    127 
    128 BLResult bl_pixel_converter_init_weak(BLPixelConverterCore* self, const BLPixelConverterCore* other) noexcept {
    129   bl_pixel_converter_copy_ref(self, other);
    130   return BL_SUCCESS;
    131 }
    132 
    133 BLResult bl_pixel_converter_destroy(BLPixelConverterCore* self) noexcept {
    134   bl_pixel_converter_release(self);
    135   self->convert_func = nullptr;
    136   return BL_SUCCESS;
    137 }
    138 
    139 // bl::PixelConverter - Reset
    140 // ==========================
    141 
    142 BLResult bl_pixel_converter_reset(BLPixelConverterCore* self) noexcept {
    143   bl_pixel_converter_release(self);
    144   bl_pixel_converter_zero_initialize(self);
    145   return BL_SUCCESS;
    146 }
    147 
    148 // bl::PixelConverter - Assign
    149 // ===========================
    150 
    151 BLResult bl_pixel_converter_assign(BLPixelConverterCore* self, const BLPixelConverterCore* other) noexcept {
    152   if (self == other)
    153     return BL_SUCCESS;
    154 
    155   bl_pixel_converter_release(self);
    156   bl_pixel_converter_copy_ref(self, other);
    157   return BL_SUCCESS;
    158 }
    159 
    160 // bl::PixelConverter - Create
    161 // ===========================
    162 
    163 BLResult bl_pixel_converter_create(BLPixelConverterCore* self, const BLFormatInfo* dst_info, const BLFormatInfo* src_info, BLPixelConverterCreateFlags create_flags) noexcept {
    164   BLFormatInfo di = *dst_info;
    165   BLFormatInfo si = *src_info;
    166 
    167   BL_PROPAGATE(di.sanitize());
    168   BL_PROPAGATE(si.sanitize());
    169 
    170   // Always create a new one and then swap it if the initialization succeeded.
    171   BLPixelConverterCore pc {};
    172   BL_PROPAGATE(bl_pixel_converter_init_internal(&pc, di, si, create_flags));
    173 
    174   bl_pixel_converter_release(self);
    175   memcpy(self, &pc, sizeof(BLPixelConverterCore));
    176   return BL_SUCCESS;
    177 }
    178 
    179 // bl::PixelConverter - Convert
    180 // ============================
    181 
    182 BLResult bl_pixel_converter_convert(const BLPixelConverterCore* self,
    183   void* dst_data, intptr_t dst_stride,
    184   const void* src_data, intptr_t src_stride,
    185   uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    186 
    187   return self->convert_func(self, static_cast<      uint8_t*>(dst_data), dst_stride,
    188                                  static_cast<const uint8_t*>(src_data), src_stride, w, h, options);
    189 }
    190 
    191 // bl::PixelConverter - Pixel Access
    192 // =================================
    193 
    194 struct BLPixelAccess8 {
    195   enum : uint32_t { kSize = 1 };
    196 
    197   static BL_INLINE uint32_t fetchA(const void* p) noexcept { return bl::MemOps::readU8(p); }
    198   static BL_INLINE uint32_t fetchU(const void* p) noexcept { return bl::MemOps::readU8(p); }
    199 
    200   static BL_INLINE void storeA(void* p, uint32_t v) noexcept { bl::MemOps::writeU8(p, uint16_t(v)); }
    201   static BL_INLINE void storeU(void* p, uint32_t v) noexcept { bl::MemOps::writeU8(p, uint16_t(v)); }
    202 };
    203 
    204 template<uint32_t ByteOrder>
    205 struct BLPixelAccess16 {
    206   enum : uint32_t { kSize = 2 };
    207 
    208   static BL_INLINE uint32_t fetchA(const void* p) noexcept { return bl::MemOps::readU16<ByteOrder, 2>(p); }
    209   static BL_INLINE uint32_t fetchU(const void* p) noexcept { return bl::MemOps::readU16<ByteOrder, 1>(p); }
    210 
    211   static BL_INLINE void storeA(void* p, uint32_t v) noexcept { bl::MemOps::writeU16<ByteOrder, 2>(p, uint16_t(v)); }
    212   static BL_INLINE void storeU(void* p, uint32_t v) noexcept { bl::MemOps::writeU16<ByteOrder, 1>(p, uint16_t(v)); }
    213 };
    214 
    215 template<uint32_t ByteOrder>
    216 struct BLPixelAccess24 {
    217   enum : uint32_t { kSize = 3 };
    218 
    219   static BL_INLINE uint32_t fetchA(const void* p) noexcept { return bl::MemOps::readU24u<ByteOrder>(p); }
    220   static BL_INLINE uint32_t fetchU(const void* p) noexcept { return bl::MemOps::readU24u<ByteOrder>(p); }
    221 
    222   static BL_INLINE void storeA(void* p, uint32_t v) noexcept { bl::MemOps::writeU24u<ByteOrder>(p, v); }
    223   static BL_INLINE void storeU(void* p, uint32_t v) noexcept { bl::MemOps::writeU24u<ByteOrder>(p, v); }
    224 };
    225 
    226 template<uint32_t ByteOrder>
    227 struct BLPixelAccess32 {
    228   enum : uint32_t { kSize = 4 };
    229 
    230   static BL_INLINE uint32_t fetchA(const void* p) noexcept { return bl::MemOps::readU32<ByteOrder, 4>(p); }
    231   static BL_INLINE uint32_t fetchU(const void* p) noexcept { return bl::MemOps::readU32<ByteOrder, 1>(p); }
    232 
    233   static BL_INLINE void storeA(void* p, uint32_t v) noexcept { bl::MemOps::writeU32<ByteOrder, 4>(p, v); }
    234   static BL_INLINE void storeU(void* p, uint32_t v) noexcept { bl::MemOps::writeU32<ByteOrder, 1>(p, v); }
    235 };
    236 
    237 // bl::PixelConverter - Copy
    238 // =========================
    239 
    240 BLResult bl_convert_copy(
    241   const BLPixelConverterCore* self,
    242   uint8_t* dst_data, intptr_t dst_stride,
    243   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    244 
    245   if (!options)
    246     options = &bl_pixel_converter_default_options;
    247 
    248   const size_t bytes_per_pixel = bl_pixel_converter_get_data(self)->mem_copy_data.bytes_per_pixel;
    249   const size_t byte_width = size_t(w) * bytes_per_pixel;
    250 
    251   const size_t gap = options->gap;
    252   dst_stride -= uintptr_t(byte_width) + gap;
    253   src_stride -= uintptr_t(byte_width);
    254 
    255   for (uint32_t y = h; y != 0; y--) {
    256     size_t i = byte_width;
    257 
    258     if (!bl::MemOps::kUnalignedMem32 && bl::PtrOps::have_equal_alignment(dst_data, src_data, 4)) {
    259       while (i && ((uintptr_t)dst_data) & 0x03) {
    260         *dst_data++ = *src_data++;
    261         i--;
    262       }
    263 
    264       while (i >= 16) {
    265         uint32_t p0 = bl::MemOps::readU32a(src_data +  0);
    266         uint32_t p1 = bl::MemOps::readU32a(src_data +  4);
    267         uint32_t p2 = bl::MemOps::readU32a(src_data +  8);
    268         uint32_t p3 = bl::MemOps::readU32a(src_data + 12);
    269 
    270         bl::MemOps::writeU32a(dst_data +  0, p0);
    271         bl::MemOps::writeU32a(dst_data +  4, p1);
    272         bl::MemOps::writeU32a(dst_data +  8, p2);
    273         bl::MemOps::writeU32a(dst_data + 12, p3);
    274 
    275         dst_data += 16;
    276         src_data += 16;
    277         i -= 16;
    278       }
    279 
    280       while (i >= 4) {
    281         bl::MemOps::writeU32a(dst_data, bl::MemOps::readU32a(src_data));
    282         dst_data += 4;
    283         src_data += 4;
    284         i -= 4;
    285       }
    286     }
    287     else {
    288       while (i >= 16) {
    289         uint32_t p0 = bl::MemOps::readU32u(src_data +  0);
    290         uint32_t p1 = bl::MemOps::readU32u(src_data +  4);
    291         uint32_t p2 = bl::MemOps::readU32u(src_data +  8);
    292         uint32_t p3 = bl::MemOps::readU32u(src_data + 12);
    293 
    294         bl::MemOps::writeU32u(dst_data +  0, p0);
    295         bl::MemOps::writeU32u(dst_data +  4, p1);
    296         bl::MemOps::writeU32u(dst_data +  8, p2);
    297         bl::MemOps::writeU32u(dst_data + 12, p3);
    298 
    299         dst_data += 16;
    300         src_data += 16;
    301         i -= 16;
    302       }
    303 
    304       while (i >= 4) {
    305         bl::MemOps::writeU32u(dst_data, bl::MemOps::readU32u(src_data));
    306         dst_data += 4;
    307         src_data += 4;
    308         i -= 4;
    309       }
    310     }
    311 
    312     while (i) {
    313       *dst_data++ = *src_data++;
    314       i--;
    315     }
    316 
    317     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    318     dst_data += dst_stride;
    319     src_data += src_stride;
    320   }
    321 
    322   return BL_SUCCESS;
    323 }
    324 
    325 // bl::PixelConverter - Copy|Or
    326 // ============================
    327 
    328 BLResult bl_convert_copy_or_8888(
    329   const BLPixelConverterCore* self,
    330   uint8_t* dst_data, intptr_t dst_stride,
    331   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    332 
    333   if (!options)
    334     options = &bl_pixel_converter_default_options;
    335 
    336   const uint32_t fill_mask = bl_pixel_converter_get_data(self)->mem_copy_data.fill_mask;
    337 
    338   const size_t gap = options->gap;
    339   dst_stride -= uintptr_t(w) * 4 + gap;
    340   src_stride -= uintptr_t(w) * 4;
    341 
    342   for (uint32_t y = h; y != 0; y--) {
    343     uint32_t i = w;
    344     if (!bl::MemOps::kUnalignedMem32 && bl::PtrOps::both_aligned(dst_data, src_data, 4)) {
    345       while (i >= 4) {
    346         uint32_t p0 = bl::MemOps::readU32a(src_data +  0);
    347         uint32_t p1 = bl::MemOps::readU32a(src_data +  4);
    348         uint32_t p2 = bl::MemOps::readU32a(src_data +  8);
    349         uint32_t p3 = bl::MemOps::readU32a(src_data + 12);
    350 
    351         bl::MemOps::writeU32a(dst_data +  0, p0 | fill_mask);
    352         bl::MemOps::writeU32a(dst_data +  4, p1 | fill_mask);
    353         bl::MemOps::writeU32a(dst_data +  8, p2 | fill_mask);
    354         bl::MemOps::writeU32a(dst_data + 12, p3 | fill_mask);
    355 
    356         dst_data += 16;
    357         src_data += 16;
    358         i -= 4;
    359       }
    360 
    361       while (i) {
    362         bl::MemOps::writeU32a(dst_data, bl::MemOps::readU32a(src_data) | fill_mask);
    363         dst_data += 4;
    364         src_data += 4;
    365         i--;
    366       }
    367     }
    368     else {
    369       while (i >= 4) {
    370         uint32_t p0 = bl::MemOps::readU32u(src_data +  0);
    371         uint32_t p1 = bl::MemOps::readU32u(src_data +  4);
    372         uint32_t p2 = bl::MemOps::readU32u(src_data +  8);
    373         uint32_t p3 = bl::MemOps::readU32u(src_data + 12);
    374 
    375         bl::MemOps::writeU32u(dst_data +  0, p0 | fill_mask);
    376         bl::MemOps::writeU32u(dst_data +  4, p1 | fill_mask);
    377         bl::MemOps::writeU32u(dst_data +  8, p2 | fill_mask);
    378         bl::MemOps::writeU32u(dst_data + 12, p3 | fill_mask);
    379 
    380         dst_data += 16;
    381         src_data += 16;
    382         i -= 4;
    383       }
    384 
    385       while (i) {
    386         bl::MemOps::writeU32u(dst_data, bl::MemOps::readU32u(src_data) | fill_mask);
    387         dst_data += 4;
    388         src_data += 4;
    389         i--;
    390       }
    391     }
    392 
    393     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    394     dst_data += dst_stride;
    395     src_data += src_stride;
    396   }
    397 
    398   return BL_SUCCESS;
    399 }
    400 
    401 // bl::PixelConverter - Premultiply & Unpremultiply
    402 // ================================================
    403 
    404 static BLResult BL_CDECL bl_convert_premultiply_8888(
    405   const BLPixelConverterCore* self,
    406   uint8_t* dst_data, intptr_t dst_stride,
    407   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    408 
    409   if (!options)
    410     options = &bl_pixel_converter_default_options;
    411 
    412   const size_t gap = options->gap;
    413   dst_stride -= uintptr_t(w) * 4 + gap;
    414   src_stride -= uintptr_t(w) * 4;
    415 
    416   const BLPixelConverterData::PremultiplyData& d = bl_pixel_converter_get_data(self)->premultiply_data;
    417   const uint32_t alpha_shift = d.alpha_shift;
    418   const uint32_t alpha_mask = 0xFFu << alpha_shift;
    419   const uint32_t fill_mask = d.fill_mask;
    420 
    421   for (uint32_t y = h; y != 0; y--) {
    422     if (!bl::MemOps::kUnalignedMem32 && bl::PtrOps::both_aligned(dst_data, src_data, 4)) {
    423       for (uint32_t i = w; i != 0; i--) {
    424         uint32_t pix = bl::MemOps::readU32a(src_data);
    425         uint32_t a = (pix >> alpha_shift) & 0xFFu;
    426 
    427         pix |= alpha_mask;
    428 
    429         uint32_t c0 = ((pix     ) & 0x00FF00FFu) * a + 0x00800080u;
    430         uint32_t c1 = ((pix >> 8) & 0x00FF00FFu) * a + 0x00800080u;
    431 
    432         c0 = (c0 + ((c0 >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
    433         c1 = (c1 + ((c1 >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
    434 
    435         bl::MemOps::writeU32a(dst_data, (c0 >> 8) | c1 | fill_mask);
    436 
    437         dst_data += 4;
    438         src_data += 4;
    439       }
    440     }
    441     else {
    442       for (uint32_t i = w; i != 0; i--) {
    443         uint32_t pix = bl::MemOps::readU32u(src_data);
    444         uint32_t a = (pix >> alpha_shift) & 0xFFu;
    445 
    446         pix |= alpha_mask;
    447 
    448         uint32_t c0 = ((pix     ) & 0x00FF00FFu) * a + 0x00800080u;
    449         uint32_t c1 = ((pix >> 8) & 0x00FF00FFu) * a + 0x00800080u;
    450 
    451         c0 = (c0 + ((c0 >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
    452         c1 = (c1 + ((c1 >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
    453 
    454         bl::MemOps::writeU32u(dst_data, (c0 >> 8) | c1 | fill_mask);
    455 
    456         dst_data += 4;
    457         src_data += 4;
    458       }
    459     }
    460 
    461     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    462     dst_data += dst_stride;
    463     src_data += src_stride;
    464   }
    465 
    466   return BL_SUCCESS;
    467 }
    468 
    469 template<uint32_t A_Shift>
    470 static BLResult BL_CDECL bl_convert_unpremultiply_8888(
    471   const BLPixelConverterCore* self,
    472   uint8_t* dst_data, intptr_t dst_stride,
    473   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    474 
    475   bl_unused(self);
    476 
    477   if (!options)
    478     options = &bl_pixel_converter_default_options;
    479 
    480   const size_t gap = options->gap;
    481   dst_stride -= uintptr_t(w) * 4 + gap;
    482   src_stride -= uintptr_t(w) * 4;
    483 
    484   const uint32_t R_Shift = (A_Shift +  8u) % 32u;
    485   const uint32_t G_Shift = (A_Shift + 16u) % 32u;
    486   const uint32_t B_Shift = (A_Shift + 24u) % 32u;
    487 
    488   for (uint32_t y = h; y != 0; y--) {
    489     if (!bl::MemOps::kUnalignedMem32 && bl::PtrOps::both_aligned(dst_data, src_data, 4)) {
    490       for (uint32_t i = w; i != 0; i--) {
    491         uint32_t pix = bl::MemOps::readU32a(src_data);
    492         uint32_t r = (pix >> R_Shift) & 0xFFu;
    493         uint32_t g = (pix >> G_Shift) & 0xFFu;
    494         uint32_t b = (pix >> B_Shift) & 0xFFu;
    495         uint32_t a = (pix >> A_Shift) & 0xFFu;
    496 
    497         bl::PixelOps::Scalar::unpremultiply_rgb_8bit(r, g, b, a);
    498         bl::MemOps::writeU32a(dst_data, (r << R_Shift) | (g << G_Shift) | (b << B_Shift) | (a << A_Shift));
    499 
    500         dst_data += 4;
    501         src_data += 4;
    502       }
    503     }
    504     else {
    505       for (uint32_t i = w; i != 0; i--) {
    506         uint32_t pix = bl::MemOps::readU32u(src_data);
    507         uint32_t r = (pix >> R_Shift) & 0xFFu;
    508         uint32_t g = (pix >> G_Shift) & 0xFFu;
    509         uint32_t b = (pix >> B_Shift) & 0xFFu;
    510         uint32_t a = (pix >> A_Shift) & 0xFFu;
    511 
    512         bl::PixelOps::Scalar::unpremultiply_rgb_8bit(r, g, b, a);
    513         bl::MemOps::writeU32u(dst_data, (r << R_Shift) | (g << G_Shift) | (b << B_Shift) | (a << A_Shift));
    514 
    515         dst_data += 4;
    516         src_data += 4;
    517       }
    518     }
    519 
    520     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    521     dst_data += dst_stride;
    522     src_data += src_stride;
    523   }
    524 
    525   return BL_SUCCESS;
    526 }
    527 
    528 // bl::PixelConverter - A8 From PRGB32/ARGB32
    529 // ==========================================
    530 
    531 BLResult bl_convert_a8_from_8888(
    532   const BLPixelConverterCore* self,
    533   uint8_t* dst_data, intptr_t dst_stride,
    534   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    535 
    536   if (!options)
    537     options = &bl_pixel_converter_default_options;
    538 
    539   const size_t gap = options->gap;
    540   dst_stride -= uintptr_t(w) + gap;
    541   src_stride -= uintptr_t(w) * 4;
    542 
    543   const BLPixelConverterData::X8FromRgb32Data& d = bl_pixel_converter_get_data(self)->x8FromRgb32Data;
    544   const size_t src_bpp = d.bytes_per_pixel;
    545 
    546 #if BL_BYTE_ORDER == 1234
    547   const size_t srcAI = d.alpha_shift / 8u;
    548 #else
    549   const size_t srcAI = (24u - d.alpha_shift) / 8u;
    550 #endif
    551 
    552   src_data += srcAI;
    553 
    554   for (uint32_t y = h; y != 0; y--) {
    555     for (uint32_t i = w; i != 0; i--) {
    556       dst_data[0] = src_data[0];
    557       dst_data += 1;
    558       src_data += src_bpp;
    559     }
    560 
    561     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    562     dst_data += dst_stride;
    563     src_data += src_stride;
    564   }
    565 
    566   return BL_SUCCESS;
    567 }
    568 
    569 // bl::PixelConverter - RGB32 From A8/L8
    570 // =====================================
    571 
    572 BLResult bl_convert_8888_from_x8(
    573   const BLPixelConverterCore* self,
    574   uint8_t* dst_data, intptr_t dst_stride,
    575   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    576 
    577   if (!options)
    578     options = &bl_pixel_converter_default_options;
    579 
    580   const size_t gap = options->gap;
    581   dst_stride -= uintptr_t(w) * 4 + gap;
    582   src_stride -= uintptr_t(w);
    583 
    584   const BLPixelConverterData::Rgb32FromX8Data& d = bl_pixel_converter_get_data(self)->rgb32FromX8Data;
    585   const uint32_t fill_mask = d.fill_mask;
    586   const uint32_t zero_mask = d.zero_mask;
    587 
    588   for (uint32_t y = h; y != 0; y--) {
    589     if (!bl::MemOps::kUnalignedMem32 && bl::IntOps::is_aligned(dst_data, 4)) {
    590       for (uint32_t i = w; i != 0; i--) {
    591         bl::MemOps::writeU32a(dst_data, ((uint32_t(src_data[0]) * 0x01010101u) & zero_mask) | fill_mask);
    592         dst_data += 4;
    593         src_data += 1;
    594       }
    595     }
    596     else {
    597       for (uint32_t i = w; i != 0; i--) {
    598         bl::MemOps::writeU32u(dst_data, ((uint32_t(src_data[0]) * 0x01010101u) & zero_mask) | fill_mask);
    599         dst_data += 4;
    600         src_data += 1;
    601       }
    602     }
    603 
    604     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    605     dst_data += dst_stride;
    606     src_data += src_stride;
    607   }
    608 
    609   return BL_SUCCESS;
    610 }
    611 
    612 // bl::PixelConverter - Any <- Indexed1
    613 // ====================================
    614 
    615 // Instead of doing a table lookup each time we create a XOR mask that is used to get the second color value from
    616 // the first one. This allows to remove the lookup completely. The only requirement is that we need all zeros or
    617 // ones depending on the source value (see the implementation, it uses signed right shift to fill these bits in).
    618 
    619 template<typename PixelAccess>
    620 static BLResult BL_CDECL bl_convert_any_from_indexed1(
    621   const BLPixelConverterCore* self,
    622   uint8_t* dst_data, intptr_t dst_stride,
    623   const uint8_t* src_line, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    624 
    625   if (!options)
    626     options = &bl_pixel_converter_default_options;
    627 
    628   const uint32_t kPixelSize = PixelAccess::kSize;
    629   const size_t gap = options->gap;
    630   dst_stride -= uintptr_t(w) * kPixelSize + gap;
    631 
    632   const BLPixelConverterData::IndexedData& d = bl_pixel_converter_get_data(self)->indexed_data;
    633   uint32_t c0 = PixelAccess::fetchA(d.embedded.table8 + 0 * kPixelSize);
    634   uint32_t cm = PixelAccess::fetchA(d.embedded.table8 + 1 * kPixelSize) ^ c0; // XOR mask.
    635 
    636   if (c0 == 0 && cm == (0xFFFFFFFFu >> (32 - kPixelSize))) {
    637     // Special case for zeros and all ones.
    638     for (uint32_t y = h; y != 0; y--) {
    639       const uint8_t* src_data = src_line;
    640       uint32_t i = w;
    641 
    642       while (i >= 8) {
    643         uint32_t b0 = uint32_t(*src_data++) << 24;
    644         uint32_t b1 = b0 << 1;
    645 
    646         PixelAccess::storeU(dst_data + 0 * kPixelSize, bl::IntOps::sar(b0, 31)); b0 <<= 2;
    647         PixelAccess::storeU(dst_data + 1 * kPixelSize, bl::IntOps::sar(b1, 31)); b1 <<= 2;
    648         PixelAccess::storeU(dst_data + 2 * kPixelSize, bl::IntOps::sar(b0, 31)); b0 <<= 2;
    649         PixelAccess::storeU(dst_data + 3 * kPixelSize, bl::IntOps::sar(b1, 31)); b1 <<= 2;
    650         PixelAccess::storeU(dst_data + 4 * kPixelSize, bl::IntOps::sar(b0, 31)); b0 <<= 2;
    651         PixelAccess::storeU(dst_data + 5 * kPixelSize, bl::IntOps::sar(b1, 31)); b1 <<= 2;
    652         PixelAccess::storeU(dst_data + 6 * kPixelSize, bl::IntOps::sar(b0, 31));
    653         PixelAccess::storeU(dst_data + 7 * kPixelSize, bl::IntOps::sar(b1, 31));
    654 
    655         dst_data += 8 * kPixelSize;
    656         i -= 8;
    657       }
    658 
    659       if (i) {
    660         uint32_t b0 = uint32_t(*src_data++) << 24;
    661         do {
    662           PixelAccess::storeU(dst_data, bl::IntOps::sar(b0, 31));
    663           dst_data += kPixelSize;
    664           b0 <<= 1;
    665         } while (--i);
    666       }
    667 
    668       dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    669       dst_data += dst_stride;
    670       src_line += src_stride;
    671     }
    672   }
    673   else {
    674     // Generic case for any other combination.
    675     for (uint32_t y = h; y != 0; y--) {
    676       const uint8_t* src_data = src_line;
    677       uint32_t i = w;
    678 
    679       while (i >= 8) {
    680         uint32_t b0 = uint32_t(*src_data++) << 24;
    681         uint32_t b1 = b0 << 1;
    682 
    683         PixelAccess::storeU(dst_data + 0 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b0, 31))); b0 <<= 2;
    684         PixelAccess::storeU(dst_data + 1 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b1, 31))); b1 <<= 2;
    685         PixelAccess::storeU(dst_data + 2 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b0, 31))); b0 <<= 2;
    686         PixelAccess::storeU(dst_data + 3 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b1, 31))); b1 <<= 2;
    687         PixelAccess::storeU(dst_data + 4 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b0, 31))); b0 <<= 2;
    688         PixelAccess::storeU(dst_data + 5 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b1, 31))); b1 <<= 2;
    689         PixelAccess::storeU(dst_data + 6 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b0, 31)));
    690         PixelAccess::storeU(dst_data + 7 * kPixelSize, c0 ^ (cm & bl::IntOps::sar(b1, 31)));
    691 
    692         dst_data += 8 * kPixelSize;
    693         i -= 8;
    694       }
    695 
    696       if (i) {
    697         uint32_t b0 = uint32_t(*src_data++) << 24;
    698         do {
    699           PixelAccess::storeU(dst_data, c0 ^ (cm & bl::IntOps::sar(b0, 31)));
    700           dst_data += kPixelSize;
    701           b0 <<= 1;
    702         } while (--i);
    703       }
    704 
    705       dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    706       dst_data += dst_stride;
    707       src_line += src_stride;
    708     }
    709   }
    710 
    711   return BL_SUCCESS;
    712 }
    713 
    714 // bl::PixelConverter - Any <- Indexed2
    715 // ====================================
    716 
    717 template<typename PixelAccess>
    718 static BLResult BL_CDECL bl_convert_any_from_indexed2(
    719   const BLPixelConverterCore* self,
    720   uint8_t* dst_data, intptr_t dst_stride,
    721   const uint8_t* src_line, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    722 
    723   if (!options)
    724     options = &bl_pixel_converter_default_options;
    725 
    726   const uint32_t kPixelSize = PixelAccess::kSize;
    727   const uint32_t kShiftToLeadingByte = (bl::IntOps::bit_size_of<uintptr_t>() - 8);
    728   const uint32_t kShiftToTableIndex  = (bl::IntOps::bit_size_of<uintptr_t>() - 2);
    729 
    730   const size_t gap = options->gap;
    731   dst_stride -= uintptr_t(w) * kPixelSize + gap;
    732 
    733   const BLPixelConverterData::IndexedData& d = bl_pixel_converter_get_data(self)->indexed_data;
    734   const uint8_t* table = d.embedded.table8;
    735 
    736   for (uint32_t y = h; y != 0; y--) {
    737     const uint8_t* src_data = src_line;
    738     uint32_t i = w;
    739 
    740     while (i >= 4) {
    741       uintptr_t b0 = uintptr_t(*src_data++) << kShiftToLeadingByte;
    742 
    743       uint32_t p0 = PixelAccess::fetchA(table + (b0 >> kShiftToTableIndex) * kPixelSize); b0 <<= 2;
    744       uint32_t p1 = PixelAccess::fetchA(table + (b0 >> kShiftToTableIndex) * kPixelSize); b0 <<= 2;
    745       uint32_t p2 = PixelAccess::fetchA(table + (b0 >> kShiftToTableIndex) * kPixelSize); b0 <<= 2;
    746       uint32_t p3 = PixelAccess::fetchA(table + (b0 >> kShiftToTableIndex) * kPixelSize);
    747 
    748       PixelAccess::storeU(dst_data + 0 * kPixelSize, p0);
    749       PixelAccess::storeU(dst_data + 1 * kPixelSize, p1);
    750       PixelAccess::storeU(dst_data + 2 * kPixelSize, p2);
    751       PixelAccess::storeU(dst_data + 3 * kPixelSize, p3);
    752 
    753       dst_data += 4 * kPixelSize;
    754       i -= 4;
    755     }
    756 
    757     if (i) {
    758       uintptr_t b0 = uintptr_t(*src_data++) << kShiftToLeadingByte;
    759       do {
    760         PixelAccess::storeU(dst_data, PixelAccess::fetchA(table + (b0 >> kShiftToTableIndex) * kPixelSize));
    761         dst_data += kPixelSize;
    762         b0 <<= 2;
    763       } while (--i);
    764     }
    765 
    766     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    767     dst_data += dst_stride;
    768     src_line += src_stride;
    769   }
    770 
    771   return BL_SUCCESS;
    772 }
    773 
    774 // bl::PixelConverter - Any <- Indexed4
    775 // ====================================
    776 
    777 template<typename PixelAccess>
    778 static BLResult BL_CDECL bl_convert_any_from_indexed4(
    779   const BLPixelConverterCore* self,
    780   uint8_t* dst_data, intptr_t dst_stride,
    781   const uint8_t* src_line, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    782 
    783   if (!options)
    784     options = &bl_pixel_converter_default_options;
    785 
    786   const uint32_t kPixelSize = PixelAccess::kSize;
    787 
    788   const BLPixelConverterData::IndexedData& d = bl_pixel_converter_get_data(self)->indexed_data;
    789   const uint8_t* table = d.embedded.table8;
    790 
    791   const size_t gap = options->gap;
    792   dst_stride -= uintptr_t(w) * kPixelSize + gap;
    793 
    794   for (uint32_t y = h; y != 0; y--) {
    795     const uint8_t* src_data = src_line;
    796     uint32_t i = w;
    797 
    798     while (i >= 2) {
    799       uintptr_t b0 = *src_data++;
    800 
    801       uint32_t p0 = PixelAccess::fetchA(table + (b0 >> 4) * kPixelSize);
    802       uint32_t p1 = PixelAccess::fetchA(table + (b0 & 15) * kPixelSize);
    803 
    804       PixelAccess::storeU(dst_data + 0 * kPixelSize, p0);
    805       PixelAccess::storeU(dst_data + 1 * kPixelSize, p1);
    806 
    807       dst_data += 2 * kPixelSize;
    808       i -= 2;
    809     }
    810 
    811     if (i) {
    812       uintptr_t b0 = src_data[0];
    813       PixelAccess::storeU(dst_data, PixelAccess::fetchA(table + (b0 >> 4) * kPixelSize));
    814       dst_data += kPixelSize;
    815     }
    816 
    817     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    818     dst_data += dst_stride;
    819     src_line += src_stride;
    820   }
    821 
    822   return BL_SUCCESS;
    823 }
    824 
    825 // bl::PixelConverter - Any <- Indexed8
    826 // ====================================
    827 
    828 // Special case - used when no copy of the palette is required.
    829 static BLResult BL_CDECL bl_convert_a8_from_indexed8_pal32(
    830   const BLPixelConverterCore* self,
    831   uint8_t* dst_data, intptr_t dst_stride,
    832   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    833 
    834   if (!options)
    835     options = &bl_pixel_converter_default_options;
    836 
    837   const size_t gap = options->gap;
    838   dst_stride -= uintptr_t(w) + gap;
    839   src_stride -= uintptr_t(w);
    840 
    841   const BLPixelConverterData::IndexedData& d = bl_pixel_converter_get_data(self)->indexed_data;
    842   const uint32_t* table = d.dynamic.table32;
    843 
    844   for (uint32_t y = h; y != 0; y--) {
    845     for (uint32_t i = w; i != 0; i--) {
    846       uintptr_t b0 = *src_data++;
    847       *dst_data++ = uint8_t(table[b0] >> 24);
    848     }
    849 
    850     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    851     dst_data += dst_stride;
    852     src_data += src_stride;
    853   }
    854 
    855   return BL_SUCCESS;
    856 }
    857 
    858 template<typename PixelAccess>
    859 static BLResult BL_CDECL bl_convert_any_from_indexed8(
    860   const BLPixelConverterCore* self,
    861   uint8_t* dst_data, intptr_t dst_stride,
    862   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    863 
    864   if (!options)
    865     options = &bl_pixel_converter_default_options;
    866 
    867   const uint32_t kPixelSize = PixelAccess::kSize;
    868   const size_t gap = options->gap;
    869   dst_stride -= uintptr_t(w) * kPixelSize + gap;
    870   src_stride -= uintptr_t(w);
    871 
    872   const BLPixelConverterData::IndexedData& d = bl_pixel_converter_get_data(self)->indexed_data;
    873   const uint8_t* table = d.dynamic.table8;
    874 
    875   for (uint32_t y = h; y != 0; y--) {
    876     for (uint32_t i = w; i != 0; i--) {
    877       uintptr_t b0 = *src_data++;
    878       PixelAccess::storeU(dst_data, PixelAccess::fetchA(table + b0 * kPixelSize));
    879       dst_data += kPixelSize;
    880     }
    881 
    882     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    883     dst_data += dst_stride;
    884     src_data += src_stride;
    885   }
    886 
    887   return BL_SUCCESS;
    888 }
    889 
    890 // bl::PixelConverter - ByteShuffle
    891 // ================================
    892 
    893 // TODO:
    894 
    895 // bl::PixelConverter - Native32 <- XRGB|ARGB|PRGB
    896 // ===============================================
    897 
    898 template<typename PixelAccess, bool AlwaysUnaligned>
    899 static BLResult BL_CDECL bl_convert_xrgb32_from_xrgb_any(
    900   const BLPixelConverterCore* self,
    901   uint8_t* dst_data, intptr_t dst_stride,
    902   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    903 
    904   if (!options)
    905     options = &bl_pixel_converter_default_options;
    906 
    907   const size_t gap = options->gap;
    908   dst_stride -= uintptr_t(w) * 4 + gap;
    909   src_stride -= uintptr_t(w) * PixelAccess::kSize;
    910 
    911   const BLPixelConverterData::NativeFromForeign& d = bl_pixel_converter_get_data(self)->native_from_foreign;
    912   uint32_t r_mask = d.masks[0];
    913   uint32_t g_mask = d.masks[1];
    914   uint32_t b_mask = d.masks[2];
    915 
    916   uint32_t r_shift = d.shifts[0];
    917   uint32_t g_shift = d.shifts[1];
    918   uint32_t b_shift = d.shifts[2];
    919 
    920   uint32_t r_scale = d.scale[0];
    921   uint32_t gScale = d.scale[1];
    922   uint32_t b_scale = d.scale[2];
    923 
    924   uint32_t fill_mask = d.fill_mask;
    925 
    926   for (uint32_t y = h; y != 0; y--) {
    927     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, 4) && bl::IntOps::is_aligned(src_data, PixelAccess::kSize)) {
    928       for (uint32_t i = w; i != 0; i--) {
    929         uint32_t pix = PixelAccess::fetchA(src_data);
    930         uint32_t r = (((pix >> r_shift) & r_mask) * r_scale) & 0x00FF0000u;
    931         uint32_t g = (((pix >> g_shift) & g_mask) * gScale) & 0x0000FF00u;
    932         uint32_t b = (((pix >> b_shift) & b_mask) * b_scale) >> 8;
    933 
    934         bl::MemOps::writeU32a(dst_data, r | g | b | fill_mask);
    935 
    936         dst_data += 4;
    937         src_data += PixelAccess::kSize;
    938       }
    939     }
    940     else {
    941       for (uint32_t i = w; i != 0; i--) {
    942         uint32_t pix = PixelAccess::fetchU(src_data);
    943         uint32_t r = (((pix >> r_shift) & r_mask) * r_scale) & 0x00FF0000u;
    944         uint32_t g = (((pix >> g_shift) & g_mask) * gScale) & 0x0000FF00u;
    945         uint32_t b = (((pix >> b_shift) & b_mask) * b_scale) >> 8;
    946 
    947         bl::MemOps::writeU32u(dst_data, r | g | b | fill_mask);
    948 
    949         dst_data += 4;
    950         src_data += PixelAccess::kSize;
    951       }
    952     }
    953 
    954     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
    955     dst_data += dst_stride;
    956     src_data += src_stride;
    957   }
    958 
    959   return BL_SUCCESS;
    960 }
    961 
    962 template<typename PixelAccess, bool AlwaysUnaligned>
    963 static BLResult BL_CDECL bl_convert_prgb32_from_argb_any(
    964   const BLPixelConverterCore* self,
    965   uint8_t* dst_data, intptr_t dst_stride,
    966   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
    967 
    968   if (!options)
    969     options = &bl_pixel_converter_default_options;
    970 
    971   const size_t gap = options->gap;
    972   dst_stride -= uintptr_t(w) * 4 + gap;
    973   src_stride -= uintptr_t(w) * PixelAccess::kSize;
    974 
    975   const BLPixelConverterData::NativeFromForeign& d = bl_pixel_converter_get_data(self)->native_from_foreign;
    976   uint32_t r_mask = d.masks[0];
    977   uint32_t g_mask = d.masks[1];
    978   uint32_t b_mask = d.masks[2];
    979   uint32_t a_mask = d.masks[3];
    980 
    981   uint32_t r_shift = d.shifts[0];
    982   uint32_t g_shift = d.shifts[1];
    983   uint32_t b_shift = d.shifts[2];
    984   uint32_t a_shift = d.shifts[3];
    985 
    986   uint32_t r_scale = d.scale[0];
    987   uint32_t gScale = d.scale[1];
    988   uint32_t b_scale = d.scale[2];
    989   uint32_t a_scale = d.scale[3];
    990 
    991   for (uint32_t y = h; y != 0; y--) {
    992     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, 4) && bl::IntOps::is_aligned(src_data, PixelAccess::kSize)) {
    993       for (uint32_t i = w; i != 0; i--) {
    994         uint32_t pix = PixelAccess::fetchA(src_data);
    995         uint32_t _a = ((((pix >> a_shift) & a_mask) * a_scale) >> 24);
    996         uint32_t ag = ((((pix >> g_shift) & g_mask) * gScale) >>  8);
    997         uint32_t rb = ((((pix >> r_shift) & r_mask) * r_scale) & 0x00FF0000u) |
    998                       ((((pix >> b_shift) & b_mask) * b_scale) >>  8);
    999 
   1000         ag |= 0x00FF0000u;
   1001         rb *= _a;
   1002         ag *= _a;
   1003 
   1004         rb += 0x00800080u;
   1005         ag += 0x00800080u;
   1006 
   1007         rb = (rb + ((rb >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
   1008         ag = (ag + ((ag >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
   1009 
   1010         rb >>= 8;
   1011         bl::MemOps::writeU32a(dst_data, ag + rb);
   1012 
   1013         dst_data += 4;
   1014         src_data += PixelAccess::kSize;
   1015       }
   1016     }
   1017     else {
   1018       for (uint32_t i = w; i != 0; i--) {
   1019         uint32_t pix = PixelAccess::fetchU(src_data);
   1020         uint32_t _a = ((((pix >> a_shift) & a_mask) * a_scale) >> 24);
   1021         uint32_t ag = ((((pix >> g_shift) & g_mask) * gScale) >>  8);
   1022         uint32_t rb = ((((pix >> r_shift) & r_mask) * r_scale) & 0x00FF0000u) |
   1023                       ((((pix >> b_shift) & b_mask) * b_scale) >>  8);
   1024 
   1025         ag |= 0x00FF0000u;
   1026         rb *= _a;
   1027         ag *= _a;
   1028 
   1029         rb += 0x00800080u;
   1030         ag += 0x00800080u;
   1031 
   1032         rb = (rb + ((rb >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
   1033         ag = (ag + ((ag >> 8) & 0x00FF00FFu)) & 0xFF00FF00u;
   1034 
   1035         rb >>= 8;
   1036         bl::MemOps::writeU32u(dst_data, ag | rb);
   1037 
   1038         dst_data += 4;
   1039         src_data += PixelAccess::kSize;
   1040       }
   1041     }
   1042 
   1043     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
   1044     dst_data += dst_stride;
   1045     src_data += src_stride;
   1046   }
   1047 
   1048   return BL_SUCCESS;
   1049 }
   1050 
   1051 template<typename PixelAccess, bool AlwaysUnaligned>
   1052 static BLResult BL_CDECL bl_convert_prgb32_from_prgb_any(
   1053   const BLPixelConverterCore* self,
   1054   uint8_t* dst_data, intptr_t dst_stride,
   1055   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
   1056 
   1057   if (!options)
   1058     options = &bl_pixel_converter_default_options;
   1059 
   1060   const size_t gap = options->gap;
   1061   dst_stride -= uintptr_t(w) * 4 + gap;
   1062   src_stride -= uintptr_t(w) * PixelAccess::kSize;
   1063 
   1064   const BLPixelConverterData::NativeFromForeign& d = bl_pixel_converter_get_data(self)->native_from_foreign;
   1065   uint32_t r_mask = d.masks[0];
   1066   uint32_t g_mask = d.masks[1];
   1067   uint32_t b_mask = d.masks[2];
   1068   uint32_t a_mask = d.masks[3];
   1069 
   1070   uint32_t r_shift = d.shifts[0];
   1071   uint32_t g_shift = d.shifts[1];
   1072   uint32_t b_shift = d.shifts[2];
   1073   uint32_t a_shift = d.shifts[3];
   1074 
   1075   uint32_t r_scale = d.scale[0];
   1076   uint32_t gScale = d.scale[1];
   1077   uint32_t b_scale = d.scale[2];
   1078   uint32_t a_scale = d.scale[3];
   1079 
   1080   for (uint32_t y = h; y != 0; y--) {
   1081     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, 4) && bl::IntOps::is_aligned(src_data, PixelAccess::kSize)) {
   1082       for (uint32_t i = w; i != 0; i--) {
   1083         uint32_t pix = PixelAccess::fetchA(src_data);
   1084         uint32_t r = ((pix >> r_shift) & r_mask) * r_scale;
   1085         uint32_t g = ((pix >> g_shift) & g_mask) * gScale;
   1086         uint32_t b = ((pix >> b_shift) & b_mask) * b_scale;
   1087         uint32_t a = ((pix >> a_shift) & a_mask) * a_scale;
   1088 
   1089         uint32_t ag = (a + (g     )) & 0xFF00FF00u;
   1090         uint32_t rb = (r + (b >> 8)) & 0x00FF00FFu;
   1091 
   1092         bl::MemOps::writeU32a(dst_data, ag | rb);
   1093 
   1094         dst_data += 4;
   1095         src_data += PixelAccess::kSize;
   1096       }
   1097     }
   1098     else {
   1099       for (uint32_t i = w; i != 0; i--) {
   1100         uint32_t pix = PixelAccess::fetchU(src_data);
   1101         uint32_t g = ((pix >> g_shift) & g_mask) * gScale;
   1102         uint32_t r = ((pix >> r_shift) & r_mask) * r_scale;
   1103         uint32_t b = ((pix >> b_shift) & b_mask) * b_scale;
   1104         uint32_t a = ((pix >> a_shift) & a_mask) * a_scale;
   1105 
   1106         uint32_t ag = (a + (g     )) & 0xFF00FF00u;
   1107         uint32_t rb = (r + (b >> 8)) & 0x00FF00FFu;
   1108 
   1109         bl::MemOps::writeU32u(dst_data, ag | rb);
   1110 
   1111         dst_data += 4;
   1112         src_data += PixelAccess::kSize;
   1113       }
   1114     }
   1115 
   1116     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
   1117     dst_data += dst_stride;
   1118     src_data += src_stride;
   1119   }
   1120 
   1121   return BL_SUCCESS;
   1122 }
   1123 
   1124 template<typename PixelAccess, bool AlwaysUnaligned>
   1125 static BLResult BL_CDECL bl_convert_argb32_from_prgb_any(
   1126   const BLPixelConverterCore* self,
   1127   uint8_t* dst_data, intptr_t dst_stride,
   1128   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
   1129 
   1130   if (!options)
   1131     options = &bl_pixel_converter_default_options;
   1132 
   1133   const size_t gap = options->gap;
   1134   dst_stride -= uintptr_t(w) * 4 + gap;
   1135   src_stride -= uintptr_t(w) * PixelAccess::kSize;
   1136 
   1137   const BLPixelConverterData::NativeFromForeign& d = bl_pixel_converter_get_data(self)->native_from_foreign;
   1138   uint32_t r_mask = d.masks[0];
   1139   uint32_t g_mask = d.masks[1];
   1140   uint32_t b_mask = d.masks[2];
   1141   uint32_t a_mask = d.masks[3];
   1142 
   1143   uint32_t r_shift = d.shifts[0];
   1144   uint32_t g_shift = d.shifts[1];
   1145   uint32_t b_shift = d.shifts[2];
   1146   uint32_t a_shift = d.shifts[3];
   1147 
   1148   uint32_t r_scale = d.scale[0];
   1149   uint32_t gScale = d.scale[1];
   1150   uint32_t b_scale = d.scale[2];
   1151   uint32_t a_scale = d.scale[3];
   1152 
   1153   for (uint32_t y = h; y != 0; y--) {
   1154     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, 4) && bl::IntOps::is_aligned(src_data, PixelAccess::kSize)) {
   1155       for (uint32_t i = w; i != 0; i--) {
   1156         uint32_t pix = PixelAccess::fetchA(src_data);
   1157         uint32_t r = (((pix >> r_shift) & r_mask) * r_scale) >> 16;
   1158         uint32_t g = (((pix >> g_shift) & g_mask) * gScale) >> 8;
   1159         uint32_t b = (((pix >> b_shift) & b_mask) * b_scale) >> 8;
   1160         uint32_t a = (((pix >> a_shift) & a_mask) * a_scale) >> 24;
   1161 
   1162         bl::PixelOps::Scalar::unpremultiply_rgb_8bit(r, g, b, a);
   1163         bl::MemOps::writeU32a(dst_data, (a << 24) | (r << 16) | (g << 8) | b);
   1164 
   1165         dst_data += 4;
   1166         src_data += PixelAccess::kSize;
   1167       }
   1168     }
   1169     else {
   1170       for (uint32_t i = w; i != 0; i--) {
   1171         uint32_t pix = PixelAccess::fetchU(src_data);
   1172         uint32_t r = (((pix >> r_shift) & r_mask) * r_scale) >> 16;
   1173         uint32_t g = (((pix >> g_shift) & g_mask) * gScale) >> 8;
   1174         uint32_t b = (((pix >> b_shift) & b_mask) * b_scale) >> 8;
   1175         uint32_t a = (((pix >> a_shift) & a_mask) * a_scale) >> 24;
   1176 
   1177         bl::PixelOps::Scalar::unpremultiply_rgb_8bit(r, g, b, a);
   1178         bl::MemOps::writeU32u(dst_data, (a << 24) | (r << 16) | (g << 8) | b);
   1179 
   1180         dst_data += 4;
   1181         src_data += PixelAccess::kSize;
   1182       }
   1183     }
   1184 
   1185     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
   1186     dst_data += dst_stride;
   1187     src_data += src_stride;
   1188   }
   1189 
   1190   return BL_SUCCESS;
   1191 }
   1192 
   1193 // bl::PixelConverter - XRGB|ARGB|PRGB <- Native32
   1194 // ===============================================
   1195 
   1196 template<typename PixelAccess, bool AlwaysUnaligned>
   1197 static BLResult BL_CDECL bl_convert_xrgb_any_from_xrgb32(
   1198   const BLPixelConverterCore* self,
   1199   uint8_t* dst_data, intptr_t dst_stride,
   1200   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
   1201 
   1202   if (!options)
   1203     options = &bl_pixel_converter_default_options;
   1204 
   1205   const size_t gap = options->gap;
   1206   dst_stride -= uintptr_t(w) * PixelAccess::kSize + gap;
   1207   src_stride -= uintptr_t(w) * 4;
   1208 
   1209   const BLPixelConverterData::ForeignFromNative& d = bl_pixel_converter_get_data(self)->foreign_from_native;
   1210   uint32_t r_mask = d.masks[0];
   1211   uint32_t g_mask = d.masks[1];
   1212   uint32_t b_mask = d.masks[2];
   1213 
   1214   uint32_t r_shift = d.shifts[0];
   1215   uint32_t g_shift = d.shifts[1];
   1216   uint32_t b_shift = d.shifts[2];
   1217 
   1218   uint32_t fill_mask = d.fill_mask;
   1219 
   1220   for (uint32_t y = h; y != 0; y--) {
   1221     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, PixelAccess::kSize) && bl::IntOps::is_aligned(src_data, 4)) {
   1222       for (uint32_t i = w; i != 0; i--) {
   1223         uint32_t pix = bl::MemOps::readU32a(src_data);
   1224 
   1225         uint32_t r = ((pix >> 16) & 0xFFu) * 0x01010101u;
   1226         uint32_t g = ((pix >>  8) & 0xFFu) * 0x01010101u;
   1227         uint32_t b = ((pix      ) & 0xFFu) * 0x01010101u;
   1228 
   1229         PixelAccess::storeA(dst_data, ((r >> r_shift) & r_mask) |
   1230                                      ((g >> g_shift) & g_mask) |
   1231                                      ((b >> b_shift) & b_mask) | fill_mask);
   1232         dst_data += PixelAccess::kSize;
   1233         src_data += 4;
   1234       }
   1235     }
   1236     else {
   1237       for (uint32_t i = w; i != 0; i--) {
   1238         uint32_t pix = bl::MemOps::readU32u(src_data);
   1239 
   1240         uint32_t r = ((pix >> 16) & 0xFFu) * 0x01010101u;
   1241         uint32_t g = ((pix >>  8) & 0xFFu) * 0x01010101u;
   1242         uint32_t b = ((pix      ) & 0xFFu) * 0x01010101u;
   1243 
   1244         PixelAccess::storeU(dst_data, ((r >> r_shift) & r_mask) |
   1245                                      ((g >> g_shift) & g_mask) |
   1246                                      ((b >> b_shift) & b_mask) | fill_mask);
   1247         dst_data += PixelAccess::kSize;
   1248         src_data += 4;
   1249       }
   1250     }
   1251 
   1252     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
   1253     dst_data += dst_stride;
   1254     src_data += src_stride;
   1255   }
   1256 
   1257   return BL_SUCCESS;
   1258 }
   1259 
   1260 template<typename PixelAccess, bool AlwaysUnaligned>
   1261 static BLResult BL_CDECL bl_convert_argb_any_from_prgb32(
   1262   const BLPixelConverterCore* self,
   1263   uint8_t* dst_data, intptr_t dst_stride,
   1264   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
   1265 
   1266   if (!options)
   1267     options = &bl_pixel_converter_default_options;
   1268 
   1269   const size_t gap = options->gap;
   1270   dst_stride -= uintptr_t(w) * PixelAccess::kSize + gap;
   1271   src_stride -= uintptr_t(w) * 4;
   1272 
   1273   const BLPixelConverterData::ForeignFromNative& d = bl_pixel_converter_get_data(self)->foreign_from_native;
   1274   uint32_t r_mask = d.masks[0];
   1275   uint32_t g_mask = d.masks[1];
   1276   uint32_t b_mask = d.masks[2];
   1277   uint32_t a_mask = d.masks[3];
   1278 
   1279   uint32_t r_shift = d.shifts[0];
   1280   uint32_t g_shift = d.shifts[1];
   1281   uint32_t b_shift = d.shifts[2];
   1282   uint32_t a_shift = d.shifts[3];
   1283 
   1284   const uint32_t* unpremultiply_rcp = bl::common_table.unpremultiply_rcp;
   1285 
   1286   for (uint32_t y = h; y != 0; y--) {
   1287     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, PixelAccess::kSize) && bl::IntOps::is_aligned(src_data, 4)) {
   1288       for (uint32_t i = w; i != 0; i--) {
   1289         uint32_t pix = bl::MemOps::readU32a(src_data);
   1290 
   1291         uint32_t a = pix >> 24;
   1292         uint32_t rcp = unpremultiply_rcp[a];
   1293 
   1294         uint32_t r = ((((pix >> 16) & 0xFFu) * rcp + 0x8000u) >> 16) * 0x01010101u;
   1295         uint32_t g = ((((pix >>  8) & 0xFFu) * rcp + 0x8000u) >> 16) * 0x01010101u;
   1296         uint32_t b = ((((pix      ) & 0xFFu) * rcp + 0x8000u) >> 16) * 0x01010101u;
   1297 
   1298         a *= 0x01010101u;
   1299         PixelAccess::storeA(dst_data, ((r >> r_shift) & r_mask) |
   1300                                      ((g >> g_shift) & g_mask) |
   1301                                      ((b >> b_shift) & b_mask) |
   1302                                      ((a >> a_shift) & a_mask));
   1303         dst_data += PixelAccess::kSize;
   1304         src_data += 4;
   1305       }
   1306     }
   1307     else {
   1308       for (uint32_t i = w; i != 0; i--) {
   1309         uint32_t pix = bl::MemOps::readU32u(src_data);
   1310 
   1311         uint32_t a = pix >> 24;
   1312         uint32_t rcp = unpremultiply_rcp[a];
   1313 
   1314         uint32_t r = ((((pix >> 16) & 0xFFu) * rcp + 0x8000u) >> 16) * 0x01010101u;
   1315         uint32_t g = ((((pix >>  8) & 0xFFu) * rcp + 0x8000u) >> 16) * 0x01010101u;
   1316         uint32_t b = ((((pix      ) & 0xFFu) * rcp + 0x8000u) >> 16) * 0x01010101u;
   1317 
   1318         a *= 0x01010101u;
   1319         PixelAccess::storeU(dst_data, ((r >> r_shift) & r_mask) |
   1320                                      ((g >> g_shift) & g_mask) |
   1321                                      ((b >> b_shift) & b_mask) |
   1322                                      ((a >> a_shift) & a_mask));
   1323         dst_data += PixelAccess::kSize;
   1324         src_data += 4;
   1325       }
   1326     }
   1327 
   1328     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
   1329     dst_data += dst_stride;
   1330     src_data += src_stride;
   1331   }
   1332 
   1333   return BL_SUCCESS;
   1334 }
   1335 
   1336 template<typename PixelAccess, bool AlwaysUnaligned>
   1337 static BLResult BL_CDECL bl_convert_prgb_any_from_prgb32(
   1338   const BLPixelConverterCore* self,
   1339   uint8_t* dst_data, intptr_t dst_stride,
   1340   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
   1341 
   1342   if (!options)
   1343     options = &bl_pixel_converter_default_options;
   1344 
   1345   const size_t gap = options->gap;
   1346   dst_stride -= uintptr_t(w) * PixelAccess::kSize + gap;
   1347   src_stride -= uintptr_t(w) * 4;
   1348 
   1349   const BLPixelConverterData::ForeignFromNative& d = bl_pixel_converter_get_data(self)->foreign_from_native;
   1350   uint32_t r_mask = d.masks[0];
   1351   uint32_t g_mask = d.masks[1];
   1352   uint32_t b_mask = d.masks[2];
   1353   uint32_t a_mask = d.masks[3];
   1354 
   1355   uint32_t r_shift = d.shifts[0];
   1356   uint32_t g_shift = d.shifts[1];
   1357   uint32_t b_shift = d.shifts[2];
   1358   uint32_t a_shift = d.shifts[3];
   1359 
   1360   for (uint32_t y = h; y != 0; y--) {
   1361     if (!AlwaysUnaligned && bl::IntOps::is_aligned(dst_data, PixelAccess::kSize) && bl::IntOps::is_aligned(src_data, 4)) {
   1362       for (uint32_t i = w; i != 0; i--) {
   1363         uint32_t pix = bl::MemOps::readU32a(src_data);
   1364 
   1365         uint32_t r = ((pix >> 16) & 0xFFu) * 0x01010101u;
   1366         uint32_t g = ((pix >>  8) & 0xFFu) * 0x01010101u;
   1367         uint32_t b = ((pix      ) & 0xFFu) * 0x01010101u;
   1368         uint32_t a = ((pix >> 24)        ) * 0x01010101u;
   1369 
   1370         PixelAccess::storeA(dst_data, ((r >> r_shift) & r_mask) |
   1371                                      ((g >> g_shift) & g_mask) |
   1372                                      ((b >> b_shift) & b_mask) |
   1373                                      ((a >> a_shift) & a_mask));
   1374         dst_data += PixelAccess::kSize;
   1375         src_data += 4;
   1376       }
   1377     }
   1378     else {
   1379       for (uint32_t i = w; i != 0; i--) {
   1380         uint32_t pix = bl::MemOps::readU32u(src_data);
   1381 
   1382         uint32_t r = ((pix >> 16) & 0xFFu) * 0x01010101u;
   1383         uint32_t g = ((pix >>  8) & 0xFFu) * 0x01010101u;
   1384         uint32_t b = ((pix      ) & 0xFFu) * 0x01010101u;
   1385         uint32_t a = ((pix >> 24)        ) * 0x01010101u;
   1386 
   1387         PixelAccess::storeU(dst_data, ((r >> r_shift) & r_mask) |
   1388                                      ((g >> g_shift) & g_mask) |
   1389                                      ((b >> b_shift) & b_mask) |
   1390                                      ((a >> a_shift) & a_mask));
   1391         dst_data += PixelAccess::kSize;
   1392         src_data += 4;
   1393       }
   1394     }
   1395 
   1396     dst_data = bl_pixel_converter_fill_gap(dst_data, gap);
   1397     dst_data += dst_stride;
   1398     src_data += src_stride;
   1399   }
   1400 
   1401   return BL_SUCCESS;
   1402 }
   1403 
   1404 // bl::PixelConverter - Init - Utilities
   1405 // =====================================
   1406 
   1407 static BL_INLINE BLResult bl_pixel_converter_init_func_generic(BLPixelConverterCore* self, BLPixelConverterFunc func, uint32_t flags = 0) noexcept {
   1408   self->convert_func = func;
   1409   self->internal_flags = uint8_t(flags | BL_PIXEL_CONVERTER_INTERNAL_FLAG_INITIALIZED);
   1410   return BL_SUCCESS;
   1411 }
   1412 
   1413 static BL_INLINE BLResult bl_pixel_converter_init_func_opt(BLPixelConverterCore* self, BLPixelConverterFunc func, uint32_t flags = 0) noexcept {
   1414   self->convert_func = func;
   1415   self->internal_flags = uint8_t(flags | BL_PIXEL_CONVERTER_INTERNAL_FLAG_INITIALIZED |
   1416                                         BL_PIXEL_CONVERTER_INTERNAL_FLAG_OPTIMIZED);
   1417   return BL_SUCCESS;
   1418 }
   1419 
   1420 static uint32_t bl_pixel_converter_calc_rgb_mask32(const BLFormatInfo& fmt_info) noexcept {
   1421   uint32_t mask = 0;
   1422   for (uint32_t i = 0; i < 3; i++)
   1423     if (fmt_info.sizes[i])
   1424       mask |= bl::IntOps::non_zero_lsb_mask<uint32_t>(fmt_info.sizes[i]) << fmt_info.shifts[i];
   1425   return mask;
   1426 }
   1427 
   1428 static uint32_t bl_pixel_converter_calc_fill_mask32(const BLFormatInfo& fmt_info) noexcept {
   1429   uint32_t mask = 0;
   1430   for (uint32_t i = 0; i < 4; i++)
   1431     if (fmt_info.sizes[i])
   1432       mask |= bl::IntOps::non_zero_lsb_mask<uint32_t>(fmt_info.sizes[i]) << fmt_info.shifts[i];
   1433   return ~mask;
   1434 }
   1435 
   1436 static void bl_pixel_converter_calc_pshufb_predicate_32_from_24(uint32_t out[4], const BLFormatInfo& dst_info, const BLFormatInfo& src_info) noexcept {
   1437   BL_ASSERT(dst_info.depth == 32);
   1438   BL_ASSERT(src_info.depth == 24);
   1439 
   1440   BL_ASSERT(dst_info.flags & BL_FORMAT_FLAG_BYTE_ALIGNED);
   1441   BL_ASSERT(src_info.flags & BL_FORMAT_FLAG_BYTE_ALIGNED);
   1442 
   1443   uint32_t r_index = uint32_t(src_info.shifts[0]) / 8u;
   1444   uint32_t gIndex = uint32_t(src_info.shifts[1]) / 8u;
   1445   uint32_t b_index = uint32_t(src_info.shifts[2]) / 8u;
   1446 
   1447   uint32_t predicate = 0x80808080u;
   1448   predicate ^= (0x80u ^ r_index) << dst_info.shifts[0];
   1449   predicate ^= (0x80u ^ gIndex) << dst_info.shifts[1];
   1450   predicate ^= (0x80u ^ b_index) << dst_info.shifts[2];
   1451 
   1452   uint32_t increment = (0x03u << dst_info.shifts[0]) |
   1453                        (0x03u << dst_info.shifts[1]) |
   1454                        (0x03u << dst_info.shifts[2]) ;
   1455 
   1456   out[0] = predicate; predicate += increment;
   1457   out[1] = predicate; predicate += increment;
   1458   out[2] = predicate; predicate += increment;
   1459   out[3] = predicate;
   1460 }
   1461 
   1462 static void bl_pixel_converter_calc_pshufb_predicate_32_from_32(uint32_t out[4], const BLFormatInfo& dst_info, const BLFormatInfo& src_info) noexcept {
   1463   BL_ASSERT(dst_info.depth == 32);
   1464   BL_ASSERT(src_info.depth == 32);
   1465 
   1466   BL_ASSERT(dst_info.flags & BL_FORMAT_FLAG_BYTE_ALIGNED);
   1467   BL_ASSERT(src_info.flags & BL_FORMAT_FLAG_BYTE_ALIGNED);
   1468 
   1469   uint32_t r_index = uint32_t(src_info.shifts[0]) / 8u;
   1470   uint32_t gIndex = uint32_t(src_info.shifts[1]) / 8u;
   1471   uint32_t b_index = uint32_t(src_info.shifts[2]) / 8u;
   1472   uint32_t a_index = uint32_t(src_info.shifts[3]) / 8u;
   1473 
   1474   uint32_t predicate = 0x80808080u;
   1475   predicate ^= (0x80u ^ r_index) << dst_info.shifts[0];
   1476   predicate ^= (0x80u ^ gIndex) << dst_info.shifts[1];
   1477   predicate ^= (0x80u ^ b_index) << dst_info.shifts[2];
   1478 
   1479   uint32_t increment = (0x04u << dst_info.shifts[0]) |
   1480                        (0x04u << dst_info.shifts[1]) |
   1481                        (0x04u << dst_info.shifts[2]) ;
   1482 
   1483   if (src_info.sizes[3] != 0 && dst_info.sizes[3] != 0) {
   1484     predicate ^= (0x80u ^ a_index) << dst_info.shifts[3];
   1485     increment |= (0x04u         ) << dst_info.shifts[3];
   1486   }
   1487 
   1488   out[0] = predicate; predicate += increment;
   1489   out[1] = predicate; predicate += increment;
   1490   out[2] = predicate; predicate += increment;
   1491   out[3] = predicate;
   1492 }
   1493 
   1494 // bl::PixelConverter - Init - Indexed
   1495 // ===================================
   1496 
   1497 static BLResult bl_pixel_converter_init_indexed(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   1498   BLPixelConverterData::IndexedData& d = bl_pixel_converter_get_data(self)->indexed_data;
   1499 
   1500   // Bail if the source depth doesn't match any supported one.
   1501   if (BL_UNLIKELY(!bl_pixel_converter_is_indexed_depth(si.depth)))
   1502     return bl_make_error(BL_ERROR_INVALID_VALUE);
   1503 
   1504   BLFormatInfo palette_format_info;
   1505   if (BL_UNLIKELY(!bl_pixel_converter_palette_format_from_format_flags(palette_format_info, si.flags)))
   1506     return bl_make_error(BL_ERROR_INVALID_VALUE);
   1507 
   1508   bool dont_copy_palette = (create_flags & BL_PIXEL_CONVERTER_CREATE_FLAG_DONT_COPY_PALETTE) != 0;
   1509   bool alterable_palette = (create_flags & BL_PIXEL_CONVERTER_CREATE_FLAG_ALTERABLE_PALETTE) != 0;
   1510 
   1511   // Special case - avoid making the copy of the palette for known conversions.
   1512   if (di.depth == 8 && si.depth == 8 && dont_copy_palette) {
   1513     if ((di.flags & (BL_FORMAT_FLAG_RGBA | BL_FORMAT_FLAG_BYTE_ALIGNED)) == (BL_FORMAT_FLAG_ALPHA | BL_FORMAT_FLAG_BYTE_ALIGNED)) {
   1514       d.convert_func = bl_convert_a8_from_indexed8_pal32;
   1515       d.internal_flags = BL_PIXEL_CONVERTER_INTERNAL_FLAG_INITIALIZED;
   1516       d.dynamic.table = si.palette;
   1517       return BL_SUCCESS;
   1518     }
   1519   }
   1520 
   1521   // We need a temporary pixel converter to convert the palette to the destination pixel format. This operation should
   1522   // not allocate any memory as the converter will convert native pixel format (BLRgba32) into a possibly non-native
   1523   // one although a native pixel format is used most of the time.
   1524   BLPixelConverterCore pal_cvt;
   1525   BL_PROPAGATE(bl_pixel_converter_init_internal(&pal_cvt, di, palette_format_info, BL_PIXEL_CONVERTER_CREATE_NO_FLAGS));
   1526 
   1527   // If the source depth is 8 bits it means that we either use the source format's palette or make a copy of it
   1528   // depending on `create_flags` and the destination format as well.
   1529   void* palette = nullptr;
   1530   uint32_t palette_size = 1u << si.depth;
   1531   uint32_t palette_size_in_bytes = palette_size * (di.depth / 8u);
   1532   uint32_t internal_flags = BL_PIXEL_CONVERTER_INTERNAL_FLAG_INITIALIZED;
   1533 
   1534   if (palette_size_in_bytes > sizeof(d.embedded.table8)) {
   1535     if (dont_copy_palette && ((pal_cvt.internal_flags & BL_PIXEL_CONVERTER_INTERNAL_FLAG_RAW_COPY) || alterable_palette)) {
   1536       palette = si.palette;
   1537       d.dynamic.table = palette;
   1538     }
   1539     else {
   1540       palette = malloc(palette_size_in_bytes + sizeof(size_t));
   1541       internal_flags |= BL_PIXEL_CONVERTER_INTERNAL_FLAG_DYNAMIC_DATA;
   1542 
   1543       if (!palette) {
   1544         bl_pixel_converter_reset(&pal_cvt);
   1545         return bl_make_error(BL_ERROR_OUT_OF_MEMORY);
   1546       }
   1547     }
   1548   }
   1549   else {
   1550     palette = d.embedded.table8;
   1551   }
   1552 
   1553   pal_cvt.convert_func(&pal_cvt, static_cast<uint8_t*>(palette), 0, reinterpret_cast<const uint8_t*>(si.palette), 0, palette_size, 1, nullptr);
   1554   bl_pixel_converter_reset(&pal_cvt);
   1555 
   1556   BLPixelConverterFunc func = nullptr;
   1557   switch (di.depth) {
   1558     case 8:
   1559       switch (si.depth) {
   1560         case 1: func = bl_convert_any_from_indexed1<BLPixelAccess8>; break;
   1561         case 2: func = bl_convert_any_from_indexed2<BLPixelAccess8>; break;
   1562         case 4: func = bl_convert_any_from_indexed4<BLPixelAccess8>; break;
   1563         case 8: func = bl_convert_any_from_indexed8<BLPixelAccess8>; break;
   1564       }
   1565       break;
   1566 
   1567     case 16:
   1568       switch (si.depth) {
   1569         case 1: func = bl_convert_any_from_indexed1<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>>; break;
   1570         case 2: func = bl_convert_any_from_indexed2<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>>; break;
   1571         case 4: func = bl_convert_any_from_indexed4<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>>; break;
   1572         case 8: func = bl_convert_any_from_indexed8<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>>; break;
   1573       }
   1574       break;
   1575 
   1576     case 24:
   1577       switch (si.depth) {
   1578         case 1: func = bl_convert_any_from_indexed1<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>>; break;
   1579         case 2: func = bl_convert_any_from_indexed2<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>>; break;
   1580         case 4: func = bl_convert_any_from_indexed4<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>>; break;
   1581         case 8: func = bl_convert_any_from_indexed8<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>>; break;
   1582       }
   1583       break;
   1584 
   1585     case 32:
   1586       switch (si.depth) {
   1587         case 1: func = bl_convert_any_from_indexed1<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>>; break;
   1588         case 2: func = bl_convert_any_from_indexed2<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>>; break;
   1589         case 4: func = bl_convert_any_from_indexed4<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>>; break;
   1590         case 8: func = bl_convert_any_from_indexed8<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>>; break;
   1591       }
   1592       break;
   1593   }
   1594 
   1595   d.convert_func = func;
   1596   d.internal_flags = uint8_t(internal_flags);
   1597 
   1598   if (internal_flags & BL_PIXEL_CONVERTER_INTERNAL_FLAG_DYNAMIC_DATA) {
   1599     size_t* ref_count = bl::PtrOps::offset<size_t>(palette, palette_size_in_bytes);
   1600     *ref_count = 1;
   1601 
   1602     d.dynamic.table = palette;
   1603     d.dynamic.ref_count = ref_count;
   1604   }
   1605 
   1606   if (!func) {
   1607     bl_pixel_converter_reset(self);
   1608     return bl_make_error(BL_ERROR_NOT_IMPLEMENTED);
   1609   }
   1610 
   1611   return BL_SUCCESS;
   1612 }
   1613 
   1614 // bl::PixelConverter - Init - Simple
   1615 // ==================================
   1616 
   1617 static BLResult bl_pixel_converter_init_copy_8888_with_fill_mask(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si) noexcept {
   1618   BLPixelConverterData::MemCopyData& d = bl_pixel_converter_get_data(self)->mem_copy_data;
   1619 
   1620   d.internal_flags = BL_PIXEL_CONVERTER_INTERNAL_FLAG_INITIALIZED | BL_PIXEL_CONVERTER_INTERNAL_FLAG_RAW_COPY ;
   1621   d.bytes_per_pixel = uint8_t(di.depth / 8u);
   1622 
   1623   // Required to handle Copy32, XRGB32<-PRGB32, and PRGB32<-XRGB32 conversions.
   1624   uint32_t common_flags = di.flags & si.flags;
   1625   if (!(common_flags & BL_FORMAT_FLAG_ALPHA)) {
   1626     if (di.flags & BL_FORMAT_FLAG_ALPHA)
   1627       d.fill_mask = 0xFFu << di.shifts[3];
   1628     else
   1629       d.fill_mask = bl_pixel_converter_calc_fill_mask32(di);
   1630   }
   1631 
   1632 #ifdef BL_BUILD_OPT_AVX2
   1633   if (bl_runtime_has_avx2(&bl_runtime_context)) {
   1634     d.convert_func = bl_convert_copy_or_8888_avx2;
   1635     return BL_SUCCESS;
   1636   }
   1637 #endif
   1638 
   1639 #ifdef BL_BUILD_OPT_SSE2
   1640   if (bl_runtime_has_sse2(&bl_runtime_context)) {
   1641     d.convert_func = bl_convert_copy_or_8888_sse2;
   1642     return BL_SUCCESS;
   1643   }
   1644 #endif
   1645 
   1646   d.convert_func = bl_convert_copy_or_8888;
   1647   return BL_SUCCESS;
   1648 }
   1649 
   1650 static BLResult bl_pixel_converter_init_premultiply_8888(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si) noexcept {
   1651   BLPixelConverterData::PremultiplyData& d = bl_pixel_converter_get_data(self)->premultiply_data;
   1652 
   1653   bool dst_has_alpha = (di.flags & BL_FORMAT_FLAG_ALPHA) != 0;
   1654   uint32_t a_shift = dst_has_alpha ? di.shifts[3] : si.shifts[3];
   1655   uint32_t fill_mask = uint32_t(dst_has_alpha ? 0 : 0xFF) << a_shift;
   1656 
   1657   d.alpha_shift = uint8_t(a_shift);
   1658   d.fill_mask = fill_mask;
   1659 
   1660 #ifdef BL_BUILD_OPT_AVX2
   1661   if (bl_runtime_has_avx2(&bl_runtime_context)) {
   1662     if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_trailing_alpha_avx2);
   1663     if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_leading_alpha_avx2);
   1664   }
   1665 #endif
   1666 
   1667 #ifdef BL_BUILD_OPT_SSE2
   1668   if (bl_runtime_has_sse2(&bl_runtime_context)) {
   1669     if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_trailing_alpha_sse2);
   1670     if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_leading_alpha_sse2);
   1671   }
   1672 #endif
   1673 
   1674   return bl_pixel_converter_init_func_generic(self, bl_convert_premultiply_8888);
   1675 }
   1676 
   1677 static BLResult bl_pixel_converter_init_unpremultiply8888(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si) noexcept {
   1678   bl_unused(si);
   1679 
   1680   BLPixelConverterData::PremultiplyData& d = bl_pixel_converter_get_data(self)->premultiply_data;
   1681 
   1682   uint32_t a_shift = di.shifts[3];
   1683   d.alpha_shift = uint8_t(a_shift);
   1684 
   1685 #ifdef BL_BUILD_OPT_AVX2
   1686   if (bl_runtime_has_avx2(&bl_runtime_context)) {
   1687     if (bl_runtime_context.optimization_info.has_fast_pmulld()) {
   1688       if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_unpremultiply_8888_trailing_alpha_pmulld_avx2);
   1689       if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_unpremultiply_8888_leading_alpha_pmulld_avx2);
   1690     }
   1691     else {
   1692       if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_unpremultiply_8888_trailing_alpha_float_avx2);
   1693       if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_unpremultiply_8888_leading_alpha_float_avx2);
   1694     }
   1695   }
   1696 #endif
   1697 
   1698 #ifdef BL_BUILD_OPT_SSE2
   1699   if (bl_runtime_has_sse2(&bl_runtime_context)) {
   1700     if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_unpremultiply_8888_trailing_alpha_sse2);
   1701     if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_unpremultiply_8888_leading_alpha_sse2);
   1702   }
   1703 #endif
   1704 
   1705   if (a_shift == 0) return bl_pixel_converter_init_func_generic(self, bl_convert_unpremultiply_8888<0>);
   1706   if (a_shift == 24) return bl_pixel_converter_init_func_generic(self, bl_convert_unpremultiply_8888<24>);
   1707 
   1708   return BL_RESULT_NOTHING;
   1709 }
   1710 
   1711 static BLResult bl_pixel_converter_init_simple(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   1712   bl_unused(create_flags);
   1713 
   1714   uint32_t depth = di.depth;
   1715   uint32_t common_flags = di.flags & si.flags;
   1716 
   1717   const uint32_t kA = BL_FORMAT_FLAG_ALPHA;
   1718   const uint32_t kP = BL_FORMAT_FLAG_PREMULTIPLIED;
   1719 
   1720   if (bl::FormatInternal::has_same_rgb_layout(di, si)) {
   1721     if (bl::FormatInternal::has_same_alpha_layout(di, si)) {
   1722       // Memory copy.
   1723       if (di.flags == si.flags) {
   1724         // Don't copy undefined bytes in 8888 formats, it's better to set them to 0xFF.
   1725         if (depth == 32 && !(di.flags & BL_FORMAT_FLAG_ALPHA) && (di.flags & BL_FORMAT_FLAG_UNDEFINED_BITS))
   1726           return bl_pixel_converter_init_copy_8888_with_fill_mask(self, di, si);
   1727 
   1728         BLPixelConverterData::MemCopyData& d = bl_pixel_converter_get_data(self)->mem_copy_data;
   1729         d.internal_flags = BL_PIXEL_CONVERTER_INTERNAL_FLAG_INITIALIZED |
   1730                           BL_PIXEL_CONVERTER_INTERNAL_FLAG_RAW_COPY ;
   1731         d.bytes_per_pixel = uint8_t(di.depth / 8u);
   1732 
   1733 #ifdef BL_BUILD_OPT_AVX2
   1734         if (bl_runtime_has_avx2(&bl_runtime_context)) {
   1735           d.convert_func = bl_convert_copy_avx2;
   1736           return BL_SUCCESS;
   1737         }
   1738 #endif
   1739 
   1740 #ifdef BL_BUILD_OPT_SSE2
   1741         if (bl_runtime_has_sse2(&bl_runtime_context)) {
   1742           d.convert_func = bl_convert_copy_sse2;
   1743           return BL_SUCCESS;
   1744         }
   1745 #endif
   1746 
   1747         d.convert_func = bl_convert_copy;
   1748         return BL_SUCCESS;
   1749       }
   1750 
   1751       // Premultiply / Unpremultiply.
   1752       if (bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_RGBA | BL_FORMAT_FLAG_BYTE_ALIGNED) && di.flags == (si.flags ^ kP)) {
   1753         // Premultiply / Unpremultiply: 32-bit format where the alpha is either first or last.
   1754         if (depth == 32) {
   1755           // If we can do any alpha index it's okay, but generally prefer only
   1756           // AlphaFirst|AlphaLast - other layouts are very unlikely to be used.
   1757           if (di.flags & kP)
   1758             BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_premultiply_8888(self, di, si));
   1759           else
   1760             BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_unpremultiply8888(self, di, si));
   1761         }
   1762       }
   1763     }
   1764     else if (depth == 32 && bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_RGB | BL_FORMAT_FLAG_BYTE_ALIGNED)) {
   1765       // Copy:
   1766       //   PRGB32 <- XRGB32 - Copy with or-mask.
   1767       //   ARGB32 <- XRGB32 - Copy with or-mask.
   1768       //   XRGB32 <- PRGB32 - Copy with or-mask.
   1769       if ((!(di.flags & kA) && (si.flags & kP)) || (!(si.flags & kA) && (di.flags & kA)))
   1770         return bl_pixel_converter_init_copy_8888_with_fill_mask(self, di, si);
   1771 
   1772       // Premultiply:
   1773       //   XRGB32 <- ARGB32 - Premultiply with or-mask.
   1774       if (!(di.flags & kA) && (si.flags & kA))
   1775         return bl_pixel_converter_init_premultiply_8888(self, di, si);
   1776     }
   1777   }
   1778   else {
   1779 #ifdef BL_BUILD_OPT_SSSE3
   1780     if (bl_runtime_has_ssse3(&bl_runtime_context)) {
   1781       if (depth == 32 && bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_RGB | BL_FORMAT_FLAG_BYTE_ALIGNED)) {
   1782         // Handle the following conversions (PSHUFB|OR):
   1783         //   XRGB32 <- XRGB32 - Shuffle with or-mask
   1784         //   ARGB32 <- XRGB32 - Shuffle with or-mask (opaque alpha)
   1785         //   PRGB32 <- XRGB32 - Shuffle with or-mask (opaque alpha)
   1786         //   ARGB32 <- ARGB32 - Shuffle
   1787         //   XRGB32 <- PRGB32 - Shuffle with or-mask (no unpremultiply)
   1788         //   PRGB32 <- PRGB32 - Shuffle
   1789         bool same_alpha = (di.flags & (kA | kP)) == (si.flags & (kA | kP));
   1790         bool dst_alpha = (di.flags & kA) != 0;
   1791         bool src_alpha = (si.flags & kA) != 0;
   1792 
   1793         if (same_alpha || !src_alpha || (!dst_alpha && bl::IntOps::bit_match(si.flags, kP))) {
   1794           BLPixelConverterData::ShufbData& d = bl_pixel_converter_get_data(self)->shufb_data;
   1795           bl_pixel_converter_calc_pshufb_predicate_32_from_32(d.shufb_predicate, di, si);
   1796 
   1797           if (!(di.flags & kA))
   1798             d.fill_mask = bl_pixel_converter_calc_fill_mask32(di);
   1799           else if (!(si.flags & kA))
   1800             d.fill_mask = 0xFFu << di.shifts[3];
   1801 
   1802 #ifdef BL_BUILD_OPT_AVX2
   1803           if (bl_runtime_has_avx2(&bl_runtime_context))
   1804             return bl_pixel_converter_init_func_opt(self, bl_convert_copy_shufb_8888_avx2);
   1805 #endif
   1806 
   1807           return bl_pixel_converter_init_func_opt(self, bl_convert_copy_shufb_8888_ssse3);
   1808         }
   1809 
   1810         // Handle the following conversions (Premultiply|Shufb)
   1811         //   PRGB32 <- ARGB32 - Shuffle with premultiply
   1812         //   XRGB32 <- ARGB32 - Shuffle with premultiply
   1813         if (((di.flags & kP) || !(di.flags & kA)) && (si.flags & (kA | kP)) == kA) {
   1814           uint32_t a_shift = di.shifts[3];
   1815 
   1816           BLPixelConverterData::ShufbData& d = bl_pixel_converter_get_data(self)->shufb_data;
   1817           bl_pixel_converter_calc_pshufb_predicate_32_from_32(d.shufb_predicate, di, si);
   1818 
   1819 #ifdef BL_BUILD_OPT_AVX2
   1820           if (bl_runtime_has_avx2(&bl_runtime_context)) {
   1821             if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_trailing_alpha_shufb_avx2);
   1822             if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_leading_alpha_shufb_avx2);
   1823           }
   1824 #endif
   1825 
   1826           if (a_shift == 0) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_trailing_alpha_shufb_ssse3);
   1827           if (a_shift == 24) return bl_pixel_converter_init_func_opt(self, bl_convert_premultiply_8888_leading_alpha_shufb_ssse3);
   1828         }
   1829       }
   1830     }
   1831 #endif
   1832   }
   1833 
   1834   return BL_RESULT_NOTHING;
   1835 }
   1836 
   1837 // bl::PixelConverter - Init - 8 From 8888
   1838 // =======================================
   1839 
   1840 static BLResult bl_pixel_converter_init_8_from_8888(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   1841   bl_unused(create_flags);
   1842   BLPixelConverterData::X8FromRgb32Data& d = bl_pixel_converter_get_data(self)->x8FromRgb32Data;
   1843 
   1844   uint32_t common_flags = di.flags & si.flags;
   1845   if (bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_ALPHA | BL_FORMAT_FLAG_BYTE_ALIGNED)) {
   1846     d.bytes_per_pixel = uint8_t(si.depth / 8u);
   1847     d.alpha_shift = uint8_t(si.shifts[3]);
   1848     return bl_pixel_converter_init_func_generic(self, bl_convert_a8_from_8888);
   1849   }
   1850 
   1851   return BL_RESULT_NOTHING;
   1852 }
   1853 
   1854 // bl::PixelConverter - Init - 8888 From 8
   1855 // =======================================
   1856 
   1857 static BLResult bl_pixel_converter_init_8888_from_8(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   1858   bl_unused(create_flags);
   1859   uint32_t rgb_mask = bl_pixel_converter_calc_rgb_mask32(di);
   1860 
   1861   BLPixelConverterData::Rgb32FromX8Data& d = bl_pixel_converter_get_data(self)->rgb32FromX8Data;
   1862   d.zero_mask = 0xFFFFFFFFu;
   1863 
   1864   if (!(si.flags & BL_FORMAT_FLAG_ALPHA)) {
   1865     // ?RGB32 <- L8.
   1866     d.fill_mask = ~rgb_mask;
   1867   }
   1868   else if (bl::IntOps::bit_match(di.flags, BL_FORMAT_FLAG_ALPHA | BL_FORMAT_FLAG_PREMULTIPLIED)) {
   1869     // PRGB32 <- A8 - RGB channels are set to A, alpha channel is kept.
   1870   }
   1871   else if (bl::IntOps::bit_match(di.flags, BL_FORMAT_FLAG_ALPHA)) {
   1872     // ARGB32 <- A8 - RGB channels are set to 255, alpha channel is kept.
   1873     d.fill_mask = rgb_mask;
   1874   }
   1875   else {
   1876     // XRGB32 <- A8 - RGB channels are set to A, alpha channel is set to 255.
   1877     d.fill_mask = ~rgb_mask;
   1878   }
   1879 
   1880 #ifdef BL_BUILD_OPT_SSE2
   1881   if (bl_runtime_has_sse2(&bl_runtime_context))
   1882     return bl_pixel_converter_init_func_opt(self, bl_convert_8888_from_x8_sse2);
   1883 #endif
   1884 
   1885   return bl_pixel_converter_init_func_generic(self, bl_convert_8888_from_x8);
   1886 }
   1887 
   1888 // bl::PixelConverter - Init - 8888 From 888
   1889 // =========================================
   1890 
   1891 static BLResult bl_pixel_converter_init_8888_from_888(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   1892   bl_unused(self, create_flags);
   1893 
   1894   uint32_t common_flags = di.flags & si.flags;
   1895 
   1896   // Maybe unused if there are no optimizations.
   1897   bl_unused(common_flags);
   1898 
   1899   // This is only possible with SSSE3 and AVX2 enabled converters.
   1900 #ifdef BL_BUILD_OPT_SSSE3
   1901   if (bl_runtime_has_ssse3(&bl_runtime_context)) {
   1902     // We expect both formats to provide RGB components and to be BYTE aligned.
   1903     if (!(common_flags & BL_FORMAT_FLAG_RGB))
   1904       return BL_RESULT_NOTHING;
   1905 
   1906     BLPixelConverterData::ShufbData& d = bl_pixel_converter_get_data(self)->shufb_data;
   1907     d.fill_mask = ~bl_pixel_converter_calc_rgb_mask32(di);
   1908     bl_pixel_converter_calc_pshufb_predicate_32_from_24(d.shufb_predicate, di, si);
   1909 
   1910 #ifdef BL_BUILD_OPT_AVX2
   1911     if (bl_runtime_has_avx2(&bl_runtime_context))
   1912       return bl_pixel_converter_init_func_opt(self, bl_convert_rgb32_from_rgb24_shufb_avx2);
   1913 #endif
   1914 
   1915     return bl_pixel_converter_init_func_opt(self, bl_convert_rgb32_from_rgb24_shufb_ssse3);
   1916   }
   1917 #endif
   1918 
   1919   return BL_RESULT_NOTHING;
   1920 }
   1921 
   1922 // bl::PixelConverter - Init - NativeFromForeign
   1923 // =============================================
   1924 
   1925 static BLResult bl_pixel_converter_init_8888_from_foreign(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   1926   bl_unused(create_flags);
   1927 
   1928   BL_ASSERT(di.depth == 32);
   1929   BL_ASSERT(di.flags & BL_FORMAT_FLAG_BYTE_ALIGNED);
   1930 
   1931   if (di.r_shift != 16 || di.g_shift != 8 || di.b_shift != 0)
   1932     return BL_RESULT_NOTHING;
   1933 
   1934   BLPixelConverterData::NativeFromForeign& d = bl_pixel_converter_get_data(self)->native_from_foreign;
   1935 
   1936   bool isSrcRGBA           = (si.flags & BL_FORMAT_FLAG_ALPHA) != 0;
   1937   bool is_src_premultiplied  = (si.flags & BL_FORMAT_FLAG_PREMULTIPLIED) != 0;
   1938   bool hasSrcHostBO        = (si.flags & BL_FORMAT_FLAG_BYTE_SWAP) == 0;
   1939 
   1940   if (!isSrcRGBA)
   1941     d.fill_mask = 0xFF000000u;
   1942 
   1943   for (uint32_t i = 0; i < 4; i++) {
   1944     uint32_t size = si.sizes[i];
   1945     uint32_t shift = si.shifts[i];
   1946 
   1947     d.masks[i] = 0;
   1948     d.shifts[i] = uint8_t(shift);
   1949     d.scale[i] = 0;
   1950 
   1951     if (size == 0)
   1952       continue;
   1953 
   1954     // Discard all bits that are below 8 most significant ones.
   1955     if (size > 8) {
   1956       shift += (size - 8);
   1957       size = 8;
   1958     }
   1959 
   1960     d.masks[i] = bl::IntOps::non_zero_lsb_mask<uint32_t>(size);
   1961     d.shifts[i] = uint8_t(shift);
   1962 
   1963     // Calculate a scale constant that will be used to expand bits in case that the source contains less than 8 bits.
   1964     // We do it by adding `size`  to the `scaled_size` until we reach the required bit-depth.
   1965     uint32_t scale = 0x1;
   1966     uint32_t scaled_size = size;
   1967 
   1968     while (scaled_size < 8) {
   1969       scale = (scale << size) | 1;
   1970       scaled_size += size;
   1971     }
   1972 
   1973     // Shift scale in a way that it contains MSB of the mask and the right position.
   1974     uint32_t scaled_shift = bl_pixel_converter_native32_from_foreign_shift_table[i] - (scaled_size - 8);
   1975     scale <<= scaled_shift;
   1976     d.scale[i] = scale;
   1977   }
   1978 
   1979   // Special case of converting LUM to RGB.
   1980   if (si.flags & BL_FORMAT_FLAG_LUM) {
   1981     // TODO:
   1982   }
   1983 
   1984   // Generic conversion.
   1985   BLPixelConverterFunc func = nullptr;
   1986   switch (si.depth) {
   1987     case 16:
   1988       // TODO:
   1989       if (is_src_premultiplied)
   1990         func = hasSrcHostBO ? bl_convert_prgb32_from_prgb_any<BLPixelAccess16<BL_BYTE_ORDER_NATIVE >, bl::MemOps::kUnalignedMem16>
   1991                             : bl_convert_prgb32_from_prgb_any<BLPixelAccess16<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem16>;
   1992       else if (isSrcRGBA)
   1993         func = hasSrcHostBO ? bl_convert_prgb32_from_argb_any<BLPixelAccess16<BL_BYTE_ORDER_NATIVE >, bl::MemOps::kUnalignedMem16>
   1994                             : bl_convert_prgb32_from_argb_any<BLPixelAccess16<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem16>;
   1995       else
   1996         func = hasSrcHostBO ? bl_convert_xrgb32_from_xrgb_any<BLPixelAccess16<BL_BYTE_ORDER_NATIVE >, bl::MemOps::kUnalignedMem16>
   1997                             : bl_convert_xrgb32_from_xrgb_any<BLPixelAccess16<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem16>;
   1998       break;
   1999 
   2000     case 24:
   2001       if (is_src_premultiplied)
   2002         func = hasSrcHostBO ? bl_convert_prgb32_from_prgb_any<BLPixelAccess24<BL_BYTE_ORDER_NATIVE >, true>
   2003                             : bl_convert_prgb32_from_prgb_any<BLPixelAccess24<BL_BYTE_ORDER_SWAPPED>, true>;
   2004       else if (isSrcRGBA)
   2005         func = hasSrcHostBO ? bl_convert_prgb32_from_argb_any<BLPixelAccess24<BL_BYTE_ORDER_NATIVE >, true>
   2006                             : bl_convert_prgb32_from_argb_any<BLPixelAccess24<BL_BYTE_ORDER_SWAPPED>, true>;
   2007       else
   2008         func = hasSrcHostBO ? bl_convert_xrgb32_from_xrgb_any<BLPixelAccess24<BL_BYTE_ORDER_NATIVE >, true>
   2009                             : bl_convert_xrgb32_from_xrgb_any<BLPixelAccess24<BL_BYTE_ORDER_SWAPPED>, true>;
   2010       break;
   2011 
   2012     case 32:
   2013       if (is_src_premultiplied)
   2014         func = hasSrcHostBO ? bl_convert_prgb32_from_prgb_any<BLPixelAccess32<BL_BYTE_ORDER_NATIVE >, bl::MemOps::kUnalignedMem32>
   2015                             : bl_convert_prgb32_from_prgb_any<BLPixelAccess32<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem32>;
   2016       else if (isSrcRGBA)
   2017         func = hasSrcHostBO ? bl_convert_prgb32_from_argb_any<BLPixelAccess32<BL_BYTE_ORDER_NATIVE >, bl::MemOps::kUnalignedMem32>
   2018                             : bl_convert_prgb32_from_argb_any<BLPixelAccess32<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem32>;
   2019       else
   2020         func = hasSrcHostBO ? bl_convert_xrgb32_from_xrgb_any<BLPixelAccess32<BL_BYTE_ORDER_NATIVE >, bl::MemOps::kUnalignedMem32>
   2021                             : bl_convert_xrgb32_from_xrgb_any<BLPixelAccess32<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem32>;
   2022       break;
   2023 
   2024     default:
   2025       return bl_make_error(BL_ERROR_INVALID_VALUE);
   2026   }
   2027 
   2028   return bl_pixel_converter_init_func_generic(self, func);
   2029 }
   2030 
   2031 // bl::PixelConverter - Init - ForeignFromNative
   2032 // =============================================
   2033 
   2034 static BLResult bl_pixel_converter_init_foreign_from_8888(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   2035   bl_unused(si, create_flags);
   2036 
   2037   BL_ASSERT(si.depth == 32);
   2038   BL_ASSERT(si.flags & BL_FORMAT_FLAG_BYTE_ALIGNED);
   2039 
   2040   if (si.r_shift != 16 || si.g_shift != 8 || si.b_shift != 0)
   2041     return BL_RESULT_NOTHING;
   2042 
   2043   if (di.flags & BL_FORMAT_FLAG_INDEXED) {
   2044     // TODO:
   2045     return bl_make_error(BL_ERROR_NOT_IMPLEMENTED);
   2046   }
   2047   else {
   2048     BLPixelConverterData::ForeignFromNative& d = bl_pixel_converter_get_data(self)->foreign_from_native;
   2049 
   2050     bool isDstRGBA          = (di.flags & BL_FORMAT_FLAG_ALPHA) != 0;
   2051     bool is_dst_premultiplied = (di.flags & BL_FORMAT_FLAG_PREMULTIPLIED) != 0;
   2052     bool hasDstHostBO       = (di.flags & BL_FORMAT_FLAG_BYTE_SWAP) == 0;
   2053 
   2054     for (uint32_t i = 0; i < 4; i++) {
   2055       uint32_t mask = 0;
   2056       uint32_t size = di.sizes[i];
   2057       uint32_t shift = di.shifts[i];
   2058 
   2059       if (size != 0) {
   2060         mask = bl::IntOps::non_zero_lsb_mask<uint32_t>(size) << shift;
   2061         shift = 32 - size - shift;
   2062       }
   2063 
   2064       d.masks[i] = mask;
   2065       d.shifts[i] = uint8_t(shift);
   2066     }
   2067 
   2068     BLPixelConverterFunc func = nullptr;
   2069     switch (di.depth) {
   2070       case 16:
   2071         if (is_dst_premultiplied)
   2072           func = hasDstHostBO ? bl_convert_prgb_any_from_prgb32<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>, bl::MemOps::kUnalignedMem16>
   2073                               : bl_convert_prgb_any_from_prgb32<BLPixelAccess16<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem16>;
   2074         else if (isDstRGBA)
   2075           func = hasDstHostBO ? bl_convert_argb_any_from_prgb32<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>, bl::MemOps::kUnalignedMem16>
   2076                               : bl_convert_argb_any_from_prgb32<BLPixelAccess16<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem16>;
   2077         else
   2078           func = hasDstHostBO ? bl_convert_xrgb_any_from_xrgb32<BLPixelAccess16<BL_BYTE_ORDER_NATIVE>, bl::MemOps::kUnalignedMem16>
   2079                               : bl_convert_xrgb_any_from_xrgb32<BLPixelAccess16<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem16>;
   2080         break;
   2081 
   2082       case 24:
   2083         if (is_dst_premultiplied)
   2084           func = hasDstHostBO ? bl_convert_prgb_any_from_prgb32<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>, true>
   2085                               : bl_convert_prgb_any_from_prgb32<BLPixelAccess24<BL_BYTE_ORDER_SWAPPED>, true>;
   2086         else if (isDstRGBA)
   2087           func = hasDstHostBO ? bl_convert_argb_any_from_prgb32<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>, true>
   2088                               : bl_convert_argb_any_from_prgb32<BLPixelAccess24<BL_BYTE_ORDER_SWAPPED>, true>;
   2089         else
   2090           func = hasDstHostBO ? bl_convert_xrgb_any_from_xrgb32<BLPixelAccess24<BL_BYTE_ORDER_NATIVE>, true>
   2091                               : bl_convert_xrgb_any_from_xrgb32<BLPixelAccess24<BL_BYTE_ORDER_SWAPPED>, true>;
   2092         break;
   2093 
   2094       case 32:
   2095         if (is_dst_premultiplied)
   2096           func = hasDstHostBO ? bl_convert_prgb_any_from_prgb32<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>, bl::MemOps::kUnalignedMem32>
   2097                               : bl_convert_prgb_any_from_prgb32<BLPixelAccess32<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem32>;
   2098         else if (isDstRGBA)
   2099           func = hasDstHostBO ? bl_convert_argb_any_from_prgb32<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>, bl::MemOps::kUnalignedMem32>
   2100                               : bl_convert_argb_any_from_prgb32<BLPixelAccess32<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem32>;
   2101         else
   2102           func = hasDstHostBO ? bl_convert_xrgb_any_from_xrgb32<BLPixelAccess32<BL_BYTE_ORDER_NATIVE>, bl::MemOps::kUnalignedMem32>
   2103                               : bl_convert_xrgb_any_from_xrgb32<BLPixelAccess32<BL_BYTE_ORDER_SWAPPED>, bl::MemOps::kUnalignedMem32>;
   2104         break;
   2105 
   2106       default:
   2107         return bl_make_error(BL_ERROR_INVALID_VALUE);
   2108     }
   2109 
   2110     return bl_pixel_converter_init_func_generic(self, func);
   2111   }
   2112 }
   2113 
   2114 // bl::PixelConverter - Init - Multi-Step
   2115 // ======================================
   2116 
   2117 static BLResult BL_CDECL bl_convert_multi_step(
   2118   const BLPixelConverterCore* self,
   2119   uint8_t* dst_data, intptr_t dst_stride,
   2120   const uint8_t* src_data, intptr_t src_stride, uint32_t w, uint32_t h, const BLPixelConverterOptions* options) noexcept {
   2121 
   2122   const BLPixelConverterData::MultiStepData& d = bl_pixel_converter_get_data(self)->multi_step_data;
   2123   uint32_t intermediate_pixel_count = d.intermediate_pixel_count;
   2124 
   2125   // NOTE: We use uintptr_t so the buffer gets properly aligned. In general we don't need a higher alignment than
   2126   // 32-bit or 64-bit depending on the target.
   2127   uintptr_t intermediate_storage[BL_PIXEL_CONVERTER_MULTISTEP_BUFFER_SIZE / sizeof(uintptr_t)];
   2128   uint8_t* intermediate_data = reinterpret_cast<uint8_t*>(intermediate_storage);
   2129 
   2130   const BLPixelConverterMultiStepContext* ctx = d.ctx;
   2131   BLPixelConverterFunc src_to_intermediate = ctx->first.convert_func;
   2132   BLPixelConverterFunc intermediate_to_dst = ctx->second.convert_func;
   2133 
   2134   if (!options)
   2135     options = &bl_pixel_converter_default_options;
   2136   BLPixelConverterOptions work_opt = *options;
   2137 
   2138   if (w > intermediate_pixel_count) {
   2139     // Process part of the scanline at a time.
   2140     uint8_t* dst_line = dst_data;
   2141     const uint8_t* src_line = src_data;
   2142 
   2143     int base_origin_x = work_opt.origin.x;
   2144     uint32_t dst_bytes_per_pixel = d.dst_bytes_per_pixel;
   2145     uint32_t src_bytes_per_pixel = d.src_bytes_per_pixel;
   2146 
   2147     for (uint32_t y = h; y; y--) {
   2148       uint32_t i = w;
   2149 
   2150       work_opt.origin.x = base_origin_x;
   2151       dst_data = dst_line;
   2152       src_data = src_line;
   2153 
   2154       while (i) {
   2155         uint32_t n = bl_min(i, intermediate_pixel_count);
   2156 
   2157         src_to_intermediate(&ctx->first, intermediate_data, 0, src_data, src_stride, n, 1, nullptr);
   2158         intermediate_to_dst(&ctx->second, dst_data, dst_stride, intermediate_data, 0, n, 1, &work_opt);
   2159 
   2160         dst_data += n * dst_bytes_per_pixel;
   2161         src_data += n * src_bytes_per_pixel;
   2162         work_opt.origin.x += int(n);
   2163 
   2164         i -= n;
   2165       }
   2166 
   2167       dst_line += dst_stride;
   2168       src_line += src_stride;
   2169       work_opt.origin.y++;
   2170     }
   2171 
   2172     return BL_SUCCESS;
   2173   }
   2174   else if (h > intermediate_pixel_count || w * h > intermediate_pixel_count) {
   2175     // Process at least one scanline at a time.
   2176     for (uint32_t y = h; y; y--) {
   2177       src_to_intermediate(&ctx->first, intermediate_data, 0, src_data, src_stride, w, 1, nullptr);
   2178       intermediate_to_dst(&ctx->second, dst_data, dst_stride, intermediate_data, 0, w, 1, &work_opt);
   2179 
   2180       dst_data += dst_stride;
   2181       src_data += src_stride;
   2182       work_opt.origin.y++;
   2183     }
   2184 
   2185     return BL_SUCCESS;
   2186   }
   2187   else {
   2188     // Process all scanlines as the `intermediate_buffer` is large enough.
   2189     intptr_t intermediate_stride = intptr_t(w) * d.intermediate_bytes_per_pixel;
   2190     src_to_intermediate(&ctx->first, intermediate_data, intermediate_stride, src_data, src_stride, w, h, nullptr);
   2191     return intermediate_to_dst(&ctx->second, dst_data, dst_stride, intermediate_data, intermediate_stride, w, h, &work_opt);
   2192   }
   2193 }
   2194 
   2195 static BLResult bl_pixel_converter_init_multi_step_internal(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& intermediate, const BLFormatInfo& si) noexcept {
   2196   BLPixelConverterMultiStepContext* ctx =
   2197     static_cast<BLPixelConverterMultiStepContext*>(malloc(sizeof(BLPixelConverterMultiStepContext)));
   2198 
   2199   if (BL_UNLIKELY(!ctx))
   2200     return bl_make_error(BL_ERROR_OUT_OF_MEMORY);
   2201 
   2202   BLResult result;
   2203   BLPixelConverterCreateFlags custom_flags = BL_PIXEL_CONVERTER_CREATE_FLAG_NO_MULTI_STEP;
   2204 
   2205   memset(ctx, 0, sizeof(*ctx));
   2206   if ((result = bl_pixel_converter_init_internal(&ctx->first, intermediate, si, custom_flags)) != BL_SUCCESS ||
   2207       (result = bl_pixel_converter_init_internal(&ctx->first, di, intermediate, custom_flags)) != BL_SUCCESS) {
   2208     bl_pixel_converter_reset(&ctx->first);
   2209     bl_pixel_converter_reset(&ctx->second);
   2210     free(ctx);
   2211     return result;
   2212   }
   2213 
   2214   BLPixelConverterData::MultiStepData& d = bl_pixel_converter_get_data(self)->multi_step_data;
   2215   d.dst_bytes_per_pixel = uint8_t(di.depth / 8u);
   2216   d.src_bytes_per_pixel = uint8_t(si.depth / 8u);
   2217   d.intermediate_bytes_per_pixel = uint8_t(intermediate.depth / 8u);
   2218   d.intermediate_pixel_count = BL_PIXEL_CONVERTER_MULTISTEP_BUFFER_SIZE / d.intermediate_bytes_per_pixel;
   2219 
   2220   ctx->ref_count = 1;
   2221   d.ref_count = (size_t*)&ctx->ref_count;
   2222   d.ctx = ctx;
   2223 
   2224   uint32_t internal_flags = BL_PIXEL_CONVERTER_INTERNAL_FLAG_MULTI_STEP |
   2225                            BL_PIXEL_CONVERTER_INTERNAL_FLAG_DYNAMIC_DATA;
   2226   return bl_pixel_converter_init_func_generic(self, bl_convert_multi_step, internal_flags);
   2227 }
   2228 
   2229 static BLResult bl_pixel_converter_init_multi_step(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   2230   bl_unused(create_flags);
   2231 
   2232   // We have foreign pixel formats on both input and output. This means that we will create two converters and
   2233   // convert through a native pixel format as otherwise it would not be possible to convert the pixels by using
   2234   // built-in converters.
   2235 
   2236   const uint32_t kA = BL_FORMAT_FLAG_ALPHA;
   2237   const uint32_t kP = BL_FORMAT_FLAG_PREMULTIPLIED;
   2238 
   2239   uint32_t common_flags = di.flags & si.flags;
   2240   if (common_flags & BL_FORMAT_FLAG_RGB) {
   2241     // Temporary format information.
   2242     BLFormatInfo intermediate = bl_format_info[BL_FORMAT_PRGB32];
   2243     if ((di.flags & (kA | kP)) == kA)
   2244       intermediate.clear_flags(BL_FORMAT_FLAG_PREMULTIPLIED);
   2245     if (!(di.flags & kA) || !(si.flags & kA))
   2246       intermediate = bl_format_info[BL_FORMAT_XRGB32];
   2247     return bl_pixel_converter_init_multi_step_internal(self, di, intermediate, si);
   2248   }
   2249 
   2250   return BL_RESULT_NOTHING;
   2251 }
   2252 
   2253 // bl::PixelConverter - Init - Internal
   2254 // ====================================
   2255 
   2256 BLResult bl_pixel_converter_init_internal(BLPixelConverterCore* self, const BLFormatInfo& di, const BLFormatInfo& si, BLPixelConverterCreateFlags create_flags) noexcept {
   2257   uint32_t common_flags = di.flags & si.flags;
   2258   // Convert - Indexed destination is not supported.
   2259   if (di.flags & BL_FORMAT_FLAG_INDEXED)
   2260     return bl_make_error(BL_ERROR_NOT_IMPLEMENTED);
   2261 
   2262   // Convert - Any from Indexed.
   2263   if (si.flags & BL_FORMAT_FLAG_INDEXED)
   2264     return bl_pixel_converter_init_indexed(self, di, si, create_flags);
   2265 
   2266   // Convert - MemCopy | Native | ShufB | Premultiply | Unpremultiply.
   2267   if (di.depth == si.depth)
   2268     BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_simple(self, di, si, create_flags));
   2269 
   2270   if (di.depth == 8 && si.depth == 32) {
   2271     // Convert - A8 <- ARGB32|PRGB32.
   2272     if (bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_ALPHA | BL_FORMAT_FLAG_BYTE_ALIGNED))
   2273       BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_8_from_8888(self, di, si, create_flags));
   2274   }
   2275 
   2276   // Convert - ?RGB32 <- A8|L8.
   2277   if (di.depth == 32 && si.depth == 8) {
   2278     if (bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_BYTE_ALIGNED) && (di.flags & BL_FORMAT_FLAG_RGB) != 0)
   2279       BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_8888_from_8(self, di, si, create_flags));
   2280   }
   2281 
   2282   // Convert - ?RGB32 <- RGB24.
   2283   if (di.depth == 32 && si.depth == 24) {
   2284     if (bl::IntOps::bit_match(common_flags, BL_FORMAT_FLAG_BYTE_ALIGNED | BL_FORMAT_FLAG_RGB))
   2285       BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_8888_from_888(self, di, si, create_flags));
   2286   }
   2287 
   2288   // Convert - ?RGB32 <- Foreign.
   2289   if (di.depth == 32 && bl::IntOps::bit_match(di.flags, BL_FORMAT_FLAG_BYTE_ALIGNED))
   2290     BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_8888_from_foreign(self, di, si, create_flags));
   2291 
   2292   // Convert - Foreign <- ?RGB32.
   2293   if (si.depth == 32 && bl::IntOps::bit_match(si.flags, BL_FORMAT_FLAG_BYTE_ALIGNED))
   2294     BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_foreign_from_8888(self, di, si, create_flags));
   2295 
   2296   // Convert - Foreign <- Foreign.
   2297   if (!(create_flags & BL_PIXEL_CONVERTER_CREATE_FLAG_NO_MULTI_STEP))
   2298     BL_PROPAGATE_IF_NOT_NOTHING(bl_pixel_converter_init_multi_step(self, di, si, create_flags));
   2299 
   2300   // Probably extreme case that is not implemented.
   2301   return bl_make_error(BL_ERROR_NOT_IMPLEMENTED);
   2302 }