2025-03-25 20:15:47 +07:00

345 lines
11 KiB
C++

#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <sys/types.h>
#include <vector>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
extern "C" unsigned char *LZS_Fast(unsigned char *raw_buffer, size_t raw_len, size_t *new_len);
extern "C" unsigned char *LZS_Code(unsigned char *raw_buffer, size_t raw_len, size_t *new_len, size_t best);
#define LZS_WRAM 0x00 // VRAM not compatible (LZS_WRAM | LZS_NORMAL)
#define LZS_VRAM 0x01 // VRAM compatible (LZS_VRAM | LZS_NORMAL)
#define RGBtoRGB15(r,g,b) (((r >> 3) & 0x1F) | (((g >> 3) & 0x1F) << 5) | (((b >> 3) & 0x1F) << 10))
#define ASSETS_PATH "../assets/"
#define PALETTE_SIZE 256
#define FRAME_PER_CHUNK 2
#define FRAMERATE 30
size_t GetTotalFrame(std::string path)
{
size_t total = 0;
for (const auto& entry : std::filesystem::directory_iterator(path)) {
if (entry.is_regular_file()) {
std::string filename = entry.path().filename().string();
int extpos = filename.find(".bmp", 4);
if (extpos != std::string::npos)
{
int number = std::stoi(filename.substr(4, extpos-4));
if (number > total) total = number;
}
}
}
return total;
}
void GetFrameDimension(std::string path, int *w, int *h)
{
int n;
unsigned char *raw = stbi_load(path.c_str(), w, h, &n, 3);
stbi_image_free(raw);
}
void ConvertPalette(uint16_t *buffer_palette, std::string path)
{
int pal_w, pal_h, pal_n;
unsigned char *palette_raw = stbi_load(path.c_str(), &pal_w, &pal_h, &pal_n, 3);
for (int i=0; i<PALETTE_SIZE; i++)
{
int index = i*3;
unsigned char r = palette_raw[index];
unsigned char g = palette_raw[index + 1];
unsigned char b = palette_raw[index + 2];
buffer_palette[i] = RGBtoRGB15(r, g, b);
}
stbi_image_free(palette_raw);
}
void ConvertFrametoPalIndex(unsigned char *buffer_frame, size_t buffer_size, uint16_t *buffer_palette, unsigned char *dest)
{
for (int rgb=0; rgb<buffer_size; rgb++)
{
int index = rgb*3;
unsigned char r = buffer_frame[index];
unsigned char g = buffer_frame[index + 1];
unsigned char b = buffer_frame[index + 2];
uint16_t palette_value = RGBtoRGB15(r, g, b);
int palette_index = -1;
for (int i=0; i<PALETTE_SIZE; i++)
{
if (buffer_palette[i] == palette_value) {
palette_index = i;
break;
}
}
if (palette_index == -1) dest[rgb] = 0;
else dest[rgb] = palette_index;
}
}
int Compress(uint8_t type, uint8_t *frame_buffer, uint32_t frame_buffer_len, uint8_t *compress_buffer)
{
// if (time_list.at(chunknum) > time_avg) type = 4;
switch (type) {
case 1: // lzss
{
size_t compress_len;
unsigned char *compress = LZS_Fast(frame_buffer, frame_buffer_len, &compress_len);
// int mode = LZS_VRAM;
// unsigned char *compress = LZS_Code(frame_buffer, frame_buffer_len, &compress_len, mode);
memcpy(compress_buffer, compress, compress_len);
return compress_len;
break;
}
case 2: // raw
{
memcpy(compress_buffer, frame_buffer, frame_buffer_len);
return frame_buffer_len;
break;
}
}
return 0;
}
void Convert(
std::string path_video1,
std::string path_palette1,
std::string path_video2,
std::string path_palette2,
std::string path_output)
{
uint16_t metadata_width1;
uint16_t metadata_width2;
uint16_t metadata_height1;
uint16_t metadata_height2;
size_t metadata_total_frame1;
size_t metadata_total_frame2;
uint16_t *buffer_palette1 = new uint16_t[PALETTE_SIZE];
uint16_t *buffer_palette2 = new uint16_t[PALETTE_SIZE];
uint8_t *buffer_frame;
uint8_t *buffer_compress;
size_t size_video1_frame;
size_t size_video2_frame;
size_t size_buffer_frame;
size_t size_compress = 0;
size_t size_compress_total = 0;
size_t size_compress_max = 0;
size_t size_compress_min = 99999999;
std::ofstream file_out;
file_out.open(path_output, std::ios::binary);
if (!file_out) {
printf("fail to open output %s\n", path_output.c_str());
return;
}
// ---
{
printf("Finding total frame...\n");
metadata_total_frame1 = GetTotalFrame(path_video1);
metadata_total_frame2 = GetTotalFrame(path_video2);
}
// ---
{
printf("Finding video dimension...\n");
int w, h;
GetFrameDimension(path_video1 + "out_1.bmp", &w, &h);
metadata_width1 = w;
metadata_height1 = h;
GetFrameDimension(path_video2 + "out_1.bmp", &w, &h);
metadata_width2 = w;
metadata_height2 = h;
size_video1_frame = metadata_width1*metadata_height1;
size_video2_frame = metadata_width2*metadata_height2;
}
printf("video1 dimension: %ix%i\n", metadata_width1, metadata_height1);
printf("video2 dimension: %ix%i\n", metadata_width2, metadata_height2);
// ---
{
size_buffer_frame = size_video1_frame + size_video2_frame;
buffer_frame = new uint8_t[size_buffer_frame*FRAME_PER_CHUNK];
buffer_compress = new uint8_t[size_buffer_frame*FRAME_PER_CHUNK];
}
// ---
{
printf("Generating palette map...\n");
ConvertPalette(buffer_palette1, path_palette1);
ConvertPalette(buffer_palette2, path_palette2);
}
// ---
{
printf("Writing metadata...\n");
uint8_t framerate = FRAMERATE;
uint8_t frame_per_chunk = FRAME_PER_CHUNK;
file_out.write(reinterpret_cast<const char*>(&framerate), sizeof(uint8_t));
file_out.write(reinterpret_cast<const char*>(&frame_per_chunk), sizeof(uint8_t));
file_out.write(reinterpret_cast<const char*>(&metadata_total_frame1), sizeof(uint32_t));
file_out.write(reinterpret_cast<const char*>(&metadata_total_frame2), sizeof(uint32_t));
file_out.write(reinterpret_cast<const char*>(&metadata_width1), sizeof(uint16_t));
file_out.write(reinterpret_cast<const char*>(&metadata_height1), sizeof(uint16_t));
file_out.write(reinterpret_cast<const char*>(&metadata_width2), sizeof(uint16_t));
file_out.write(reinterpret_cast<const char*>(&metadata_height2), sizeof(uint16_t));
}
// ---
{
printf("Writing palette map...\n");
file_out.write(reinterpret_cast<const char*>(buffer_palette1), PALETTE_SIZE*sizeof(uint16_t));
file_out.write(reinterpret_cast<const char*>(buffer_palette2), PALETTE_SIZE*sizeof(uint16_t));
}
// ---
printf("Generating image map...\n");
std::stringstream ss;
// Calculate the number of complete iterations needed
int numIterations = std::max(metadata_total_frame1, metadata_total_frame2) / FRAME_PER_CHUNK;
// Calculate the number of remaining items
int remainingItems = std::max(metadata_total_frame1, metadata_total_frame2) % FRAME_PER_CHUNK;
for (int i=0; i<numIterations; i++)
{
memset(buffer_frame, 0, size_buffer_frame*FRAME_PER_CHUNK);
for (int i_chunk=0; i_chunk<FRAME_PER_CHUNK; i_chunk++) {
int itemIndex = i * FRAME_PER_CHUNK + i_chunk + 1;
int frame_w, frame_h, frame_n;
if (itemIndex <= metadata_total_frame1)
{
ss.str("");
ss << path_video1 << "out_" << itemIndex << ".bmp";
unsigned char *frame_raw = stbi_load(ss.str().c_str(), &frame_w, &frame_h, &frame_n, 3);
ConvertFrametoPalIndex(frame_raw, frame_w*frame_h, buffer_palette1, &buffer_frame[i_chunk*size_buffer_frame]);
stbi_image_free(frame_raw);
}
if (itemIndex <= metadata_total_frame2)
{
ss.str("");
ss << path_video2 << "out_" << itemIndex << ".bmp";
unsigned char *frame_raw = stbi_load(ss.str().c_str(), &frame_w, &frame_h, &frame_n, 3);
ConvertFrametoPalIndex(frame_raw, frame_w*frame_h, buffer_palette2, &buffer_frame[i_chunk*size_buffer_frame+size_video1_frame]);
stbi_image_free(frame_raw);
}
}
size_compress = Compress(1, buffer_frame, size_buffer_frame*FRAME_PER_CHUNK, buffer_compress);
size_compress_max = std::max(size_compress, size_compress_max);
size_compress_min = std::min(size_compress, size_compress_min);
size_compress_total += size_compress;
file_out.write(reinterpret_cast<const char*>(&size_compress), sizeof(uint32_t));
file_out.write(reinterpret_cast<const char*>(buffer_compress), size_compress);
printf("write %i bytes (%i/%i)\n", size_compress, i+1, numIterations);
}
// Handle remaining frame
if (remainingItems > 0) {
memset(buffer_frame, 0, size_buffer_frame*FRAME_PER_CHUNK);
int i_chunk = 0;
int frame_total = std::max(metadata_total_frame1, metadata_total_frame2);
for (int k = frame_total-remainingItems+1; k<=frame_total; k++) {
int frame_w, frame_h, frame_n;
if (k <= metadata_total_frame1)
{
ss.str("");
ss << path_video1 << "out_" << k << ".bmp";
unsigned char *frame_raw = stbi_load(ss.str().c_str(), &frame_w, &frame_h, &frame_n, 3);
ConvertFrametoPalIndex(frame_raw, frame_w*frame_h, buffer_palette1, &buffer_frame[i_chunk*size_buffer_frame]);
stbi_image_free(frame_raw);
}
if (k <= metadata_total_frame2)
{
ss.str("");
ss << path_video2 << "out_" << k << ".bmp";
unsigned char *frame_raw = stbi_load(ss.str().c_str(), &frame_w, &frame_h, &frame_n, 3);
ConvertFrametoPalIndex(frame_raw, frame_w*frame_h, buffer_palette2, &buffer_frame[i_chunk*size_buffer_frame+size_video1_frame]);
stbi_image_free(frame_raw);
}
i_chunk++;
}
size_compress = Compress(1, buffer_frame, size_buffer_frame*FRAME_PER_CHUNK, buffer_compress);
size_compress_max = std::max(size_compress, size_compress_max);
size_compress_min = std::min(size_compress, size_compress_min);
size_compress_total += size_compress;
file_out.write(reinterpret_cast<const char*>(&size_compress), sizeof(uint32_t));
file_out.write(reinterpret_cast<const char*>(buffer_compress), size_compress);
printf("write remaining chunk %i bytes\n", size_compress);
}
file_out.close();
// Print
printf("\n");
printf("fps: %i\n", FRAMERATE);
printf("frame per chunk: %i\n", FRAME_PER_CHUNK);
printf("video1 total frame: %i\n", metadata_total_frame1);
printf("video2 total frame: %i\n", metadata_total_frame2);
printf("video1 dimension: %ix%i\n", metadata_width1, metadata_height1);
printf("video2 dimension: %ix%i\n", metadata_width2, metadata_height2);
printf("compress size max: %i\n", size_compress_max);
printf("compress size min: %i\n", size_compress_min);
printf("size total : %1.fmb\n", static_cast<double>(size_compress_total) / (1024*1024));
delete[] buffer_palette1;
delete[] buffer_palette2;
delete[] buffer_frame;
delete[] buffer_compress;
}
int main()
{
Convert(
ASSETS_PATH "out/",
ASSETS_PATH "palette1.png",
ASSETS_PATH "out2/",
ASSETS_PATH "palette2.png",
"../nitrofs/video.sillyvideo");
}