v0.14 intial release

This commit is contained in:
sillysagiri 2025-06-16 00:19:27 +07:00
parent 77b0fb85bd
commit 9bd6c5be71
7 changed files with 275 additions and 202 deletions

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "external/FastLZ"] [submodule "external/FastLZ"]
path = external/FastLZ path = external/FastLZ
url = https://github.com/ariya/FastLZ.git url = https://github.com/ariya/FastLZ.git
[submodule "external/murmurhash"]
path = external/murmurhash
url = https://github.com/jwerle/murmurhash.c.git

View File

@ -27,15 +27,14 @@ FetchContent_MakeAvailable(argparse)
# yes... im using glob... dont judge me.... # yes... im using glob... dont judge me....
file(GLOB_RECURSE PROJECT_SOURCES CONFIGURE_DEPENDS "src/*.cpp") file(GLOB_RECURSE PROJECT_SOURCES CONFIGURE_DEPENDS "src/*.cpp")
file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS file(GLOB EXTERNAL_SOURCES CONFIGURE_DEPENDS
"external/murmurhash/murmurhash.c"
"external/FastLZ/fastlz.c") "external/FastLZ/fastlz.c")
set(PROJECT_INCLUDE set(PROJECT_INCLUDE
"src" "src"
${STB_INCLUDE_DIRS} ${STB_INCLUDE_DIRS}
${LIQ_INCLUDE_DIRS} ${LIQ_INCLUDE_DIRS}
${COLORM_INCLUDE_DIRS}
"external/ChernoTimer" "external/ChernoTimer"
"external/murmurhash"
"external/FastLZ") "external/FastLZ")
set(PROJECT_LIBRARY set(PROJECT_LIBRARY

1
external/murmurhash vendored

@ -1 +0,0 @@
Subproject commit 10ba9c25abbcf8952b5f4abecf9bf4fc148e8e65

View File

@ -11,22 +11,22 @@ the idea is to simply load image data without worrying about image metadata.
the metadata contain basic stuff like width, height, format, etc... the metadata contain basic stuff like width, height, format, etc...
color palette are embedded in the file. color palette are embedded in the file.
a palette hash is provided. this can be used to comparing
whether another image uses the same palette
usage: sillyimage -i <input image> -o <output dir> [options] usage: sillyimage -i <input image> -o <output dir> [options]
options: options:
-i, --input input files [required] [may be repeated] -i, --input input files [required]
-o, --out output directory [required] -o, --out output directory [required]
-h, --help shows help message and exits -h, --help shows help message and exits
-v, --version prints version information and exits -v, --version prints version information and exits
-f, --format texture format { rgb256, rgb16, indexed4, indexed16, indexed256, indexed32a3, indexed8a5 } [default: "rgb16"] -f, --format texture format { rgb256, rgb16, indexed4, indexed16, indexed256, indexed32a8, indexed8a32 } [default: "rgb16"]
-pf, --palette-format palette format { rgb16, rgb256 } [default: "rgb16"]
-be, --big-endian enable big endian mode [default: false]
binary format: binary format:
[string] sillyimg (0x676D69796C6C6973 in hex or 7452728928599042419 in decimal (uint64)) [string] sillyimg (0x676D69796C6C6973 in hex or 7452728928599042419 in decimal (uint64))
[uint8] version (current version is 13) [int8] version (current version is 14)
[uint8] format [int8] format
0 - RGB256 0 - RGB256
1 - RGB16 1 - RGB16
2 - INDEXED4 2 - INDEXED4
@ -35,13 +35,16 @@ binary format:
5 - INDEXED32A3 5 - INDEXED32A3
6 - INDEXED8A5 6 - INDEXED8A5
7 - PALETTE16 7 - PALETTE16
[uint16] width [int8] big endian mode
[uint16] height [int8] palette format
[uint16] palette count 1 - rgb16 (2 bytes per palette)
[uint32] palette hash 2 - rgb256 (4 bytes per palette)
[uint32] compress size [int16] width
[uint32] original size [int16] height
[palette buffer] [int16] palette count
[int32] original size
[int32] compress size
[palette buffer] (palette count * palette format size)
[image buffer] [image buffer]
TODO: TODO:
@ -55,8 +58,9 @@ TODO:
dependencies: dependencies:
argparse (https://github.com/p-ranav/argparse) argparse (https://github.com/p-ranav/argparse)
stb_image (https://github.com/nothings/stb) stb_image (https://github.com/nothings/stb)
murmurhash.c (https://github.com/jwerle/murmurhash.c) libimagequant (https://github.com/ImageOptim/libimagequant)
Timer.h from cherno (https://gist.github.com/TheCherno/b2c71c9291a4a1a29c889e76173c8d14) Timer.h from cherno (https://gist.github.com/TheCherno/b2c71c9291a4a1a29c889e76173c8d14)
FastLZ (https://github.com/ariya/FastLZ)
license: license:
GPL v3 GPL v3

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 sillysagiri * Copyright (C) 2025 sillysagiri
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -16,23 +16,16 @@
#include <string> #include <string>
#include <vector> #include <vector>
// wporkaround surpress clangd pragma pack warning
// https://github.com/clangd/clangd/issues/1167
static_assert(true);
#pragma pack(push, 1)
struct Metadata { struct Metadata {
uint64_t header = 0x676D69796C6C6973; uint64_t header = 0x676D69796C6C6973;
uint8_t version = 13; int8_t version = 14;
uint8_t format; int8_t format;
uint16_t width; int16_t width;
uint16_t height; int16_t height;
uint16_t palette_count; int16_t palette_count;
uint32_t palette_hash; int32_t original_size;
uint32_t original_size; int32_t compress_size;
uint32_t compress_size;
}; };
#pragma pack(pop)
enum Format enum Format
{ {
@ -71,12 +64,37 @@ typedef std::vector<uint8_t> ImageMapped;
typedef std::vector<Color> Palette; typedef std::vector<Color> Palette;
struct Options { struct Options {
std::vector<std::string> path_input; std::string path_input;
std::string path_output; std::string path_output;
std::string format; std::string format;
std::string palette_format;
// std::string compression;
bool enable_be = false;
}; };
void Verify(); void WriteOutput(const Options &opt, const Metadata &meta, const Palette &palette, void *image_buffer);
void Convert(); ImageMapped Quantize(const Image &image, Palette &palette_output, int num_colors, bool dither, uint8_t format);
std::vector<ImageMapped> Quantize(const std::vector<Image>& images, Palette &palette_output, int num_colors, bool dither, uint8_t format); static inline uint16_t to_be_int16(int16_t value) {
uint16_t uval = static_cast<uint16_t>(value);
return (uval >> 8) | (uval << 8);
}
static inline uint32_t to_be_int32(int32_t value) {
uint32_t uval = static_cast<uint32_t>(value);
return ((uval >> 24) & 0x000000FF) |
((uval >> 8) & 0x0000FF00) |
((uval << 8) & 0x00FF0000) |
((uval << 24) & 0xFF000000);
}
static inline uint16_t to_be_int16(uint16_t value) {
return (value >> 8) | (value << 8);
}
static inline uint32_t to_be_int32(uint32_t value) {
return ((value >> 24) & 0x000000FF) |
((value >> 8) & 0x0000FF00) |
((value << 8) & 0x00FF0000) |
((value << 24) & 0xFF000000);
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2024 sillysagiri * Copyright (C) 2025 sillysagiri
* *
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
* *
@ -8,16 +8,9 @@
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. * You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
#include <cassert>
#include <cstdint> #include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdint.h>
#include <format>
#include <iostream>
#include <vector>
#define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION
@ -25,22 +18,22 @@
#include "header.hpp" #include "header.hpp"
#include "Timer.h" #include "Timer.h"
#include "fastlz.h" #include "fastlz.h"
#include "murmurhash.h"
namespace fs = std::filesystem; namespace fs = std::filesystem;
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
srand(time(0)); srand(time(0));
argparse::ArgumentParser program("sillyimage", "1.3"); argparse::ArgumentParser program("sillyimage", "0.14");
Options opt; Options opt;
// Register argument // Register argument
{ {
program.add_argument("-i", "--input") program.add_argument("-i", "--input")
.help("input files") .help("input files")
.append() .default_value<std::string>("")
.required() .required()
.nargs(1)
.store_into(opt.path_input); .store_into(opt.path_input);
program.add_argument("-o", "--out") program.add_argument("-o", "--out")
@ -54,6 +47,26 @@ int main(int argc, char **argv)
.choices("rgb256", "rgb16", "indexed4", "indexed16", "indexed256", "indexed32a8", "indexed8a32") .choices("rgb256", "rgb16", "indexed4", "indexed16", "indexed256", "indexed32a8", "indexed8a32")
.nargs(1) .nargs(1)
.store_into(opt.format); .store_into(opt.format);
program.add_argument("-pf", "--palette-format")
.help("palette format { rgb16, rgb256 }")
.default_value<std::string>("rgb16")
.choices("rgb16", "rgb256")
.nargs(1)
.store_into(opt.palette_format);
// program.add_argument("-c", "--compression")
// .help("compression { none, fastlz }")
// .default_value<std::string>("fastlz")
// .choices("none", "fastlz")
// .nargs(1)
// .store_into(opt.compression);
program.add_argument("-be", "--big-endian")
.help("enable big endian mode")
.default_value<bool>(false)
.nargs(1)
.store_into(opt.enable_be);
} }
// Parse argument // Parse argument
@ -63,9 +76,8 @@ int main(int argc, char **argv)
program.parse_args(argc, argv); program.parse_args(argc, argv);
// input list // input list
for (auto &i : opt.path_input) if (opt.path_input.empty() || !fs::exists(opt.path_input))
if (i.empty() || !fs::exists(i)) throw std::runtime_error(std::format("file doesnt exsist: \"{}\"", opt.path_input));
throw std::runtime_error(std::format("file doesnt exsist: \"{}\"", i));
// output directory // output directory
if (opt.path_output.empty() || !fs::is_directory(opt.path_output)) if (opt.path_output.empty() || !fs::is_directory(opt.path_output))
@ -84,59 +96,60 @@ int main(int argc, char **argv)
if (opt.format == "rgb256") if (opt.format == "rgb256")
{ {
for(auto &input : opt.path_input) Image img;
img.Load(opt.path_input);
uint32_t img255_size = img.width*img.height;
uint32_t img255[img255_size];
for (int i=0; i<img255_size; i++)
{ {
Image img; img255[i] = (static_cast<uint32_t>(img.data[i*4+3]) << 24) | // A
img.Load(input); (static_cast<uint32_t>(img.data[i*4+0]) << 16) | // R
(static_cast<uint32_t>(img.data[i*4+1]) << 8) | // G
static_cast<uint32_t>(img.data[i*4+2]); // B
Metadata meta; if (opt.enable_be)
meta.format = Format::Format_RGB_256; img255[i] = to_be_int32(img255[i]);
meta.width = img.width;
meta.height = img.height;
meta.palette_count = 0;
meta.palette_hash = 0;
meta.original_size = img.data.size();
uint8_t compress[uint64_t(meta.original_size*1.5)];
meta.compress_size = fastlz_compress_level(1, img.data.data(), meta.original_size, compress);
auto output = fs::path(opt.path_output) / std::format("{}.sillyimg", fs::path(input).stem().string());
std::ofstream out(output, std::ios::binary);
out.write(reinterpret_cast<const char*>(&meta), sizeof(meta));
out.write(reinterpret_cast<const char*>(compress), meta.compress_size);
out.close();
} }
Metadata meta;
meta.format = Format::Format_RGB_256;
meta.width = img.width;
meta.height = img.height;
meta.palette_count = 0;
meta.original_size = img.data.size();
uint8_t compress[uint64_t(meta.original_size*1.5)];
meta.compress_size = fastlz_compress_level(1, img.data.data(), meta.original_size, compress);
Palette palette(0);
WriteOutput(opt, meta, palette, compress);
} }
if (opt.format == "rgb16") if (opt.format == "rgb16")
{ {
for(auto &input : opt.path_input) Image img;
{ img.Load(opt.path_input);
Image img; std::vector<uint16_t> img16(img.width*img.height);
img.Load(input);
std::vector<uint16_t> img16(img.width*img.height);
// convert into RGB16 color // convert into RGB16 color
for (int i=0; i<img.width*img.height; i++) for (int i=0; i<img.width*img.height; i++)
img16[i] = RGB255_RGB16(img.data[i*4], img.data[i*4+1], img.data[i*4+2], img.data[i*4+3]); img16[i] = RGB255_RGB16(img.data[i*4], img.data[i*4+1], img.data[i*4+2], img.data[i*4+3]);
Metadata meta; Metadata meta;
meta.format = Format::Format_RGB_16; meta.format = Format::Format_RGB_16;
meta.width = img.width; meta.width = img.width;
meta.height = img.height; meta.height = img.height;
meta.palette_count = 0; meta.palette_count = 0;
meta.palette_hash = 0; meta.original_size = img16.size()*sizeof(uint16_t);
meta.original_size = img16.size()*sizeof(uint16_t);
uint8_t compress[uint64_t(meta.original_size*1.5)]; uint8_t compress[uint64_t(meta.original_size*1.5)];
meta.compress_size = fastlz_compress_level(2, img16.data(), meta.original_size, compress); meta.compress_size = fastlz_compress_level(2, img16.data(), meta.original_size, compress);
auto output = fs::path(opt.path_output) / std::format("{}.sillyimg", fs::path(input).stem().string()); Palette palette(0);
std::ofstream out(output, std::ios::binary);
out.write(reinterpret_cast<const char*>(&meta), sizeof(meta)); WriteOutput(opt, meta, palette, compress);
out.write(reinterpret_cast<const char*>(compress), meta.compress_size);
out.close();
}
} }
if (opt.format == "indexed4" || if (opt.format == "indexed4" ||
@ -175,41 +188,22 @@ int main(int argc, char **argv)
} }
Palette palette; Palette palette;
std::vector<uint16_t> palette16; Image image;
std::vector<Image> images(opt.path_input.size()); image.Load(opt.path_input);
ImageMapped remap = Quantize(image, palette, numcol, false, format);
for (int i=0; i<images.size(); i++) Metadata meta;
images[i].Load(opt.path_input[i]); meta.format = format;
meta.width = image.width;
meta.height = image.height;
meta.palette_count = palette.size();
meta.original_size = remap.size();
auto remap = Quantize(images, palette, numcol, false, format); uint8_t compress[uint64_t(meta.original_size*1.5)];
meta.compress_size = fastlz_compress_level(2, remap.data(), meta.original_size, compress);
palette16.resize(palette.size());
for (int i=1; i<palette.size(); i++)
palette16[i] = RGB255_RGB16(palette[i].r, palette[i].g, palette[i].b, palette[i].a);
uint32_t hash = murmurhash(reinterpret_cast<const char*>(palette16.data()), sizeof(uint16_t)*palette16.size(), 0); WriteOutput(opt, meta, palette, compress);
for (int i=0; i<images.size(); i++)
{
Metadata meta;
meta.format = format;
meta.width = images[i].width;
meta.height = images[i].height;
meta.palette_count = palette16.size();
meta.palette_hash = hash;
meta.original_size = remap[i].size();
uint8_t compress[uint64_t(meta.original_size*1.5)];
meta.compress_size = fastlz_compress_level(2, remap[i].data(), meta.original_size, compress);
auto output = fs::path(opt.path_output) / std::format("{}.sillyimg", fs::path(opt.path_input[i]).stem().string());
std::ofstream out(output, std::ios::binary);
out.write(reinterpret_cast<const char*>(&meta), sizeof(meta));
out.write(reinterpret_cast<const char*>(palette16.data()), sizeof(uint16_t)*palette.size());
out.write(reinterpret_cast<const char*>(compress), meta.compress_size);
out.close();
}
} }
} }
catch(const std::exception &e) catch(const std::exception &e)
@ -236,8 +230,85 @@ void Image::Load(const std::filesystem::path &path)
stbi_uc *ptr_img = stbi_load(path.c_str(), &width, &height, &comp, 4); stbi_uc *ptr_img = stbi_load(path.c_str(), &width, &height, &comp, 4);
if (ptr_img == nullptr) throw std::runtime_error(stbi_failure_reason()); if (ptr_img == nullptr) throw std::runtime_error(stbi_failure_reason());
int size = width*height*comp; int size = width*height*4;
data.assign(ptr_img, ptr_img+size); data.reserve(size);
for (int i = 0; i < size; ++i)
data.push_back(ptr_img[i]);
stbi_image_free(ptr_img); stbi_image_free(ptr_img);
}
void WriteOutput(const Options &opt, const Metadata &meta, const Palette &palette, void *image_buffer)
{
std::filesystem::path input_file(opt.path_input);
std::filesystem::path output_dir(opt.path_output);
std::string basename = input_file.stem();
std::string output_file = output_dir / std::format("{}.sillyimg", basename);
std::ofstream out(output_file, std::ios::binary);
out.write(reinterpret_cast<const char*>(&meta.header), sizeof(uint64_t));
out.write(reinterpret_cast<const char*>(&meta.version), sizeof(int8_t));
out.write(reinterpret_cast<const char*>(&meta.format), sizeof(int8_t));
int8_t isBE = opt.enable_be;
int8_t palette_format;
if (opt.palette_format == "rgb16") palette_format = 1;
if (opt.palette_format == "rgb256") palette_format = 2;
out.write(reinterpret_cast<const char*>(&isBE), sizeof(int8_t));
out.write(reinterpret_cast<const char*>(&palette_format), sizeof(int8_t));
if (!opt.enable_be)
{
out.write(reinterpret_cast<const char*>(&meta.width), sizeof(int16_t));
out.write(reinterpret_cast<const char*>(&meta.height), sizeof(int16_t));
out.write(reinterpret_cast<const char*>(&meta.palette_count), sizeof(int16_t));
out.write(reinterpret_cast<const char*>(&meta.original_size), sizeof(int32_t));
out.write(reinterpret_cast<const char*>(&meta.compress_size), sizeof(int32_t));
}
else
{
uint16_t be_width = to_be_int16(meta.width);
uint16_t be_height = to_be_int16(meta.height);
uint16_t be_palette_count = to_be_int16(meta.palette_count);
uint32_t be_original_size = to_be_int32(meta.original_size);
uint32_t be_compress_size = to_be_int32(meta.compress_size);
out.write(reinterpret_cast<const char*>(&be_width), sizeof(uint16_t));
out.write(reinterpret_cast<const char*>(&be_height), sizeof(uint16_t));
out.write(reinterpret_cast<const char*>(&be_palette_count), sizeof(uint16_t));
out.write(reinterpret_cast<const char*>(&be_original_size), sizeof(uint32_t));
out.write(reinterpret_cast<const char*>(&be_compress_size), sizeof(uint32_t));
}
if (!palette.empty())
{
if (palette_format == 1)
{
uint16_t pal16[meta.palette_count];
for (int i=0; i<meta.palette_count; i++)
pal16[i] = RGB255_RGB16(palette[i].r, palette[i].g, palette[i].b, palette[i].a);
out.write(reinterpret_cast<const char*>(pal16), sizeof(uint16_t)*meta.palette_count);
}
else if (palette_format == 2)
{
uint32_t pal256[meta.palette_count];
for (int i=0; i<meta.palette_count; i++)
{
pal256[i] = (static_cast<uint32_t>(palette[i].a) << 24) | // A
(static_cast<uint32_t>(palette[i].r) << 16) | // R
(static_cast<uint32_t>(palette[i].g) << 8) | // G
static_cast<uint32_t>(palette[i].b); // B
if (opt.enable_be) pal256[i] = to_be_int32(pal256[i]);
}
out.write(reinterpret_cast<const char*>(pal256), sizeof(uint32_t)*meta.palette_count);
}
}
out.write(reinterpret_cast<const char*>(image_buffer), meta.compress_size);
out.close();
} }

View File

@ -1,31 +1,41 @@
/*
* Copyright (C) 2025 sillysagiri
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <cmath>
#include <cstdio>
#include <libimagequant.h> #include <libimagequant.h>
#include <stdexcept> #include <stdexcept>
#include "header.hpp" #include "header.hpp"
std::vector<ImageMapped> Quantize(const std::vector<Image>& images, Palette &palette_output, int num_colors, bool dither, uint8_t format) ImageMapped Quantize(const Image &image, Palette &palette_output, int num_colors, bool dither, uint8_t format)
{ {
liq_attr *attr = liq_attr_create(); liq_attr *attr = liq_attr_create();
liq_histogram *hist = liq_histogram_create(attr);
liq_set_max_colors(attr, num_colors); liq_set_max_colors(attr, num_colors);
liq_set_speed(attr, 1);
liq_set_quality(attr, 0, 100);
std::vector<liq_image*> liq_images; liq_image *liq_img = liq_image_create_rgba(attr, image.data.data(), image.width, image.height, 0);
liq_images.reserve(images.size());
for (const Image &img : images)
{
liq_image *liq_img = liq_image_create_rgba(attr, img.data.data(), img.width, img.height, 0);
liq_histogram_add_image(hist, attr, liq_img);
liq_images.push_back(liq_img);
}
liq_result *result; liq_result *result;
if (LIQ_OK != liq_histogram_quantize(hist, attr, &result)) if (LIQ_OK != liq_image_quantize(liq_img, attr, &result))
throw std::runtime_error("failed to quantize histogram"); throw std::runtime_error("failed to quantize histogram");
if (dither) liq_set_dithering_level(result, 1.0f); if (dither) liq_set_dithering_level(result, 1.0f);
else liq_set_dithering_level(result, 0.0f); else liq_set_dithering_level(result, 0.0f);
ImageMapped output(image.width*image.height);
if (LIQ_OK != liq_write_remapped_image(result, liq_img, output.data(), output.size()))
throw std::runtime_error("failed to remap image");
const liq_palette *pal = liq_get_palette(result); const liq_palette *pal = liq_get_palette(result);
palette_output.resize(pal->count); palette_output.resize(pal->count);
for (int i=0; i<pal->count; i++) for (int i=0; i<pal->count; i++)
@ -36,38 +46,25 @@ std::vector<ImageMapped> Quantize(const std::vector<Image>& images, Palette &pal
palette_output[i].a = pal->entries[i].a; palette_output[i].a = pal->entries[i].a;
} }
std::vector<ImageMapped> output(images.size()); liq_image_destroy(liq_img);
for (int i=0; i<images.size(); i++)
{
output[i].resize(images[i].width*images[i].height);
if (LIQ_OK != liq_write_remapped_image(result, liq_images[i], output[i].data(), output[i].size()))
throw std::runtime_error("failed to remap image");
}
for (auto &i : liq_images) liq_image_destroy(i);
liq_histogram_destroy(hist);
liq_attr_destroy(attr); liq_attr_destroy(attr);
liq_result_destroy(result);
// --- // ---
if (format == Format_INDEXED_4) if (format == Format_INDEXED_4)
{ {
std::vector<ImageMapped> packed(output.size()); ImageMapped packed;
packed.reserve((output.size()+3) / 4);
for (int i=0; i<output.size(); i++) for (int chunk_start=0; chunk_start<output.size(); chunk_start+=4) {
{ uint8_t current_byte = 0;
packed[i].reserve((output[i].size()+3) / 4);
for (int chunk_start=0; chunk_start<output[i].size(); chunk_start+=4) { for (int offset=0; offset<4 && (chunk_start+offset)<output.size(); ++offset) {
uint8_t current_byte = 0; current_byte |= (output[chunk_start + offset] & 0b11) << (offset * 2);
for (int offset=0; offset<4 && (chunk_start+offset)<output[i].size(); ++offset) {
current_byte |= (output[i][chunk_start + offset] & 0b11) << (offset * 2);
}
packed[i].push_back(current_byte);
} }
packed.push_back(current_byte);
} }
return packed; return packed;
@ -75,21 +72,17 @@ std::vector<ImageMapped> Quantize(const std::vector<Image>& images, Palette &pal
else if (format == Format_INDEXED_16) else if (format == Format_INDEXED_16)
{ {
std::vector<ImageMapped> packed(output.size()); ImageMapped packed;
packed.reserve((output.size()+3) / 4);
for (int i=0; i<output.size(); i++) for (int offset=0; offset<output.size(); offset+=2) {
{ uint8_t current_byte = 0;
packed[i].reserve((output[i].size()+3) / 4);
for (int offset=0; offset<output[i].size(); offset+=2) { current_byte |= (output[offset] & 0b1111);
uint8_t current_byte = 0; if (offset+1 <output.size())
current_byte |= (output[offset+1] & 0b1111) << 4;
current_byte |= (output[i][offset] & 0b1111); packed.push_back(current_byte);
if (i+1 <output[i].size())
current_byte |= (output[i][offset + 1] & 0b1111) << 4;
packed[i].push_back(current_byte);
}
} }
return packed; return packed;
@ -97,19 +90,15 @@ std::vector<ImageMapped> Quantize(const std::vector<Image>& images, Palette &pal
else if (format == Format_INDEXED_8A32) else if (format == Format_INDEXED_8A32)
{ {
std::vector<ImageMapped> packed(output.size()); ImageMapped packed;
packed.reserve((output.size()+3) / 4);
for (int i=0; i<output.size(); i++) for (int offset=0; offset<output.size(); offset++) {
{ uint8_t current_byte = 0;
packed[i].reserve((output[i].size()+3) / 4);
for (int offset=0; offset<output[i].size(); offset++) { uint8_t alpha = image.data[offset*4+3] >> 3;
uint8_t current_byte = 0; current_byte |= (output[offset] & 0b111) | (alpha << 3);
packed.push_back(current_byte);
uint8_t alpha = images[i].data[offset*4+3] >> 3;
current_byte |= (output[i][offset] & 0b111) | (alpha << 3);
packed[i].push_back(current_byte);
}
} }
return packed; return packed;
@ -117,19 +106,15 @@ std::vector<ImageMapped> Quantize(const std::vector<Image>& images, Palette &pal
else if (format == Format_INDEXED_32A8) else if (format == Format_INDEXED_32A8)
{ {
std::vector<ImageMapped> packed(output.size()); ImageMapped packed(output.size());
packed.reserve((output.size()+3) / 4);
for (int i=0; i<output.size(); i++) for (int offset=0; offset<output.size(); offset++) {
{ uint8_t current_byte = 0;
packed[i].reserve((output[i].size()+3) / 4);
for (int offset=0; offset<output[i].size(); offset++) { uint8_t alpha = image.data[offset*4+3] >> 5;
uint8_t current_byte = 0; current_byte |= (output[offset] & 0b11111) | (alpha << 5);
packed.push_back(current_byte);
uint8_t alpha = images[i].data[offset*4+3] >> 5;
current_byte |= (output[i][offset] & 0b11111) | (alpha << 5);
packed[i].push_back(current_byte);
}
} }
return packed; return packed;