Tried to do sliced ham
This commit is contained in:
parent
53e105117a
commit
4737a5eb74
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <palette file>\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'},
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#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);
|
||||
}
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue