#include #include #include #include #include #include #include #include #include #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 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(&framerate), sizeof(uint8_t)); file_out.write(reinterpret_cast(&frame_per_chunk), sizeof(uint8_t)); file_out.write(reinterpret_cast(&metadata_total_frame1), sizeof(uint32_t)); file_out.write(reinterpret_cast(&metadata_total_frame2), sizeof(uint32_t)); file_out.write(reinterpret_cast(&metadata_width1), sizeof(uint16_t)); file_out.write(reinterpret_cast(&metadata_height1), sizeof(uint16_t)); file_out.write(reinterpret_cast(&metadata_width2), sizeof(uint16_t)); file_out.write(reinterpret_cast(&metadata_height2), sizeof(uint16_t)); } // --- { printf("Writing palette map...\n"); file_out.write(reinterpret_cast(buffer_palette1), PALETTE_SIZE*sizeof(uint16_t)); file_out.write(reinterpret_cast(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(&size_compress), sizeof(uint32_t)); file_out.write(reinterpret_cast(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(&size_compress), sizeof(uint32_t)); file_out.write(reinterpret_cast(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(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"); }