X-Git-Url: https://git.draconx.ca/gitweb/liblbx.git/blobdiff_plain/594902a517f5a77535a2d7bb903f8e1de358edf6..HEAD:/src/pnm.c diff --git a/src/pnm.c b/src/pnm.c index ace5e18..43108f8 100644 --- a/src/pnm.c +++ b/src/pnm.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,37 @@ #include "tools.h" #include "imgoutput.h" +/* PNM formats are ASCII, check if C exec charset is too */ +#if '\a' == 0x07 && '\b' == 0x08 && '\t' == 0x09 && '\n' == 0x0a && \ + '\v' == 0x0b && '\f' == 0x0c && '\r' == 0x0d && ' ' == 0x20 && \ + '!' == 0x21 && '"' == 0x22 && '#' == 0x23 && '%' == 0x25 && \ + '&' == 0x26 && '\'' == 0x27 && '(' == 0x28 && ')' == 0x29 && \ + '*' == 0x2a && '+' == 0x2b && ',' == 0x2c && '-' == 0x2d && \ + '.' == 0x2e && '/' == 0x2f && '0' == 0x30 && '1' == 0x31 && \ + '2' == 0x32 && '3' == 0x33 && '4' == 0x34 && '5' == 0x35 && \ + '6' == 0x36 && '7' == 0x37 && '8' == 0x38 && '9' == 0x39 && \ + ':' == 0x3a && ';' == 0x3b && '<' == 0x3c && '=' == 0x3d && \ + '>' == 0x3e && '?' == 0x3f && 'A' == 0x41 && 'B' == 0x42 && \ + 'C' == 0x43 && 'D' == 0x44 && 'E' == 0x45 && 'F' == 0x46 && \ + 'G' == 0x47 && 'H' == 0x48 && 'I' == 0x49 && 'J' == 0x4a && \ + 'K' == 0x4b && 'L' == 0x4c && 'M' == 0x4d && 'N' == 0x4e && \ + 'O' == 0x4f && 'P' == 0x50 && 'Q' == 0x51 && 'R' == 0x52 && \ + 'S' == 0x53 && 'T' == 0x54 && 'U' == 0x55 && 'V' == 0x56 && \ + 'W' == 0x57 && 'X' == 0x58 && 'Y' == 0x59 && 'Z' == 0x5a && \ + '[' == 0x5b && '\\' == 0x5c && ']' == 0x5d && '^' == 0x5e && \ + '_' == 0x5f && 'a' == 0x61 && 'b' == 0x62 && 'c' == 0x63 && \ + 'd' == 0x64 && 'e' == 0x65 && 'f' == 0x66 && 'g' == 0x67 && \ + 'h' == 0x68 && 'i' == 0x69 && 'j' == 0x6a && 'k' == 0x6b && \ + 'l' == 0x6c && 'm' == 0x6d && 'n' == 0x6e && 'o' == 0x6f && \ + 'p' == 0x70 && 'q' == 0x71 && 'r' == 0x72 && 's' == 0x73 && \ + 't' == 0x74 && 'u' == 0x75 && 'v' == 0x76 && 'w' == 0x77 && \ + 'x' == 0x78 && 'y' == 0x79 && 'z' == 0x7a && '{' == 0x7b && \ + '|' == 0x7c && '}' == 0x7d && '~' == 0x7e +# define NATIVE_ASCII 1 +#else +# define NATIVE_ASCII 0 +#endif + /* Convert c from the basic execution character set to ASCII. */ static unsigned char to_ascii(unsigned char c) { @@ -139,42 +171,32 @@ static unsigned char to_ascii(unsigned char c) return c; } -/* Printf variant which converts all output to ASCII. */ +#if NATIVE_ASCII && defined(NDEBUG) +/* fprintf can be used directly. */ +#define fprintf_ascii fprintf +#else +/* Variant of fprintf which converts all output to ASCII. */ static int fprintf_ascii(FILE *f, char *fmt, ...) { - unsigned char *buf; + char buf[100]; va_list ap; - int rc, len; - size_t ret; + int i, len; va_start(ap, fmt); - rc = vsnprintf(NULL, 0, fmt, ap); + len = vsnprintf(buf, sizeof buf, fmt, ap); va_end(ap); - if (rc < 0) - return -1; - - assert(rc < SIZE_MAX); - buf = malloc(rc+1u); - if (!buf) + if (len < 0 || len >= sizeof buf) return -1; - va_start(ap, fmt); - len = vsprintf((char *)buf, fmt, ap); - va_end(ap); - - assert(rc == len); - for (int i = 0; i < len; i++) { - buf[i] = to_ascii(buf[i]); + for (i = 0; i < len; i++) { + if (fputc(to_ascii(buf[i]), f) == EOF) + return -1; } - ret = fwrite(buf, 1, len, f); - free(buf); - - if (ret < len) - return -(int)ret; - return ret; + return len; } +#endif /* * Output filter for Netpbm's "plain" PBM format. This is a bitmap format @@ -290,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 @@ -301,59 +385,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; - size_t depth; - - if (fprintf_ascii(f, "P7\nWIDTH %u\nHEIGHT %u\n", width, height) < 0) - goto err; - - if (palette) { - depth = masked ? 4 : 3; - - if (fprintf_ascii(f, "DEPTH %zu\nMAXVAL 63\n" - "TUPLTYPE RGB%s\nENDHDR\n", - depth, masked ? "_ALPHA" : "") < 0) - goto err; + unsigned char *rowbuf; + unsigned long offset; + unsigned y, depth; + int rc; + + depth = (palette ? 3 : 1) + masked; + 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; + } - 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; + rc = fprintf_ascii(f, "P7\nWIDTH %u\nHEIGHT %u\nDEPTH %u\n", + width, height, depth); + if (rc < 0) + goto write_err; - 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; + rc = fprintf_ascii(f, "MAXVAL %d\nTUPLTYPE %s", + palette ? 63 : 255, + palette ? "RGB" : "GRAYSCALE"); + if (rc < 0) + goto write_err; - if (fwrite(buf, 1, depth, f) < depth) - goto err; + rc = fprintf_ascii(f, "_ALPHA\nENDHDR\n" + 6*!masked); + if (rc < 0) + goto write_err; + + 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; } - } else { - depth = masked ? 2 : 1; - if (fprintf_ascii(f, "DEPTH %zu\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++)) { - 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; - } + 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; }