use new phosg::Image class

This commit is contained in:
Martin Michelsen
2025-07-01 09:56:42 -07:00
parent 072e647c7b
commit 0a5065707c
8 changed files with 65 additions and 87 deletions
+2
View File
@@ -14,6 +14,7 @@ CTestTestfile.cmake
install_manifest.txt
Makefile
Testing
build
# Files modified by the user and/or server that don't have defaults
system/config.json
@@ -35,6 +36,7 @@ system/patch-bb/.metadata-cache.json
# repository
files
make_release.py
notes-private
old-khyller
old-newserv
release
+10 -11
View File
@@ -35,7 +35,7 @@ struct GVRHeader {
be_uint16_t height;
} __packed_ws__(GVRHeader, 0x10);
string encode_gvm(const phosg::Image& img, GVRDataFormat data_format, const string& internal_name, uint32_t global_index) {
string encode_gvm(const phosg::ImageRGBA8888& img, GVRDataFormat data_format, const string& internal_name, uint32_t global_index) {
int8_t dimensions_field = -2;
{
size_t h = img.get_height();
@@ -90,17 +90,16 @@ string encode_gvm(const phosg::Image& img, GVRDataFormat data_format, const stri
for (size_t x = 0; x < img.get_width(); x += 4) {
for (size_t yy = 0; yy < 4; yy++) {
for (size_t xx = 0; xx < 4; xx++) {
uint64_t a, r, g, b;
img.read_pixel(x + xx, y + yy, &r, &g, &b, &a);
uint32_t c = img.read(x + xx, y + yy);
switch (data_format) {
case GVRDataFormat::RGB565:
w.put_u16b(encode_rgb565(r, g, b));
w.put_u16b(phosg::rgb565_for_rgba8888(c));
break;
case GVRDataFormat::RGB5A3:
w.put_u16b(encode_rgb5a3(r, g, b, a));
w.put_u16b(encode_rgb5a3(c));
break;
case GVRDataFormat::ARGB8888:
w.put_u32b(encode_argb8888(r, g, b, a));
w.put_u32b(phosg::argb8888_for_rgba8888(c));
break;
default:
throw logic_error("cannot encode pixel format");
@@ -115,15 +114,15 @@ string encode_gvm(const phosg::Image& img, GVRDataFormat data_format, const stri
static const array<uint32_t, 4> fon_colors = {0x000000FF, 0x555555FF, 0xAAAAAAFF, 0xFFFFFFFF};
phosg::Image decode_fon(const string& data, size_t width) {
phosg::ImageRGB888 decode_fon(const string& data, size_t width) {
size_t num_pixels = data.size() * 4;
size_t height = num_pixels / width;
phosg::Image ret(width, height);
phosg::ImageRGB888 ret(width, height);
phosg::BitReader r(data);
for (size_t y = 0; y < height; y++) {
for (size_t x = 0; x < width; x++) {
ret.write_pixel(x, y, fon_colors[r.read(2)]);
ret.write(x, y, fon_colors[r.read(2)]);
}
}
return ret;
@@ -133,11 +132,11 @@ constexpr size_t uabs(size_t a, size_t b) {
return (a > b) ? (a - b) : (b - a);
}
string encode_fon(const phosg::Image& img) {
string encode_fon(const phosg::ImageRGB888& img) {
phosg::BitWriter w;
for (size_t y = 0; y < img.get_height(); y++) {
for (size_t x = 0; x < img.get_width(); x++) {
uint32_t color = img.read_pixel(x, y);
uint32_t color = img.read(x, y);
size_t result_delta = 0x400;
size_t result_index = 0;
+20 -34
View File
@@ -19,43 +19,29 @@ enum class GVRDataFormat : uint8_t {
DXT1 = 0x0E,
};
std::string encode_gvm(const phosg::Image& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index);
phosg::Image decode_fon(const std::string& data, size_t width);
std::string encode_fon(const phosg::Image& img);
std::string encode_gvm(
const phosg::ImageRGBA8888& img, GVRDataFormat data_format, const std::string& internal_name, uint32_t global_index);
phosg::ImageRGB888 decode_fon(const std::string& data, size_t width);
std::string encode_fon(const phosg::ImageRGB888& img);
constexpr uint16_t encode_rgb565(uint8_t r, uint8_t g, uint8_t b) {
return ((r << 8) & 0xF800) | ((g << 3) & 0x07E0) | ((b >> 3) & 0x001F);
}
constexpr uint16_t encode_rgb5a3(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
if ((a & 0xE0) == 0xE0) {
return 0x8000 | ((r << 7) & 0x7C00) | ((g << 2) & 0x03E0) | ((b >> 3) & 0x001F);
constexpr uint16_t encode_rgb5a3(uint32_t c) {
if ((phosg::get_a(c) & 0xE0) == 0xE0) {
return 0x8000 | ((phosg::get_r(c) << 7) & 0x7C00) | ((phosg::get_g(c) << 2) & 0x03E0) | ((phosg::get_b(c) >> 3) & 0x001F);
} else {
return ((a << 7) & 0x7000) | ((r << 4) & 0x0F00) | (g & 0x00F0) | ((b >> 4) & 0x000F);
return ((phosg::get_a(c) << 7) & 0x7000) | ((phosg::get_r(c) << 4) & 0x0F00) | (phosg::get_g(c) & 0x00F0) | ((phosg::get_b(c) >> 4) & 0x000F);
}
}
constexpr uint32_t encode_argb8888(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
return (a << 24) | (r << 16) | (g << 8) | b;
}
constexpr uint16_t encode_argb8888_to_argb1555(uint32_t argb8888) {
// In: aaaaaaaarrrrrrrrggggggggbbbbbbbb
// Out: arrrrrgggggbbbbb
return ((argb8888 >> 9) & 0x7C00) | ((argb8888 >> 6) & 0x03E0) | ((argb8888 >> 3) & 0x001F) | ((argb8888 >> 16) & 0x8000);
}
constexpr uint16_t encode_rgba8888_to_argb1555(uint32_t rgba8888) {
// In: rrrrrrrrggggggggbbbbbbbbaaaaaaaa
// Out: arrrrrgggggbbbbb
return ((rgba8888 >> 17) & 0x7C00) | ((rgba8888 >> 14) & 0x03E0) | ((rgba8888 >> 11) & 0x001F) | ((rgba8888 << 8) & 0x8000);
}
constexpr uint32_t decode_argb1555_to_rgba8888(uint16_t argb1555) {
// In: arrrrrgggggbbbbb
// Out: rrrrrrrrggggggggbbbbbbbbaaaaaaaa
return ((argb1555 << 17) & 0xF8000000) | ((argb1555 << 12) & 0x07000000) |
((argb1555 << 14) & 0x00F80000) | ((argb1555 << 9) & 0x00070000) |
((argb1555 << 11) & 0x0000F800) | ((argb1555 << 6) & 0x00000700) |
((argb1555 & 0x8000) ? 0x000000FF : 0x00000000);
template <phosg::PixelFormat Format>
bool has_any_transparent_pixels(const phosg::Image<Format>& img) {
if constexpr (phosg::Image<Format>::HAS_ALPHA) {
for (size_t y = 0; y < img.get_height(); y++) {
for (size_t x = 0; x < img.get_height(); x++) {
if (phosg::get_a(img.read(x, y)) != 0xFF) {
return true;
}
}
}
}
return false;
}
+22 -21
View File
@@ -1166,7 +1166,7 @@ Action a_decode_gci_snapshot(
}
auto img = file.decode_image();
string saved = img.save(phosg::Image::Format::WINDOWS_BITMAP);
string saved = img.serialize(phosg::ImageFormat::WINDOWS_BITMAP);
write_output_data(args, saved.data(), saved.size(), "bmp");
});
@@ -1177,13 +1177,16 @@ Action a_encode_gvm(
GVM file can be used as an Episode 3 lobby banner.\n",
+[](phosg::Arguments& args) {
const string& input_filename = args.get<string>(1, false);
phosg::Image img;
string data;
if (!input_filename.empty() && (input_filename != "-")) {
img = phosg::Image(input_filename);
data = phosg::load_file(input_filename);
} else {
img = phosg::Image(stdin);
data = phosg::read_all(stdin);
}
string encoded = encode_gvm(img, img.get_has_alpha() ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565, "image.gvr", 0);
auto img = phosg::ImageRGBA8888::from_file_data(data);
// If the image has any transparent pixels at all, use RGB5A3
string encoded = encode_gvm(
img, has_any_transparent_pixels(img) ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565, "image.gvr", 0);
write_output_data(args, encoded.data(), encoded.size(), "gvm");
});
@@ -1196,7 +1199,7 @@ Action a_decode_bitmap_font(
+[](phosg::Arguments& args) {
std::string data = read_input_data(args);
size_t width = args.get<size_t>("width");
std::string bmp_data = decode_fon(data, width).save(phosg::Image::Format::WINDOWS_BITMAP);
std::string bmp_data = decode_fon(data, width).serialize(phosg::ImageFormat::WINDOWS_BITMAP);
write_output_data(args, bmp_data.data(), bmp_data.size(), "bmp");
});
Action a_encode_bitmap_font(
@@ -1207,12 +1210,13 @@ Action a_encode_bitmap_font(
original fon\'s dimensions.\n",
+[](phosg::Arguments& args) {
const string& input_filename = args.get<string>(1, false);
phosg::Image img;
string data;
if (!input_filename.empty() && (input_filename != "-")) {
img = phosg::Image(input_filename);
data = phosg::load_file(input_filename);
} else {
img = phosg::Image(stdin);
data = phosg::read_all(stdin);
}
auto img = phosg::ImageRGB888::from_file_data(data);
string encoded = encode_fon(img);
write_output_data(args, encoded.data(), encoded.size(), "fon");
});
@@ -2591,22 +2595,19 @@ Action a_generate_ep3_cards_html(
phosg::parallel_range<uint32_t>([&](uint32_t index, size_t) -> bool {
auto& info = this->card_infos[index];
if (!info.large_filename.empty()) {
phosg::Image img(info.large_filename);
phosg::Image cropped(512, 399);
cropped.blit(img, 0, 0, 512, 399, 0, 0);
info.large_data_url = cropped.png_data_url();
auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(info.large_filename));
img.resize(512, 399);
info.large_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
if (!info.medium_filename.empty()) {
phosg::Image img(info.medium_filename);
phosg::Image cropped(184, 144);
cropped.blit(img, 0, 0, 184, 144, 0, 0);
info.medium_data_url = cropped.png_data_url();
auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(info.medium_filename));
img.resize(184, 144);
info.medium_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
if (!info.small_filename.empty()) {
phosg::Image img(info.small_filename);
phosg::Image cropped(58, 43);
cropped.blit(img, 0, 0, 58, 43, 0, 0);
info.small_data_url = cropped.png_data_url();
auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(info.small_filename));
img.resize(58, 43);
info.small_data_url = img.serialize(phosg::ImageFormat::PNG_DATA_URL);
}
return false;
},
+3 -13
View File
@@ -218,16 +218,7 @@ bool PSOGCSnapshotFile::checksum_correct() const {
return (crc == this->checksum);
}
static uint32_t decode_rgb565(uint16_t c) {
// Input bits: rrrrrggg gggbbbbb
// Output bits: rrrrrrrr gggggggg bbbbbbbb aaaaaaaa
return ((c << 16) & 0xF8000000) | ((c << 11) & 0x07000000) | // R
((c << 13) & 0x00FC0000) | ((c << 7) & 0x00030000) | // G
((c << 11) & 0x0000F800) | ((c << 6) & 0x00000700) | // B
0x000000FF; // A
}
phosg::Image PSOGCSnapshotFile::decode_image() const {
phosg::ImageRGB888 PSOGCSnapshotFile::decode_image() const {
size_t width = this->width ? this->width.load() : 256;
size_t height = this->height ? this->height.load() : 192;
if (width != 256) {
@@ -238,14 +229,13 @@ phosg::Image PSOGCSnapshotFile::decode_image() const {
}
// 4x4 blocks of pixels
phosg::Image ret(width, height, false);
phosg::ImageRGB888 ret(width, height);
size_t offset = 0;
for (size_t y = 0; y < this->height; y += 4) {
for (size_t x = 0; x < this->width; x += 4) {
for (size_t yy = 0; yy < 4; yy++) {
for (size_t xx = 0; xx < 4; xx++) {
uint32_t color = decode_rgb565(this->pixels[offset++]);
ret.write_pixel(x + xx, y + yy, color);
ret.write(x + xx, y + yy, phosg::rgba8888_for_rgb565(this->pixels[offset++]));
}
}
}
+1 -1
View File
@@ -1066,7 +1066,7 @@ struct PSOGCSnapshotFile {
/* 1818C */
bool checksum_correct() const;
phosg::Image decode_image() const;
phosg::ImageRGB888 decode_image() const;
} __packed_ws__(PSOGCSnapshotFile, 0x1818C);
////////////////////////////////////////////////////////////////////////////////
+2 -2
View File
@@ -966,10 +966,10 @@ void ServerState::load_config_early() {
} else if (lower_path.ends_with(".gvm")) {
decompressed_gvm_data = phosg::load_file(path);
} else if (lower_path.ends_with(".bmp")) {
phosg::Image img(path);
auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(path));
decompressed_gvm_data = encode_gvm(
img,
img.get_has_alpha() ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565,
has_any_transparent_pixels(img) ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565,
std::format("bnr{}", banner_index),
0x80 | banner_index);
banner_index++;
+5 -5
View File
@@ -98,14 +98,14 @@ void TeamIndex::Team::save_config() const {
}
void TeamIndex::Team::load_flag() {
phosg::Image img(this->flag_filename());
auto img = phosg::ImageRGBA8888::from_file_data(phosg::load_file(this->flag_filename()));
if (img.get_width() != 32 || img.get_height() != 32) {
throw runtime_error("incorrect flag image dimensions");
}
this->flag_data.reset(new parray<le_uint16_t, 0x20 * 0x20>());
for (size_t y = 0; y < 32; y++) {
for (size_t x = 0; x < 32; x++) {
this->flag_data->at(y * 0x20 + x) = encode_rgba8888_to_argb1555(img.read_pixel(x, y));
this->flag_data->at(y * 0x20 + x) = phosg::argb1555_for_rgba8888(img.read(x, y));
}
}
}
@@ -114,13 +114,13 @@ void TeamIndex::Team::save_flag() const {
if (!this->flag_data) {
return;
}
phosg::Image img(32, 32, true);
phosg::ImageRGBA8888 img(32, 32);
for (size_t y = 0; y < 32; y++) {
for (size_t x = 0; x < 32; x++) {
img.write_pixel(x, y, decode_argb1555_to_rgba8888(this->flag_data->at(y * 0x20 + x)));
img.write(x, y, phosg::rgba8888_for_argb1555(this->flag_data->at(y * 0x20 + x)));
}
}
img.save(this->flag_filename(), phosg::Image::Format::WINDOWS_BITMAP);
phosg::save_file(this->flag_filename(), img.serialize(phosg::ImageFormat::WINDOWS_BITMAP));
}
void TeamIndex::Team::delete_files() const {