2023-11-17 12:30:24 -08:00
|
|
|
#include <Magick++.h>
|
|
|
|
#include <iostream>
|
|
|
|
#include <fstream>
|
|
|
|
#include <map>
|
|
|
|
#include "palette.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <exception>
|
2023-11-21 19:15:08 -08:00
|
|
|
#include <lib.h>
|
2023-12-01 15:46:43 -08:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <filesystem>
|
2023-12-01 16:01:45 -08:00
|
|
|
#include "win32compat.h"
|
2023-11-17 12:30:24 -08:00
|
|
|
#include "bitmapx16.h"
|
|
|
|
using std::vector;
|
|
|
|
using std::map;
|
|
|
|
using std::stoi;
|
|
|
|
using namespace Magick;
|
|
|
|
void usage() {
|
2023-12-01 15:46:43 -08:00
|
|
|
printf("Usage: bmxconvert: [options]\n");
|
2023-11-17 12:30:24 -08:00
|
|
|
printf("Options may be:\n");
|
|
|
|
printf("-in <input>\n");
|
|
|
|
printf("\tSets the input file.\n");
|
|
|
|
printf("-out <output>\n");
|
|
|
|
printf("\tSets the output file");
|
|
|
|
printf("-bpp <bpp>\n");
|
2023-11-17 14:12:46 -08:00
|
|
|
printf("\tSets the desired bits per pixel. May be 0 (default), 1, 2, 4, or 8. 0: automatic.\n");
|
2023-11-17 12:30:24 -08:00
|
|
|
printf("-significant <color count>\n");
|
|
|
|
printf("\tSets the desired number of significant palette entries. Must be at least 0 and at most 256. 0 means automatic. Default: 0\n");
|
|
|
|
printf("-resize <width> <height>\n");
|
|
|
|
printf("\tResizes the image before converting.\n");
|
|
|
|
printf("-dither\n");
|
|
|
|
printf("\tEnables dithering of the output\n");
|
|
|
|
printf("-border <r> <g> <b>\n");
|
|
|
|
printf("\tIf possible, adds a border color with the specified RGB values which are in the range of 0-15.\n");
|
2023-12-01 15:46:43 -08:00
|
|
|
printf("-border-idx <palette index>\n");
|
|
|
|
printf("\tSets the border color by palette index\n");
|
2023-11-17 12:30:24 -08:00
|
|
|
printf("-reverse\n");
|
|
|
|
printf("\tConverts to PC formats. Incompatible with -dither, -type, and -significant - they will be ignored.\n");
|
2023-11-18 12:55:26 -08:00
|
|
|
printf("-debug <flags>\n");
|
|
|
|
printf("\tEnables debugging flags. May contain any number of single-letter debugging flags.\n");
|
|
|
|
printf("\tFlags: p = show palette entry, c = show palette closeness lookups\n");
|
|
|
|
printf("\tUse an ! before a flag to disable it.\n");
|
2023-11-21 19:15:08 -08:00
|
|
|
printf("-compress\n");
|
|
|
|
printf("\tCompresses the image with LZSA compression\n");
|
2023-12-01 15:46:43 -08:00
|
|
|
printf("-probe\n");
|
|
|
|
printf("\tDisplays information about a file, assuming BMX format.");
|
|
|
|
printf("-palette-file <file>\n");
|
|
|
|
printf("\tSets the palette file to use.\n");
|
2023-11-17 12:30:24 -08:00
|
|
|
printf("-help\n");
|
|
|
|
printf("\tDisplays this help message.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
|
|
const char *input = NULL, *output = NULL;
|
|
|
|
size_t tw = 0, th = 0; // Target width & height;
|
|
|
|
uint8_t tbpp = 0;
|
|
|
|
uint16_t tcolorcount = 0;
|
2023-11-18 12:55:26 -08:00
|
|
|
const char *error_msg_part = "load the image";
|
2023-12-01 15:46:43 -08:00
|
|
|
const char *palette_file = NULL;
|
2023-11-17 12:30:24 -08:00
|
|
|
bool dither = false;
|
|
|
|
bool reverse = false;
|
2023-11-21 19:15:08 -08:00
|
|
|
bool compress = false;
|
2023-11-17 12:30:24 -08:00
|
|
|
uint8_t br, bg, bb;
|
|
|
|
bool border_set = false;
|
2023-12-01 15:46:43 -08:00
|
|
|
bool probe_only = false;
|
2023-12-01 16:07:57 -08:00
|
|
|
std::string executable_path = std::filesystem::weakly_canonical(*argv).parent_path().generic_string();
|
2023-12-01 15:46:43 -08:00
|
|
|
#ifdef _WIN32
|
2023-12-01 16:01:45 -08:00
|
|
|
setenv("MAGICK_CODER_MODULE_PATH", executable_path.c_str(), 0);
|
2023-12-01 15:46:43 -08:00
|
|
|
#endif
|
2023-11-17 12:30:24 -08:00
|
|
|
InitializeMagick(*argv);
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
while (argc > 0) {
|
|
|
|
if (!strcmp(argv[0], "-in")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
input = argv[0];
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (!strcmp(argv[0], "-out")) {
|
|
|
|
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
output = argv[0];
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (!strcmp(argv[0], "-bpp")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
tbpp = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
switch (tbpp) {
|
|
|
|
case 0:
|
2023-11-17 14:12:46 -08:00
|
|
|
case 1:
|
2023-11-17 12:30:24 -08:00
|
|
|
case 2:
|
|
|
|
case 4:
|
|
|
|
case 8:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (!strcmp(argv[0], "-significant")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
tcolorcount = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if (tcolorcount > 256) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (!strcmp(argv[0], "-resize")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
tw = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
th = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (!strcmp(argv[0], "-border")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
border_set = true;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
br = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
bg = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
bb = stoi(argv[0]);
|
|
|
|
} catch (std::exception&) {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (br > 15 || bg > 15 || bb > 15) {
|
|
|
|
printf("Border RGB values must be in the range of 0-15.\n");
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
} else if (!strcmp(argv[0], "-dither")) {
|
|
|
|
dither = true;
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
} else if (!strcmp(argv[0], "-reverse")) {
|
|
|
|
reverse = true;
|
|
|
|
argc--;
|
|
|
|
argv++;
|
2023-11-21 19:15:08 -08:00
|
|
|
} else if (!strcmp(argv[0], "-compress")) {
|
|
|
|
compress = true;
|
|
|
|
argc--;
|
|
|
|
argv++;
|
2023-12-01 15:46:43 -08:00
|
|
|
} else if (!strcmp(argv[0], "-probe")) {
|
|
|
|
probe_only = true;
|
|
|
|
argc--;
|
|
|
|
argv++;
|
2023-11-17 12:30:24 -08:00
|
|
|
} else if (!strcmp(argv[0], "-help")) {
|
|
|
|
usage();
|
2023-12-01 15:46:43 -08:00
|
|
|
} else if (!strcmp(argv[0], "-palette-file")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
palette_file = argv[0];
|
|
|
|
argc--;
|
|
|
|
argv++;
|
2023-11-18 12:55:26 -08:00
|
|
|
} else if (!strcmp(argv[0], "-debug")) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
if (!argc || argv[0][0] == '-') {
|
|
|
|
usage();
|
|
|
|
}
|
|
|
|
bool flag_enable = true;
|
|
|
|
for (size_t i = 0; i < strlen(argv[0]); i++) {
|
|
|
|
switch (argv[0][i]) {
|
|
|
|
case '!': {
|
|
|
|
flag_enable = false;
|
|
|
|
} break;
|
|
|
|
case 'p': {
|
|
|
|
BitmapX16::set_debug_flag(DebugShowPalette, flag_enable);
|
|
|
|
} break;
|
|
|
|
case 'c': {
|
|
|
|
BitmapX16::set_debug_flag(DebugShowCloseness, flag_enable);
|
|
|
|
} break;
|
|
|
|
default: {
|
|
|
|
printf("Error: Invalid debugging flag.\n");
|
|
|
|
usage();
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
if (argv[0][i] != '!') {
|
|
|
|
flag_enable = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
argc--;
|
|
|
|
argv++;
|
2023-12-01 15:46:43 -08:00
|
|
|
} else if (strlen(argv[0]) == 0) {
|
|
|
|
// Skip empty arguments.
|
|
|
|
argc--;
|
|
|
|
argv++;
|
2023-11-17 12:30:24 -08:00
|
|
|
} else {
|
2023-12-01 15:46:43 -08:00
|
|
|
printf("Error: Invalid command line argument: '%s'\n", argv[0]);
|
2023-11-17 12:30:24 -08:00
|
|
|
usage();
|
|
|
|
}
|
|
|
|
}
|
2023-12-01 15:46:43 -08:00
|
|
|
if (input == NULL || ((output != NULL) == probe_only)) {
|
|
|
|
if (probe_only) {
|
|
|
|
printf("Input must be specified and output must not be!\n");
|
|
|
|
} else {
|
|
|
|
printf("Input and output must be specified!\n");
|
|
|
|
}
|
2023-11-17 12:30:24 -08:00
|
|
|
usage();
|
|
|
|
}
|
|
|
|
if (tbpp == 0) tbpp = 8;
|
|
|
|
if (tcolorcount == 0) tcolorcount = (1 << tbpp);
|
2023-12-07 13:04:14 -08:00
|
|
|
if (probe_only) {
|
|
|
|
tw = 0;
|
|
|
|
th = 0;
|
|
|
|
tbpp = 0;
|
|
|
|
tcolorcount = 0;
|
|
|
|
}
|
2023-11-17 12:30:24 -08:00
|
|
|
try {
|
|
|
|
BitmapX16 bitmap;
|
2023-11-21 19:15:08 -08:00
|
|
|
bitmap.enable_compression(compress);
|
2023-11-17 12:30:24 -08:00
|
|
|
if (reverse) {
|
2023-11-18 12:55:26 -08:00
|
|
|
printf("Loading X16 bitmap file '%s' to be convertedto a PC format...\n", input);
|
2023-11-17 12:30:24 -08:00
|
|
|
bitmap.load_x16(input);
|
2023-11-18 12:55:26 -08:00
|
|
|
printf("Image has %u significant colors starting at %u and is %u bpp\n", bitmap.get_significant(), bitmap.get_significant_start(), bitmap.get_bpp());
|
2023-11-17 12:30:24 -08:00
|
|
|
} else {
|
2023-11-18 12:55:26 -08:00
|
|
|
printf("Loading PC image file '%s' to be converted to the X16 bitmap format...\n", input);
|
|
|
|
printf("Using at most %u colors (excluding border color) at %u bpp\n", tcolorcount, tbpp);
|
2023-12-07 13:04:14 -08:00
|
|
|
if (palette_file != NULL) {
|
|
|
|
printf("Using palette file '%s'\n", palette_file);
|
|
|
|
} else {
|
|
|
|
printf("Using generated palette.\n");
|
|
|
|
}
|
2023-11-21 19:15:08 -08:00
|
|
|
if (bitmap.compression_enabled()) printf("Compression enabled\n");
|
2023-11-17 12:30:24 -08:00
|
|
|
bitmap.load_pc(input);
|
|
|
|
}
|
|
|
|
if (tw != 0 && th != 0) {
|
2023-11-18 12:55:26 -08:00
|
|
|
error_msg_part = "resize the image";
|
2023-11-17 12:30:24 -08:00
|
|
|
bitmap.queue_resize(tw, th);
|
|
|
|
}
|
2023-12-01 15:46:43 -08:00
|
|
|
if (probe_only) {
|
|
|
|
error_msg_part = "obtain information";
|
|
|
|
tbpp = bitmap.get_bpp();
|
|
|
|
uint8_t border = bitmap.get_border_color();
|
|
|
|
uint16_t w, h;
|
|
|
|
w = bitmap.get_width();
|
|
|
|
h = bitmap.get_height();
|
|
|
|
uint8_t entry_count = bitmap.get_significant();
|
|
|
|
uint8_t entry_start = bitmap.get_significant_start();
|
2023-12-07 13:04:14 -08:00
|
|
|
printf("Image BPP: %u\n", tbpp);
|
|
|
|
printf("Image size: (%u, %u)\n", w, h);
|
|
|
|
printf("Palette starts at %u, has %u entries, and ends at %u\n", entry_start, entry_count == 0 ? 256 : entry_count, entry_start + entry_count);
|
|
|
|
printf("Palette:\n");
|
|
|
|
vector<PaletteEntry> entries = bitmap.get_palette();
|
|
|
|
for (size_t i = 0; i < entries.size(); i+=8) {
|
|
|
|
for (size_t j = 0; j < 8; j++) {
|
|
|
|
if (i + j > entries.size()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
printf("[%u] = %s, ", i + j, entries[i + j].to_string().c_str());
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
return 0;
|
2023-12-01 15:46:43 -08:00
|
|
|
}
|
2023-11-17 12:30:24 -08:00
|
|
|
if (reverse) {
|
2023-11-18 12:55:26 -08:00
|
|
|
error_msg_part = "write the file";
|
|
|
|
printf("Writing PC image file...\n");
|
2023-11-17 12:30:24 -08:00
|
|
|
bitmap.write_pc(output);
|
|
|
|
} else {
|
2023-11-18 12:55:26 -08:00
|
|
|
error_msg_part = "apply the settings";
|
|
|
|
printf("Applying settings...\n");
|
2023-12-01 15:46:43 -08:00
|
|
|
if (palette_file != NULL) {
|
|
|
|
bitmap.read_palette(palette_file);
|
|
|
|
}
|
2023-11-17 12:30:24 -08:00
|
|
|
bitmap.enable_dithering(dither);
|
|
|
|
bitmap.set_bpp(tbpp);
|
|
|
|
bitmap.set_significant(tcolorcount);
|
|
|
|
if (border_set) {
|
|
|
|
PaletteEntry entry(br, bg, bb);
|
|
|
|
bitmap.set_border_color(bitmap.add_palette_entry(entry));
|
|
|
|
}
|
|
|
|
bitmap.apply();
|
2023-11-18 12:55:26 -08:00
|
|
|
uint8_t significant_start = bitmap.get_significant_start();
|
|
|
|
uint8_t significant_count = bitmap.get_significant();
|
|
|
|
uint8_t significant_end = significant_start + significant_count;
|
|
|
|
printf("Significant colors start at %u and end at %u (%u entries)\n", significant_start, significant_end, significant_count);
|
|
|
|
printf("Writing X16 bitmap file...\n");
|
|
|
|
error_msg_part = "write the file";
|
2023-11-17 12:30:24 -08:00
|
|
|
bitmap.write_x16(output);
|
|
|
|
}
|
|
|
|
} catch (std::exception &e) {
|
2023-11-18 12:55:26 -08:00
|
|
|
printf("Failed to %s!\n", error_msg_part);
|
2023-12-01 15:46:43 -08:00
|
|
|
return 1;
|
2023-11-17 12:30:24 -08:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|