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->img_current_frame = 0;
el->next_frame_tick = sys_system(16, 0, 0, 0, 0) + (delays[0] * 60 / 1000); 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++) { for (int i = 0; i < frame_count; i++) {
el->img_frames[i] = malloc(fit_w * fit_h * sizeof(uint32_t)); el->img_frames[i] = malloc(fit_w * fit_h * sizeof(uint32_t));
if (el->img_frames[i]) { if (el->img_frames[i]) {
unsigned char *src_frame = rgba + (i * img_w_orig * img_h_orig * 4); 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++) { 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++) { for (int x = 0; x < fit_w; x++) {
int sx = x * img_w_orig / fit_w; int sx = src_w_table[x];
int idx = (sy * img_w_orig + sx) * 4; int idx = (src_row_off + sx) * 4;
uint32_t r = src_frame[idx]; uint32_t r = src_frame[idx];
uint32_t g = src_frame[idx+1]; uint32_t g = src_frame[idx+1];
uint32_t b = src_frame[idx+2]; uint32_t b = src_frame[idx+2];
uint32_t a = src_frame[idx+3]; 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_delays[i] = delays[i];
} }
el->img_w = fit_w; el->img_h = fit_h; 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 { } else {
el->img_pixels = malloc(fit_w * fit_h * sizeof(uint32_t)); el->img_pixels = malloc(fit_w * fit_h * sizeof(uint32_t));
if (el->img_pixels) { 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++) { 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++) { for (int x = 0; x < fit_w; x++) {
int sx = x * img_w_orig / fit_w; int sx = (x * step_x) >> 16;
int idx = (sy * img_w_orig + sx) * 4; int idx = (src_row_off + sx) * 4;
uint32_t r = rgba[idx]; uint32_t r = rgba[idx];
uint32_t g = rgba[idx+1]; uint32_t g = rgba[idx+1];
uint32_t b = rgba[idx+2]; uint32_t b = rgba[idx+2];
uint32_t a = rgba[idx+3]; 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; 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; stbi_uc *p, *c;
int idx; int idx;
stbi__uint16 stack[4096];
int top = 0;
stbi__uint16 current = code;
// recurse to decode the prefixes, since the linked-list is backwards, // Follow the chain of prefixes to the root, pushing suffixes onto the stack
// and working backwards through an interleaved image would be nasty while (current != (stbi__uint16)-1) {
if (g->codes[code].prefix >= 0) if (top >= 4096) break;
stbi__out_gif_code(g, g->codes[code].prefix); 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; idx = g->cur_x + g->cur_y;
p = &g->out[idx]; p = &g->out[idx];
g->history[idx / 4] = 1; 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; if (c[3] > 128) { // don't render transparent pixels;
p[0] = c[2]; p[0] = c[2];
p[1] = c[1]; p[1] = c[1];
@ -6700,6 +6708,7 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code)
--g->parse; --g->parse;
} }
} }
}
} }
static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) 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); int layers = 0;
STBI_FREE(g->history); stbi_uc flags;
STBI_FREE(g->background); int tag;
int off = s->read_from_callbacks ? 0 : (int)(s->img_buffer - s->img_buffer_original);
if (out) STBI_FREE(out); stbi__skip(s, 6); // GIF87a/GIF89a
if (delays && *delays) STBI_FREE(*delays); stbi__skip(s, 4); // w, h
return stbi__errpuc("outofmem", "Out of memory"); 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) 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_uc *two_back = 0;
stbi__gif g; stbi__gif g;
int stride; int stride;
int out_size = 0; int total_layers = 0;
int delays_size = 0;
STBI_NOTUSED(out_size);
STBI_NOTUSED(delays_size);
memset(&g, 0, sizeof(g)); 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) { 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 { do {
u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); u = stbi__gif_load_next(s, &g, comp, req_comp, two_back);
if (u == (stbi_uc *) s) u = 0; // end of animated gif marker 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; *x = g.w;
*y = g.h; *y = g.h;
++layers; ++layers;
stride = g.w * g.h * 4;
if (out) { if (layers > total_layers) break; // Defensive
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 (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 ); memcpy( out + ((layers - 1) * stride), u, stride );
if (layers >= 2) { if (layers >= 2) {
two_back = out - 2 * stride; two_back = out + (layers - 2) * stride;
} }
if (delays) { if (delays) {