Use a temporary float buffer when scaling. Return original 4bit if unchanged.
This commit is contained in:
parent
3e05244b95
commit
5b20c5df29
1 changed files with 101 additions and 42 deletions
|
@ -3,6 +3,7 @@
|
|||
#include <cassert>
|
||||
#include <climits>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace {
|
||||
namespace bmp {
|
||||
|
@ -30,33 +31,98 @@ namespace {
|
|||
};
|
||||
} //namespace bmp
|
||||
|
||||
void srgb_to_linear (std::vector<char>& data, int w, int h) {
|
||||
[[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) {
|
||||
}
|
||||
|
||||
void srgb_to_linear (std::vector<float>& data, int, int) {
|
||||
std::transform(data.begin(), data.end(), data.begin(), &to_linear);
|
||||
}
|
||||
|
||||
void linear_to_srgb (std::vector<float>& data, int w, int h) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
void linear_to_srgb (std::vector<char>& data, int w, int h) {
|
||||
std::vector<float> bilinear_resize (int in_w, int in_h, const std::vector<float>& data, int out_w, int out_h) {
|
||||
assert(false);
|
||||
return std::vector<float>();
|
||||
}
|
||||
|
||||
std::vector<char> bilinear_resize (int in_w, int in_h, const std::vector<char>& data, int out_w, int out_h) {
|
||||
assert(false);
|
||||
return std::vector<char>();
|
||||
std::vector<float> rgb4_to_float (const std::vector<char>& data, const std::vector<uint8_t>& palette, int w, int h) {
|
||||
assert(w * h);
|
||||
std::vector<float> retval(w * h);
|
||||
const int scanl_sz = scanline_size(w, 4);
|
||||
|
||||
std::vector<float> float_palette;
|
||||
{
|
||||
float_palette.reserve(palette.size());
|
||||
const float inv255 = 1.0f / 255.0f;
|
||||
for (auto clr : palette) {
|
||||
float_palette.push_back(static_cast<float>(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 * scanl_sz + x * 2 * 3;
|
||||
const int dst_index_b = dst_index_a + 3;
|
||||
const int src_index_a = (curr bitand 0x0f) * 4;
|
||||
const int src_index_b = ((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<char> float_to_rgb24 (const std::vector<float>& data) {
|
||||
std::vector<char> retval;
|
||||
retval.reserve(data.size());
|
||||
for (auto f : data) {
|
||||
retval.push_back(static_cast<char>(static_cast<uint8_t>(f * 255.0f)));
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
} //unnamed namespace
|
||||
|
||||
std::vector<std::vector<char>> icon_fetch (const ConstBlock& block, int width, int height) {
|
||||
const int in_height = 16, in_width = 16;
|
||||
const bool scale = (in_width != width or in_height != height);
|
||||
const auto slot_begin = block.cbegin();
|
||||
const auto slot_end = block.cend();
|
||||
const std::vector<uint8_t>& palette = block.palette();
|
||||
assert(palette.size() == 16 * 4);
|
||||
const int32_t palette_padded_size = 0; //((palette.size() * CHAR_BIT + 31) bitand ~31) / CHAR_BIT;
|
||||
//assert(palette.size() == palette_padded_size);
|
||||
const int32_t palette_padded_size = (not scale) * (((palette.size() * CHAR_BIT + 31) bitand ~31) / CHAR_BIT);
|
||||
assert(palette.size() == palette_padded_size or scale);
|
||||
|
||||
const int32_t bpp = 24;
|
||||
const int32_t scanline_size = ((width * bpp + 31) bitand ~31) / CHAR_BIT;
|
||||
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 + scanline_size * height;
|
||||
const std::size_t bmp_byte_size = data_offset + scan_sz * height;
|
||||
|
||||
bmp::FileHeader fhead;
|
||||
fhead.bfSize = static_cast<uint32_t>(bmp_byte_size);
|
||||
|
@ -66,9 +132,9 @@ std::vector<std::vector<char>> icon_fetch (const ConstBlock& block, int width, i
|
|||
ihead.biWidth = width;
|
||||
ihead.biHeight = height;
|
||||
ihead.biBitCount = static_cast<uint32_t>(bpp);
|
||||
ihead.biClrUsed = 0; //static_cast<uint32_t>(palette.size() / 4);
|
||||
ihead.biClrUsed = static_cast<uint32_t>(palette.size() / 4) * (not scale);
|
||||
ihead.biClrImportant = ihead.biClrUsed;
|
||||
ihead.biSizeImage = scanline_size * height;
|
||||
ihead.biSizeImage = scan_sz * height;
|
||||
|
||||
std::vector<std::vector<char>> retval(3);
|
||||
int32_t icon_num = 0;
|
||||
|
@ -79,48 +145,41 @@ std::vector<std::vector<char>> icon_fetch (const ConstBlock& block, int width, i
|
|||
std::copy(reinterpret_cast<const char*>(&bmp::g_signature), reinterpret_cast<const char*>(&bmp::g_signature + 1), std::back_inserter(frame));
|
||||
std::copy(reinterpret_cast<char*>(&fhead), reinterpret_cast<char*>(&fhead + 1), std::back_inserter(frame));
|
||||
std::copy(reinterpret_cast<char*>(&ihead), reinterpret_cast<char*>(&ihead + 1), std::back_inserter(frame));
|
||||
//std::copy(palette.begin(), palette.end(), std::back_inserter(frame));
|
||||
//assert(palette_padded_size >= palette.size());
|
||||
//std::fill_n(std::back_inserter(frame), palette_padded_size - palette.size(), 0);
|
||||
if (not scale) {
|
||||
std::copy(palette.begin(), palette.end(), std::back_inserter(frame));
|
||||
assert(palette_padded_size >= palette.size());
|
||||
std::fill_n(std::back_inserter(frame), palette_padded_size - palette.size(), 0);
|
||||
}
|
||||
|
||||
const int h = 16, w = 16;
|
||||
const int in_scanline_size = ((w * 24 + 31) bitand ~31) / CHAR_BIT;
|
||||
orig_rgb.resize(in_scanline_size * h);
|
||||
for (int y = 0; y < h; ++y) {
|
||||
for (int x = 0; x < w / 2; ++x) {
|
||||
const uint8_t curr = *(slot_begin + 128 + (w / 2 * y) + 128 * icon_num + x);
|
||||
const int dst_index_a = y * in_scanline_size + x * 2 * 3;
|
||||
const int dst_index_b = dst_index_a + 3;
|
||||
const int src_index_a = (curr bitand 0x0f) * 4;
|
||||
const int src_index_b = ((curr bitand 0xf0) >> 4) * 4;
|
||||
|
||||
orig_rgb[dst_index_a + 0] = palette[src_index_a + 0];
|
||||
orig_rgb[dst_index_a + 1] = palette[src_index_a + 1];
|
||||
orig_rgb[dst_index_a + 2] = palette[src_index_a + 2];
|
||||
orig_rgb[dst_index_b + 0] = palette[src_index_b + 0];
|
||||
orig_rgb[dst_index_b + 1] = palette[src_index_b + 1];
|
||||
orig_rgb[dst_index_b + 2] = palette[src_index_b + 2];
|
||||
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<char> resized_rgb;
|
||||
if (w != width or h != height) {
|
||||
srgb_to_linear(orig_rgb, w, h);
|
||||
resized_rgb = bilinear_resize(w, h, orig_rgb, width, height);
|
||||
linear_to_srgb(resized_rgb, width, height);
|
||||
if (scale) {
|
||||
std::vector<float> 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 = bilinear_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);
|
||||
}
|
||||
else {
|
||||
std::swap(resized_rgb, orig_rgb);
|
||||
assert(in_scanline_size == scanline_size);
|
||||
assert(in_scan_sz == scan_sz);
|
||||
}
|
||||
|
||||
assert(frame.size() == data_offset);
|
||||
frame.resize(data_offset + scanline_size * height);
|
||||
for (int32_t y = 0; y < h; ++y) {
|
||||
frame.resize(data_offset + scan_sz * height);
|
||||
for (int32_t y = 0; y < in_height; ++y) {
|
||||
std::copy_n(
|
||||
resized_rgb.begin() + scanline_size * (h - 1 - y),
|
||||
scanline_size,
|
||||
frame.begin() + data_offset + y * scanline_size
|
||||
resized_rgb.begin() + scan_sz * (in_height - 1 - y),
|
||||
scan_sz,
|
||||
frame.begin() + data_offset + y * scan_sz
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue