drastic image loading optimization

This commit is contained in:
boreddevnl 2026-03-09 14:31:24 +01:00
parent 6d7f9870f3
commit 1b181772a1
3 changed files with 125 additions and 82 deletions

Binary file not shown.

View file

@ -340,23 +340,38 @@ static void decode_image(unsigned char *data, int len, RenderElement *el) {
el->img_current_frame = 0;
el->next_frame_tick = sys_system(16, 0, 0, 0, 0) + (delays[0] * 60 / 1000);
uint32_t step_x = (img_w_orig << 16) / fit_w;
uint32_t step_y = (img_h_orig << 16) / fit_h;
for (int i = 0; i < frame_count; i++) {
el->img_frames[i] = malloc(fit_w * fit_h * sizeof(uint32_t));
if (el->img_frames[i]) {
unsigned char *src_frame = rgba + (i * img_w_orig * img_h_orig * 4);
uint16_t *src_h_table = malloc(fit_h * sizeof(uint16_t));
uint16_t *src_w_table = malloc(fit_w * sizeof(uint16_t));
if (src_h_table && src_w_table) {
for (int y = 0; y < fit_h; y++) src_h_table[y] = (y * step_y) >> 16;
for (int x = 0; x < fit_w; x++) src_w_table[x] = (x * step_x) >> 16;
for (int y = 0; y < fit_h; y++) {
int sy = y * img_h_orig / fit_h;
int sy = src_h_table[y];
uint32_t src_row_off = sy * img_w_orig;
uint32_t dst_row_off = y * fit_w;
for (int x = 0; x < fit_w; x++) {
int sx = x * img_w_orig / fit_w;
int idx = (sy * img_w_orig + sx) * 4;
int sx = src_w_table[x];
int idx = (src_row_off + sx) * 4;
uint32_t r = src_frame[idx];
uint32_t g = src_frame[idx+1];
uint32_t b = src_frame[idx+2];
uint32_t a = src_frame[idx+3];
el->img_frames[i][y * fit_w + x] = (a << 24) | (r << 16) | (g << 8) | b;
el->img_frames[i][dst_row_off + x] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
}
if (src_h_table) free(src_h_table);
if (src_w_table) free(src_w_table);
}
el->img_delays[i] = delays[i];
}
el->img_w = fit_w; el->img_h = fit_h;
@ -364,16 +379,20 @@ static void decode_image(unsigned char *data, int len, RenderElement *el) {
} else {
el->img_pixels = malloc(fit_w * fit_h * sizeof(uint32_t));
if (el->img_pixels) {
uint32_t step_x = (img_w_orig << 16) / fit_w;
uint32_t step_y = (img_h_orig << 16) / fit_h;
for (int y = 0; y < fit_h; y++) {
int sy = y * img_h_orig / fit_h;
int sy = (y * step_y) >> 16;
uint32_t src_row_off = sy * img_w_orig;
uint32_t dst_row_off = y * fit_w;
for (int x = 0; x < fit_w; x++) {
int sx = x * img_w_orig / fit_w;
int idx = (sy * img_w_orig + sx) * 4;
int sx = (x * step_x) >> 16;
int idx = (src_row_off + sx) * 4;
uint32_t r = rgba[idx];
uint32_t g = rgba[idx+1];
uint32_t b = rgba[idx+2];
uint32_t a = rgba[idx+3];
el->img_pixels[y * fit_w + x] = (a << 24) | (r << 16) | (g << 8) | b;
el->img_pixels[dst_row_off + x] = (a << 24) | (r << 16) | (g << 8) | b;
}
}
el->img_w = fit_w; el->img_h = fit_h;

View file

@ -6669,19 +6669,27 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
{
stbi_uc *p, *c;
int idx;
stbi__uint16 stack[4096];
int top = 0;
stbi__uint16 current = code;
// recurse to decode the prefixes, since the linked-list is backwards,
// and working backwards through an interleaved image would be nasty
if (g->codes[code].prefix >= 0)
stbi__out_gif_code(g, g->codes[code].prefix);
// Follow the chain of prefixes to the root, pushing suffixes onto the stack
while (current != (stbi__uint16)-1) {
if (top >= 4096) break;
stack[top++] = current;
current = (stbi__uint16)g->codes[current].prefix;
}
if (g->cur_y >= g->max_y) return;
// Pop and process suffixes in order
while (top > 0) {
stbi__uint16 c_code = stack[--top];
if (g->cur_y >= g->max_y) continue;
idx = g->cur_x + g->cur_y;
p = &g->out[idx];
g->history[idx / 4] = 1;
c = &g->color_table[g->codes[code].suffix * 4];
c = &g->color_table[g->codes[c_code].suffix * 4];
if (c[3] > 128) { // don't render transparent pixels;
p[0] = c[2];
p[1] = c[1];
@ -6701,6 +6709,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
}
}
}
}
static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g)
{
@ -6963,15 +6972,46 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i
}
}
static void *stbi__load_gif_main_outofmem(stbi__gif *g, stbi_uc *out, int **delays)
static int stbi__gif_count_frames(stbi__context *s)
{
STBI_FREE(g->out);
STBI_FREE(g->history);
STBI_FREE(g->background);
int layers = 0;
stbi_uc flags;
int tag;
int off = s->read_from_callbacks ? 0 : (int)(s->img_buffer - s->img_buffer_original);
if (out) STBI_FREE(out);
if (delays && *delays) STBI_FREE(*delays);
return stbi__errpuc("outofmem", "Out of memory");
stbi__skip(s, 6); // GIF87a/GIF89a
stbi__skip(s, 4); // w, h
flags = stbi__get8(s);
stbi__skip(s, 2);
if (flags & 0x80) stbi__skip(s, 3 * (2 << (flags & 7)));
while (!stbi__at_eof(s)) {
tag = stbi__get8(s);
if (tag == 0x2C) {
layers++;
stbi__skip(s, 8);
flags = stbi__get8(s);
if (flags & 0x80) stbi__skip(s, 3 * (2 << (flags & 7)));
stbi__get8(s); // LZW size
while (1) {
int len = stbi__get8(s);
if (len == 0) break;
stbi__skip(s, len);
}
} else if (tag == 0x21) {
stbi__get8(s);
while (1) {
int len = stbi__get8(s);
if (len == 0) break;
stbi__skip(s, len);
}
} else if (tag == 0x3B) break;
else break;
}
stbi__rewind(s);
if (off) stbi__skip(s, off);
return layers;
}
static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp)
@ -6983,17 +7023,28 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
stbi_uc *two_back = 0;
stbi__gif g;
int stride;
int out_size = 0;
int delays_size = 0;
STBI_NOTUSED(out_size);
STBI_NOTUSED(delays_size);
int total_layers = 0;
memset(&g, 0, sizeof(g));
total_layers = stbi__gif_count_frames(s);
if (total_layers <= 0) return stbi__errpuc("no frames", "Corrupt GIF");
// Initial header parse to get dimensions
if (!stbi__gif_header(s, &g, comp, 0)) return 0;
stride = g.w * g.h * 4;
stbi__rewind(s); // Rewind again for the main loop
out = (stbi_uc*)stbi__malloc(total_layers * stride);
if (!out) return stbi__errpuc("outofmem", "Out of memory");
if (delays) {
*delays = 0;
*delays = (int*)stbi__malloc(total_layers * sizeof(int));
if (!*delays) { STBI_FREE(out); return stbi__errpuc("outofmem", "Out of memory"); }
}
memset(&g, 0, sizeof(g)); // Reset for real load
do {
u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
if (u == (stbi_uc *) s) u = 0; // end of animated gif marker
@ -7002,39 +7053,12 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y,
*x = g.w;
*y = g.h;
++layers;
stride = g.w * g.h * 4;
if (out) {
void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride );
if (!tmp)
return stbi__load_gif_main_outofmem(&g, out, delays);
else {
out = (stbi_uc*) tmp;
out_size = layers * stride;
}
if (layers > total_layers) break; // Defensive
if (delays) {
int *new_delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers );
if (!new_delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
*delays = new_delays;
delays_size = layers * sizeof(int);
}
} else {
out = (stbi_uc*)stbi__malloc( layers * stride );
if (!out)
return stbi__load_gif_main_outofmem(&g, out, delays);
out_size = layers * stride;
if (delays) {
*delays = (int*) stbi__malloc( layers * sizeof(int) );
if (!*delays)
return stbi__load_gif_main_outofmem(&g, out, delays);
delays_size = layers * sizeof(int);
}
}
memcpy( out + ((layers - 1) * stride), u, stride );
if (layers >= 2) {
two_back = out - 2 * stride;
two_back = out + (layers - 2) * stride;
}
if (delays) {