From 31b96a5d3d06122cbca0d38d32e0bbfa87cb58c7 Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Wed, 22 Sep 2021 02:42:24 -0400 Subject: [PATCH] Improve lbx_colour structure layout. There is no point in using bit-fields for the components of this structure. The structure will never be smaller than 4 bytes so there is no space savings compared to just using four unsigned char members. This avoids the compiler needing to do any kind of bit shuffling. In the typical case where such a structure has no padding, this means the layout of these members will now exactly match the PAM format, enabling a modest speedup. --- src/image.h | 2 +- src/pnm.c | 113 ++++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/src/image.h b/src/image.h index 454b15b..c375ce9 100644 --- a/src/image.h +++ b/src/image.h @@ -11,7 +11,7 @@ struct lbx_image { }; struct lbx_colour { - unsigned red:6, green:6, blue:6, active:1; + unsigned char red, green, blue, active; }; struct lbx_image *lbx_img_open(void *f, const struct lbx_file_ops *fops, diff --git a/src/pnm.c b/src/pnm.c index 1596c5a..43108f8 100644 --- a/src/pnm.c +++ b/src/pnm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -311,6 +312,68 @@ err: return -1; } +static void pam_format_rgba(unsigned char *rowbuf, const unsigned char *pixels, + unsigned char *mask, unsigned width, + unsigned long offset, struct lbx_colour *palette) +{ + unsigned x; + + for (x = 0; x < width; x++) { + struct lbx_colour val = palette[pixels[offset+x]]; + unsigned vis = 1 & ( mask[(offset+x)/CHAR_BIT] + >> ((offset+x)%CHAR_BIT) ); + + if (sizeof val == 4) { + memcpy(rowbuf+4ul*x, &val, 4); + } else { + rowbuf[4ul*x+0] = val.red; + rowbuf[4ul*x+1] = val.green; + rowbuf[4ul*x+2] = val.blue; + } + rowbuf[4ul*x+3] = -vis & 0x3f; + } +} + +static void pam_format_rgb(unsigned char *rowbuf, const unsigned char *pixels, + unsigned width, unsigned long offset, + struct lbx_colour *palette) +{ + unsigned x; + + for (x = 0; x < width; x++) { + struct lbx_colour val = palette[pixels[offset+x]]; + + if (sizeof val == 4) { + memcpy(rowbuf+3ul*x, &val, 4); + } else { + rowbuf[3ul*x+0] = val.red; + rowbuf[3ul*x+1] = val.green; + rowbuf[3ul*x+2] = val.blue; + } + } +} + +static void pam_format_ga(unsigned char *rowbuf, + const unsigned char *pixels, const unsigned char *mask, + unsigned width, unsigned long offset) +{ + unsigned x; + + for (x = 0; x < width; x++) { + unsigned vis = 1 & ( mask[(offset+x)/CHAR_BIT] + >> ((offset+x)%CHAR_BIT) ); + + rowbuf[2ul*x+0] = pixels[offset+x]; + rowbuf[2ul*x+1] = -vis & 0xff; + } +} + +static void pam_format_g(unsigned char *rowbuf, const unsigned char *pixels, + unsigned width, unsigned long offset) +{ + memcpy(rowbuf, pixels+offset, width); +} + /* * Output filter for Netpbm's PAM format. This format combines the features * of all the other Netpbm formats, supporting RGB and grayscale images with @@ -322,12 +385,18 @@ 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, maxval = palette ? 63 : 255; unsigned char *rowbuf; + unsigned long offset; + unsigned y, depth; int rc; depth = (palette ? 3 : 1) + masked; - if (width > (size_t)-1 / depth || !(rowbuf = malloc(width * depth))) { + if (width >= (size_t)-1 / depth) + goto alloc_err; + + rowbuf = malloc(width * depth + 1); + if (!rowbuf) { +alloc_err: tool_err(-1, "failed to allocate memory"); return -1; } @@ -337,8 +406,9 @@ int img_output_pam(FILE *f, const char *filename, if (rc < 0) goto write_err; - rc = fprintf_ascii(f, "MAXVAL %u\nTUPLTYPE %s", - maxval, palette ? "RGB" : "GRAYSCALE"); + rc = fprintf_ascii(f, "MAXVAL %d\nTUPLTYPE %s", + palette ? 63 : 255, + palette ? "RGB" : "GRAYSCALE"); if (rc < 0) goto write_err; @@ -346,26 +416,20 @@ int img_output_pam(FILE *f, const char *filename, if (rc < 0) goto write_err; - for (y = 0; y < height; y++) { - for (x = 0; x < width; x++) { - unsigned long offset = (unsigned long) y * width + x; - 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; - } + for (offset = y = 0; y < height; y++, offset += width) { + switch (depth) { + case 4: + pam_format_rgba(rowbuf, pixels, mask, width, offset, palette); + break; + case 3: + pam_format_rgb(rowbuf, pixels, width, offset, palette); + break; + case 2: + pam_format_ga(rowbuf, pixels, mask, width, offset); + break; + case 1: + pam_format_g(rowbuf, pixels, width, offset); + break; } if (fwrite(rowbuf, depth, width, f) < width) @@ -376,7 +440,6 @@ int img_output_pam(FILE *f, const char *filename, return 0; write_err: tool_err(0, "error writing %s", filename); - free(rowbuf); return -1; } -- 2.43.2