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:
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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'},
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user