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 }