From 395b742050af148137758f1d4492a3922569fb91 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 22 Sep 2021 01:24:25 -0400 Subject: [PATCH] lbximg: Improve performance of PAM output. The PAM output currently calls fwrite for every pixel, which amounts to about half a million fwrite calls to output a full screen image. For a fullscreen animation like logo.lbx.001, the majority of the execution time is spent in fwrite. Let's reduce this a lot by formatting and outputting an entire row at a time, which almost completely eliminates fwrite from the profile. --- src/pnm.c | 87 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 45 insertions(+), 42 deletions(-) diff --git a/src/pnm.c b/src/pnm.c index d70d8b5..1596c5a 100644 --- a/src/pnm.c +++ b/src/pnm.c @@ -322,58 +322,61 @@ int img_output_pam(FILE *f, const char *filename, struct lbx_colour *palette) { bool masked = img_is_masked(mask, width, height); - unsigned x, y, depth; + unsigned x, y, depth, maxval = palette ? 63 : 255; + unsigned char *rowbuf; + int rc; - if (fprintf_ascii(f, "P7\nWIDTH %u\nHEIGHT %u\n", width, height) < 0) - goto err; + depth = (palette ? 3 : 1) + masked; + if (width > (size_t)-1 / depth || !(rowbuf = malloc(width * depth))) { + tool_err(-1, "failed to allocate memory"); + return -1; + } - if (palette) { - depth = masked ? 4 : 3; + rc = fprintf_ascii(f, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH %u\n", + width, height, depth); + if (rc < 0) + goto write_err; - if (fprintf_ascii(f, "DEPTH %u\nMAXVAL 63\n" - "TUPLTYPE RGB%s\nENDHDR\n", - depth, masked ? "_ALPHA" : "") < 0) - goto err; + rc = fprintf_ascii(f, "MAXVAL %u\nTUPLTYPE %s", + maxval, palette ? "RGB" : "GRAYSCALE"); + if (rc < 0) + goto write_err; - for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { - unsigned long offset = (unsigned long) y * width + x; - struct lbx_colour *c = &palette[pixels[offset]]; - unsigned char buf[4]; - bool vis; - - vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); - buf[0] = c->red; - buf[1] = c->green; - buf[2] = c->blue; - buf[3] = vis ? 63 : 0; - - if (fwrite(buf, 1, depth, f) < depth) - goto err; - } - } else { - depth = masked ? 2 : 1; + rc = fprintf_ascii(f, "_ALPHA\nENDHDR\n" + 6*!masked); + if (rc < 0) + goto write_err; - if (fprintf_ascii(f, "DEPTH %u\nMAXVAL 255\n" - "TUPLTYPE GRAYSCALE%s\nENDHDR\n", - depth, masked ? "_ALPHA" : "") < 0) - goto err; - - for (x = y = 0; y < height; ++x < width || (x = 0, y++)) { + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { unsigned long offset = (unsigned long) y * width + x; - unsigned char buf[2]; - bool vis; - - vis = mask[offset/CHAR_BIT] & (1u << offset%CHAR_BIT); - buf[0] = pixels[offset]; - buf[1] = vis ? 255 : 0; - - if (fwrite(buf, 1, depth, f) < depth) - goto err; + size_t p = (size_t)depth * x; // pixel start + + if (palette) { + struct lbx_colour *c = &palette[pixels[offset]]; + rowbuf[p+0] = c->red; + rowbuf[p+1] = c->green; + rowbuf[p+2] = c->blue; + } else { + rowbuf[p+0] = pixels[offset]; + } + + if (masked) { + bool vis = 1 & ( mask[offset/CHAR_BIT] + >> (offset%CHAR_BIT) ); + + rowbuf[p+depth-1] = vis*maxval; + } } + + if (fwrite(rowbuf, depth, width, f) < width) + goto write_err; } + free(rowbuf); return 0; -err: +write_err: tool_err(0, "error writing %s", filename); + + free(rowbuf); return -1; } -- 2.43.2