X-Git-Url: https://git.draconx.ca/gitweb/liblbx.git/blobdiff_plain/395b742050af148137758f1d4492a3922569fb91..HEAD:/src/pnm.c 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; }