From e74a0eff164dafd6bfe7360974696383c97afecd Mon Sep 17 00:00:00 2001 From: ScorpionInc Date: Mon, 3 Apr 2023 19:17:37 -0400 Subject: [PATCH] Adds some basic 16BPP BMP Support. This commit adds some basic 16bpp support for BMP File Format. Added support for reading and using of 16 bit mask values from file. All values are scaled to ARGB255 format based on bit depth of source color channel. Removed warning, it's no longer required as 16 bit with alpha bit(s) are now supported. Adjusted spacing, added spacing and brackets to make clang static check happy. --- modules/bmp/image_loader_bmp.cpp | 64 +++++++++++++++++++++++++++++--- modules/bmp/image_loader_bmp.h | 19 ++++++++++ 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/modules/bmp/image_loader_bmp.cpp b/modules/bmp/image_loader_bmp.cpp index 5b451fbf6b8..56052553672 100644 --- a/modules/bmp/image_loader_bmp.cpp +++ b/modules/bmp/image_loader_bmp.cpp @@ -32,6 +32,16 @@ #include "core/io/file_access_memory.h" +static uint8_t get_mask_width(uint16_t mask) { + // Returns number of ones in the binary value of the parameter: mask. + // Uses a Simple pop_count. + uint8_t c = 0u; + for (; mask != 0u; mask &= mask - 1u) { + c++; + } + return c; +} + Error ImageLoaderBMP::convert_to_image(Ref p_image, const uint8_t *p_buffer, const uint8_t *p_color_buffer, @@ -71,9 +81,6 @@ Error ImageLoaderBMP::convert_to_image(Ref p_image, vformat("4-bpp BMP images must have a width that is a multiple of 2, but the imported BMP is %d pixels wide.", int(width))); ERR_FAIL_COND_V_MSG(height % 2 != 0, ERR_UNAVAILABLE, vformat("4-bpp BMP images must have a height that is a multiple of 2, but the imported BMP is %d pixels tall.", int(height))); - - } else if (bits_per_pixel == 16) { - ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "16-bpp BMP images are not supported."); } // Image data (might be indexed) @@ -96,7 +103,7 @@ Error ImageLoaderBMP::convert_to_image(Ref p_image, // The actual data traversal is determined by // the data width in case of 8/4/2/1 bit images - const uint32_t w = bits_per_pixel >= 24 ? width : width_bytes; + const uint32_t w = bits_per_pixel >= 16 ? width : width_bytes; const uint8_t *line = p_buffer + (line_width * (height - 1)); const uint8_t *end_buffer = p_buffer + p_header.bmp_file_header.bmp_file_size - p_header.bmp_file_header.bmp_file_offset; @@ -149,6 +156,34 @@ Error ImageLoaderBMP::convert_to_image(Ref p_image, index += 1; line_ptr += 1; } break; + case 16: { + uint16_t rgb = (static_cast(line_ptr[1]) << 8) | line_ptr[0]; + // A1R5G5B5/X1R5G5B5 => uint16_t + // [A/X]1R5G2 | G3B5 => uint8_t | uint8_t + uint8_t ba = (rgb & p_header.bmp_bitfield.alpha_mask) >> p_header.bmp_bitfield.alpha_offset; // Alpha 0b 1000 ... + uint8_t b0 = (rgb & p_header.bmp_bitfield.red_mask) >> p_header.bmp_bitfield.red_offset; // Red 0b 0111 1100 ... + uint8_t b1 = (rgb & p_header.bmp_bitfield.green_mask) >> p_header.bmp_bitfield.green_offset; // Green 0b 0000 0011 1110 ... + uint8_t b2 = (rgb & p_header.bmp_bitfield.blue_mask); // >> p_header.bmp_bitfield.blue_offset; // Blue 0b ... 0001 1111 + + // Next we apply some color scaling going from a variable value space to a 256 value space. + // This may be simplified some but left as is for legibility. + // float scaled_value = unscaled_value * byte_max_value / color_channel_maxium_value + rounding_offset; + float f0 = b0 * 255.0f / static_cast(p_header.bmp_bitfield.red_max) + 0.5f; + float f1 = b1 * 255.0f / static_cast(p_header.bmp_bitfield.green_max) + 0.5f; + float f2 = b2 * 255.0f / static_cast(p_header.bmp_bitfield.blue_max) + 0.5f; + write_buffer[index + 0] = static_cast(f0); // R + write_buffer[index + 1] = static_cast(f1); // G + write_buffer[index + 2] = static_cast(f2); // B + + if (p_header.bmp_bitfield.alpha_mask_width > 0) { + write_buffer[index + 3] = ba * 0xFF; // Alpha value(Always true or false so no scaling) + } else { + write_buffer[index + 3] = 0xFF; // No Alpha channel, Show everything. + } + + index += 4; + line_ptr += 2; + } break; case 24: { write_buffer[index + 2] = line_ptr[0]; write_buffer[index + 1] = line_ptr[1]; @@ -253,13 +288,32 @@ Error ImageLoaderBMP::load_image(Ref p_image, Ref f, BitField bmp_header.bmp_info_header.bmp_important_colors = f->get_32(); switch (bmp_header.bmp_info_header.bmp_compression) { + case BI_BITFIELDS: { + bmp_header.bmp_bitfield.red_mask = f->get_32(); + bmp_header.bmp_bitfield.green_mask = f->get_32(); + bmp_header.bmp_bitfield.blue_mask = f->get_32(); + bmp_header.bmp_bitfield.alpha_mask = f->get_32(); + + bmp_header.bmp_bitfield.red_mask_width = get_mask_width(bmp_header.bmp_bitfield.red_mask); + bmp_header.bmp_bitfield.green_mask_width = get_mask_width(bmp_header.bmp_bitfield.green_mask); + bmp_header.bmp_bitfield.blue_mask_width = get_mask_width(bmp_header.bmp_bitfield.blue_mask); + bmp_header.bmp_bitfield.alpha_mask_width = get_mask_width(bmp_header.bmp_bitfield.alpha_mask); + + bmp_header.bmp_bitfield.alpha_offset = bmp_header.bmp_bitfield.red_mask_width + bmp_header.bmp_bitfield.green_mask_width + bmp_header.bmp_bitfield.blue_mask_width; + bmp_header.bmp_bitfield.red_offset = bmp_header.bmp_bitfield.green_mask_width + bmp_header.bmp_bitfield.blue_mask_width; + bmp_header.bmp_bitfield.green_offset = bmp_header.bmp_bitfield.blue_mask_width; + + bmp_header.bmp_bitfield.red_max = (1 << bmp_header.bmp_bitfield.red_mask_width) - 1; + bmp_header.bmp_bitfield.green_max = (1 << bmp_header.bmp_bitfield.green_mask_width) - 1; + bmp_header.bmp_bitfield.blue_max = (1 << bmp_header.bmp_bitfield.blue_mask_width) - 1; + } break; case BI_RLE8: case BI_RLE4: case BI_CMYKRLE8: case BI_CMYKRLE4: { // Stop parsing. ERR_FAIL_V_MSG(ERR_UNAVAILABLE, - vformat("Compressed BMP files are not supported: %s", f->get_path())); + vformat("RLE compressed BMP files are not yet supported: %s", f->get_path())); } break; } // Don't rely on sizeof(bmp_file_header) as structure padding diff --git a/modules/bmp/image_loader_bmp.h b/modules/bmp/image_loader_bmp.h index 08b9115c87a..cb51d7f0de1 100644 --- a/modules/bmp/image_loader_bmp.h +++ b/modules/bmp/image_loader_bmp.h @@ -74,6 +74,25 @@ protected: uint32_t bmp_colors_used = 0; uint32_t bmp_important_colors = 0; } bmp_info_header; + + struct bmp_bitfield_s { + uint16_t alpha_mask = 0x8000; + uint16_t red_mask = 0x7C00; + uint16_t green_mask = 0x03E0; + uint16_t blue_mask = 0x001F; + uint16_t alpha_mask_width = 1u; + uint16_t red_mask_width = 5u; + uint16_t green_mask_width = 5u; + uint16_t blue_mask_width = 5u; + uint8_t alpha_offset = 15u; // Used for bit shifting. + uint8_t red_offset = 10u; // Used for bit shifting. + uint8_t green_offset = 5u; // Used for bit shifting. + //uint8_t blue_offset = 0u; // Always LSB aligned no shifting needed. + //uint8_t alpha_max = 1u; // Always boolean or on, so no scaling needed. + uint8_t red_max = 32u; // Used for color space scaling. + uint8_t green_max = 32u; // Used for color space scaling. + uint8_t blue_max = 32u; // Used for color space scaling. + } bmp_bitfield; }; static Error convert_to_image(Ref p_image,