png2hicolorgb: merge in upstream changes

- Added --palendbit : Sets unused bit .15 = 1 for last u16 entry in palette data indicating end, instead of having to count or check the current scanline. 
- Added --addendcolor=N: Appends 32 x color N (in hex BGR555, 64 extra bytes total) in the palette data to clear the background and avoid artifacts on source images that are shorter than the screen height. 
- Added --precompiled mode
This commit is contained in:
bbbbbr
2025-01-11 01:20:31 -08:00
parent 22f287b1b6
commit 4512564d78
22 changed files with 546 additions and 232 deletions

View File

@@ -35,7 +35,7 @@ bool file_c_output_write(const char * fname_base, int bank_num, int tile_count,
ch++;
}
log_verbose("Writing C format to: %s, %s\n", filename_c, filename_h);
VERBOSE("Writing C format to: %s, %s\n", filename_c, filename_h);
// === C Source output ===
FILE * file_out_c = fopen(filename_c, "w");

View File

@@ -3,6 +3,8 @@
#ifndef C_SOURCE_H
#define C_SOURCE_H
#include <stdbool.h>
bool file_c_output_write(const char * fname_base, int bank_num, int tile_count, int height_in_tiles);
#endif

View File

@@ -3,6 +3,8 @@
#ifndef _COMMON_H
#define _COMMON_H
#include <stdbool.h>
#define TILE_HEIGHT_PX 8
#define TILE_WIDTH_PX 8
@@ -13,6 +15,15 @@
#define MAX_PATH (MAX_STR_LEN)
#ifndef __has_attribute
#define __has_attribute(attr) 0
#endif
#if __has_attribute(format)
#define FMT(kind, str_idx, first_to_check) __attribute__((format(kind, str_idx, first_to_check)))
#else
#define FMT(kind, str_idx, first_to_check)
#endif
enum {
IMG_TYPE_PNG
};

View File

@@ -7,7 +7,7 @@
// Read from a file into a buffer (will allocate needed memory)
// Returns NULL if reading file didn't succeed
uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size) {
uint8_t * file_read_into_buffer(char * filename, size_t *ret_size) {
long fsize;
FILE * file_in = fopen(filename, "rb");
@@ -22,12 +22,12 @@ uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size) {
filedata = (uint8_t *)malloc(fsize);
if (filedata) {
if (fsize != fread(filedata, 1, fsize, file_in)) {
if (fsize != (long)fread(filedata, 1, fsize, file_in)) {
printf("Warning: File read size didn't match expected for %s\n", filename);
filedata = NULL;
}
// Read was successful, set return size
*ret_size = fsize;
*ret_size = (size_t)fsize;
} else printf("ERROR: Failed to allocate memory to read file %s\n", filename);
} else printf("ERROR: Failed to read size of file %s\n", filename);
@@ -41,10 +41,9 @@ uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size) {
// Writes a buffer to a file
bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len) {
bool file_write_from_buffer(char * filename, uint8_t * p_buf, size_t data_len) {
bool status = false;
size_t wrote_bytes;
FILE * file_out = fopen(filename, "wb");
if (file_out) {
@@ -64,7 +63,7 @@ bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len)
// Read from a file into a buffer (will allocate needed memory)
// Returns NULL if reading file didn't succeed
char * file_read_into_buffer_char(char * filename, uint32_t *ret_size) {
char * file_read_into_buffer_char(char * filename, size_t *ret_size) {
long fsize;
// On windows windows fread() will auto-translate CRLF to just LF and
@@ -81,12 +80,12 @@ char * file_read_into_buffer_char(char * filename, uint32_t *ret_size) {
filedata = (char *)malloc(fsize);
if (filedata) {
if (fsize != fread(filedata, 1, fsize, file_in)) {
if (fsize != (long)fread(filedata, 1, fsize, file_in)) {
printf("Warning: File read size didn't match expected for %s\n", filename);
filedata = NULL;
}
// Read was successful, set return size
*ret_size = fsize;
*ret_size = (size_t)fsize;
} else printf("ERROR: Failed to allocate memory to read file %s\n", filename);
} else printf("ERROR: Failed to read size of file %s\n", filename);
@@ -100,10 +99,9 @@ char * file_read_into_buffer_char(char * filename, uint32_t *ret_size) {
// Writes a buffer to a file
bool file_write_from_buffer_char(char * filename, char * p_buf, uint32_t data_len) {
bool file_write_from_buffer_char(char * filename, char * p_buf, size_t data_len) {
bool status = false;
size_t wrote_bytes;
FILE * file_out = fopen(filename, "w");
if (file_out) {

View File

@@ -1,11 +1,14 @@
#ifndef _FILES_H
#define _FILES_H
uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size);
bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len);
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
char * file_read_into_buffer_char(char * filename, uint32_t *ret_size);
bool file_write_from_buffer_char(char * filename, char * p_buf, uint32_t data_len);
uint8_t * file_read_into_buffer(char * filename, size_t *ret_size);
bool file_write_from_buffer(char * filename, uint8_t * p_buf, size_t data_len);
char * file_read_into_buffer_char(char * filename, size_t *ret_size);
bool file_write_from_buffer_char(char * filename, char * p_buf, size_t data_len);
#endif // _FILES_H

View File

@@ -47,12 +47,36 @@ enum conversion_types {
CONV_TYPE_MAX = CONV_TYPE_WU
};
enum pattern_types {
HICOLOR_PATTERN_ADAPTIVE_FAST = 0,
HICOLOR_PATTERN_ADAPTIVE_MED = 1,
HICOLOR_PATTERN_ADAPTIVE_BEST = 2,
HICOLOR_PATTERN_ADAPTIVE_COUNT = 3,
HICOLOR_PATTERN_FIXED_COUNT = 80,
HICOLOR_PATTERN_MIN = 0,
HICOLOR_PATTERN_MAX = (HICOLOR_PATTERN_FIXED_COUNT - 1),
HICOLOR_PATTERN_OPT_MIN = 0,
HICOLOR_PATTERN_OPT_MAX = ((HICOLOR_PATTERN_FIXED_COUNT + HICOLOR_PATTERN_ADAPTIVE_COUNT) - 1),
HICOLOR_PATTERN_NOT_FOUND = 998,
HICOLOR_PATTERN_NOT_FOUND_HAS_CHARS = 999
};
#define CONV_SIDE_LEFT 0
#define CONV_SIDE_RIGHT 1
#define CONV_Y_SHIFT_UP_1 1
#define CONV_Y_SHIFT_NO 0
#define SM83_OPCODE_HALT 0x76u
#define SM83_OPCODE_LD_HL_B 0x70u
#define SM83_OPCODE_LD_HL_C 0x71u
#define SM83_OPCODE_LD_HL_D 0x72u
#define SM83_OPCODE_LD_HL_E 0x73u
#define SM83_OPCODE_LD_HL_IMM8 0x36u
#define SM83_OPCODE_RET 0xC9u
#define PAL_REGION_HEIGHT_PX 2
@@ -66,4 +90,9 @@ enum conversion_types {
#define VALIDATE_WIDTH 160 // (BUF_WIDTH)
#define VALIDATE_HEIGHT (BUF_HEIGHT)
#define BYTES_PER_COLOR 2
#define COLORS_PER_PAL 4
#define PALS_PER_SIDE 4
#endif

View File

@@ -1,6 +1,8 @@
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdint.h>
@@ -10,13 +12,13 @@
#include "wu.h"
#include <common.h>
#include <options.h>
#include <files.h>
#include <image_info.h>
#include <logging.h>
#include <c_source.h>
#include <tile_dedupe.h>
#include "common.h"
#include "options.h"
#include "files.h"
#include "image_info.h"
#include "logging.h"
#include "c_source.h"
#include "tile_dedupe.h"
/* Gameboy Hi-Colour Convertor */
/* Glen Cook */
@@ -143,17 +145,23 @@ static uint8_t *pBitsdest = Bitsdest;
#define MAX_CONVERSION_TYPES 83
#define MAX_QUANTISER_TYPES 4
int image_y_min;
int image_y_max;
int image_height;
int y_region_count_left;
int y_region_count_right;
int y_region_count_lr_rndup;
int y_region_count_both_sides;
int y_height_in_tiles_left;
int y_height_in_tiles_right;
int y_height_in_tiles;
int y_height_in_tiles_lr_rndup;
pattern_entry named_patterns[] = {
{.num = HICOLOR_PATTERN_ADAPTIVE_FAST, .name="adaptive-fast"},
{.num = HICOLOR_PATTERN_ADAPTIVE_MED, .name="adaptive-medium"},
{.num = HICOLOR_PATTERN_ADAPTIVE_BEST, .name="adaptive-best"}
};
static unsigned int image_y_min;
static unsigned int image_y_max;
static unsigned int image_height;
static unsigned int y_region_count_left;
static unsigned int y_region_count_right;
static unsigned int y_region_count_lr_rndup;
static unsigned int y_region_count_both_sides;
static unsigned int y_height_in_tiles_left;
static unsigned int y_height_in_tiles_right;
static unsigned int y_height_in_tiles;
static unsigned int y_height_in_tiles_lr_rndup;
static void PrepareTileSet(void);
@@ -163,6 +171,7 @@ static void PrepareAttributes(void);
static void DedupeTileset(void);
static void ExportPalettes(const char * fname_base);
static void ExportPalettesPrecompiled(const char * fname_base);
static void ExportTileSet(const char * fname_base);
static void ExportMap(const char * fname_base);
static void ExportMapAttributes(const char * fname_base);
@@ -170,13 +179,14 @@ static void ExportMapAttributes(const char * fname_base);
void hicolor_init(void) {
// Defaults
LConversion = 3; // Default Conversion (Fixed 3-2-3-2) Left Screen
RConversion = 3; // Default Conversion (Fixed 3-2-3-2) Righ Screen
ConvertType = 1; // Normal default is 1 ("Median cut - no dither")
LConversion = HICOLOR_PATTERN_ADAPTIVE_MED; // Default Conversion adaptive-medium Left Screen
RConversion = HICOLOR_PATTERN_ADAPTIVE_MED; // Default Conversion adaptive-medium Righ Screen
ConvertType = CONV_TYPE_MED_CUT_NO_DITHER; // Normal default is 1 ("Median cut - no dither")
}
static void hicolor_vars_prep(image_data * p_loaded_image) {
log_debug("hicolor_vars_prep()\n");
DBG("hicolor_vars_prep()\n");
image_height = p_loaded_image->height;
image_y_min = 0;
@@ -204,25 +214,53 @@ static void hicolor_vars_prep(image_data * p_loaded_image) {
}
// Look up user specified L/R pattern by name if possible
unsigned int hicolor_get_pattern_by_name(const char * opt_str) {
char opt_str_lower[MAX_STR_LEN];
unsigned int c;
// Convert user input to lowercase first
for(c = 0; (opt_str[c] != '\0') && (c < MAX_STR_LEN); c++)
opt_str_lower[c] = tolower(opt_str[c]);
opt_str_lower[c] = '\0';
// Return if it matches any names in the named pattern list
for (c = 0; c < ARRAY_LEN(named_patterns); c++) {
if (strcmp(opt_str_lower, named_patterns[c].name) == 0)
return named_patterns[c].num;
}
// If there was no match, return if it contained any non-digit characters,
// meaning it should not later be converted as a raw numeric value for the option
while (*opt_str != '\0') {
if (isdigit(*opt_str) == 0) {
return HICOLOR_PATTERN_NOT_FOUND_HAS_CHARS;
}
opt_str++;
}
return HICOLOR_PATTERN_NOT_FOUND;
}
void hicolor_set_convert_left_pattern(uint8_t new_value) {
// IDC_CONVERTLEFT
LConversion = new_value;
log_verbose("HiColor: Left pattern set to %d\n", new_value);
VERBOSE("HiColor: Left pattern set to %d\n", new_value);
}
void hicolor_set_convert_right_pattern(uint8_t new_value) {
// IDC_CONVERTRIGHT
RConversion = new_value;
log_verbose("HiColor: Right pattern set to %d\n", new_value);
VERBOSE("HiColor: Right pattern set to %d\n", new_value);
}
void hicolor_set_type(uint8_t new_value) {
// IDC_CONVERTTYPE
ConvertType = new_value;
log_verbose("HiColor: Convert type set to %d\n", new_value);
VERBOSE("HiColor: Convert type set to %d\n", new_value);
}
@@ -230,14 +268,14 @@ void hicolor_set_type(uint8_t new_value) {
// Equivalent of former file loading
static void hicolor_image_import(image_data * p_loaded_image) {
log_debug("hicolor_image_import()\n");
DBG("hicolor_image_import()\n");
// TODO: input guarding
// TODO: deduplicate some of the array copying around
uint8_t * p_input_img = p_loaded_image->p_img_data;
for (int y=0; y< image_height; y++) {
for (int x=0; x< 160; x++) {
for (unsigned int y=0; y< image_height; y++) {
for (unsigned int x=0; x< 160; x++) {
// Clamp to CGB max R/G/B value in RGB 888 mode (31u << 3)
// png_image[].rgb -> pic2[].rgb -> pBitssource[].bgr??
@@ -253,9 +291,9 @@ static void hicolor_image_import(image_data * p_loaded_image) {
// It's convoluted, but pBitssource & pBitsdest are used for:
// - display as windows DIBs (formerly)
// - and for some calculations at the end of ConvertRegions()
for (int y=0; y<image_height; y++) {
for (int x=0; x<160; x++) {
for (int z=0; z<3; z++) {
for (unsigned int y=0; y<image_height; y++) {
for (unsigned int x=0; x<160; x++) {
for (unsigned int z=0; z<3; z++) {
// TODO: (2-z) seems to be swapping RGB for BGR?
*(pBitssource+(image_y_max-y)*3*160+x*3+z) = pic2[x][y][2-z]; // Invert the dib, cos windows likes it like that !!
}
@@ -268,17 +306,17 @@ static void hicolor_image_import(image_data * p_loaded_image) {
// TODO: fix
// TODO: Operates on RGB data in pic[] copied from RGB data in pic2
static void hicolor_convert(void) {
log_debug("hicolor_convert()\n");
DBG("hicolor_convert()\n");
for(int x=0; x<160; x++)
for(unsigned int x=0; x<160; x++)
{
for(int y=0; y<image_height; y++)
for(unsigned int y=0; y<image_height; y++)
{
pic[x][y][0] = pic2[x][y][0];
pic[x][y][1] = pic2[x][y][1];
pic[x][y][2] = pic2[x][y][2];
for(int i=0; i<3; i++)
for(unsigned int i=0; i<3; i++)
{
*(Data + y*160*3+x*3+i) = pic[x][y][i];
}
@@ -294,7 +332,7 @@ static void hicolor_save(const char * fname_base) {
// Default tile count to non-deduplicated number
int tile_count = y_height_in_tiles * (160 / TILE_WIDTH_PX);
log_debug("hicolor_save()\n");
DBG("hicolor_save()\n");
PrepareTileSet();
PrepareMap();
PrepareAttributes();
@@ -305,7 +343,11 @@ static void hicolor_save(const char * fname_base) {
}
ExportTileSet(fname_base);
ExportPalettes(fname_base);
if (opt_get_precompiled_palette())
ExportPalettesPrecompiled(fname_base);
else
ExportPalettes(fname_base);
ExportMap(fname_base);
ExportMapAttributes(fname_base);
@@ -317,7 +359,7 @@ static void hicolor_save(const char * fname_base) {
// Currently expects width x height x 3(RGB888)
void hicolor_process_image(image_data * p_loaded_image, const char * fname_base) {
log_debug("hicolor_process_image(), fname_base: \"%s\"\n", fname_base);
DBG("hicolor_process_image(), fname_base: \"%s\"\n", fname_base);
hicolor_vars_prep(p_loaded_image);
hicolor_image_import(p_loaded_image);
@@ -340,8 +382,8 @@ static void DedupeTileset(void)
TileCountDeduped = 0;
// Traverse all tiles in the image/map
for (int mapy = 0; mapy < y_height_in_tiles; mapy++) {
for (int mapx = 0; mapx < 20; mapx++) {
for (unsigned int mapy = 0; mapy < y_height_in_tiles; mapy++) {
for (unsigned int mapx = 0; mapx < 20; mapx++) {
map_tile_id = MapTileIDs[mapx][mapy];
map_tile_id += (MapAttributes[mapx][mapy] & CGB_ATTR_TILES_BANK) ? CGB_TILES_START_BANK_1 : CGB_TILES_START_BANK_0;
@@ -360,7 +402,7 @@ static void DedupeTileset(void)
MapAttributes[mapx][mapy] = (MapAttributes[mapx][mapy] & CGB_ATTR_PALETTES_ONLY) | new_attribs;
}
}
log_verbose("DedupeTileset(): Reduced tiles from %d (%d bytes) to %d (%d bytes) = %d bytes saved. %%%d of original size\n",
VERBOSE("DedupeTileset(): Reduced tiles from %d (%d bytes) to %d (%d bytes) = %d bytes saved. %%%d of original size\n",
map_tile_id + 1, (map_tile_id + 1) * TILE_SZ, TileCountDeduped, TileCountDeduped * TILE_SZ,
((map_tile_id + 1) * TILE_SZ) - (TileCountDeduped * TILE_SZ), (TileCountDeduped * 100) / (map_tile_id + 1));
}
@@ -370,7 +412,6 @@ static void DedupeTileset(void)
static void PrepareTileSet(void) {
uint32_t byteWritten;
u32 x, y;
u8 c1,c2;
u8 dx,dy;
@@ -408,11 +449,11 @@ static void PrepareMap(void) {
// Set up export Map Tile IDs
// Note: The indexes are clipped to 0-255 (instead of 0-512),
// the attribute tile index+256 bit is auto-calculated in the attribute map in PrepareAttributes()
int tile_id = 0;
for (int mapy = 0; mapy < y_height_in_tiles; mapy++) {
for (int mapx = 0; mapx < 20; mapx++) {
uint8_t tile_id = 0;
for (unsigned int mapy = 0; mapy < y_height_in_tiles; mapy++) {
for (unsigned int mapx = 0; mapx < 20; mapx++) {
MapTileIDs[mapx][mapy] = (uint8_t)tile_id;
MapTileIDs[mapx][mapy] = tile_id;
tile_id++;
}
}
@@ -422,9 +463,9 @@ static void PrepareMap(void) {
static void PrepareAttributes(void) {
// Set up the Map Attributes table
unsigned int tile_id = 0;
for(int MastY=0;MastY<y_height_in_tiles_right;MastY++)
for(unsigned int MastY=0;MastY<y_height_in_tiles_right;MastY++)
{
for(int MastX=0;MastX<2;MastX++)
for(unsigned int MastX=0;MastX<2;MastX++)
{
int Line=Best[MastX][MastY];
int width=0;
@@ -454,7 +495,7 @@ static void ExportTileSet(const char * fname_base)
strcpy(filename, fname_base);
strcat(filename, ".til");
log_verbose("Writing Tile Patterns to: %s\n", filename);
VERBOSE("Writing Tile Patterns to: %s\n", filename);
if (opt_get_tile_dedupe()) {
@@ -473,16 +514,25 @@ static void ExportTileSet(const char * fname_base)
static void ExportPalettes(const char * fname_base)
{
char filename[MAX_PATH * 2];
uint32_t byteWritten;
uint8_t tmpByte;
s32 i, j, k;
unsigned int i, j, k;
s32 r,g,b,v;
uint16_t pal_end_color_bgr555 = 0x0000u;
int pal_end_color_count = 0;
strcpy(filename, fname_base);
strcat(filename, ".pal");
log_verbose("Writing Palette to: %s\n", filename);
VERBOSE("Writing Palette to: %s\n", filename);
// No longer +1 for the trailing 0x2D
int outbuf_sz_pals = (y_region_count_both_sides * PALS_PER_SIDE * COLORS_PER_PAL * BYTES_PER_COLOR);
// Handle resize if trailing end colors have been appended
if (opt_get_enable_pal_end_color()) {
opt_load_pal_end_color(&pal_end_color_bgr555, &pal_end_color_count);
outbuf_sz_pals += (pal_end_color_count * BYTES_PER_COLOR);
}
int outbuf_sz_pals = (((y_region_count_both_sides) * 4 * 4 * 2) + 1);
uint8_t output_buf[outbuf_sz_pals];
uint8_t * p_buf = output_buf;
@@ -497,7 +547,7 @@ static void ExportPalettes(const char * fname_base)
g = IdealPal[(i%2)*4+j][i/2][k][1];
b = IdealPal[(i%2)*4+j][i/2][k][2];
// TODO: Converting to BGR555 probably
// Converting to BGR555
v = ((b/8)*32*32) + ((g/8)*32) + (r/8);
// 2 bytes per color
@@ -507,8 +557,22 @@ static void ExportPalettes(const char * fname_base)
}
}
// TODO: What is this and why? :)
*p_buf++ = 0x2d;
// Add trailing 32 colors to clear BG if enabled
if (opt_get_enable_pal_end_color()) {
for (int c = 0; c < pal_end_color_count; c++) {
*p_buf++ = (u8)(pal_end_color_bgr555 & 255);
*p_buf++ = (u8)(pal_end_color_bgr555 / 256);
}
}
// Set unused bit .15 = 1 for last u16 palette entry
// to indicate it's the final one
if (opt_get_pal_end_bit())
output_buf[outbuf_sz_pals - 1] |= 0x80u;
// This has an unknown purpose and was present in
// the original source code, but doesn't appear to be needed.
// *p_buf++ = 0x2d;
if (!file_write_from_buffer(filename, output_buf, outbuf_sz_pals))
set_exit_error();
@@ -516,6 +580,104 @@ static void ExportPalettes(const char * fname_base)
}
#define LDHL_2x_SZ 2 // Scale factor for pal color bytes loaded via `ld [hl], <byte>`
#define RET_SZ 1 // Size of ret opcode
#define VBLANK_LOAD_LINE_CNT 2 // Number of lines loaded in vblank
#define HALT_LOAD_SZ 5 // Size of Halt + LD HL, B/C/D/E on non-vblank scanlines
#define STAT_PRELOAD_SAVE_SZ 4 // Number of pal color bytes that get pre-loaded in STAT isr, so don't need 2x sizing for ld [hl], <byte>
#define PAL_BYTES_PER_LINE (PALS_PER_SIDE * COLORS_PER_PAL * BYTES_PER_COLOR)
static void ExportPalettesPrecompiled(const char * fname_base)
{
char filename[MAX_PATH * 2];
unsigned int line, pal, col;
s32 r,g,b,v;
size_t outbuf_sz_pals = 0;
strcpy(filename, fname_base);
strcat(filename, ".pal");
VERBOSE("Writing Pre-compiled Palette to: %s\n", filename);
// How to calculate output size:
//
// VBLANK ISR (2 Lines)
// ~ No wait + load header code
// + Always uses LD [HL] (so 2x num pal bytes)
// + 1 ret shared by the 2 lines
// = (Pal bytes per line x 2) x (2 lines) + 1 ret
// (((4 x 4 x 2) x 2) x 2) + 1 = 129
outbuf_sz_pals = ((PAL_BYTES_PER_LINE * LDHL_2x_SZ) * VBLANK_LOAD_LINE_CNT) + RET_SZ;
// Then...
// STAT ISR (Num Lines - 2)
// - 4 bytes preload in STAT ISR without LD [HL] (so: 4 pal bytes without 2x sizing)
// + Then wait + load header code (so +5 bytes)
// + Then remainder of pal bytes get LD [HL] (so 2x num pal bytes)
// + 1 ret per line
// = (( (Pal bytes per line x 2) - 4 preload bytes + 5 header + 1 ret) x (num lines - 2 vblank lines)
// ( ( (4 x 4 x 2) x 2) - 4) + 5 + 1) = 66 x (num lines - 2)
outbuf_sz_pals += ((PAL_BYTES_PER_LINE * LDHL_2x_SZ) - STAT_PRELOAD_SAVE_SZ + HALT_LOAD_SZ + RET_SZ) * (y_region_count_both_sides - VBLANK_LOAD_LINE_CNT);
uint8_t output_buf[outbuf_sz_pals];
uint8_t * p_buf = output_buf;
// Note: "line" 0 is equivalent to something like scanline -1
// (due to left side region starting 1 scanline before line 0)
for (line = 0; line < (y_region_count_both_sides); line++) // Number of palette sets (left side updates + right side updates)
{
for (pal = 0; pal < 4; pal++) // Each palette in the line
{
for(col = 0; col < 4;col++) // Each color in the palette
{
// Precompiled mode has a "header" inserted after the first two colours of palette 0,
// except for the first two scanline lines (which are during VBlank so can load directly without a preload + wait)
if (line >= 2 && pal == 0 && col == 2) {
*p_buf++ = SM83_OPCODE_HALT;
*p_buf++ = SM83_OPCODE_LD_HL_B;
*p_buf++ = SM83_OPCODE_LD_HL_C;
*p_buf++ = SM83_OPCODE_LD_HL_D;
*p_buf++ = SM83_OPCODE_LD_HL_E;
}
r = IdealPal[(line % 2)*4 + pal][line / 2][col][0];
g = IdealPal[(line % 2)*4 + pal][line / 2][col][1];
b = IdealPal[(line % 2)*4 + pal][line / 2][col][2];
// Converting to BGR555
v = ((b/8)*32*32) + ((g/8)*32) + (r/8);
// Load 2 bytes per color
// Insert LD [HL] opcode before pal data bytes... when:
// - Any time during first two lines (i.e for all pal bytes in vblank)
// - Or is the Second Palette or more (of each Line)
// - Or is the Third Color or more (of each Palette. the STAT isr has pre-load code for the first two pal colors)
if (line < 2 || pal >= 1 || col >= 2) {
*p_buf++ = SM83_OPCODE_LD_HL_IMM8; // ld [hl], <imm8>
}
*p_buf++ = (u8)(v & 255);
if (line < 2 || pal >= 1 || col >= 2) {
*p_buf++ = SM83_OPCODE_LD_HL_IMM8; // ld [hl], <imm8>
}
*p_buf++ = (u8)(v / 256);
}
}
// Skip return for the first palette line (during vblank)
if (line >= 1)
*p_buf++ = SM83_OPCODE_RET;
}
// This has an unknown purpose and was present in
// the original source code, but doesn't appear to be needed.
// *p_buf++ = 0x2d;
if (!file_write_from_buffer(filename, output_buf, outbuf_sz_pals))
set_exit_error();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -525,14 +687,14 @@ static void ExportMap(const char * fname_base)
strcpy(filename, fname_base);
strcat(filename, ".map");
log_verbose("Writing Tile Map to: %s\n", filename);
VERBOSE("Writing Tile Map to: %s\n", filename);
int outbuf_sz_map = (20 * y_height_in_tiles);
uint8_t output_buf_map[outbuf_sz_map];
int tile_id = 0;
for (int y = 0; y < y_height_in_tiles; y++) {
for (int x = 0; x < 20; x++) {
for (unsigned int y = 0; y < y_height_in_tiles; y++) {
for (unsigned int x = 0; x < 20; x++) {
uint8_t tile_num = MapTileIDs[x][y];
// This needs to happen here, after optional deduplication stage
@@ -556,15 +718,15 @@ static void ExportMapAttributes(const char * fname_base)
strcpy(filename, fname_base);
strcat(filename, ".atr");
log_verbose("Writing Attribute Map to: %s\n", filename);
VERBOSE("Writing Attribute Map to: %s\n", filename);
int outbuf_sz_map = (20 * y_height_in_tiles);
uint8_t output_buf_map[outbuf_sz_map];
int tile_id = 0;
for (int y = 0; y < y_height_in_tiles; y++)
for (unsigned int y = 0; y < y_height_in_tiles; y++)
{
for (int x = 0; x < 20; x++)
for (unsigned int x = 0; x < 20; x++)
{
output_buf_map[tile_id++] = MapAttributes[x][y];
}
@@ -653,7 +815,7 @@ RGBQUAD translate(uint8_t rgb[3])
// The higher the adaptive level, the more combinations of attributes are tested.
u8 SplitData[80][4]=
u8 SplitData[HICOLOR_PATTERN_FIXED_COUNT][4]=
{
{3,2,3,2},{2,3,2,3},{2,2,3,3},{2,3,3,2},{3,2,2,3},{3,3,2,2},{4,2,2,2},{2,2,2,4},{2,2,4,2},{2,4,2,2},{1,1,2,6},
{1,1,3,5},{1,1,4,4},{1,1,5,3},{1,1,6,2},{1,2,1,6},{1,2,2,5},{1,2,3,4},{1,2,4,3},{1,2,5,2},{1,2,6,1},{1,3,1,5},
@@ -669,7 +831,7 @@ u8 SplitData[80][4]=
unsigned int ImageRating(u8 *src, u8 *dest, int StartX, int StartY, int Width, int Height)
{
log_debug("ImageRating()\n");
DBG("ImageRating()\n");
unsigned int tot;
int x,y;
unsigned int accum=0;
@@ -694,16 +856,15 @@ unsigned int ImageRating(u8 *src, u8 *dest, int StartX, int StartY, int Width, i
// TODO: rename to something that aligns with other convert functions
void ConvertToHiColor(int ConvertType)
{
log_debug("ConvertToHiColor()\n");
DBG("ConvertToHiColor()\n");
int res;
int x,y,z,i;
unsigned int x,y;
// TODO: Change "Adaptive Pattern" settings to be a separate variable so StartSplit doesn't have to be offset by -3
// Just set these directly:
// * StartSplit (first pattern to start checking with)
// * NumSplit (number of patterns to iterate through for testing, 1 = just use the one in StartSplit)
int StartSplit=0;
int NumSplit=1;
int Steps;
int MastX,MastY;
int Line;
int width;
unsigned int tile_id;
switch(LConversion)
{
@@ -711,55 +872,27 @@ void ConvertToHiColor(int ConvertType)
StartSplit=0;
NumSplit=6;
Steps=504;
break;
case 1:
StartSplit=0;
NumSplit=10;
Steps=792;
break;
case 2:
StartSplit=0;
NumSplit=80;
Steps=5832;
break;
default:
StartSplit=LConversion-3;
NumSplit=1;
Steps=image_height;
break;
}
switch(RConversion)
{
case 0:
Steps+=504;
break;
case 1:
Steps+=792;
break;
case 2:
Steps+=5832;
break;
default:
Steps+=image_height;
break;
}
// Convert left side with one extra tile of height to fix
// the glitching where the last scanline on left bottom region
// lacks tile and palette data
@@ -823,7 +956,7 @@ void ConvertToHiColor(int ConvertType)
raw[1][x][y][2] = GBView.rgbBlue;
}
}
log_progress("\n");
VERBOSE("\n");
}
@@ -833,12 +966,12 @@ void ConvertToHiColor(int ConvertType)
// StartY = 0 - 17 : Starting attribute block
// Height = Number of attribute blocks to check / process
int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, int FinishJ, int ConvertType)
int ConvertRegions(unsigned int StartX, unsigned int Width, unsigned int StartY, unsigned int Height, unsigned int StartJ, unsigned int FinishJ, int ConvertType)
{
log_debug("ConvertRegions()\n");
u32 Accum,width,x1,ts,tw,y2,x2,y_offset;
s32 x,y;
s32 i,j;
DBG("ConvertRegions()\n");
u32 width,x1,ts,tw,y2,x2,y_offset;
unsigned int x,y;
unsigned int i,j;
u8 col;
@@ -856,7 +989,6 @@ int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, in
for(j=StartJ;j<(StartJ+FinishJ);j++)
{
Accum=0;
width=0;
for(i=0;i<4;i++)
{
@@ -867,7 +999,7 @@ int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, in
for(y=StartY*4;y<(StartY+Height)*4;y++)
{
log_progress(".");
VERBOSE(".");
for(x1=0;x1<4;x1++)
{
@@ -876,10 +1008,14 @@ int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, in
for(y2=0;y2<2;y2++)
{
// Skip case where y_line would evaluate to -1 to avoid unsigned wraparound)
// (scanline 0, left side of the image where 80 x 2 pixel box goes from scanline -1 to 0)
if (y_offset > ((y*2) + y2)) continue;
// Skip if Y line is outside image borders (prevents buffer overflow)
// (Left side calcs hang off top and bottom of screen
// due to Left/Right palette update interleaving)
s32 y_line = (y*2+y2-y_offset);
unsigned int y_line = (y*2+y2-y_offset);
if ((y_line < image_y_min) || (y_line > image_y_max)) continue;
for(x2=0;x2<tw;x2++)
@@ -895,12 +1031,12 @@ int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, in
switch(ConvertType)
{
case 0:
to_indexed(Data,4,0,TileWidth[x1],2); // Median Reduction No Dither
to_indexed(Data,0,TileWidth[x1],2); // Median Reduction No Dither
break;
case 1:
to_indexed(Data,4,1,TileWidth[x1],2); // Median Reduction With Dither
to_indexed(Data,1,TileWidth[x1],2); // Median Reduction With Dither
break;
case 2:
@@ -924,9 +1060,13 @@ int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, in
{
for(x2=0;x2<tw;x2++)
{
// Skip case where y_line would evaluate to -1 to avoid unsigned wraparound)
// (scanline 0, left side of the image where 80 x 2 pixel box goes from scanline -1 to 0)
if (y_offset > ((y*2) + y2)) continue;
// Skip if Y line is outside image borders (prevents buffer overflow)
// since Left side calcs hang off top and bottom of image/screen
s32 y_line = (y*2+y2-y_offset);
unsigned int y_line = (y*2+y2-y_offset);
if ((y_line < image_y_min) || (y_line > image_y_max)) continue;
col=Picture256[y2*tw+x2];

View File

@@ -13,13 +13,18 @@
// For displaying available conversion patterns to the user
#define HELP_CONV_PATTERN_STR \
"Available conversion palette/attribute pattern widths for -cL:N and -cR:N\n" \
"Available screen palette arrangement widths for -L=N and -R=N\n" \
"\n" \
"- Default for Left and Right is 3\n" \
"- Fixed pattern widths are faster but less optimal than adaptive\n" \
"- Adaptive pattern width speeds are 1: fastest, 2: medium, 3: slowest (slower = higher quality)\n" \
"- Default for Left and Right is \"adaptive-medium\"\n" \
"- Fixed settings are faster but the converted image tends to have more visual artifacts.\n" \
"- Adaptive settings try to find an optimal palette arrangement.\n" \
"- The adaptive tiers select between a trade-off of faster conversion or reduced visual artifacts.\n" \
"\n" \
" 0: Adaptive-1 1: Adaptive-2 2: Adaptive-3 3: 3-2-3-2 4: 2-3-2-3 \n" \
" Adaptive screen palette arrangements:\n" \
" adaptive-fast, adaptive-medium (**Default**), adaptive-best\n" \
"\n" \
" Fixed screen palette arrangements:\n" \
" 3: 3-2-3-2 4: 2-3-2-3 \n" \
" 5: 2-2-3-3 6: 2-3-3-2 7: 3-2-2-3 8: 3-3-2-2 9: 4-2-2-2 \n" \
" 10: 2-2-2-4 11: 2-2-4-2 12: 2-4-2-2 13: 1-1-2-6 14: 1-1-3-5 \n" \
" 15: 1-1-4-4 16: 1-1-5-3 17: 1-1-6-2 18: 1-2-1-6 19: 1-2-2-5 \n" \
@@ -37,6 +42,11 @@
" 75: 5-1-2-2 76: 5-1-3-1 77: 5-2-1-2 78: 5-2-2-1 79: 5-3-1-1 \n" \
" 80: 6-1-1-2 81: 6-1-2-1 82: 6-2-1-1 \n"
typedef struct pattern_entry {
unsigned int num;
const char * name;
} pattern_entry;
extern u8 TileOffset[4]; // Offset into screen for attribute start
extern u8 TileWidth[4]; // No of character attributes width
@@ -46,6 +56,7 @@ extern RGBQUAD GBView;
void hicolor_init(void);
unsigned int hicolor_get_pattern_by_name(const char * option_name);
void hicolor_set_type(uint8_t new_value);
void hicolor_set_convert_left_pattern(uint8_t new_value);
void hicolor_set_convert_right_pattern(uint8_t new_value);
@@ -57,7 +68,7 @@ void hicolor_process_image(image_data * p_decoded_image, const char * fname);
RGBQUAD translate(uint8_t rgb[3]);
unsigned int ImageRating(u8 *src, u8 *dest, int StartX, int StartY, int Width, int Height);
void ConvertToHiColor(int ConvertType);
int ConvertRegions(int StartX, int Width, int StartY, int Height, int StartJ, int FinishJ, int ConvertType);
int ConvertRegions(unsigned int StartX, unsigned int Width, unsigned int StartY, unsigned int Height, unsigned int StartJ, unsigned int FinishJ, int ConvertType);
#endif

View File

@@ -116,7 +116,7 @@ void update_box_rgb(boxptr boxp)
}
}
have_Rmin:
have_Rmin:
if (Rmax > Rmin)
for (R = Rmax; R >= Rmin; R--)
@@ -131,7 +131,7 @@ void update_box_rgb(boxptr boxp)
}
}
have_Rmax:
have_Rmax:
if (Gmax > Gmin)
for (G = Gmin; G <= Gmax; G++)
@@ -146,7 +146,7 @@ void update_box_rgb(boxptr boxp)
}
}
have_Gmin:
have_Gmin:
if (Gmax > Gmin)
for (G = Gmax; G >= Gmin; G--)
@@ -161,7 +161,7 @@ void update_box_rgb(boxptr boxp)
}
}
have_Gmax:
have_Gmax:
if (Bmax > Bmin)
for (B = Bmin; B <= Bmax; B++)
@@ -176,7 +176,7 @@ void update_box_rgb(boxptr boxp)
}
}
have_Bmin:
have_Bmin:
if (Bmax > Bmin)
for (B = Bmax; B >= Bmin; B--)
@@ -191,7 +191,7 @@ void update_box_rgb(boxptr boxp)
}
}
have_Bmax:
have_Bmax:
// Update box volume.
// We use 2-norm rather than real volume here; this biases the method
@@ -390,7 +390,6 @@ s32 find_nearby_colors(s32 minR,s32 minG,s32 minB,s32 colorlist[])
* the colors that need further consideration.
*/
{
s32 numcolors = actual_number_of_colors;
s32 maxR, maxG, maxB;
s32 centerR, centerG, centerB;
s32 i, x, ncolors;
@@ -694,7 +693,7 @@ void fill_inverse_cmap_rgb(s32 R, s32 G, s32 B)
/* This is pass 1 */
void median_cut_pass1_rgb(u8 *src,u8 *dest,s32 width,s32 height)
void median_cut_pass1_rgb(u8 *src,s32 width,s32 height)
{
s32 num_elems;
ColorFreq *col;
@@ -797,7 +796,7 @@ s32 *init_error_limit(void)
void to_indexed(u8 *input,s32 ncolors,s32 dither,s32 width,s32 height)
void to_indexed(u8 *input,s32 dither,s32 width,s32 height)
{
s32 i, j;
u8 *src;
@@ -828,7 +827,7 @@ void to_indexed(u8 *input,s32 ncolors,s32 dither,s32 width,s32 height)
histogram = AHistorgram;
median_cut_pass1_rgb(input, Picture256, width, height);
median_cut_pass1_rgb(input, width, height);
src=input;
dest=Picture256;

View File

@@ -1,6 +1,8 @@
#ifndef __median_h__
#define __median_h__
#include "defines.h"
#define MAXNUMCOLORS 256
@@ -66,9 +68,9 @@ void compute_color_rgb(boxptr boxp,s32 icolor);
s32 find_nearby_colors(s32 minR,s32 minG,s32 minB,s32 colorlist[]);
void find_best_colors(s32 minR, s32 minG, s32 minB,s32 numcolors,s32 colorlist[],s32 bestcolor[]);
void fill_inverse_cmap_rgb(s32 R, s32 G, s32 B);
void median_cut_pass1_rgb(u8 *src,u8 *dest,s32 width,s32 height);
void median_cut_pass1_rgb(u8 *src,s32 width,s32 height);
s32 *init_error_limit(void);
void to_indexed(u8 *input,s32 ncolors,s32 dither,s32 width,s32 height);
void to_indexed(u8 *input,s32 dither,s32 width,s32 height);
#endif

View File

@@ -2,11 +2,11 @@
// image_info.h
//
#include <stdint.h>
#ifndef IMAGE_INFO_HEADER
#define IMAGE_INFO_HEADER
#include <stdint.h>
#define RGBA_32SZ 4 // 4 bytes
#define RGBA_RED 0
#define RGBA_GREEN 1

View File

@@ -26,7 +26,7 @@ static bool image_load_png(image_data *, const char *);
static bool image_validate(image_data * p_decoded_image) {
if (p_decoded_image->width != VALIDATE_WIDTH) {
log_error("Error: Image width is %d, must be %d\n", p_decoded_image->width, VALIDATE_WIDTH);
ERR("Error: Image width is %d, must be %d\n", p_decoded_image->width, VALIDATE_WIDTH);
return false;
}
@@ -36,17 +36,17 @@ static bool image_validate(image_data * p_decoded_image) {
// Which is the highest possible within the 512 available on the CGB
// without reloading the tiles and maps using an offset
if ((p_decoded_image->height == 0) || (p_decoded_image->height > VALIDATE_HEIGHT)) {
log_error("Error: Image height is %d, must be between 0 and %d\n", p_decoded_image->height, VALIDATE_HEIGHT);
ERR("Error: Image height is %d, must be between 0 and %d\n", p_decoded_image->height, VALIDATE_HEIGHT);
return false;
}
if ((p_decoded_image->height % 8) != 0u) {
log_verbose("Error: Image height must be a multiple of 8, %d is not\n", p_decoded_image->height);
VERBOSE("Error: Image height must be a multiple of 8, %d is not\n", p_decoded_image->height);
return false;
}
if ((p_decoded_image->width % 8) != 0u) {
log_verbose("Error: Image width must be a multiple of 8, %d is not\n", p_decoded_image->width);
VERBOSE("Error: Image width must be a multiple of 8, %d is not\n", p_decoded_image->width);
return false;
}
@@ -67,7 +67,7 @@ static bool image_load_png(image_data * p_decoded_image, const char * filename)
if (error) {
status = false;
log_error("Error: PNG load: %u: %s\n", error, lodepng_error_text(error));
ERR("Error: PNG load: %u: %s\n", error, lodepng_error_text(error));
} else {
p_decoded_image->bytes_per_pixel = RGB_24SZ;
p_decoded_image->size = p_decoded_image->width * p_decoded_image->height * p_decoded_image->bytes_per_pixel;
@@ -86,7 +86,7 @@ bool image_load(image_data * p_decoded_image, const char * filename, const uint8
bool status = true;
log_verbose("Loading image from file: %s, type: %d\n", filename, image_type);
VERBOSE("Loading image from file: %s, type: %d\n", filename, image_type);
switch (image_type) {
case IMG_TYPE_PNG:
@@ -95,7 +95,7 @@ bool image_load(image_data * p_decoded_image, const char * filename, const uint8
default:
status = false;
log_error("Invalid image format. No image will be loaded\n");
ERR("Invalid image format. No image will be loaded\n");
break;
}
@@ -103,10 +103,10 @@ bool image_load(image_data * p_decoded_image, const char * filename, const uint8
status = image_validate(p_decoded_image);
if (status) {
log_verbose("Decoded image.width: %d\n", p_decoded_image->width);
log_verbose("Decoded image.height: %d\n", p_decoded_image->height);
log_verbose("Decoded image.size: %d\n", p_decoded_image->size);
log_verbose("Decoded image.bytes_per_pixel: %d\n", p_decoded_image->bytes_per_pixel);
VERBOSE("Decoded image.width: %d\n", p_decoded_image->width);
VERBOSE("Decoded image.height: %d\n", p_decoded_image->height);
VERBOSE("Decoded image.size: %d\n", p_decoded_image->size);
VERBOSE("Decoded image.bytes_per_pixel: %d\n", p_decoded_image->bytes_per_pixel);
}
return status;

View File

@@ -2,6 +2,8 @@
#ifndef _IMAGE_LOAD_C
#define _IMAGE_LOAD_C
#include <stdbool.h>
#include "image_info.h"
bool image_load(image_data * p_src_image, const char * filename, const uint8_t img_type);

View File

@@ -5,7 +5,7 @@
#include "logging.h"
int output_level = OUTPUT_LEVEL_DEFAULT;
static enum output_level output_level = OUTPUT_LEVEL_DEFAULT;
#define VA_LIST_PRINT() \
@@ -16,33 +16,18 @@ int output_level = OUTPUT_LEVEL_DEFAULT;
fflush(stdout); // If logging a lot of output to a file turn off the excessive flushing
void log_set_level(int new_output_level) {
void set_log_level(enum output_level new_output_level) {
output_level = new_output_level;
}
void log_debug(const char * format, ...){
int log_(enum output_level level, const char *fmt, ...) {
if (output_level > level) {
return 0; // Pretend everything went smoothly. (It did, kind of!)
}
if (output_level > OUTPUT_LEVEL_DEBUG) return;
VA_LIST_PRINT();
va_list ap;
va_start(ap, fmt);
int ret = vfprintf(stderr, fmt, ap);
va_end(ap);
return ret;
}
void log_verbose(const char * format, ...){
if (output_level > OUTPUT_LEVEL_VERBOSE) return;
VA_LIST_PRINT();
}
void log_standard(const char * format, ...){
// Only print if quiet mode and error_only are NOT enabled
if ((output_level == OUTPUT_LEVEL_QUIET) ||
(output_level == OUTPUT_LEVEL_ONLY_ERRORS)) return;
VA_LIST_PRINT();
}
void log_error(const char * format, ...){
// Only print if quiet mode is NOT enabled
if (output_level == OUTPUT_LEVEL_QUIET) return;
VA_LIST_PRINT();
}

View File

@@ -3,20 +3,20 @@
#ifndef _LOGGING_H
#define _LOGGING_H
enum output_levels {
#include "common.h"
enum output_level {
OUTPUT_LEVEL_DEBUG,
OUTPUT_LEVEL_VERBOSE,
OUTPUT_LEVEL_DEFAULT,
OUTPUT_LEVEL_ONLY_ERRORS,
OUTPUT_LEVEL_QUIET
};
#define log_progress log_verbose
void set_log_level(enum output_level new_output_level);
int log_(enum output_level level, char const *fmt, ...) FMT(printf, 2, 3);
#define DBG(...) log_(OUTPUT_LEVEL_DEBUG, __VA_ARGS__)
#define VERBOSE(...) log_(OUTPUT_LEVEL_VERBOSE, __VA_ARGS__)
#define LOG(...) log_(OUTPUT_LEVEL_DEFAULT, __VA_ARGS__)
#define ERR(...) log_(OUTPUT_LEVEL_ONLY_ERRORS, __VA_ARGS__)
void log_set_level(int new_output_level);
void log_debug(const char * format, ...);
void log_verbose(const char * format, ...);
void log_standard(const char * format, ...);
void log_error(const char * format, ...);
#endif
#endif

View File

@@ -17,20 +17,36 @@
#include <hicolour.h>
#define VERSION "version 1.4.1"
#define VERSION "version 1.4.2"
#define HELP_HISTORICAL_CREDITS_STR \
"Historical credits and info:\n" \
" Original Concept : Icarus Productions\n" \
" Original Code : Jeff Frohwein\n" \
" Full Screen Modification : Anon\n" \
" Adaptive Code : Glen Cook\n" \
" Windows Interface : Glen Cook\n" \
" Additional Windows Programming : Rob Jones\n" \
" Original Quantiser Code : Benny\n" \
" Quantiser Conversion : Glen Cook\n" \
"\n"
static void init(void);
static void cleanup(void);
static void display_help(void);
static int handle_args(int argc, char * argv[]);
static void set_drag_and_drop_mode_defaults(void);
static bool handle_args_L_R_patterns(const char * str_opt_side, const char * str_value);
#ifdef DRAG_AND_DROP_MODE
static void set_drag_and_drop_mode_defaults(void);
#endif
image_data src_image;
char filename_in[MAX_STR_LEN] = {'\0'};
char opt_filename_out[MAX_STR_LEN] = "";
// bool opt_strip_output_filename_ext = true;
int show_help_and_exit = false;
bool show_help_and_exit = false;
bool show_full_credits = false;
static void init(void) {
@@ -97,7 +113,7 @@ int main( int argc, char *argv[] ) {
#ifdef DRAG_AND_DROP_MODE
// Wait for input to keep the console window open after processing
log_standard("\n\nPress Any Key to Continue\n");
LOG("\n\nPress Any Key to Continue\n");
getchar();
#endif
@@ -106,7 +122,7 @@ int main( int argc, char *argv[] ) {
static void display_help(void) {
log_standard(
LOG(
"\n"
"png2hicolorgb input_image.png [options]\n"
VERSION": bbbbbr. Based on Glen Cook's Windows GUI \"hicolour.exe\" 1.2\n"
@@ -122,36 +138,38 @@ static void display_help(void) {
"--type=N : Set conversion type where N is one of below \n"
" 1: Median Cut - No Dither (*Default*)\n"
" 2: Median Cut - With Dither\n"
" 3: Wu Quantiser\n"
" 3: Wu Quantiser (best quality)\n"
"-p : Show screen attribute pattern options (no processing)\n"
"-L=N : Set Left screen attribute pattern where N is decimal entry (-p to show patterns)\n"
"-R=N : Set Right screen attribute pattern where N is decimal entry (-p to show patterns)\n"
"-L=N : Set Left side of screen palette arrangement where N is name listed below or decimal entry\n"
"-R=N : Set Right side of screen palette arrangement where N is name listed below or decimal entry\n"
" Named options for N: \"adaptive-fast\", \"adaptive-medium\", \"adaptive-best\" (-p for full options) \n"
"--best : Use highest quality conversion settings (--type=3 -L=adaptive-best -R=adaptive-best)\n"
"--vaddrid : Map uses vram id (128->255->0->127) instead of (*Default*) sequential tile order (0->255)\n"
"--nodedupe : Turn off tile pattern deduplication\n"
"--precompiled : Export Palette data as pre-compiled executable loading code\n"
"--palendbit : Set unused bit .15 = 1 for last u16 entry in palette data indicating end (not in precompiled)\n"
"--addendcolor=N : Append 32 x color N (hex BGR555) in pal data to clear BG for shorter images (64 bytes) (not in precompiled)\n"
"\n"
"Example 1: \"png2hicolorgb myimage.png\"\n"
"Example 2: \"png2hicolorgb myimage.png --csource -o=my_output_filename\"\n"
"* Default settings provide good results. Better quality but slower: \"--type=3 -L=2 -R=2\"\n"
"\n"
"Historical credits and info:\n"
" Original Concept : Icarus Productions\n"
" Original Code : Jeff Frohwein\n"
" Full Screen Modification : Anon\n"
" Adaptive Code : Glen Cook\n"
" Windows Interface : Glen Cook\n"
" Additional Windows Programming : Rob Jones\n"
" Original Quantiser Code : Benny\n"
" Quantiser Conversion : Glen Cook\n"
"Example 2: \"png2hicolorgb myimage.png --palendbit --addendcolor=0x7FFF -o=my_output_filename\"\n"
"* Default settings provide good results. Better quality but slower: \"--type=3 -L=adaptive-best -R=adaptive-best\"\n"
"\n"
);
if (show_full_credits)
LOG(HELP_HISTORICAL_CREDITS_STR);
}
#ifdef DRAG_AND_DROP_MODE
// Default options for Windows Drag and Drop recipient mode
static void set_drag_and_drop_mode_defaults(void) {
// Set some options here
}
#endif
static int handle_args(int argc, char * argv[]) {
@@ -171,27 +189,28 @@ static int handle_args(int argc, char * argv[]) {
for (i = 1; i <= (argc -1); i++ ) {
if ((strstr(argv[i], "-h") == argv[i]) || (strstr(argv[i], "-?") == argv[i])) {
show_full_credits = true;
display_help();
show_help_and_exit = true;
return true; // Don't parse further input when -h is used
} else if (strstr(argv[i], "-p") == argv[i]) {
log_standard(HELP_CONV_PATTERN_STR);
LOG(HELP_CONV_PATTERN_STR);
show_help_and_exit = true;
return true; // Don't parse further input when -h is used
} else if (strstr(argv[i], "-vD") == argv[i]) {
log_set_level(OUTPUT_LEVEL_DEBUG);
set_log_level(OUTPUT_LEVEL_DEBUG);
} else if (strstr(argv[i], "-vE") == argv[i]) {
log_set_level(OUTPUT_LEVEL_ONLY_ERRORS);
set_log_level(OUTPUT_LEVEL_ONLY_ERRORS);
} else if (strstr(argv[i], "-vQ") == argv[i]) {
log_set_level(OUTPUT_LEVEL_QUIET);
set_log_level(OUTPUT_LEVEL_ONLY_ERRORS);
} else if (strstr(argv[i], "-v") == argv[i]) {
log_set_level(OUTPUT_LEVEL_VERBOSE);
set_log_level(OUTPUT_LEVEL_VERBOSE);
} else if (strstr(argv[i], "--type=") == argv[i]) {
uint8_t new_type = strtol(argv[i] + strlen("--type="), NULL, 10);
if ((new_type < CONV_TYPE_MIN) || (new_type > CONV_TYPE_MAX)) {
log_standard("Error: --type specified with invalid conversion setting: %d\n", new_type);
LOG("Error: --type specified with invalid conversion setting: %d\n", new_type);
display_help();
show_help_and_exit = true;
return false; // Abort
@@ -199,22 +218,35 @@ static int handle_args(int argc, char * argv[]) {
else
hicolor_set_type(new_type);
} else if (strstr(argv[i], "-L=") == argv[i]) {
hicolor_set_convert_left_pattern( strtol(argv[i] + strlen("-L="), NULL, 10));
if (!handle_args_L_R_patterns((const char *)"-L=", argv[i] + strlen("-L="))) {
show_help_and_exit = true;
return false; // Abort
}
} else if (strstr(argv[i], "-R=") == argv[i]) {
hicolor_set_convert_right_pattern( strtol(argv[i] + strlen("-R="), NULL, 10));
if (!handle_args_L_R_patterns((const char *)"-R=", argv[i] + strlen("-R="))) {
show_help_and_exit = true;
return false; // Abort
}
} else if (strstr(argv[i], "--best") == argv[i]) {
hicolor_set_type(CONV_TYPE_WU);
hicolor_set_convert_left_pattern(HICOLOR_PATTERN_ADAPTIVE_BEST);
hicolor_set_convert_right_pattern(HICOLOR_PATTERN_ADAPTIVE_BEST);
} else if (strstr(argv[i], "-o") == argv[i]) {
if (i < (argc -1))
i++; // Move to next argument if one is available
else {
log_standard("Error: -o specified but filename is missing\n");
LOG("Error: -o specified but filename is missing\n");
show_help_and_exit = true;
return false; // Abort
}
// Require colon and filename to be present
if (*argv[i] == '-')
log_standard("Warning: -o specified but filename has dash and looks like an option argument. Usage: -o my_base_output_filename\n");
if (*argv[i] == '-')
LOG("Warning: -o specified but filename has dash and looks like an option argument. Usage: -o my_base_output_filename\n");
snprintf(opt_filename_out, sizeof(opt_filename_out), "%s", argv[i]);
// } else if (strstr(argv[i], "--keepext") == argv[i]) {
@@ -226,7 +258,7 @@ static int handle_args(int argc, char * argv[]) {
} else if (strstr(argv[i], "--bank=") == argv[i]) {
opt_set_bank_num( strtol(argv[i] + strlen("--bank="), NULL, 10) );
if ((opt_get_bank_num() < BANK_NUM_MIN) || (opt_get_bank_num() > BANK_NUM_MAX)) {
log_standard("Error: Invalid bank number specified with --bank=%d\n", opt_get_bank_num());
LOG("Error: Invalid bank number specified with --bank=%d\n", opt_get_bank_num());
display_help();
show_help_and_exit = true;
return false; // Abort
@@ -238,14 +270,66 @@ static int handle_args(int argc, char * argv[]) {
} else if (strstr(argv[i], "--nodedupe") == argv[i]) {
opt_set_tile_dedupe(false);
} else if (strstr(argv[i], "--precompiled") == argv[i]) {
opt_set_precompiled_palette(true);
} else if (strstr(argv[i], "--palendbit") == argv[i]) {
opt_set_pal_end_bit(true);
} else if (strstr(argv[i], "--addendcolor=") == argv[i]) {
// Color should be in BGR555 hex format, so white: 0x7FFFF, max red 0x7C00, max green 0x3E00, etc
opt_set_enable_pal_end_color( strtol(argv[i] + strlen("--addendcolor="), NULL, 16) );
} else if (argv[i][0] == '-') {
log_error("Unknown argument: %s\n\n", argv[i]);
ERR("Unknown argument: %s\n\n", argv[i]);
display_help();
return false;
}
}
// Check and warn about option compatibility
if (opt_get_pal_end_bit() && opt_get_precompiled_palette()) {
LOG("Warning: --palendbit ignored when --precompiled is enabled\n");
}
if (opt_get_enable_pal_end_color() && opt_get_precompiled_palette()) {
LOG("Warning: --addendcolor ignored when --precompiled is enabled\n");
}
return true;
}
static bool handle_args_L_R_patterns(const char * str_opt_side, const char * str_value) {
if (*str_value == '\0') {
ERR("Missing value for %s argument\n", str_opt_side);
return false;
}
unsigned int pattern_num = hicolor_get_pattern_by_name(str_value);
if (pattern_num == HICOLOR_PATTERN_NOT_FOUND_HAS_CHARS) {
ERR("Invalid value for %s argument: %s\n", str_opt_side, str_value);
return false;
}
else if (pattern_num == HICOLOR_PATTERN_NOT_FOUND) {
// Try numeric conversion on option instead named conversion
pattern_num = (unsigned int)strtoul(str_value, NULL, 10);
}
if (pattern_num > HICOLOR_PATTERN_OPT_MAX) {
ERR("Invalid value for %s argument: %s\n", str_opt_side, str_value);
return false;
}
// Got a valid option value, now apply it
if (strstr(str_opt_side, "-L=") == str_opt_side)
hicolor_set_convert_left_pattern(pattern_num);
else
hicolor_set_convert_right_pattern(pattern_num);
return true;
}

View File

@@ -12,7 +12,11 @@
bool opt_map_use_sequential_tile_index = true;
bool opt_tile_dedupe = true;
bool opt_c_file_output = false;
bool opt_c_precompiled = false;
int opt_bank_num = BANK_NUM_UNSET;
bool opt_pal_end_bit = false;
bool opt_enable_pal_end_col = false;
uint16_t pal_end_color = 0x0000u; // Black in BGR555
void opt_set_map_tile_order(bool newval) { opt_map_use_sequential_tile_index = newval; }
bool opt_get_map_tile_order(void) { return opt_map_use_sequential_tile_index; }
@@ -26,3 +30,31 @@ bool opt_get_c_file_output(void) { return opt_c_file_output; }
void opt_set_bank_num(int newval) { opt_bank_num = newval; }
int opt_get_bank_num(void) { return opt_bank_num; }
void opt_set_precompiled_palette(bool newval) { opt_c_precompiled = newval; }
bool opt_get_precompiled_palette(void) { return opt_c_precompiled; }
void opt_set_pal_end_bit(bool newval) { opt_pal_end_bit = newval; }
bool opt_get_pal_end_bit(void) { return opt_pal_end_bit; }
void opt_set_enable_pal_end_color(uint16_t end_color_bgr555) {
opt_enable_pal_end_col = true;
// Make sure unused high bit is not set for compat with pal_end_bit
pal_end_color = (end_color_bgr555 & BGR555_MASK);
}
bool opt_get_enable_pal_end_color(void) {
return opt_enable_pal_end_col;
}
void opt_load_pal_end_color(uint16_t * p_end_color, int * pal_end_color_count) {
*p_end_color = pal_end_color;
if (opt_enable_pal_end_col) {
*pal_end_color_count = APPEND_END_COLOR_COUNT; // Add 8 Palettes x 4 Colors to palette data (2 bytes each)
} else {
*pal_end_color_count = 0; // Not enabled, so add zero bytes to pal size
}
}

View File

@@ -3,8 +3,8 @@
#ifndef OPTIONS_H
#define OPTIONS_H
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
#define BANK_NUM_UNSET 0
#define BANK_NUM_MIN 1
@@ -12,6 +12,9 @@
#define OPTION_UNSET 0xFFFF
#define BGR555_MASK 0x7FFFu
#define APPEND_END_COLOR_COUNT (8 * 4); // Add 8 Palettes x 4 Colors each x 2 bytes per color to pal size
#define OPT_MAP_TILE_ORDER_BY_VRAM_ID false
#define OPT_MAP_TILE_SEQUENTIAL_ORDER true
@@ -24,6 +27,16 @@ bool opt_get_tile_dedupe(void);
void opt_set_c_file_output(bool newval);
bool opt_get_c_file_output(void);
void opt_set_precompiled_palette(bool newval);
bool opt_get_precompiled_palette(void);
void opt_set_pal_end_bit(bool newval);
bool opt_get_pal_end_bit(void);
void opt_set_enable_pal_end_color(uint16_t end_color_bgr555);
bool opt_get_enable_pal_end_color(void);
void opt_load_pal_end_color(uint16_t * p_end_color, int * pal_end_color_count);
void opt_set_bank_num(int newval);
int opt_get_bank_num(void);

View File

@@ -4,7 +4,9 @@
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdbool.h>
#include "common.h"
#include "path_ops.h"

View File

@@ -5,8 +5,9 @@
#ifndef _PATH_OPS_H
#define _PATH_OPS_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
void filename_replace_extension(char * filename, char * new_ext, size_t maxlen);
void filename_replace_path(char * filename, char * new_path, size_t maxlen);
@@ -15,4 +16,4 @@ void filename_remove_extension(char * path);
bool get_path_without_filename(const char * path, char * path_only, uint32_t str_max);
bool matches_extension(char *, char *);
#endif // _PATH_OPS_H
#endif // _PATH_OPS_H

View File

@@ -92,7 +92,7 @@ bool tileset_find_matching_tile(const uint8_t * src_tile, const uint8_t * tile_s
for (unsigned int index_dedupe = 0; index_dedupe < tile_count_deduped; index_dedupe++) {
for (int f = 0; f < ARRAY_LEN(tile_flip_flags); f++) {
for (size_t f = 0; f < ARRAY_LEN(tile_flip_flags); f++) {
if (memcmp(&tile_set_deduped[index_dedupe * TILE_SZ], &tile_permutations[f * TILE_SZ], TILE_SZ) == 0) {
// Match Found

View File

@@ -1,8 +1,8 @@
#ifndef TILE_DEDUPE_H
#define TILE_DEDUPE_H
#include <stdint.h>
#include <stdbool.h>
#include <stdint.h>
bool tileset_find_matching_tile(const uint8_t * src_tile, const uint8_t * tile_set_deduped, unsigned int tile_count_deduped, uint8_t * new_tile_id, uint8_t * new_attribs);