1
0
mirror of https://frontier.innolan.net/github/AmigaExamples.git synced 2026-01-13 00:58:30 +00:00

Slightly improved dithering

This commit is contained in:
alpine9000
2016-03-08 17:21:28 +11:00
parent 9000ad029c
commit 75186eb4d9
8 changed files with 185 additions and 89 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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)));
}
}
}

View File

@ -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

View File

@ -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 <palette file>\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'},

View File

@ -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;

View File

@ -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);
}