diff --git a/tools/imagecon/Makefile b/tools/imagecon/Makefile index e432f56..30d03c7 100644 --- a/tools/imagecon/Makefile +++ b/tools/imagecon/Makefile @@ -1,5 +1,5 @@ IMAGECON=./out/imagecon -OBJS=out/imagecon.o out/png.o +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) LIBS=-lpng -limagequant @@ -28,12 +28,14 @@ REFERENCE_HAM_BASE=reference/ham $(IMAGECON): out bin $(OBJS) gcc $(OBJS) -o $(IMAGECON) $(LIBS) - -out/imagecon.o: imagecon.h -out/png.o: imagecon.h +-include $(OBJS:.o=.d) out/%.o: %.c gcc -c $(HOST_CFLAGS) $< -o $@ + @gcc -MM $(HOST_CFLAGS) $*.c > out/$*.d + @mv -f out/$*.d out/$*.d.tmp + @sed -e 's/.*/out\/&/' < out/$*.d.tmp > out/$*.d + @rm -f out/$*.d.tmp out: mkdir out diff --git a/tools/imagecon/color.c b/tools/imagecon/color.c new file mode 100644 index 0000000..8b04fe6 --- /dev/null +++ b/tools/imagecon/color.c @@ -0,0 +1,187 @@ +#include "imagecon.h" + +int +color_delta(amiga_color_t c1, amiga_color_t c2) +{ + int dr = abs((c1.r)-(c2.r)); + int dg = abs((c1.g)-(c2.g)); + int db = abs((c1.b)-(c2.b)); + return sqrt((dr*dr)+(dg*dg)+(db*db)); +} + + +void +color_print(amiga_color_t color) +{ + printf("R = %03d G = %03d B = %03d A = %03d", color.r, color.g, color.b, color.a); +} + + +int +color_findClosestPaletteIndex(imagecon_image_t* ic, amiga_color_t color) +{ + int delta = INT_MAX; + int index = 0; + + for (int i = 0; i < ic->numColors; i++) { + int dc = color_delta(color, ic->palette[i]); + if (dc < delta) { + delta = dc; + index = i; + } + } + return index; +} + +amiga_color_t +color_findClosestPalettePixel(imagecon_image_t* ic, amiga_color_t color) +{ + return ic->palette[color_findClosestPaletteIndex(ic, color)]; +} + +ham_control_t +color_findClosestHamPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last) +{ + int delta = INT_MAX; + + ham_control_t ham = {0}; + + for (int i = 0; i < ic->numColors; i++) { + int dc = color_delta(color, ic->palette[i]); + if (dc < delta) { + delta = dc; + ham.data = i; + ham.pixel = ic->palette[i]; + } + } + + delta = color_delta(color, ham.pixel); + if (last.r != -1) { + for (int c = 0; c <= 0xF; c++) { + amiga_color_t copy = last; + copy.r = c<<4; + int dc = color_delta(color, copy); + if (dc < delta) { + ham.control = 2; + ham.data = c; + ham.pixel = copy; + delta = dc; + } + } + for (int c = 0; c <= 0xF; c++) { + amiga_color_t copy = last; + copy.g = c<<4; + int dc = color_delta(color, copy); + if (dc < delta) { + ham.control = 3; + ham.data = c; + ham.pixel = copy; + delta = dc; + } + } + for (int c = 0; c <= 0xF; c++) { + amiga_color_t copy = last; + copy.b = c<<4; + int dc = color_delta(color, copy); + if (dc < delta) { + ham.control = 1; + ham.data = c; + ham.pixel = copy; + delta = dc; + } + } + } + + return ham; +} + + +amiga_color_t +color_getOriginalPixel(imagecon_image_t* ic, int x, int y) +{ + png_byte* ptr = ic->rowPointers[y] + (x*4); + amiga_color_t color; + color.r = ptr[0]; + color.g = ptr[1]; + color.b = ptr[2]; + color.a = ptr[3]; + return color; +} + + +void +color_setOriginalPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color) +{ + png_byte* ptr = ic->rowPointers[y] + (x*4); + ptr[0] = color.r; + ptr[1] = color.g; + ptr[2] = color.b; + ptr[3] = color.a; +} + + +amiga_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) +{ + amiga_color_t *d = &ic->dithered[(y*ic->width)+x]; + + if (color.r > 255) { + color.r = 255; + } + if (color.r < 0) { + color.r = 0; + } + if (color.g > 255) { + color.g = 255; + } + if (color.g < 0) { + color.g = 0; + } + if (color.b > 255) { + color.b = 255; + } + if (color.b < 0) { + color.b = 0; + } + + d->r = color.r; + d->g = color.g; + d->b = color.b; + d->a = color.a; +} + +amiga_color_t +color_getPalettedPixel(imagecon_image_t* ic, int x, int y) +{ + int paletteIndex = ic->amigaImage[(ic->width*y)+x]; + amiga_color_t color; + color.r = ic->palette[paletteIndex].r; + color.g = ic->palette[paletteIndex].g; + color.b = ic->palette[paletteIndex].b; + color.a = ic->palette[paletteIndex].a; + return color; +} + +void +color_setPalettedPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color) +{ + int paletteIndex = color_findClosestPaletteIndex(ic, color); + ic->amigaImage[(ic->width*y)+x] = paletteIndex; +} + + +void +color_transferPalettedToOriginal(imagecon_image_t* ic) +{ + for (int y = 0; y < ic->height; y++) { + for (int x = 0; x < ic->width; x++) { + color_setOriginalPixel(ic, x, y, color_getPalettedPixel(ic, x, y)); + } + } +} diff --git a/tools/imagecon/color.h b/tools/imagecon/color.h new file mode 100644 index 0000000..7d27e0a --- /dev/null +++ b/tools/imagecon/color.h @@ -0,0 +1,34 @@ +#pragma once + +int +color_delta(amiga_color_t c1, amiga_color_t c2); + +amiga_color_t +color_findClosestPalettePixel(imagecon_image_t* ic, amiga_color_t color); + +int +color_findClosestPaletteIndex(imagecon_image_t* ic, amiga_color_t color); + +ham_control_t +color_findClosestHamPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last); + +amiga_color_t +color_getPalettedPixel(imagecon_image_t* ic, int x, int y); + +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); + +amiga_color_t +color_getDitheredPixel(imagecon_image_t* ic, int x, int y); + +amiga_color_t +color_getOriginalPixel(imagecon_image_t* ic, int x, int y); + +void +color_setOriginalPixel(imagecon_image_t* ic, int x, int y, amiga_color_t color); + +void +color_transferPalettedToOriginal(imagecon_image_t* ic); diff --git a/tools/imagecon/dither.c b/tools/imagecon/dither.c new file mode 100644 index 0000000..6918ab1 --- /dev/null +++ b/tools/imagecon/dither.c @@ -0,0 +1,125 @@ +#include "imagecon.h" + +static void +_dither_createDither(imagecon_image_t* ic); + +amiga_color_t +dither_getPalettedColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last) +{ + return color_findClosestPalettePixel(ic, color); +} + + +amiga_color_t +dither_getHamColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last) +{ + ham_control_t ham = color_findClosestHamPixel(ic, color, last); + return ham.pixel; +} + +void +dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t*, amiga_color_t color, amiga_color_t last)) +{ + _dither_createDither(ic); + + for (int y = 0; y < ic->height; y++) { + 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); + + amiga_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); + 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); + } + } + } + + } + +} + + +void +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)); + } + } +} + + +ham_control_t* +dither_createHams(imagecon_image_t* ic) +{ + ham_control_t* hams = malloc(sizeof(ham_control_t)*ic->width*ic->height); + + 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); + ham_control_t ham = color_findClosestHamPixel(ic, orig, lastPixel); + lastPixel = ham.pixel; + hams[(y*ic->width)+x] = ham; + } + } + + return hams; +} + + +static void +_dither_createDither(imagecon_image_t* ic) +{ + ic->dithered = malloc(sizeof(amiga_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)); + } + } +} diff --git a/tools/imagecon/dither.h b/tools/imagecon/dither.h new file mode 100644 index 0000000..b3d09df --- /dev/null +++ b/tools/imagecon/dither.h @@ -0,0 +1,16 @@ +#pragma once + +amiga_color_t +dither_getPalettedColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last); + +amiga_color_t +dither_getHamColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last); + +void +dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last)); + +void +dither_transferToPaletted(imagecon_image_t* ic); + +ham_control_t* +dither_createHams(imagecon_image_t* ic); diff --git a/tools/imagecon/file.c b/tools/imagecon/file.c new file mode 100644 index 0000000..f774e12 --- /dev/null +++ b/tools/imagecon/file.c @@ -0,0 +1,39 @@ +#include "imagecon.h" + +FILE * +file_openWrite(const char * s, ...) +{ + char buffer[4096]; + va_list args; + va_start(args, s); + vsprintf(buffer, s, args); + va_end(args); + + if (config.verbose) { + printf("Opening %s for writing\n", buffer); + } + + FILE* fp = fopen(buffer, "w+"); + if (!fp) { + abort_("Failed to open %s for writing\n", buffer); + } + return fp; +} + + +FILE * +file_openRead(const char * s, ...) +{ + char buffer[4096]; + va_list args; + va_start(args, s); + vsprintf(buffer, s, args); + va_end(args); + + FILE* fp = fopen(buffer, "r"); + if (!fp) { + abort_("Failed to open %s for reading\n", buffer); + } + return fp; +} + diff --git a/tools/imagecon/file.h b/tools/imagecon/file.h new file mode 100644 index 0000000..f68d4fd --- /dev/null +++ b/tools/imagecon/file.h @@ -0,0 +1,7 @@ +#pragma once + +FILE * +file_openWrite(const char * s, ...); + +FILE * +file_openRead(const char * s, ...); diff --git a/tools/imagecon/ham.c b/tools/imagecon/ham.c new file mode 100644 index 0000000..1508cf5 --- /dev/null +++ b/tools/imagecon/ham.c @@ -0,0 +1,111 @@ +#include "imagecon.h" + +static void +_ham_outputBitplanes(char* outFilename, imagecon_image_t* ic) +{ + if (config.verbose) { + printf("outputHamBitplanes\n"); + } + + int numBitPlanes = 6; + int byteWidth = (ic->width + 7) / 8; + + char** bitplanes = malloc(sizeof(void*)*numBitPlanes); + for (int i = 0; i < numBitPlanes; i++) { + bitplanes[i] = calloc(byteWidth*ic->height, 1); + } + + + ham_control_t* hams; + + if (config.dither) { + dither_image(ic, dither_getHamColor); + hams = dither_createHams(ic); + } else { + hams = malloc(sizeof(ham_control_t)*ic->width*ic->height); + + 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_getOriginalPixel(ic, x, y); + ham_control_t ham = color_findClosestHamPixel(ic, orig, lastPixel); + lastPixel = ham.pixel; + hams[(y*ic->width)+x] = ham; + } + } + } + + + for (int y = 0, writeIndex = 0; y < ic->height; y++) { + for (int byte = 0;byte < byteWidth; byte++) { + for (int bit = 0; bit < 8; bit++) { + int x = byte * 8 + 7 - bit; + ham_control_t ham = hams[(y*ic->width)+x]; + int _numBitPlanes = 4; + for (int plane_index = 0; plane_index < _numBitPlanes; plane_index++) { + char* plane = bitplanes[plane_index]; + plane[writeIndex] |= ((ham.data >> plane_index) & 1) << bit; + } + + for (int plane_index = 0; plane_index < 2; plane_index++) { + char* plane = bitplanes[4+plane_index]; + plane[writeIndex] |= ((ham.control >> plane_index) & 1) << bit; + } + + + } + writeIndex++; + } + } + + + FILE* fp = file_openWrite("%s-ham.bin", outFilename); + + for (int y = 0; y < ic->height; y++) { + for (int plane_index = 0; plane_index < numBitPlanes; plane_index++) { + char* plane = bitplanes[plane_index]; + fwrite(&plane[y*byteWidth], byteWidth, 1, fp); + } + } + + fclose(fp); + if (config.verbose) { + printf("done\n\n"); + } +} + +#if 0 +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.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/ham.h b/tools/imagecon/ham.h new file mode 100644 index 0000000..962a09d --- /dev/null +++ b/tools/imagecon/ham.h @@ -0,0 +1,4 @@ +#pragma once + +void +ham_process(char* outFilename, imagecon_image_t* ic); diff --git a/tools/imagecon/imagecon.c b/tools/imagecon/imagecon.c index 8662a40..92142bc 100644 --- a/tools/imagecon/imagecon.c +++ b/tools/imagecon/imagecon.c @@ -2,19 +2,8 @@ * Amiga bitplane creation inspired (copied) from https://github.com/vilcans/amiga-startup */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "imagecon.h" - imagecon_config_t config = { .maxColors = MAX_PALETTE, .outputPalette = 0, @@ -26,6 +15,7 @@ imagecon_config_t config = { .ehbMode = 0, .hamMode = 0, .quantize = 0, + .dither = 0, .overridePalette = 0 }; @@ -47,6 +37,7 @@ usage() " --output-palette\n"\ " --extra-half-brite\n"\ " --ham\n"\ + " --dither\n"\ " --use-palette \n"\ " --verbose\n", config.argv[0]); exit(1); @@ -66,144 +57,8 @@ abort_(const char * s, ...) } -FILE * -openFileWrite(const char * s, ...) -{ - char buffer[4096]; - va_list args; - va_start(args, s); - vsprintf(buffer, s, args); - va_end(args); - - if (config.verbose) { - printf("Opening %s for writing\n", buffer); - } - - FILE* fp = fopen(buffer, "w+"); - if (!fp) { - abort_("Failed to open %s for writing\n", buffer); - } - return fp; -} -FILE * -openFileRead(const char * s, ...) -{ - char buffer[4096]; - va_list args; - va_start(args, s); - vsprintf(buffer, s, args); - va_end(args); - - FILE* fp = fopen(buffer, "r"); - if (!fp) { - abort_("Failed to open %s for reading\n", buffer); - } - return fp; -} - - -void -outputPalette(char* outFilename, imagecon_image_t* ic) -{ - if (config.verbose) { - printf("outputPalette...%d colors\n", ic->numColors); - } - - FILE* fp = 0; - FILE* paletteFP = 0; - FILE* paletteAsmFP = 0; - FILE* paletteGreyFP = 0; - - if (config.outputCopperList) { - fp = openFileWrite("%s-copper-list.s", outFilename); - } - - if (config.outputPalette) { - paletteFP = openFileWrite("%s.pal", outFilename); - } - - if (config.outputPaletteGrey) { - paletteGreyFP = openFileWrite("%s-grey.s", outFilename); - fprintf(paletteGreyFP, "\tmovem.l d0-a6,-(sp)\n\tlea CUSTOM,a6\n"); - } - - if (config.outputPaletteAsm) { - paletteAsmFP = openFileWrite("%s-palette.s", outFilename); - fprintf(paletteAsmFP, "\tmovem.l d0-a6,-(sp)\n\tlea CUSTOM,a6\n"); - } - - if (config.verbose) { - printf("outputPalette:\n"); - } - - for (int i = 0; i < (config.ehbMode ? ic->numColors/2 : ic->numColors); i++) { - if (config.verbose) { - printf("%02d: hex=%03x r=%03d g=%03d b=%03d a=%03d\n", i , ic->palette[i].r << 8 | ic->palette[i].g << 4 | ic->palette[i].b, ic->palette[i].r, ic->palette[i].g, ic->palette[i].b, ic->palette[i].a); - } - if (paletteFP) { - fprintf(paletteFP, "%03x\n", (ic->palette[i].r >> 4) << 8 | (ic->palette[i].g >>4) << 4 | (ic->palette[i].b >>4)); - } - if (paletteAsmFP) { - fprintf(paletteAsmFP, "\tlea COLOR%02d(a6),a0\n\tmove.w #$%03x,(a0)\n", i, (ic->palette[i].r >> 4) << 8 | (ic->palette[i].g >>4) << 4 | (ic->palette[i].b >>4)); - } - if (paletteGreyFP) { - // TODO: this is for compat, can be better - unsigned grey = (((ic->palette[i].r>>4) + (ic->palette[i].g>>4) + (ic->palette[i].b>>4))/3); - fprintf(paletteGreyFP, "\tlea COLOR%02d(a6),a0\n\tmove.w #$%03x,(a0)\n", i, grey << 8 | grey << 4 | grey); - } - - if (fp) { - fprintf(fp, "\tdc.w $%x,$%x\n", 0x180+(i*2), (ic->palette[i].r >> 4) << 8 | (ic->palette[i].g >>4) << 4 | (ic->palette[i].b >>4)); - } - } - - if (paletteFP) { - fclose(paletteFP); - } - - if (paletteGreyFP) { - fprintf(paletteGreyFP, "\tmovem.l (sp)+,d0-a6\n"); - fclose(paletteGreyFP); - } - - if (paletteAsmFP) { - fprintf(paletteAsmFP, "\tmovem.l (sp)+,d0-a6\n"); - fclose(paletteFP); - } - - if (fp) { - fclose(fp); - } - - if (config.verbose) { - printf("done\n\n"); - } -} - -void loadPaletteFile(imagecon_image_t* ic) -{ - FILE* fp = openFileRead(config.overridePalette); - int paletteIndex; - - for (paletteIndex = 0; paletteIndex < MAX_PALETTE; paletteIndex++) { - unsigned int c; - char buffer[255]; - char* line = fgets(buffer, 255, fp); - if (!line) { - break; - } - sscanf(buffer, "%x\n", &c); - - ic->palette[paletteIndex].r = (c >> 8 & 0xF) << 4; - ic->palette[paletteIndex].g = (c >> 4 & 0xF) << 4; - ic->palette[paletteIndex].b = (c >> 0 & 0xF) << 4; - ic->palette[paletteIndex].a = 255; - } - - ic->numColors = paletteIndex; -} void generateQuantizedImage(imagecon_image_t* ic, int usePalette) @@ -227,9 +82,11 @@ generateQuantizedImage(imagecon_image_t* ic, int usePalette) } liq_set_max_colors(attr, config.maxColors); + // no liq_set_quality(attr, 0, 100); + //liq_set_min_posterization(attr, 4); liq_set_speed(attr, 1); liq_result *res = liq_quantize_image(attr, image); - + // liq_set_dithering_level(res, 1.0); liq_write_remapped_image(res, image, ic->amigaImage, ic->width*ic->height); const liq_palette *pal = liq_get_palette(res); @@ -256,35 +113,12 @@ generateQuantizedImage(imagecon_image_t* ic, int usePalette) ic->numColors = pal->count; } -amiga_color_t -getOriginalColor(int x, int y, imagecon_image_t* ic) -{ - png_byte* ptr = ic->rowPointers[y] + (x*4); - amiga_color_t color; - color.r = ptr[0]; - color.g = ptr[1]; - color.b = ptr[2]; - color.a = ptr[3]; - return color; -} - -amiga_color_t -getColor(int x, int y, imagecon_image_t* ic) -{ - int paletteIndex = ic->amigaImage[(ic->width*y)+x]; - amiga_color_t color; - color.r = ic->palette[paletteIndex].r; - color.g = ic->palette[paletteIndex].g; - color.b = ic->palette[paletteIndex].b; - color.a = ic->palette[paletteIndex].a; - return color; -} void -generatePalette(imagecon_image_t* ic) +generatePalettedImage(imagecon_image_t* ic) { if (config.verbose) { - printf("generatePalette...\n"); + printf("generatePalettedImage...\n"); } int paletteIndex = 0; @@ -359,7 +193,7 @@ outputBitplanes(char* outFilename, imagecon_image_t* ic) for (int i = 0; i < numBitPlanes; i++) { bitplanes[i] = calloc(byteWidth*ic->height, 1); } - + for (int y = 0, writeIndex = 0; y < ic->height; y++) { for (int byte = 0;byte < byteWidth; byte++) { for (int bit = 0; bit < 8; bit++) { @@ -391,7 +225,7 @@ outputBitplanes(char* outFilename, imagecon_image_t* ic) } } - FILE* fp = openFileWrite("%s.bin", outFilename); + FILE* fp = file_openWrite("%s.bin", outFilename); for (int y = 0; y < ic->height; y++) { for (int plane_index = 0; plane_index < numBitPlanes; plane_index++) { @@ -425,7 +259,7 @@ outputMask(char* outFilename, imagecon_image_t* ic) for (int byte = 0;byte < byteWidth; byte++) { for (int bit = 0; bit < 8; bit++) { int x = byte * 8 + 7 - bit; - amiga_color_t c = getOriginalColor(x, y, ic); + amiga_color_t c = color_getOriginalPixel(ic, x, y); int bitmask = c.a > 0 ? 0xFF : 0; for (int plane_index = 0; plane_index < numBitPlanes; plane_index++) { char* plane = bitplanes[plane_index]; @@ -436,7 +270,7 @@ outputMask(char* outFilename, imagecon_image_t* ic) } } - FILE* fp = openFileWrite("%s-mask.bin", outFilename); + FILE* fp = file_openWrite("%s-mask.bin", outFilename); for (int y = 0; y < ic->height; y++) { for (int plane_index = 0; plane_index < numBitPlanes; plane_index++) { @@ -472,194 +306,8 @@ generateEHBImage(imagecon_image_t* ic) generateQuantizedImage(ic, 1); } -void -printColor(amiga_color_t color) -{ - printf("R = %03d G = %03d B = %03d A = %03d", color.r, color.g, color.b, color.a); -} - -int -colorDelta(amiga_color_t c1, amiga_color_t c2) -{ - int dr = abs((c1.r)-(c2.r)); - int dg = abs((c1.g)-(c2.g)); - int db = abs((c1.b)-(c2.b)); - return (dr+dg+db) / 3; -} - -typedef struct { - int control; - int data; - amiga_color_t pixel; -} ham_control_t; - -ham_control_t -findBestHamPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t orig, amiga_color_t last) -{ - int delta = INT_MAX; - - ham_control_t ham = {0}; - - for (int i = 0; i < ic->numColors; i++) { - int dc = colorDelta(color, ic->palette[i]); - if (dc < delta) { - delta = dc; - ham.data = i; - ham.pixel = ic->palette[i]; - } - } - - delta = colorDelta(orig, ham.pixel); - if (last.r != -1) { - for (int c = 0; c <= 0xF; c++) { - amiga_color_t copy = last; - copy.r = c<<4; - int dc = colorDelta(orig, copy); - if (dc < delta) { - ham.control = 2; - ham.data = c; - ham.pixel = copy; - delta = dc; - } - } - for (int c = 0; c <= 0xF; c++) { - amiga_color_t copy = last; - copy.g = c<<4; - int dc = colorDelta(orig, copy); - if (dc < delta) { - ham.control = 3; - ham.data = c; - ham.pixel = copy; - delta = dc; - } - } - for (int c = 0; c <= 0xF; c++) { - amiga_color_t copy = last; - copy.b = c<<4; - int dc = colorDelta(orig, copy); - if (dc < delta) { - ham.control = 1; - ham.data = c; - ham.pixel = copy; - delta = dc; - } - } - } - - return ham; -} - - -void -outputHamBitplanes(char* outFilename, imagecon_image_t* ic) -{ - if (config.verbose) { - printf("outputHamBitplanes\n"); - } - - int numBitPlanes = 6; - int numColors = 16; - - if (config.verbose) { - printf("ham number of colors = %d\n", numColors); - printf("ham number of bitplanes = %d\n", numBitPlanes); - } - - int byteWidth = (ic->width + 7) / 8; - - char** bitplanes = malloc(sizeof(void*)*numBitPlanes); - for (int i = 0; i < numBitPlanes; i++) { - bitplanes[i] = calloc(byteWidth*ic->height, 1); - } - - ham_control_t* hams = malloc(sizeof(ham_control_t)*ic->width*ic->height); - - 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 = getColor(x, y, ic); - amiga_color_t orig = getOriginalColor(x, y, ic); - - ham_control_t ham = findBestHamPixel(ic, color, orig, lastPixel); - lastPixel = ham.pixel; - -#if 0 - printf("Color: "); - printColor(color); - printf("\n"); - printf("Orig: "); - printColor(orig); - printf("\n"); - - printf("Ham:\n"); - printf("Control: %d\n", ham.control); - printf("Data: %d\n", ham.data); - printf("Color: "); - printColor(ham.pixel); - printf("\n\n"); -#endif - - hams[(y*ic->width)+x] = ham; - } - } - - - for (int y = 0, writeIndex = 0; y < ic->height; y++) { - for (int byte = 0;byte < byteWidth; byte++) { - for (int bit = 0; bit < 8; bit++) { - int x = byte * 8 + 7 - bit; - - ham_control_t ham = hams[(y*ic->width)+x]; - - int _numBitPlanes = 4; - for (int plane_index = 0; plane_index < _numBitPlanes; plane_index++) { - char* plane = bitplanes[plane_index]; - plane[writeIndex] |= ((ham.data >> plane_index) & 1) << bit; - } - - for (int plane_index = 0; plane_index < 2; plane_index++) { - char* plane = bitplanes[4+plane_index]; - plane[writeIndex] |= ((ham.control >> plane_index) & 1) << bit; - } - - - } - writeIndex++; - } - } - - - FILE* fp = openFileWrite("%s-ham.bin", outFilename); - - for (int y = 0; y < ic->height; y++) { - for (int plane_index = 0; plane_index < numBitPlanes; plane_index++) { - char* plane = bitplanes[plane_index]; - fwrite(&plane[y*byteWidth], byteWidth, 1, fp); - } - } - fclose(fp); - if (config.verbose) { - printf("done\n\n"); - } -} - -static void -doHamMode(char* outFilename, imagecon_image_t* ic) -{ - config.maxColors = 16; - generateQuantizedImage(ic, 0); - - if (config.outputBitplanes) { - outputHamBitplanes(outFilename, ic); - } - - outputPalette(outFilename, ic); - -} - static void processFile(char* outFilename, imagecon_image_t* ic) { @@ -668,21 +316,26 @@ processFile(char* outFilename, imagecon_image_t* ic) } if (config.hamMode) { - doHamMode(outFilename, ic); + ham_process(outFilename, ic); } else { if (config.quantize || config.overridePalette) { if (config.ehbMode) { generateEHBImage(ic); } else { if (config.overridePalette) { - loadPaletteFile(ic); + palette_loadFile(ic); } generateQuantizedImage(ic, config.overridePalette != 0); } } else { - generatePalette(ic); + generatePalettedImage(ic); } - + + if (config.dither) { + dither_image(ic, dither_getPalettedColor); + dither_transferToPaletted(ic); + } + if (config.outputBitplanes) { outputBitplanes(outFilename, ic); } @@ -691,7 +344,7 @@ processFile(char* outFilename, imagecon_image_t* ic) outputMask(outFilename, ic); } - outputPalette(outFilename, ic); + palette_output(outFilename, ic); } if (config.verbose) { @@ -779,6 +432,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}, + {"dither", no_argument, &config.dither, 1}, {"use-palette", required_argument, 0, 'p'}, {"output", required_argument, 0, 'o'}, {"colors", required_argument, 0, 'c'}, @@ -860,3 +514,5 @@ main(int argc, char **argv) return 0; } + + diff --git a/tools/imagecon/imagecon.h b/tools/imagecon/imagecon.h index bad5512..713deba 100644 --- a/tools/imagecon/imagecon.h +++ b/tools/imagecon/imagecon.h @@ -1,5 +1,16 @@ #pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #define MAX_PALETTE 32 typedef struct { @@ -13,20 +24,13 @@ typedef struct { int outputCopperList; int ehbMode; int hamMode; + int dither; char* overridePalette; int quantize; int verbose; char** argv; } imagecon_config_t; -#if 0 -typedef struct { - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -} amiga_color_t; -#else typedef struct { int r; int g; @@ -34,7 +38,11 @@ typedef struct { int a; } amiga_color_t; -#endif +typedef struct { + int control; + int data; + amiga_color_t pixel; +} ham_control_t; typedef struct { @@ -44,9 +52,23 @@ typedef struct { png_bytep* rowPointers; unsigned char* amigaImage; amiga_color_t palette[MAX_PALETTE*2]; // extra half brite mode + amiga_color_t* dithered; } imagecon_image_t; +#include "color.h" +#include "dither.h" +#include "ham.h" +#include "file.h" +#include "palette.h" + extern imagecon_config_t config; -extern void abort_(const char * s, ...); -extern void png_read(char* file_name, imagecon_image_t* ic); + +extern void +abort_(const char * s, ...); + +extern void +png_read(char* file_name, imagecon_image_t* ic); + +void +generateQuantizedImage(imagecon_image_t* ic, int usePalette); diff --git a/tools/imagecon/palette.c b/tools/imagecon/palette.c new file mode 100644 index 0000000..b81865d --- /dev/null +++ b/tools/imagecon/palette.c @@ -0,0 +1,103 @@ +#include "imagecon.h" + +void palette_loadFile(imagecon_image_t* ic) +{ + FILE* fp = file_openRead(config.overridePalette); + int paletteIndex; + + for (paletteIndex = 0; paletteIndex < MAX_PALETTE; paletteIndex++) { + unsigned int c; + char buffer[255]; + char* line = fgets(buffer, 255, fp); + if (!line) { + break; + } + sscanf(buffer, "%x\n", &c); + + ic->palette[paletteIndex].r = (c >> 8 & 0xF) << 4; + ic->palette[paletteIndex].g = (c >> 4 & 0xF) << 4; + ic->palette[paletteIndex].b = (c >> 0 & 0xF) << 4; + ic->palette[paletteIndex].a = 255; + } + + ic->numColors = paletteIndex; +} + + +void +palette_output(char* outFilename, imagecon_image_t* ic) +{ + if (config.verbose) { + printf("outputPalette...%d colors\n", ic->numColors); + } + + FILE* fp = 0; + FILE* paletteFP = 0; + FILE* paletteAsmFP = 0; + FILE* paletteGreyFP = 0; + + if (config.outputCopperList) { + fp = file_openWrite("%s-copper-list.s", outFilename); + } + + if (config.outputPalette) { + paletteFP = file_openWrite("%s.pal", outFilename); + } + + if (config.outputPaletteGrey) { + paletteGreyFP = file_openWrite("%s-grey.s", outFilename); + fprintf(paletteGreyFP, "\tmovem.l d0-a6,-(sp)\n\tlea CUSTOM,a6\n"); + } + + if (config.outputPaletteAsm) { + paletteAsmFP = file_openWrite("%s-palette.s", outFilename); + fprintf(paletteAsmFP, "\tmovem.l d0-a6,-(sp)\n\tlea CUSTOM,a6\n"); + } + + if (config.verbose) { + printf("outputPalette:\n"); + } + + for (int i = 0; i < (config.ehbMode ? ic->numColors/2 : ic->numColors); i++) { + if (config.verbose) { + printf("%02d: hex=%03x r=%03d g=%03d b=%03d a=%03d\n", i , ic->palette[i].r << 8 | ic->palette[i].g << 4 | ic->palette[i].b, ic->palette[i].r, ic->palette[i].g, ic->palette[i].b, ic->palette[i].a); + } + if (paletteFP) { + fprintf(paletteFP, "%03x\n", (ic->palette[i].r >> 4) << 8 | (ic->palette[i].g >>4) << 4 | (ic->palette[i].b >>4)); + } + if (paletteAsmFP) { + fprintf(paletteAsmFP, "\tlea COLOR%02d(a6),a0\n\tmove.w #$%03x,(a0)\n", i, (ic->palette[i].r >> 4) << 8 | (ic->palette[i].g >>4) << 4 | (ic->palette[i].b >>4)); + } + if (paletteGreyFP) { + // TODO: this is for compat, can be better + unsigned grey = (((ic->palette[i].r>>4) + (ic->palette[i].g>>4) + (ic->palette[i].b>>4))/3); + fprintf(paletteGreyFP, "\tlea COLOR%02d(a6),a0\n\tmove.w #$%03x,(a0)\n", i, grey << 8 | grey << 4 | grey); + } + + if (fp) { + fprintf(fp, "\tdc.w $%x,$%x\n", 0x180+(i*2), (ic->palette[i].r >> 4) << 8 | (ic->palette[i].g >>4) << 4 | (ic->palette[i].b >>4)); + } + } + + if (paletteFP) { + fclose(paletteFP); + } + + if (paletteGreyFP) { + fprintf(paletteGreyFP, "\tmovem.l (sp)+,d0-a6\n"); + fclose(paletteGreyFP); + } + + if (paletteAsmFP) { + fprintf(paletteAsmFP, "\tmovem.l (sp)+,d0-a6\n"); + fclose(paletteFP); + } + + if (fp) { + fclose(fp); + } + + if (config.verbose) { + printf("done\n\n"); + } +} diff --git a/tools/imagecon/palette.h b/tools/imagecon/palette.h new file mode 100644 index 0000000..68a3db7 --- /dev/null +++ b/tools/imagecon/palette.h @@ -0,0 +1,7 @@ +#pragma once + +void +palette_output(char* outFilename, imagecon_image_t* ic); + +void +palette_loadFile(imagecon_image_t* ic); diff --git a/tools/imagecon/png.c b/tools/imagecon/png.c index e52a571..b8cdc63 100644 --- a/tools/imagecon/png.c +++ b/tools/imagecon/png.c @@ -117,4 +117,6 @@ png_read(char* file_name, imagecon_image_t* ic) } ic->amigaImage = calloc(ic->width*ic->height, 1); + + } diff --git a/tools/imagecon/reference/ham-ham.bin b/tools/imagecon/reference/ham-ham.bin index f41390a..e50af84 100644 Binary files a/tools/imagecon/reference/ham-ham.bin and b/tools/imagecon/reference/ham-ham.bin differ