diff --git a/tools/imagecon/Makefile b/tools/imagecon/Makefile index 30d03c7..aedadec 100644 --- a/tools/imagecon/Makefile +++ b/tools/imagecon/Makefile @@ -1,7 +1,8 @@ IMAGECON=./out/imagecon OBJS=out/imagecon.o out/png.o out/color.o out/dither.o out/ham.o out/palette.o out/file.o -HOST_WARNINGS=-pedantic-errors -Wfatal-errors -Wall -Werror -Wextra -Wno-unused-parameter -Wshadow -limagequant -HOST_CFLAGS=$(HOST_WARNINGS) +WARN_ERROR=-Werror +HOST_WARNINGS=$(WARN_ERROR) -pedantic-errors -Wfatal-errors -Wall -Wextra -Wno-unused-parameter -Wshadow -limagequant +HOST_CFLAGS=$(HOST_WARNINGS) -O3 LIBS=-lpng -limagequant # test stuff diff --git a/tools/imagecon/color.c b/tools/imagecon/color.c index 8b04fe6..8f0b525 100644 --- a/tools/imagecon/color.c +++ b/tools/imagecon/color.c @@ -120,16 +120,16 @@ color_setOriginalPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color) } -amiga_color_t +dither_color_t color_getDitheredPixel(imagecon_image_t* ic, int x, int y) { return ic->dithered[(y*ic->width)+x]; } void -color_setDitheredPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color) +color_setDitheredPixel(imagecon_image_t* ic, int x, int y, dither_color_t color) { - amiga_color_t *d = &ic->dithered[(y*ic->width)+x]; + dither_color_t *d = &ic->dithered[(y*ic->width)+x]; if (color.r > 255) { color.r = 255; @@ -185,3 +185,27 @@ color_transferPalettedToOriginal(imagecon_image_t* ic) } } } + + +amiga_color_t +color_ditheredToAmiga(dither_color_t color) +{ + amiga_color_t c; + c.r = color.r; + c.g = color.g; + c.b = color.b; + c.a = color.a; + + return c; +} + +dither_color_t +color_amigaToDithered(amiga_color_t color) +{ + dither_color_t c; + c.r = color.r; + c.g = color.g; + c.b = color.b; + c.a = color.a; + return c; +} diff --git a/tools/imagecon/color.h b/tools/imagecon/color.h index 7d27e0a..ebc6c9e 100644 --- a/tools/imagecon/color.h +++ b/tools/imagecon/color.h @@ -3,6 +3,9 @@ int color_delta(amiga_color_t c1, amiga_color_t c2); +void +color_print(amiga_color_t color); + amiga_color_t color_findClosestPalettePixel(imagecon_image_t* ic, amiga_color_t color); @@ -19,9 +22,9 @@ void color_setPalettedPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color); void -color_setDitheredPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color); +color_setDitheredPixel(imagecon_image_t* ic, int x, int y, dither_color_t color); -amiga_color_t +dither_color_t color_getDitheredPixel(imagecon_image_t* ic, int x, int y); amiga_color_t @@ -32,3 +35,9 @@ color_setOriginalPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color); void color_transferPalettedToOriginal(imagecon_image_t* ic); + +amiga_color_t +color_ditheredToAmiga(dither_color_t color); + +dither_color_t +color_amigaToDithered(amiga_color_t color); diff --git a/tools/imagecon/dither.c b/tools/imagecon/dither.c index 6918ab1..706a3dc 100644 --- a/tools/imagecon/dither.c +++ b/tools/imagecon/dither.c @@ -17,6 +17,24 @@ dither_getHamColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last return ham.pixel; } +float +_gamma(float x) +{ + return x * 0.55; +} + +static void +_propagateError(imagecon_image_t* ic, float factor, int x, int y, dither_color_t error) +{ + if (x >= 0 && y >= 0 && x < ic->width && y < ic->height) { + dither_color_t color = color_getDitheredPixel(ic, x, y); + color.r += _gamma(error.r * factor); + color.g += _gamma(error.g * factor); + color.b += _gamma(error.b * factor); + color_setDitheredPixel(ic, x, y, color); + } +} + void dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t*, amiga_color_t color, amiga_color_t last)) { @@ -26,55 +44,21 @@ dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t*, amiga_color_t last = {-1, -1, -1, -1}; for (int x = 0; x < ic->width; x++) { - amiga_color_t old = color_getDitheredPixel(ic, x, y); - amiga_color_t new = selector(ic, old, last); + dither_color_t old = color_getDitheredPixel(ic, x, y); + amiga_color_t new = selector(ic, color_ditheredToAmiga(old), last); - amiga_color_t error; + dither_color_t error; error.r = old.r - new.r; error.g = old.g - new.g; error.b = old.b - new.b; - color_setDitheredPixel(ic, x, y, new); + color_setDitheredPixel(ic, x, y, color_amigaToDithered(new)); last = new; - amiga_color_t color; - float factor; - - if (x+1 < ic->width) { - color = color_getDitheredPixel(ic, x+1, y); - factor = 7.0/16.0; - color.r = color.r + ((float)error.r * factor); - color.g = color.g + ((float)error.g * factor); - color.b = color.b + ((float)error.b * factor); - color_setDitheredPixel(ic, x+1, y, color); - } - - if (x > 0 && y+1 < ic->height) { - color = color_getDitheredPixel(ic, x-1, y+1); - factor = 3.0/16.0; - color.r = color.r + ((float)error.r * factor); - color.g = color.g + ((float)error.g * factor); - color.b = color.b + ((float)error.b * factor); - color_setDitheredPixel(ic, x-1, y+1, color); - } - - if (y+1 < ic->height) { - color = color_getDitheredPixel(ic, x, y+1); - factor = 5.0/16.0; - color.r = color.r + ((float)error.r * factor); - color.g = color.g + ((float)error.g * factor); - color.b = color.b + ((float)error.b * factor); - color_setDitheredPixel(ic, x, y+1, color); - - if (x+1 < ic->width) { - color = color_getDitheredPixel(ic, x+1, y+1); - factor = 1.0/16.0; - color.r = color.r + ((float)error.r * factor); - color.g = color.g + ((float)error.g * factor); - color.b = color.b + ((float)error.b * factor); - color_setDitheredPixel(ic, x+1, y+1, color); - } - } + _propagateError(ic, 7.0/16.0, x+1, y, error); + _propagateError(ic, 3.0/16.0, x-1, y+1, error); + _propagateError(ic, 5.0/16.0, x , y+1, error); + _propagateError(ic, 1.0/16.0, x+1, y+1, error); } } @@ -87,7 +71,7 @@ dither_transferToPaletted(imagecon_image_t* ic) { for (int y = 0; y < ic->height; y++) { for (int x = 0; x < ic->width; x++) { - color_setPalettedPixel(ic, x, y, color_getDitheredPixel(ic, x, y)); + color_setPalettedPixel(ic, x, y, color_ditheredToAmiga(color_getDitheredPixel(ic, x, y))); } } } @@ -101,7 +85,7 @@ dither_createHams(imagecon_image_t* ic) for (int y = 0; y < ic->height; y++) { amiga_color_t lastPixel = { -1, -1, -1, -1}; for (int x = 0; x < ic->width; x++) { - amiga_color_t orig = color_getDitheredPixel(ic, x, y); + amiga_color_t orig = color_ditheredToAmiga(color_getDitheredPixel(ic, x, y)); ham_control_t ham = color_findClosestHamPixel(ic, orig, lastPixel); lastPixel = ham.pixel; hams[(y*ic->width)+x] = ham; @@ -115,11 +99,11 @@ dither_createHams(imagecon_image_t* ic) static void _dither_createDither(imagecon_image_t* ic) { - ic->dithered = malloc(sizeof(amiga_color_t)*ic->width*ic->height); + ic->dithered = malloc(sizeof(dither_color_t)*ic->width*ic->height); for (int y = 0; y < ic->height; y++) { for (int x = 0; x < ic->width; x++) { - color_setDitheredPixel(ic, x, y, color_getOriginalPixel(ic, x, y)); + color_setDitheredPixel(ic, x, y, color_amigaToDithered(color_getOriginalPixel(ic, x, y))); } } } diff --git a/tools/imagecon/ham.c b/tools/imagecon/ham.c index 1508cf5..73e5bf5 100644 --- a/tools/imagecon/ham.c +++ b/tools/imagecon/ham.c @@ -15,7 +15,6 @@ _ham_outputBitplanes(char* outFilename, imagecon_image_t* ic) bitplanes[i] = calloc(byteWidth*ic->height, 1); } - ham_control_t* hams; if (config.dither) { @@ -35,7 +34,6 @@ _ham_outputBitplanes(char* outFilename, imagecon_image_t* ic) } } - for (int y = 0, writeIndex = 0; y < ic->height; y++) { for (int byte = 0;byte < byteWidth; byte++) { for (int bit = 0; bit < 8; bit++) { @@ -74,38 +72,104 @@ _ham_outputBitplanes(char* outFilename, imagecon_image_t* ic) } } -#if 0 + +static int +_score(imagecon_image_t* ic) +{ + long error = 0; + for (int y = 0; y < ic->height; y++) { + amiga_color_t lastPixel = { -1, -1, -1, -1}; + for (int x = 0; x < ic->width; x++) { + amiga_color_t color = color_getOriginalPixel(ic, x, y); + ham_control_t ham = color_findClosestHamPixel(ic, color, lastPixel); + error += color_delta(color, color_findClosestPalettePixel(ic, ham.pixel)); + lastPixel = ham.pixel; + } + } + + return error; +} + + +static void +_ham_bruteForcePalette(imagecon_image_t* ic) +{ + int totalCombinations = 0xF*0xF*0xF; + amiga_color_t *combos = malloc(sizeof(amiga_color_t)*totalCombinations); + int index = 0; + int length = 16; + + bzero(ic->palette, sizeof(ic->palette)); + ic->numColors = 16; + + for (unsigned char r = 0; r <= 0xF; r++) { + for (unsigned char g = 0; g <= 0xF; g++) { + for (unsigned char b = 0; b <= 0xF; b++) { + combos[index].a = 255; + combos[index].r = r<<4; + combos[index].g = g<<4; + combos[index++].b = b<<4; + } + } + } + + long big = totalCombinations*length; + long b = 0; + + for (int x = 0; x < length; x++) { + long benchmark = LONG_MAX; + int bmIndex = 0; + for (int i = 0; i < totalCombinations; i++, b++) { + ic->palette[x] = combos[i]; + fflush(stdout); + printf("%c7", 27); + fflush(stdout); + printf(" %ld/%ld (%ld%%)", b, big, (b*100L)/big); + fflush(stdout); + printf("%c8", 27); + fflush(stdout); + long score = _score(ic); + if (score < benchmark) { + benchmark = score; + bmIndex = i; + } + } + ic->palette[x] = combos[bmIndex]; + + + } + + for (int i = 0; i < ic->numColors; i++) { + printf("%d: ", i); + color_print(ic->palette[i]); + printf("\n"); + } +} + + void ham_process(char* outFilename, imagecon_image_t* ic) { - config.maxColors = 256; - generateQuantizedImage(ic, 0); - dither_image(ic, dither_getPalettedColor); - dither_transferToPaletted(ic); - color_transferPalettedToOriginal(ic); - config.maxColors = 16; - generateQuantizedImage(ic, 0); + + if (config.hamBruteForce) { + + _ham_bruteForcePalette(ic); + + } else { + config.maxColors = 16; + + if (config.overridePalette) { + palette_loadFile(ic); + } + + generateQuantizedImage(ic, config.overridePalette != 0); + } + + if (config.outputBitplanes) { + _ham_outputBitplanes(outFilename, ic); + } - - if (config.outputBitplanes) { - _ham_outputBitplanes(outFilename, ic); - } - palette_output(outFilename, ic); } -#else -void -ham_process(char* outFilename, imagecon_image_t* ic) -{ - config.maxColors = 16; - generateQuantizedImage(ic, 0); - - if (config.outputBitplanes) { - _ham_outputBitplanes(outFilename, ic); - } - - palette_output(outFilename, ic); -} -#endif diff --git a/tools/imagecon/imagecon.c b/tools/imagecon/imagecon.c index 92142bc..c48771b 100644 --- a/tools/imagecon/imagecon.c +++ b/tools/imagecon/imagecon.c @@ -14,6 +14,7 @@ imagecon_config_t config = { .outputCopperList = 0, .ehbMode = 0, .hamMode = 0, + .hamBruteForce = 0, .quantize = 0, .dither = 0, .overridePalette = 0 @@ -37,6 +38,7 @@ usage() " --output-palette\n"\ " --extra-half-brite\n"\ " --ham\n"\ + " --ham-brute-force\n"\ " --dither\n"\ " --use-palette \n"\ " --verbose\n", config.argv[0]); @@ -432,6 +434,7 @@ main(int argc, char **argv) {"output-mask", no_argument, &config.outputMask, 1}, {"extra-half-brite", no_argument, &config.ehbMode, 1}, {"ham", no_argument, &config.hamMode, 1}, + {"ham-brute-force", no_argument, &config.hamBruteForce, 1}, {"dither", no_argument, &config.dither, 1}, {"use-palette", required_argument, 0, 'p'}, {"output", required_argument, 0, 'o'}, diff --git a/tools/imagecon/imagecon.h b/tools/imagecon/imagecon.h index 713deba..936a75e 100644 --- a/tools/imagecon/imagecon.h +++ b/tools/imagecon/imagecon.h @@ -24,6 +24,7 @@ typedef struct { int outputCopperList; int ehbMode; int hamMode; + int hamBruteForce; int dither; char* overridePalette; int quantize; @@ -38,6 +39,13 @@ typedef struct { int a; } amiga_color_t; +typedef struct { + float r; + float g; + float b; + float a; +} dither_color_t; + typedef struct { int control; int data; @@ -52,7 +60,7 @@ typedef struct { png_bytep* rowPointers; unsigned char* amigaImage; amiga_color_t palette[MAX_PALETTE*2]; // extra half brite mode - amiga_color_t* dithered; + dither_color_t* dithered; } imagecon_image_t; diff --git a/tools/imagecon/png.c b/tools/imagecon/png.c index b8cdc63..b75dee9 100644 --- a/tools/imagecon/png.c +++ b/tools/imagecon/png.c @@ -16,6 +16,12 @@ #include "imagecon.h" +static void +_wrapjmp(png_structp png_ptr) +{ + if (setjmp(png_jmpbuf(png_ptr))) + abort_("Error during init_io"); +} void png_read(char* file_name, imagecon_image_t* ic) { @@ -23,7 +29,7 @@ png_read(char* file_name, imagecon_image_t* ic) png_byte color_type; png_byte bit_depth; png_infop info_ptr; - int number_of_passes, rowbytes; + int number_of_passes = 0, rowbytes; unsigned char header[8]; // 8 is the maximum size that can be checked /* open file and test for it being a png */ @@ -43,9 +49,8 @@ png_read(char* file_name, imagecon_image_t* ic) if (!info_ptr) abort_("png_create_info_struct failed"); - if (setjmp(png_jmpbuf(png_ptr))) - abort_("Error during init_io"); - + + _wrapjmp(png_ptr); png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); @@ -117,6 +122,4 @@ png_read(char* file_name, imagecon_image_t* ic) } ic->amigaImage = calloc(ic->width*ic->height, 1); - - }