diff --git a/tools/imagecon/Makefile b/tools/imagecon/Makefile index 5eee97f..dd35fc1 100644 --- a/tools/imagecon/Makefile +++ b/tools/imagecon/Makefile @@ -1,5 +1,5 @@ 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 +OBJS=out/imagecon.o out/png.o out/color.o out/dither.o out/ham.o out/palette.o out/file.o out/sliced_ham.o out/quant.o WARN_ERROR=-Werror HOST_WARNINGS=$(WARN_ERROR) -pedantic-errors -Wfatal-errors -Wall -Wextra -Wno-unused-parameter -Wshadow -limagequant HOST_CFLAGS=$(HOST_WARNINGS) -O3 @@ -39,7 +39,7 @@ 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 + @sed 's/^.*\:/out\/&/' < out/$*.d.tmp > out/$*.d @rm -f out/$*.d.tmp out: diff --git a/tools/imagecon/color.c b/tools/imagecon/color.c index b624a3a..02e8da1 100644 --- a/tools/imagecon/color.c +++ b/tools/imagecon/color.c @@ -110,6 +110,12 @@ color_setDitheredPixel(imagecon_image_t* ic, int x, int y, dither_color_t color) if (color.b < 0) { color.b = 0; } + if (color.a > 255) { + color.a = 255; + } + if (color.a < 0) { + color.a = 0; + } d->r = color.r; d->g = color.g; diff --git a/tools/imagecon/dither.c b/tools/imagecon/dither.c index f2cd6e4..5b1d2f6 100644 --- a/tools/imagecon/dither.c +++ b/tools/imagecon/dither.c @@ -4,9 +4,9 @@ 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) +dither_getPalettedColor(dither_data_t data) { - return color_findClosestPalettePixel(ic, color); + return color_findClosestPalettePixel(data.ic, data.color); } @@ -31,22 +31,28 @@ _propagateError(imagecon_image_t* ic, float factor, int x, int y, dither_color_t void -dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t*, amiga_color_t color, amiga_color_t last)) +dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(dither_data_t data)) { _dither_createDither(ic); - + + dither_data_t data; for (int y = 0; y < ic->height; y++) { amiga_color_t last = {-1, -1, -1, -1}; for (int x = 0; x < ic->width; x++) { dither_color_t old = color_getDitheredPixel(ic, x, y); - amiga_color_t new = selector(ic, color_ditheredToAmiga(old), last); + data.color = color_ditheredToAmiga(old); + data.last = last; + data.x = x; + data.y = y; + data.ic = ic; + amiga_color_t new = selector(data); dither_color_t error; float gamma = 1.0; - error.r = old.r - (new.r*gamma); - error.g = old.g - (new.g*gamma); - error.b = old.b - (new.b*gamma); + error.r = CLAMP(old.r - (new.r*gamma)); + error.g = CLAMP(old.g - (new.g*gamma)); + error.b = CLAMP(old.b - (new.b*gamma)); color_setDitheredPixel(ic, x, y, color_amigaToDithered(new)); last = new; diff --git a/tools/imagecon/dither.h b/tools/imagecon/dither.h index 9751c32..bf1f303 100644 --- a/tools/imagecon/dither.h +++ b/tools/imagecon/dither.h @@ -1,10 +1,19 @@ #pragma once +typedef struct { + imagecon_image_t* ic; + amiga_color_t color; + amiga_color_t last; + int x; + int y; +} dither_data_t; + + amiga_color_t -dither_getPalettedColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last); +dither_getPalettedColor(dither_data_t data); void -dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last)); +dither_image(imagecon_image_t* ic, amiga_color_t (*selector)(dither_data_t data)); void dither_transferToPaletted(imagecon_image_t* ic); diff --git a/tools/imagecon/ham.c b/tools/imagecon/ham.c index 086099a..eca6860 100644 --- a/tools/imagecon/ham.c +++ b/tools/imagecon/ham.c @@ -1,7 +1,7 @@ #include "imagecon.h" -static ham_control_t -_ham_findClosestPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last) +ham_control_t +ham_findClosestPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last) { int delta = INT_MAX; @@ -58,9 +58,9 @@ _ham_findClosestPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t l static amiga_color_t -_ham_getHamColor(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last) +_ham_getHamColor(dither_data_t data) { - ham_control_t ham = _ham_findClosestPixel(ic, color, last); + ham_control_t ham = ham_findClosestPixel(data.ic, data.color, data.last); return ham.pixel; } @@ -74,7 +74,7 @@ _ham_createHams(imagecon_image_t* ic) amiga_color_t lastPixel = { -1, -1, -1, -1}; for (int x = 0; x < ic->width; x++) { amiga_color_t orig = color_ditheredToAmiga(color_getDitheredPixel(ic, x, y)); - ham_control_t ham = _ham_findClosestPixel(ic, orig, lastPixel); + ham_control_t ham = ham_findClosestPixel(ic, orig, lastPixel); lastPixel = ham.pixel; hams[(y*ic->width)+x] = ham; } @@ -111,7 +111,7 @@ _ham_outputBitplanes(char* outFilename, imagecon_image_t* ic) 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 = _ham_findClosestPixel(ic, orig, lastPixel); + ham_control_t ham = ham_findClosestPixel(ic, orig, lastPixel); lastPixel = ham.pixel; hams[(y*ic->width)+x] = ham; } @@ -157,7 +157,7 @@ _ham_outputBitplanes(char* outFilename, imagecon_image_t* ic) } -static int +static int _score(imagecon_image_t* ic) { long error = 0; @@ -165,7 +165,7 @@ _score(imagecon_image_t* ic) 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 = _ham_findClosestPixel(ic, color, lastPixel); + ham_control_t ham = ham_findClosestPixel(ic, color, lastPixel); error += color_delta(color, color_findClosestPalettePixel(ic, ham.pixel)); lastPixel = ham.pixel; } @@ -232,7 +232,7 @@ _ham_bruteForcePalette(imagecon_image_t* ic) void -ham_process(char* outFilename, imagecon_image_t* ic) +ham_process(imagecon_image_t* ic, char* outFilename) { config.maxColors = 16; @@ -241,8 +241,7 @@ ham_process(char* outFilename, imagecon_image_t* ic) _ham_bruteForcePalette(ic); } else { - config.maxColors = 16; - + if (config.overridePalette) { palette_loadFile(ic); } diff --git a/tools/imagecon/ham.h b/tools/imagecon/ham.h index 962a09d..22b3ece 100644 --- a/tools/imagecon/ham.h +++ b/tools/imagecon/ham.h @@ -1,4 +1,11 @@ #pragma once +ham_control_t +ham_findClosestPixel(imagecon_image_t* ic, amiga_color_t color, amiga_color_t last); + void -ham_process(char* outFilename, imagecon_image_t* ic); +ham_process(imagecon_image_t* ic, char* outFilename); + + +void +sham_process(imagecon_image_t* ic, char* outFilename); diff --git a/tools/imagecon/imagecon.c b/tools/imagecon/imagecon.c index 2e6915f..bcd967d 100644 --- a/tools/imagecon/imagecon.c +++ b/tools/imagecon/imagecon.c @@ -15,6 +15,7 @@ imagecon_config_t config = { .ehbMode = 0, .hamMode = 0, .hamBruteForce = 0, + .slicedHam = 0, .quantize = 0, .dither = 0, .overridePalette = 0 @@ -39,6 +40,7 @@ usage() " --extra-half-brite\n"\ " --ham\n"\ " --ham-brute-force\n"\ + " --sliced-ham\n"\ " --dither\n"\ " --use-palette \n"\ " --verbose\n", config.argv[0]); @@ -120,6 +122,36 @@ generateQuantizedImage(imagecon_image_t* ic, int usePalette) } +void +generateQuant2(imagecon_image_t* ic) +{ + quant_image_t* image = quant_newImage(ic->width, ic->height); + + for (int c = 0, y=0; y< ic->height; y++) { + png_byte* row = ic->rowPointers[y]; + for (int x=0; x < ic->width; x++) { + png_byte* ptr = &(row[x*4]); + image->pix[c++] = ptr[0]; + image->pix[c++] = ptr[1]; + image->pix[c++] = ptr[2]; + } + } + + quant_quantize(image, config.maxColors, config.dither); + + for (int c = 0,y=0; y< ic->height; y++) { + png_byte* row = ic->rowPointers[y]; + for (int x=0; x < ic->width; x++) { + png_byte* ptr = &(row[x*4]); + ptr[0] = image->pix[c++]; + ptr[1] = image->pix[c++]; + ptr[2] = image->pix[c++]; + ptr[3] = 255; + + } + } +} + void generatePalettedImage(imagecon_image_t* ic) { @@ -133,12 +165,11 @@ generatePalettedImage(imagecon_image_t* ic) for (int x=0; x < ic->width; x++) { png_byte* ptr = &(row[x*4]); - // TODO: this shift is probably wrong now amiga_color_t color; - color.r = ptr[0] >> 4; - color.g = ptr[1] >> 4; - color.b = ptr[2] >> 4; - color.a = ptr[3] >> 4; + color.r = ptr[0]; + color.g = ptr[1]; + color.b = ptr[2]; + color.a = ptr[3]; int index = -1; for (int i = 0; i < paletteIndex; i++) { @@ -321,8 +352,10 @@ processFile(char* outFilename, imagecon_image_t* ic) printf("processFile...\n"); } - if (config.hamMode) { - ham_process(outFilename, ic); + if (config.slicedHam) { + sham_process(ic, outFilename); + } else if (config.hamMode) { + ham_process(ic, outFilename); } else { if (config.quantize || config.overridePalette) { if (config.ehbMode) { @@ -439,6 +472,7 @@ main(int argc, char **argv) {"extra-half-brite", no_argument, &config.ehbMode, 1}, {"ham", no_argument, &config.hamMode, 1}, {"ham-brute-force", no_argument, &config.hamBruteForce, 1}, + {"sliced-ham", no_argument, &config.slicedHam, 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 936a75e..3f0b274 100644 --- a/tools/imagecon/imagecon.h +++ b/tools/imagecon/imagecon.h @@ -13,8 +13,10 @@ #define MAX_PALETTE 32 -typedef struct { +#define RGB24TORGB12(x) (x >> 4) +#define CLAMP(x) (x > 255.0 ? 255.0 : (x < -255.0 ? -255.0 : x)) +typedef struct { int maxColors; int outputPalette; int outputPaletteAsm; @@ -25,6 +27,7 @@ typedef struct { int ehbMode; int hamMode; int hamBruteForce; + int slicedHam; int dither; char* overridePalette; int quantize; @@ -69,6 +72,7 @@ typedef struct { #include "ham.h" #include "file.h" #include "palette.h" +#include "quant.h" extern imagecon_config_t config; diff --git a/tools/imagecon/palette.c b/tools/imagecon/palette.c index 200068b..2ea1a46 100644 --- a/tools/imagecon/palette.c +++ b/tools/imagecon/palette.c @@ -23,7 +23,6 @@ void palette_loadFile(imagecon_image_t* ic) ic->numColors = paletteIndex; } -#define RGB24TORGB12(x) (x >> 4) void palette_output(char* outFilename, imagecon_image_t* ic) diff --git a/tools/imagecon/quant.c b/tools/imagecon/quant.c new file mode 100644 index 0000000..7261d5c --- /dev/null +++ b/tools/imagecon/quant.c @@ -0,0 +1,316 @@ +/* + * + * Originally from: https://rosettacode.org/wiki/Color_quantization/C + * License information http://www.gnu.org/licenses/fdl-1.2.html + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "quant.h" + + +quant_image_t* +quant_newImage(int w, int h) +{ + quant_image_t* im = malloc(sizeof(quant_image_t) + h * w * 3); + im->w = w; im->h = h; + im->pix = (unsigned char *)(im + 1); + return im; +} + +#define ON_INHEAP 1 + +typedef struct oct_node_t oct_node_t, *oct_node; +struct oct_node_t{ + int64_t r, g, b; /* sum of all child node colors */ + int count, heap_idx; + unsigned char n_kids, kid_idx, flags, depth; + oct_node kids[8], parent; +}; + +typedef struct { + int alloc, n; + oct_node* buf; +} node_heap; + +inline int cmp_node(oct_node a, oct_node b) +{ + if (a->n_kids < b->n_kids) return -1; + if (a->n_kids > b->n_kids) return 1; + + int ac = a->count >> a->depth; + int bc = b->count >> b->depth; + return ac < bc ? -1 : ac > bc; +} + +void down_heap(node_heap *h, oct_node p) +{ + int n = p->heap_idx, m; + while (1) { + m = n * 2; + if (m >= h->n) break; + if (m + 1 < h->n && cmp_node(h->buf[m], h->buf[m + 1]) > 0) m++; + + if (cmp_node(p, h->buf[m]) <= 0) break; + + h->buf[n] = h->buf[m]; + h->buf[n]->heap_idx = n; + n = m; + } + h->buf[n] = p; + p->heap_idx = n; +} + +void up_heap(node_heap *h, oct_node p) +{ + int n = p->heap_idx; + oct_node prev; + + while (n > 1) { + prev = h->buf[n / 2]; + if (cmp_node(p, prev) >= 0) break; + + h->buf[n] = prev; + prev->heap_idx = n; + n /= 2; + } + h->buf[n] = p; + p->heap_idx = n; +} + +void heap_add(node_heap *h, oct_node p) +{ + if ((p->flags & ON_INHEAP)) { + down_heap(h, p); + up_heap(h, p); + return; + } + + p->flags |= ON_INHEAP; + if (!h->n) h->n = 1; + if (h->n >= h->alloc) { + while (h->n >= h->alloc) h->alloc += 1024; + h->buf = realloc(h->buf, sizeof(oct_node) * h->alloc); + } + + p->heap_idx = h->n; + h->buf[h->n++] = p; + up_heap(h, p); +} + +oct_node pop_heap(node_heap *h) +{ + if (h->n <= 1) return 0; + + oct_node ret = h->buf[1]; + h->buf[1] = h->buf[--h->n]; + + h->buf[h->n] = 0; + + h->buf[1]->heap_idx = 1; + down_heap(h, h->buf[1]); + + return ret; +} + +static oct_node pool = 0; +oct_node node_new(unsigned char idx, unsigned char depth, oct_node p) +{ + static int len = 0; + if (len <= 1) { + oct_node p_ = calloc(sizeof(oct_node_t), 2048); + p_->parent = pool; + pool = p_; + len = 2047; + } + + oct_node x = pool + len--; + x->kid_idx = idx; + x->depth = depth; + x->parent = p; + if (p) p->n_kids++; + return x; +} + +void node_free() +{ + oct_node p; + while (pool) { + p = pool->parent; + free(pool); + pool = p; + } +} + +oct_node node_insert(oct_node root, unsigned char *pix) +{ + unsigned char i, bit, depth = 0; + + for (bit = 1 << 7; ++depth < 8; bit >>= 1) { + i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit); + if (!root->kids[i]) + root->kids[i] = node_new(i, depth, root); + + root = root->kids[i]; + } + + root->r += pix[0]; + root->g += pix[1]; + root->b += pix[2]; + root->count++; + return root; +} + +oct_node node_fold(oct_node p) +{ + if (p->n_kids) abort(); + oct_node q = p->parent; + q->count += p->count; + + q->r += p->r; + q->g += p->g; + q->b += p->b; + q->n_kids --; + q->kids[p->kid_idx] = 0; + return q; +} + +void color_replace(oct_node root, unsigned char *pix) +{ + unsigned char i, bit; + + for (bit = 1 << 7; bit; bit >>= 1) { + i = !!(pix[1] & bit) * 4 + !!(pix[0] & bit) * 2 + !!(pix[2] & bit); + if (!root->kids[i]) break; + root = root->kids[i]; + } + + pix[0] = root->r; + pix[1] = root->g; + pix[2] = root->b; +} + +static oct_node +nearest_color(int *v, node_heap *h) { + int i; + int diff, max = 100000000; + oct_node o = 0; + for (i = 1; i < h->n; i++) { + diff = 3 * abs(h->buf[i]->r - v[0]) + + 5 * abs(h->buf[i]->g - v[1]) + + 2 * abs(h->buf[i]->b - v[2]); + if (diff < max) { + max = diff; + o = h->buf[i]; + } + } + return o; +} + +void error_diffuse(quant_image_t* im, node_heap *h) +{ + +#define POS(i, j) (3 * ((i) * im->w + (j))) + int i, j; + int *npx = calloc(sizeof(int), im->h * im->w * 3), *px; + int v[3]; + unsigned char *pix = im->pix; + oct_node nd; + +#define C10 7 +#define C01 5 +#define C11 2 +#define C00 1 +#define CTOTAL (C00 + C11 + C10 + C01) + + for (px = npx, i = 0; i < im->h; i++) { + for (j = 0; j < im->w; j++, pix += 3, px += 3) { + px[0] = (int)pix[0] * CTOTAL; + px[1] = (int)pix[1] * CTOTAL; + px[2] = (int)pix[2] * CTOTAL; + } + } +#define clamp(x, i) if (x[i] > 255) x[i] = 255; if (x[i] < 0) x[i] = 0 + pix = im->pix; + for (px = npx, i = 0; i < im->h; i++) { + for (j = 0; j < im->w; j++, pix += 3, px += 3) { + px[0] /= CTOTAL; + px[1] /= CTOTAL; + px[2] /= CTOTAL; + clamp(px, 0); clamp(px, 1); clamp(px, 2); + + nd = nearest_color(px, h); + + v[0] = px[0] - nd->r; + v[1] = px[1] - nd->g; + v[2] = px[2] - nd->b; + + pix[0] = nd->r; pix[1] = nd->g; pix[2] = nd->b; + if (j < im->w - 1) { + npx[POS(i, j+1) + 0] += v[0] * C10; + npx[POS(i, j+1) + 1] += v[1] * C10; + npx[POS(i, j+1) + 2] += v[2] * C10; + } + if (i >= im->h - 1) continue; + + npx[POS(i+1, j) + 0] += v[0] * C01; + npx[POS(i+1, j) + 1] += v[1] * C01; + npx[POS(i+1, j) + 2] += v[2] * C01; + + if (j < im->w - 1) { + npx[POS(i+1, j+1) + 0] += v[0] * C11; + npx[POS(i+1, j+1) + 1] += v[1] * C11; + npx[POS(i+1, j+1) + 2] += v[2] * C11; + } + if (j) { + npx[POS(i+1, j-1) + 0] += v[0] * C00; + npx[POS(i+1, j-1) + 1] += v[1] * C00; + npx[POS(i+1, j-1) + 2] += v[2] * C00; + } + } + } + free(npx); +} + +void +quant_quantize(quant_image_t* im, int n_colors, int dither) +{ + int i; + unsigned char *pix = im->pix; + node_heap heap = { 0, 0, 0 }; + + oct_node root = node_new(0, 0, 0), got; + + for (i = 0; i < im->w * im->h; i++, pix += 3) + heap_add(&heap, node_insert(root, pix)); + + + while (heap.n > n_colors + 1) + heap_add(&heap, node_fold(pop_heap(&heap))); + + + + double c; + for (i = 1; i < heap.n; i++) { + got = heap.buf[i]; + c = got->count; + got->r = got->r / c + .5; + got->g = got->g / c + .5; + got->b = got->b / c + .5; + } + + if (dither) error_diffuse(im, &heap); + else + for (i = 0, pix = im->pix; i < im->w * im->h; i++, pix += 3) + color_replace(root, pix); + + node_free(); + free(heap.buf); +} diff --git a/tools/imagecon/quant.h b/tools/imagecon/quant.h new file mode 100644 index 0000000..067081b --- /dev/null +++ b/tools/imagecon/quant.h @@ -0,0 +1,12 @@ +#pragma once + +typedef struct { + int w, h; + unsigned char *pix; +} quant_image_t; + +void +quant_quantize(quant_image_t* im, int n_colors, int dither); + +quant_image_t* +quant_newImage(int w, int h); diff --git a/tools/imagecon/sliced_ham.c b/tools/imagecon/sliced_ham.c new file mode 100644 index 0000000..0a3a332 --- /dev/null +++ b/tools/imagecon/sliced_ham.c @@ -0,0 +1,294 @@ +#include "imagecon.h" + +static void +_sham_generateLinePalette(imagecon_image_t* ic, int line); +static void +_sham_outputCopperLine(imagecon_image_t* ic, FILE* fp, int line); + +static FILE* _copperFP; +static amiga_color_t *combos; +static int totalCombinations = 16*16*16; + +static amiga_color_t +_sham_getHamColor(dither_data_t data) +{ + static int row = -1; + + if (row != data.y) { + row = data.y; + _sham_generateLinePalette(data.ic, row); + _sham_outputCopperLine(data.ic, _copperFP, row); + } + + + ham_control_t ham = ham_findClosestPixel(data.ic, data.color, data.last); + return ham.pixel; +} + + +static ham_control_t* +_sham_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}; + _sham_generateLinePalette(ic, y); + for (int x = 0; x < ic->width; x++) { + amiga_color_t orig = color_ditheredToAmiga(color_getDitheredPixel(ic, x, y)); + ham_control_t ham = ham_findClosestPixel(ic, orig, lastPixel); + lastPixel = ham.pixel; + hams[(y*ic->width)+x] = ham; + } + } + + return hams; +} + +static int +_score(imagecon_image_t* ic, int y) +{ + long error = 0; + 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 = ham_findClosestPixel(ic, color, lastPixel); + error += color_delta(color, color_findClosestPalettePixel(ic, ham.pixel)); + lastPixel = ham.pixel; + } + + return error; +} + +static void +_sham_bruteForceInit(imagecon_image_t* ic) +{ + combos = malloc(sizeof(amiga_color_t)*totalCombinations); + int index = 0; + 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; + } + } + } +} +static void +_sham_bruteForceLinePalette(imagecon_image_t* ic, int row) +{ + int length = 16; + + bzero(ic->palette, sizeof(ic->palette)); + ic->numColors = 16; + + int step = 1; + 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+=step, b+=step) { + ic->palette[x] = combos[i]; + fflush(stdout); + printf("%c7", 27); + fflush(stdout); + printf(" %03d %05ld/%05ld (%ld%%)", row, b, big, (b*100L)/big); + fflush(stdout); + printf("%c8", 27); + fflush(stdout); + long score = _score(ic, row); + if (score < benchmark) { + benchmark = score; + bmIndex = i; + } + } + ic->palette[x] = combos[bmIndex]; + + + } +} + + +static void +_sham_generateQuantizedLinePalette(imagecon_image_t* ic, int line) +{ + liq_attr *attr = liq_attr_create(); + + void* ptr[1] = { &ic->rowPointers[line] }; + liq_image *image = liq_image_create_rgba_rows(attr, ptr, ic->width, 1, /* gamma */0.0); + + liq_set_max_colors(attr, config.maxColors); + + liq_color color = {.a = 255, .r = 0, .b = 0, .g = 0}; + liq_image_add_fixed_color(image, color); + liq_result *res = liq_quantize_image(attr, image); + + const liq_palette *pal = liq_get_palette(res); + + if (config.verbose) { + printf("_sham_generateQuantizedLinePalette: pal->count = %d\n", pal->count); + } + + for (unsigned i = 0; i < pal->count; i++) { + ic->palette[i].r = pal->entries[i].r; + ic->palette[i].g = pal->entries[i].g; + ic->palette[i].b = pal->entries[i].b; + ic->palette[i].a = pal->entries[i].a; + } + + for (unsigned i = 0; i < pal->count; i++) { + if (ic->palette[i].r == 0 && + ic->palette[i].g == 0 && + ic->palette[i].b == 0 && + ic->palette[i].a == 255) { + + if (i != 0) { + amiga_color_t black = ic->palette[i]; + amiga_color_t other = ic->palette[0]; + ic->palette[0] = black; + ic->palette[i] = other; + } + break; + } + } + + + ic->numColors = pal->count; + + for (int i = 0; i < ic->numColors; i++) { + if (config.verbose) { + printf("%02d: r=%03d g=%03d b=%03d a=%03d\n", i, ic->palette[i].r, ic->palette[i].g, ic->palette[i].b, ic->palette[i].a); + } + } + + liq_attr_destroy(attr); + liq_image_destroy(image); + liq_result_destroy(res); +} + +static void +_sham_generateLinePalette(imagecon_image_t* ic, int line) +{ + if (config.hamBruteForce) { + _sham_bruteForceLinePalette(ic, line); + } else { + _sham_generateQuantizedLinePalette(ic, line); + } + +} + +static void +_sham_outputCopperLine(imagecon_image_t* ic, FILE* fp, int line) +{ + int endPos = 0xe1; + + line = line+0x2c-1; + if (line <= 255) { + fprintf(fp, "\tdc.w $%x,$fffe\n",(line)<<8|endPos|1); + } else { + fprintf(fp, "\tdc.w $%x,$fffe\n", ((line)-256)<<8|endPos|1); + } + + for (int i = 1; i < ic->numColors; i++) { + fprintf(fp, "\tdc.w $%x,$%x\n", 0x180+(i*2), RGB24TORGB12(ic->palette[i].r) << 8 | RGB24TORGB12(ic->palette[i].g) << 4 | RGB24TORGB12(ic->palette[i].b)); + } + + +} + +static void +_sham_outputBitplanes(imagecon_image_t* ic, FILE* copperFP, FILE* bitplaneFP) +{ + + 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, _sham_getHamColor); + hams = _sham_createHams(ic); + } else { + hams = malloc(sizeof(ham_control_t)*ic->width*ic->height); + + for (int y = 0; y < ic->height; y++) { + _sham_generateLinePalette(ic, y); + _sham_outputCopperLine(ic, copperFP, 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 = ham_findClosestPixel(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++; + } + } + + + 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, bitplaneFP); + } + } + + + if (config.verbose) { + printf("done\n\n"); + } +} + + + + +void +sham_process(imagecon_image_t* ic, char* outFilename) +{ + config.maxColors = 16; + + _copperFP = file_openWrite("%s-sham-copper.s", outFilename); + FILE* bitplaneFP = file_openWrite("%s-sham.bin", outFilename); + + if (config.hamBruteForce) { + _sham_bruteForceInit(ic); + } + + if (config.outputBitplanes) { + _sham_outputBitplanes(ic, _copperFP, bitplaneFP); + } + + fclose(_copperFP); + fclose(bitplaneFP); +} +