#include "icon_fetch.hpp" #include "resize_harris.hpp" #include #include #include #include #include namespace { namespace bmp { const uint16_t g_signature = 'B' bitor (static_cast('M') << 8); struct FileHeader { uint32_t bfSize = 0; uint16_t bfReserved1 = 0; uint16_t bfReserved2 = 0; uint32_t bfOffBits = 0; }; struct ImageHeader { uint32_t biSize = 40; int32_t biWidth = 0; int32_t biHeight = 0; uint16_t biPlanes = 1; uint16_t biBitCount = 24; uint32_t biCompression = 0; uint32_t biSizeImage = 0; uint32_t biXPelsPerMeter = 0; uint32_t biYPelsPerMeter = 0; uint32_t biClrUsed = 0; uint32_t biClrImportant = 0; }; } //namespace bmp [[gnu::pure]] constexpr int scanline_size (int width, int bpp) { return ((width * bpp + 31) bitand ~31) / CHAR_BIT; } [[gnu::pure]] float to_linear (float s) { const float inv1292 = 1.0f / 12.92f; const float inv1055 = 1.0f / 1.055f; if (s <= 0.0404482362771082f) return s * inv1292; else return std::pow((s + 0.055f) * inv1055, 2.4f); } [[gnu::pure]] float to_srgb (float l) { if (l <= 0.00313066844250063f) return l * 12.92f; else return 1.055f * std::pow(l, 1.0f / 2.4f) - 0.055f; } void srgb_to_linear (std::vector& data, int, int) { std::transform(data.begin(), data.end(), data.begin(), &to_linear); } void linear_to_srgb (std::vector& data, int w, int h) { std::transform(data.begin(), data.end(), data.begin(), &to_srgb); } std::vector rgb4_to_float (const std::vector& data, const std::vector& palette, int w, int h) { assert(w * h != 0); std::vector retval(w * h * 3); const int scanl_sz = scanline_size(w, 4); std::vector float_palette; { float_palette.reserve(palette.size()); const float inv255 = 1.0f / 255.0f; for (auto clr : palette) { float_palette.push_back(static_cast(clr) * inv255); } } for (int y = 0; y < h; ++y) { const int row = y * scanl_sz; for (int x = 0; x < w / 2; ++x) { const uint8_t curr = data[row + x]; const int dst_index_a = y * (w * 3) + x * 2 * 3; const int dst_index_b = dst_index_a + 3; const int src_index_b = (curr bitand 0x0f) * 4; const int src_index_a = ((curr bitand 0xf0) >> 4) * 4; retval[dst_index_a + 0] = float_palette[src_index_a + 0]; retval[dst_index_a + 1] = float_palette[src_index_a + 1]; retval[dst_index_a + 2] = float_palette[src_index_a + 2]; retval[dst_index_b + 0] = float_palette[src_index_b + 0]; retval[dst_index_b + 1] = float_palette[src_index_b + 1]; retval[dst_index_b + 2] = float_palette[src_index_b + 2]; } } return retval; } std::vector float_to_rgb24 (const std::vector& data, int w, int h) { const int scanl_sz = scanline_size(w, 24); std::vector retval(scanl_sz * h); assert(retval.size() >= data.size()); assert(data.size() == static_cast(w * h * 3)); for (int y = 0; y < h; ++y) { for (int x = 0; x < w; ++x) { for (int c = 0; c < 3; ++c) { retval[y * scanl_sz + x * 3 + c] = static_cast(static_cast(data[y * w * 3 + x * 3 + c] * 255.0f)); } } } return retval; } } //unnamed namespace std::vector> icon_fetch (const ConstBlock& block, int width, int height) { const constexpr int in_height = 16, in_width = 16; const bool scale = (in_width != width or in_height != height); const auto slot_begin = block.cbegin(); const std::vector& palette = block.palette(); assert(palette.size() == 16 * 4); const int32_t palette_padded_size = (not scale) * (((palette.size() * CHAR_BIT + 31) bitand ~31) / CHAR_BIT); assert(palette.size() == static_cast(palette_padded_size) or scale); const int32_t bpp = (scale ? 24 : 4); const int32_t scan_sz = scanline_size(width, bpp); const std::size_t data_offset = sizeof(bmp::g_signature) + sizeof(bmp::FileHeader) + sizeof(bmp::ImageHeader) + palette_padded_size; const std::size_t bmp_byte_size = data_offset + scan_sz * height; bmp::FileHeader fhead; fhead.bfSize = static_cast(bmp_byte_size); fhead.bfOffBits = static_cast(data_offset); bmp::ImageHeader ihead; ihead.biWidth = width; ihead.biHeight = height; ihead.biBitCount = static_cast(bpp); ihead.biClrUsed = static_cast(palette.size() / 4) * (not scale); ihead.biClrImportant = ihead.biClrUsed; ihead.biSizeImage = scan_sz * height; const auto icon_count = calc_icon_count(block.icon_display_flag()); std::vector> retval; retval.reserve(icon_count); std::vector orig_rgb; for (int32_t icon_num = 0; icon_num < icon_count; ++icon_num) { retval.emplace_back(); auto& frame = retval.back(); frame.reserve(bmp_byte_size); std::copy(reinterpret_cast(&bmp::g_signature), reinterpret_cast(&bmp::g_signature + 1), std::back_inserter(frame)); std::copy(reinterpret_cast(&fhead), reinterpret_cast(&fhead + 1), std::back_inserter(frame)); std::copy(reinterpret_cast(&ihead), reinterpret_cast(&ihead + 1), std::back_inserter(frame)); if (not scale) { std::copy(palette.begin(), palette.end(), std::back_inserter(frame)); assert(static_cast(palette_padded_size) >= palette.size()); std::fill_n(std::back_inserter(frame), palette_padded_size - palette.size(), 0); } const int in_scan_sz = scanline_size(in_width, 4); orig_rgb.resize(in_scan_sz * in_height); for (int y = 0; y < in_height; ++y) { for (int x = 0; x < in_width / 2; ++x) { const uint8_t curr = *(slot_begin + 128 + (in_width / 2 * y) + 128 * icon_num + x); orig_rgb[y * in_scan_sz + x] = ((curr bitand 0xf0) >> 4) bitor ((curr bitand 0x0f) << 4); } } std::vector resized_rgb; if (scale) { std::vector float_data = rgb4_to_float(orig_rgb, palette, in_width, in_height); srgb_to_linear(float_data, in_width, in_height); auto float_resized_rgb = blackmanharris_resize(in_width, in_height, float_data, width, height); linear_to_srgb(float_resized_rgb, width, height); resized_rgb = float_to_rgb24(float_resized_rgb, width, height); } else { std::swap(resized_rgb, orig_rgb); assert(in_scan_sz == scan_sz); } assert(frame.size() == data_offset); frame.resize(data_offset + scan_sz * height); for (int32_t y = 0; y < height; ++y) { std::copy_n( resized_rgb.begin() + scan_sz * (height - 1 - y), scan_sz, frame.begin() + data_offset + y * scan_sz ); } } return retval; }