Dither alpha in SIXEL output

This commit is contained in:
Michael Grant
2026-08-07 11:21:10 +01:00
parent 002d38cb6b
commit b6c709307e

View File

@@ -1033,10 +1033,10 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
const u_char *pixel;
uint16_t *cache;
int *current, *next, *tmp;
int red_error, green_error, blue_error;
int red_error, green_error, blue_error, alpha_error;
u_int x, y, sx, sy, index, error_index;
u_int sourcex0, sourcey0, sourcewidth, sourceheight;
u_int red, green, blue, colour, i, ncolours;
u_int red, green, blue, alpha, colour, i, ncolours;
uint64_t destination_width, destination_height;
uint64_t content_width, content_height, x0, x1, y0, y1;
@@ -1078,7 +1078,7 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
for (x = 0; x < sx; x++) {
pixel = sixel_from_image_pixel(&source, sourcex0, sourcey0,
sourcewidth, sourceheight, sx, sy, x, y);
if (pixel[3] < 128)
if (pixel[3] == 0)
continue;
index = ((pixel[0] >> 3) << 10)|
((pixel[1] >> 3) << 5)|(pixel[2] >> 3);
@@ -1112,46 +1112,55 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
cache = xmalloc(SIXEL_HISTOGRAM_SIZE * sizeof *cache);
memset(cache, 0xff, SIXEL_HISTOGRAM_SIZE * sizeof *cache);
current = xcalloc(((size_t)sx + 2) * 3, sizeof *current);
next = xcalloc(((size_t)sx + 2) * 3, sizeof *next);
current = xcalloc(((size_t)sx + 2) * 4, sizeof *current);
next = xcalloc(((size_t)sx + 2) * 4, sizeof *next);
for (y = 0; y < sy; y++) {
for (x = 0; x < sx; x++) {
pixel = sixel_from_image_pixel(&source, sourcex0, sourcey0,
sourcewidth, sourceheight, sx, sy, x, y);
if (pixel[3] < 128)
continue;
error_index = (x + 1) * 3;
red = sixel_clamp_colour((int)pixel[0] +
current[error_index] / 16);
green = sixel_clamp_colour((int)pixel[1] +
current[error_index + 1] / 16);
blue = sixel_clamp_colour((int)pixel[2] +
current[error_index + 2] / 16);
colour = sixel_nearest_colour(palette, ncolours, cache,
red, green, blue);
if (sixel_set_pixel(si, x, y, colour + 1) != 0)
goto fail;
error_index = (x + 1) * 4;
/* SIXEL pixels are binary, so dither alpha separately. */
alpha = sixel_clamp_colour((int)pixel[3] +
current[error_index + 3] / 16);
alpha_error = (int)alpha;
if (alpha >= 128) {
alpha_error -= 255;
red = sixel_clamp_colour((int)pixel[0] +
current[error_index] / 16);
green = sixel_clamp_colour((int)pixel[1] +
current[error_index + 1] / 16);
blue = sixel_clamp_colour((int)pixel[2] +
current[error_index + 2] / 16);
colour = sixel_nearest_colour(palette, ncolours, cache,
red, green, blue);
if (sixel_set_pixel(si, x, y, colour + 1) != 0)
goto fail;
red_error = (int)red - palette[colour].red;
green_error = (int)green - palette[colour].green;
blue_error = (int)blue - palette[colour].blue;
current[error_index + 3] += red_error * 7;
current[error_index + 4] += green_error * 7;
current[error_index + 5] += blue_error * 7;
next[error_index - 3] += red_error * 3;
next[error_index - 2] += green_error * 3;
next[error_index - 1] += blue_error * 3;
next[error_index] += red_error * 5;
next[error_index + 1] += green_error * 5;
next[error_index + 2] += blue_error * 5;
next[error_index + 3] += red_error;
next[error_index + 4] += green_error;
next[error_index + 5] += blue_error;
red_error = (int)red - palette[colour].red;
green_error = (int)green - palette[colour].green;
blue_error = (int)blue - palette[colour].blue;
current[error_index + 4] += red_error * 7;
current[error_index + 5] += green_error * 7;
current[error_index + 6] += blue_error * 7;
next[error_index - 4] += red_error * 3;
next[error_index - 3] += green_error * 3;
next[error_index - 2] += blue_error * 3;
next[error_index] += red_error * 5;
next[error_index + 1] += green_error * 5;
next[error_index + 2] += blue_error * 5;
next[error_index + 4] += red_error;
next[error_index + 5] += green_error;
next[error_index + 6] += blue_error;
}
current[error_index + 7] += alpha_error * 7;
next[error_index - 1] += alpha_error * 3;
next[error_index + 3] += alpha_error * 5;
next[error_index + 7] += alpha_error;
}
tmp = current;
current = next;
next = tmp;
memset(next, 0, ((size_t)sx + 2) * 3 * sizeof *next);
memset(next, 0, ((size_t)sx + 2) * 4 * sizeof *next);
}
free(current);
free(next);