Comment sixel_from_image().

This commit is contained in:
Michael Grant
2026-08-07 22:41:00 +01:00
parent d53f8eff9e
commit 04644c7267

View File

@@ -1065,6 +1065,7 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
uint64_t destination_width, destination_height;
uint64_t content_width, content_height, x0, x1, y0, y1;
/* Work out the requested cell crop in destination pixel coordinates. */
source.pixels = image_get_pixels(im, &source.stride, NULL);
image_get_dimensions(im, &source.width, &source.height);
image_get_canvas_dimensions(im, &source.canvas_width,
@@ -1078,6 +1079,8 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
source.canvas_width - 1) / source.canvas_width;
content_height = ((uint64_t)source.height * destination_height +
source.canvas_height - 1) / source.canvas_height;
/* Convert the requested cell rectangle to clipped output pixel bounds. */
x0 = (uint64_t)ox * xpixel;
y0 = (uint64_t)oy * ypixel;
x1 = ((uint64_t)ox + cells_x) * xpixel;
@@ -1088,16 +1091,21 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
y1 = content_height;
if (x1 <= x0 || y1 <= y0)
return (NULL);
/* The clipped output bounds determine the SIXEL image dimensions. */
sx = x1 - x0;
sy = y1 - y0;
if (sx == 0 || sy == 0 || sx > SIXEL_WIDTH_LIMIT ||
sy > SIXEL_HEIGHT_LIMIT)
return (NULL);
/* Map the requested cell crop to the source image's pixel rectangle. */
image_get_pixel_rectangle(im, ox, oy, cells_x, cells_y, &sourcex0,
&sourcey0, &sourcewidth, &sourceheight);
if (sourcewidth == 0 || sourceheight == 0)
return (NULL);
/* Build an adaptive palette from the visible nontransparent pixels. */
histogram = xcalloc(SIXEL_HISTOGRAM_SIZE, sizeof *histogram);
for (y = 0; y < sy; y++) {
for (x = 0; x < sx; x++) {
@@ -1105,6 +1113,8 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
sourcewidth, sourceheight, sx, sy, x, y);
if (pixel[3] == 0)
continue;
/* Add this opaque pixel to its 5-bit RGB histogram bucket. */
index = ((pixel[0] >> 3) << 10)|
((pixel[1] >> 3) << 5)|(pixel[2] >> 3);
entry = &histogram[index];
@@ -1119,6 +1129,7 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
if (ncolours == 0)
return (NULL);
/* Create the indexed SIXEL image and convert its palette to SIXEL RGB. */
si = xcalloc(1, sizeof *si);
si->xpixel = xpixel;
si->ypixel = ypixel;
@@ -1135,6 +1146,7 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
si->colours[i] = (2U << 25)|(red << 16)|(green << 8)|blue;
}
/* Floyd-Steinberg dither colour and alpha into the indexed image. */
cache = xmalloc(SIXEL_HISTOGRAM_SIZE * sizeof *cache);
memset(cache, 0xff, SIXEL_HISTOGRAM_SIZE * sizeof *cache);
current = xcalloc(((size_t)sx + 2) * 4, sizeof *current);
@@ -1161,9 +1173,14 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
if (sixel_set_pixel(si, x, y, colour + 1) != 0)
goto fail;
/* Calculate the RGB error introduced by palette quantization. */
red_error = (int)red - palette[colour].red;
green_error = (int)green - palette[colour].green;
blue_error = (int)blue - palette[colour].blue;
/*
* Diffuse the error with the Floyd-Steinberg 7/16, 3/16,
* 5/16, 1/16 kernel; the accumulated error is divided by 16.
*/
current[error_index + 4] += red_error * 7;
current[error_index + 5] += green_error * 7;
current[error_index + 6] += blue_error * 7;
@@ -1177,22 +1194,28 @@ sixel_from_image(struct image *im, u_int ox, u_int oy, u_int cells_x,
next[error_index + 5] += green_error;
next[error_index + 6] += blue_error;
}
/* Diffuse alpha independently using the same kernel. */
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;
}
/* Advance to the next output row's accumulated error. */
tmp = current;
current = next;
next = tmp;
/* Reuse the old row buffer to accumulate the row after that. */
memset(next, 0, ((size_t)sx + 2) * 4 * sizeof *next);
}
free(current);
free(next);
free(cache);
return (si);
fail:
/* Discard a partially built image after an allocation or size failure. */
free(current);
free(next);
free(cache);