/***************************************************************************** NAME sngc.c -- compile SNG to PNG. *****************************************************************************/ #include #include #include #include #include #define PNG_INTERNAL #include "png.h" #include "sng.h" extern int verbose; typedef int bool; #define FALSE 0 #define TRUE 1 #define NONE -2 typedef struct { char *name; /* name of chunk type */ bool multiple_ok; /* OK to have more than one? */ int count; /* how many have we seen? */ } chunkprops; #ifndef PNG_KEYWORD_MAX_LENGTH #define PNG_KEYWORD_MAX_LENGTH 79 #endif /* PNG_KEYWORD_MAX_LENGTH */ #define MEMORY_QUANTUM 1024 #define MAX_PARAMS 16 #define PNG_MAX_LONG 2147483647L /* 2^31 */ /* chunk types */ static chunkprops properties[] = { /* * The PNG 1.0 chunks, listed in order of the summary table in section 4.3. * IEND is not listed here because it doesn't have to appear in the file. */ #define IHDR 0 {"IHDR", FALSE, 0}, #define PLTE 1 {"PLTE", FALSE, 0}, #define IDAT 2 {"IDAT", TRUE, 0}, #define cHRM 3 {"cHRM", FALSE, 0}, #define gAMA 4 {"gAMA", FALSE, 0}, #define iCCP 5 {"iCCP", FALSE, 0}, #define sBIT 6 {"sBIT", FALSE, 0}, #define sRGB 7 {"sRGB", FALSE, 0}, #define bKGD 8 {"bKGD", FALSE, 0}, #define hIST 9 {"hIST", FALSE, 0}, #define tRNS 10 {"tRNS", FALSE, 0}, #define pHYs 11 {"pHYs", FALSE, 0}, #define sPLT 12 {"sPLT", TRUE, 0}, #define tIME 13 {"tIME", FALSE, 0}, #define iTXt 14 {"iTXt", TRUE, 0}, #define tEXt 15 {"tEXt", TRUE, 0}, #define zTXt 16 {"zTXt", TRUE, 0}, /* * Special-purpose chunks in PNG Extensions 1.2.0 specification. */ #define oFFs 17 {"oFFs", FALSE, 0}, #define pCAL 18 {"pCAL", FALSE, 0}, #define sCAL 19 {"sCAL", FALSE, 0}, #define gIFg 20 {"gIFg", FALSE, 0}, #define gIFt 21 {"gIFt", FALSE, 0}, #define gIFx 22 {"gIFx", FALSE, 0}, #define fRAc 23 {"fRAc", FALSE, 0}, /* * Image pseudo-chunk */ #define IMAGE 24 {"IMAGE", FALSE, 0}, /* * Private chunks */ #define PRIVATE 25 {"private", TRUE, 0}, }; static png_color palette[256]; /************************************************************************* * * Color-name handling * ************************************************************************/ static color_item *cname_hashbuckets[COLOR_HASH_MODULUS]; static int cname_initialized; static int write_transform_options; static int hash_by_cname(color_item *cp) /* hash by color's RGB value */ { unsigned int h = 0; char *p; for (p = cp->name; *p; p++) h = COLOR_HASH_MODULUS * h + *p; return(h % COLOR_HASH_MODULUS); } static color_item *find_by_cname(char *name) /* find a character name */ { color_item sc, *hp; sc.name = name; for (hp = cname_hashbuckets[hash_by_cname(&sc)]; hp; hp = hp->next) if (strcmp(hp->name, name) == 0) return(hp); return((color_item *)NULL); } /************************************************************************* * * Token-parsing code * ************************************************************************/ static char token_buffer[BUFSIZ]; static int token_class; #define STRING_TOKEN 1 #define PUNCT_TOKEN 2 #define WORD_TOKEN 3 static bool pushed; static void escapes(cp, tp) /* process standard C-style escape sequences in a string */ const char *cp; /* source string with escapes */ char *tp; /* target buffer for digested string */ { while (*cp) { int cval = 0; if (*cp == '\\' && strchr("0123456789xX", cp[1])) { char *dp; const char *hex = "00112233445566778899aAbBcCdDeEfF"; int dcount = 0; if (*++cp == 'x' || *cp == 'X') for (++cp; (dp = strchr(hex, *cp)) && (dcount++ < 2); cp++) cval = (cval * 16) + (dp - hex) / 2; else if (*cp == '0') while (strchr("01234567",*cp) != (char*)NULL && (dcount++ < 3)) cval = (cval * 8) + (*cp++ - '0'); else while ((strchr("0123456789",*cp)!=(char*)NULL)&&(dcount++ < 3)) cval = (cval * 10) + (*cp++ - '0'); } else if (*cp == '\\') /* C-style character escapes */ { switch (*++cp) { case '\\': cval = '\\'; break; case 'n': cval = '\n'; break; case 't': cval = '\t'; break; case 'b': cval = '\b'; break; case 'r': cval = '\r'; break; default: cval = *cp; } cp++; } else cval = *cp++; *tp++ = cval; } *tp = '\0'; } static int get_token(void) /* grab a token from yyin */ { char w, c, *tp = token_buffer; if (pushed) { pushed = FALSE; if (verbose > 1) fprintf(stderr, "saved token: %s\n", token_buffer); return(TRUE); } /* * Skip leading whitespace. * * Treat commas, colons, and semicolons as whitespace -- this is * a mildly sleazy way to ensure that the compiler has * *really* tolerant syntax...in case we ever decide to change * this, there are comments reading "comma" at every point * where we might actually want same. */ for (;;) { w = fgetc(yyin); if (w == '\n') linenum++; if (feof(yyin)) return(FALSE); else if (isspace(w) || w == ',' || w == ';' || w == ':') continue; else if (w == '#') /* comment */ { for (;;) { w = fgetc(yyin); if (feof(yyin)) return(FALSE); if (w == '\n') { ungetc(w, yyin); break; } } } else /* non-space character */ { *tp++ = w; break; } } /* accumulate token */ if (w == '\'' || w == '"') { int literal = FALSE; tp = token_buffer; for (;;) { c = fgetc(yyin); if (feof(yyin)) return(FALSE); else if (c == '\\' && !literal) { literal = TRUE; continue; } else if (c == w && !literal) break; else if (c == '\n' && !literal) fatal("runaway string"); else if (tp >= token_buffer + sizeof(token_buffer) - 1) fatal("string token too long"); else { if (literal) { *tp++ = '\\'; literal = FALSE; } *tp++ = c; } } *tp = '\0'; escapes(token_buffer, token_buffer); token_class = STRING_TOKEN; } else if (ispunct(w)) { *tp = '\0'; token_class = PUNCT_TOKEN; } else { for (;;) { c = fgetc(yyin); if (feof(yyin)) return(FALSE); else if (isspace(c)) { if (c == '\n') linenum++; break; } else if (ispunct(c) && c != '.') { ungetc(c, yyin); break; } else if (tp >= token_buffer + sizeof(token_buffer)) fatal("token too long"); else *tp++ = c; } *tp = '\0'; token_class = WORD_TOKEN; } if (verbose > 1) fprintf(stderr, "token: %s\n", token_buffer); return(TRUE); } static int token_equals(const char *str) /* does the currently fetched token equal a specified string? */ { return !strcmp(str, token_buffer); } static int get_inner_token(void) /* get a token within a chunk specification */ { if (!get_token()) fatal("unexpected EOF"); return(!token_equals("}")); /* do we see end delimiter? */ } static void push_token(void) /* push back a token; must always be followed immediately by get_token */ { if (verbose > 1) fprintf(stderr, "pushing token: %s\n", token_buffer); pushed = TRUE; } static void require_or_die(const char *str) /* croak if the next token doesn't match what we expect */ { if (!get_token()) fatal("unexpected EOF"); else if (!token_equals(str)) fatal("unexpected token `%s' while waiting for %s", token_buffer, str); } static png_uint_32 long_numeric(bool token_ok) /* validate current token as a PNG long (range 0..2^31-1) */ { unsigned long result; char *vp; if (!token_ok) fatal("EOF while expecting long-integer constant"); result = strtoul(token_buffer, &vp, 0); if (*vp || result >= PNG_MAX_LONG) fatal("invalid or out of range long constant `%s'", token_buffer); return(result); } static png_int_32 slong_numeric(bool token_ok) /* validate current token as a signed PNG long (range 0..2^31-1) */ { long result; char *vp; if (!token_ok) fatal("EOF while expecting signed long-integer constant"); result = strtol(token_buffer, &vp, 0); if (*vp || result >= PNG_MAX_LONG || result <= -PNG_MAX_LONG) fatal("invalid or out of range long constant `%s'", token_buffer); return(result); } static png_uint_16 short_numeric(bool token_ok) /* validate current token as a PNG long (range 0..2^16-1) */ { unsigned long result; char *vp; if (!token_ok) fatal("EOF while expecting short-integer constant"); result = strtoul(token_buffer, &vp, 0); if (*vp || result == 65536) fatal("invalid or out of range short constant `%s'", token_buffer); return(result); } static png_byte byte_numeric(bool token_ok) /* validate current token as a byte */ { unsigned long result; char *vp; if (!token_ok) fatal("EOF while expecting byte constant"); result = strtoul(token_buffer, &vp, 0); if (*vp || result > 255) fatal("invalid or out of range byte constant `%s'", token_buffer); return(result); } static double double_numeric(bool token_ok) /* validate current token as a double-precision value */ { double result; char *vp; if (!token_ok) fatal("EOF while expecting double-precision constant"); result = strtod(token_buffer, &vp); if (*vp || result < 0) fatal("invalid or out of range double-precision constant `%s'", token_buffer); return(result); } static int string_validate(bool token_ok, char *stash) /* validate current token as a string */ { if (!token_ok) fatal("EOF while expecting string constant"); /* else */ { int len = strlen(token_buffer); if (len > PNG_STRING_MAX_LENGTH) fatal("string token is too long"); strncpy(stash, token_buffer, PNG_STRING_MAX_LENGTH); return(len); } } static int keyword_validate(bool token_ok, char *stash) /* validate current token as a PNG keyword */ { if (!token_ok) fatal("EOF while expecting PNG keyword"); /* else */ { int len = strlen(token_buffer); unsigned char *cp; if (len > PNG_KEYWORD_MAX_LENGTH) fatal("keyword token is too long"); strncpy(stash, token_buffer, PNG_KEYWORD_MAX_LENGTH); if (isspace(stash[0]) || isspace(stash[len-1])) fatal("keywords may not contain leading or trailing spaces"); for (cp = (unsigned char *)stash; *cp; cp++) if (*cp < 32 || (*cp > 126 && *cp < 161)) fatal("keywords must contain Latin-1 characters only"); else if (isspace(cp[0]) && isspace(cp[1])) fatal("keywords may not contain consecutive spaces"); return(len); } } static void collect_data(int *pnbytes, char **pbytes) /* collect data in either bitmap format */ { /* * A data segment consists of a byte stream. * There are presently three formats: * * string: * An ASCII string, doublequote-delimited. * * base64: * One character per byte; values are as per RFC2045 base64: * 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/ * * hex: * Two hex digits per byte. * * P1: * pbm format P1 (see pbm(5)). * * P3: * ppm format P3 (see ppm(5)). * * In either format, whitespace is ignored. */ char *bytes = xalloc(MEMORY_QUANTUM); int quanta = 1; int nbytes = 0; int ocount = 0; int c, maxval = 0; #define BASE64_FMT 0 #define HEX_FMT 1 #define P1_FMT 2 #define P3_FMT 3 int fmt = 0; if (!get_inner_token()) fatal("missing format type in data segment"); else if (token_class == STRING_TOKEN) { *pnbytes = 0; *pbytes = NULL; do { int seglen = strlen(token_buffer); *pbytes = xrealloc(*pbytes, *pnbytes + seglen); memcpy(*pbytes + *pnbytes, token_buffer, seglen); *pnbytes += seglen; } while (get_inner_token() && token_class == STRING_TOKEN); push_token(); return; } else if (token_equals("base64")) fmt = BASE64_FMT; else if (token_equals("hex")) fmt = HEX_FMT; else if (token_equals("P1")) { int width = short_numeric(get_token()); int height = short_numeric(get_token()); if (width != info_ptr->width && height != info_ptr->height) fatal("pbm image dimensions don't mastch IHDR"); fmt = P1_FMT; } else if (token_equals("P3")) { int width = short_numeric(get_token()); int height = short_numeric(get_token()); maxval = short_numeric(get_token()); if (width != info_ptr->width && height != info_ptr->height) fatal("ppm image dimensions don't match IHDR"); fmt = P3_FMT; } else fatal("unknown data format"); while ((c = fgetc(yyin))) { if (feof(yyin)) fatal("unexpected EOF in data segment"); else if (c == ';') break; else if (c == '}') { ungetc('}', yyin); break; } else if (c == '#') /* handle comments */ { while ((c = fgetc(yyin))) if (feof(yyin) || c == '\n') break; if (c == '\n') linenum++; continue; } else if (isspace(c)) /* skip whitespace */ { if (c == '\n') linenum++; continue; } else { unsigned char value = 0; if (nbytes >= quanta * MEMORY_QUANTUM) bytes = xrealloc(bytes, MEMORY_QUANTUM * ++quanta); switch(fmt) { case BASE64_FMT: if (!isalpha(c) && !isdigit(c)) fatal("bad character %02x in data block", c); else if (isdigit(c)) value = c - '0'; else if (isupper(c)) value = (c - 'A') + 10; else if (islower(c)) value = (c - 'a') + 36; else if (c == '+') value = 62; else /* if (c == '/') */ value = 63; bytes[nbytes++] = value; break; case HEX_FMT: if (!isxdigit(c)) fatal("bad hex character %02x in data block", c); else if (isdigit(c)) value = c - '0'; else if (isupper(c)) value = (c - 'A') + 10; else value = (c - 'a') + 10; if (ocount++ % 2) bytes[nbytes++] |= value; else bytes[nbytes] = value * 16; break; case P1_FMT: if (c == '0') bytes[nbytes++] = 0; else if (c == '1') bytes[nbytes++] = 1; else fatal("bad pbm character %02x in data block", c); break; case P3_FMT: c = short_numeric(get_token()); if (c > maxval) fatal("channel value out of range in pbm block"); /* * Channel order in PBM is R, then G, then B, same as PNG; * so a straight copy in the order we see them will work. */ bytes[nbytes++] = c; break; } } } #undef BASE64_FMT #undef HEX_FMT #undef P1_FMT #undef P3_FMT *pnbytes = nbytes; *pbytes = bytes; } /************************************************************************* * * The compiler itself * ************************************************************************/ static void compile_IHDR(void) /* parse IHDR specification, set corresponding bits in info_ptr */ { int d = 0; /* read IHDR data */ info_ptr->bit_depth = 8; info_ptr->color_type = PNG_COLOR_TYPE_GRAY; info_ptr->interlace_type = PNG_INTERLACE_NONE; while (get_inner_token()) if (token_equals("height")) info_ptr->height = long_numeric(get_token()); else if (token_equals("width")) info_ptr->width = long_numeric(get_token()); else if (token_equals("bitdepth")) d = byte_numeric(get_token()); else if (token_equals("using")) continue; /* `uses' is just syntactic sugar */ else if (token_equals("grayscale")) continue; /* so is grayscale */ else if (token_equals("palette")) info_ptr->color_type |= PNG_COLOR_MASK_PALETTE; else if (token_equals("color")) info_ptr->color_type |= PNG_COLOR_MASK_COLOR; else if (token_equals("alpha")) info_ptr->color_type |= PNG_COLOR_MASK_ALPHA; else if (token_equals("with")) continue; /* `with' is just syntactic sugar */ else if (token_equals("interlace")) info_ptr->interlace_type = PNG_INTERLACE_ADAM7; else fatal("bad token `%s' in IHDR specification", token_buffer); /* IHDR sanity checks */ if (!info_ptr->height) fatal("image height is zero or nonexistent"); else if (!info_ptr->width) fatal("image width is zero or nonexistent"); else if (d != 1 && d != 2 && d != 4 && d != 8 && d != 16) fatal("illegal bit depth %d in IHDR", d); else if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { if (d > 8) fatal("bit depth of paletted images must be 1, 2, 4, or 8"); } else if ((info_ptr->color_type != PNG_COLOR_TYPE_GRAY) && d!=8 && d!=16) fatal("bit depth of RGB- and alpha-using images must be 8 or 16"); info_ptr->bit_depth = d; } static void compile_PLTE(void) /* parse PLTE specification, set corresponding bits in info_ptr */ { int ncolors; initialize_hash(hash_by_cname, cname_hashbuckets, &cname_initialized); memset(palette, '\0', sizeof(palette)); ncolors = 0; while (get_inner_token()) { if (ncolors >= 256) fatal("too many palette entries in PLTE specification"); if (token_class == STRING_TOKEN) { color_item *cp = find_by_cname(token_buffer); if (!cp) fatal("unknown color name `%s' in PLTE", token_buffer); else { palette[ncolors].red = cp->r; palette[ncolors].green = cp->g; palette[ncolors].blue = cp->b; ncolors++; } } else if (token_equals("(")) { palette[ncolors].red = byte_numeric(get_token()); /* comma */ palette[ncolors].green = byte_numeric(get_token()); /* comma */ palette[ncolors].blue = byte_numeric(get_token()); require_or_die(")"); ncolors++; } else fatal("bad token %s in PLTE", token_buffer); } /* register the accumulated palette entries into the info structure */ png_set_PLTE(png_ptr, info_ptr, palette, ncolors); } static void compile_IDAT(void) /* parse IDAT specification and emit corresponding bits */ { int nbits; char *bits; #ifdef PNG_INFO_IMAGE_SUPPORTED png_unknown_chunk chunk; #endif /* PNG_INFO_IMAGE_SUPPORTED */ /* * Collect raw hex data and write it out as a chunk. */ collect_data(&nbits, &bits); require_or_die("}"); #ifndef PNG_INFO_IMAGE_SUPPORTED png_write_chunk(png_ptr, "IDAT", bits, nbits); free(bits); #else strcpy(chunk.name, "IDAT"); chunk.data = bits; chunk.size = nbits; png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); png_free(png_ptr, bits); #endif /* PNG_INFO_IMAGE_SUPPORTED */ } static void compile_cHRM(void) /* parse cHRM specification, set corresponding bits in info_ptr */ { char cmask = 0; float wx = 0, wy = 0, rx = 0, ry = 0, gx = 0, gy = 0, bx = 0, by = 0; while (get_inner_token()) { if (token_equals("white")) { require_or_die("("); wx = double_numeric(get_inner_token()); /* comma */ wy = double_numeric(get_inner_token()); require_or_die(")"); cmask |= 0x01; } else if (token_equals("red")) { require_or_die("("); rx = double_numeric(get_inner_token()); /* comma */ ry = double_numeric(get_inner_token()); require_or_die(")"); cmask |= 0x02; } else if (token_equals("green")) { require_or_die("("); gx = double_numeric(get_inner_token()); /* comma */ gy = double_numeric(get_inner_token()); require_or_die(")"); cmask |= 0x04; } else if (token_equals("blue")) { require_or_die("("); bx = double_numeric(get_inner_token()); /* comma */ by = double_numeric(get_inner_token()); require_or_die(")"); cmask |= 0x08; } else fatal("invalid color `%s' name in cHRM specification",token_buffer); } if (cmask != 0x0f) fatal("cHRM specification is not complete"); else { #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_cHRM(png_ptr, info_ptr, wx, wy, rx, ry, gx, gy, bx, by); #else #ifdef PNG_FIXED_POINT_SUPPORTED png_set_cHRM_fixed(png_ptr, info_ptr, FLOAT_TO_FIXED(wx), FLOAT_TO_FIXED(wy), FLOAT_TO_FIXED(rx), FLOAT_TO_FIXED(ry), FLOAT_TO_FIXED(gx), FLOAT_TO_FIXED(gy), FLOAT_TO_FIXED(bx), FLOAT_TO_FIXED(by)); #endif #endif } } static void compile_gAMA(void) /* compile and emit an gAMA chunk */ { double gamma = double_numeric(get_token()); #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_gAMA(png_ptr, info_ptr, gamma); #endif #ifdef PNG_FIXED_POINT_SUPPORTED png_set_gAMA_fixed(png_ptr, info_ptr, FLOAT_TO_FIXED(gamma)); #endif if (!get_token() || !token_equals("}")) fatal("bad token `%s' in gAMA specification", token_buffer); } static void compile_iCCP(void) /* compile and emit an iCCP chunk */ { int nname = 0, data_len = 0; char name[PNG_KEYWORD_MAX_LENGTH+1], *data; while (get_inner_token()) if (token_equals("name")) nname = keyword_validate(get_token(), name); else if (token_equals("profile")) collect_data(&data_len, &data); if (!nname || !data_len) fatal("incomplete iCCP specification"); png_set_iCCP(png_ptr, info_ptr, name, PNG_TEXT_COMPRESSION_NONE, data, data_len); free(data); } static void compile_sBIT(void) /* compile an sBIT chunk, set corresponding bits in info_ptr */ { png_color_8 sigbits; bool color = (info_ptr->color_type & (PNG_COLOR_MASK_PALETTE | PNG_COLOR_MASK_COLOR)); int sample_depth = ((info_ptr->color_type & PNG_COLOR_MASK_PALETTE) ? 8 : info_ptr->bit_depth); while (get_inner_token()) if (token_equals("red")) { if (!color) fatal("No color channels in this image type"); sigbits.red = byte_numeric(get_token()); if (sigbits.red > sample_depth) fatal("red sample depth out of range"); } else if (token_equals("green")) { if (!color) fatal("No color channels in this image type"); sigbits.green = byte_numeric(get_token()); if (sigbits.green > sample_depth) fatal("red sample depth out of range"); } else if (token_equals("blue")) { if (!color) fatal("No color channels in this image type"); sigbits.blue = byte_numeric(get_token()); if (sigbits.blue > sample_depth) fatal("red sample depth out of range"); } else if (token_equals("gray")) { if (color) fatal("No gray channel in this image type"); sigbits.gray = byte_numeric(get_token()); if (sigbits.gray > sample_depth) fatal("gray sample depth out of range"); } else if (token_equals("alpha")) { if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA) fatal("No alpha channel in this image type"); sigbits.alpha = byte_numeric(get_token()); if (sigbits.alpha > sample_depth) fatal("alpha sample depth out of range"); } else fatal("invalid channel name `%s' in sBIT specification", token_buffer); png_set_sBIT(png_ptr, info_ptr, &sigbits); } static void compile_bKGD(void) /* compile a bKGD chunk, put data in info structure */ { png_color_16 bkgbits; while (get_inner_token()) if (token_equals("red")) { if (!(info_ptr->color_type & PNG_COLOR_MASK_COLOR)) fatal("Can't use color background with this image type"); bkgbits.red = short_numeric(get_token()); } else if (token_equals("green")) { if (!(info_ptr->color_type & PNG_COLOR_MASK_COLOR)) fatal("Can't use color background with this image type"); bkgbits.green = short_numeric(get_token()); } else if (token_equals("blue")) { if (!(info_ptr->color_type & PNG_COLOR_MASK_COLOR)) fatal("Can't use color background with this image type"); bkgbits.blue = short_numeric(get_token()); } else if (token_equals("gray")) { if (info_ptr->color_type & (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)) fatal("Can't use color background with this image type"); bkgbits.gray = short_numeric(get_token()); } else if (token_equals("index")) { if (!(info_ptr->color_type & PNG_COLOR_MASK_PALETTE)) fatal("Can't use index background with a non-palette image"); bkgbits.index = byte_numeric(get_token()); } else fatal("invalid channel `%s' name in bKGD specification", token_buffer); png_set_bKGD(png_ptr, info_ptr, &bkgbits); } static void compile_hIST(void) /* compile a hIST chunk, put data in info structure */ { png_uint_16 hist[256]; int nhist = 0; while (get_inner_token()) /* comma */ hist[nhist++] = short_numeric(TRUE); #ifndef MNG_INTERFACE if (nhist != info_ptr->num_palette) fatal("number of hIST values (%d) for palette doesn't match palette size (%d)", nhist, info_ptr->num_palette); #else if (nhist != info_ptr->palette.size) fatal("number of hIST values (%d) for palette doesn't match palette size (%d)", nhist, info_ptr->palette.size); #endif /* GUG */ png_set_hIST(png_ptr, info_ptr, hist); } static void compile_tRNS(void) /* compile a tRNS chunk, put data in info structure */ { png_byte trans[256]; int ntrans = 0; png_color_16 tRNSbits; memset(&tRNSbits, '0', sizeof(tRNSbits)); switch (info_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: require_or_die("gray"); tRNSbits.gray = short_numeric(get_token()); require_or_die("}"); break; case PNG_COLOR_TYPE_PALETTE: while (get_inner_token()) /* comma */ trans[ntrans++] = byte_numeric(TRUE); break; case PNG_COLOR_TYPE_RGB: while (get_inner_token()) if (token_equals("red")) tRNSbits.red = short_numeric(get_token()); else if (token_equals("green")) tRNSbits.green = short_numeric(get_token()); else if (token_equals("blue")) tRNSbits.blue = short_numeric(get_token()); else fatal("invalid channel name `%s' in tRNS specification", token_buffer); break; case PNG_COLOR_TYPE_RGB_ALPHA: case PNG_COLOR_TYPE_GRAY_ALPHA: fatal("tRNS chunk not permitted with this image type"); default: /* should never happen */ fatal("unknown color type"); } png_set_tRNS(png_ptr, info_ptr, trans, ntrans, &tRNSbits); } static void compile_pHYs(void) /* compile a pHYs chunk, put data in info structure */ { png_byte unit = PNG_RESOLUTION_UNKNOWN; png_uint_32 res_x = 0, res_y = 0; while (get_inner_token()) if (token_equals("xpixels")) res_x = long_numeric(get_token()); else if (token_equals("ypixels")) res_y = long_numeric(get_token()); else if (token_equals("per")) continue; else if (token_equals("meter")) unit = PNG_RESOLUTION_METER; else fatal("invalid token `%s' in pHYs", token_buffer); if (!res_x || !res_y) fatal("illegal or missing resolutions in pHYs specification"); png_set_pHYs(png_ptr, info_ptr, res_x, res_y, unit); } static void compile_sPLT(void) /* compile sPLT chunk */ { char keyword[PNG_KEYWORD_MAX_LENGTH+1]; int nentries = 0, nkeyword = 0; png_sPLT_t new_palette; png_sPLT_entry entries[256]; initialize_hash(hash_by_cname, cname_hashbuckets, &cname_initialized); new_palette.depth = 0; while (get_inner_token()) if (token_equals("name")) nkeyword = keyword_validate(get_token(), keyword); else if (token_equals("depth")) { new_palette.depth = byte_numeric(get_token()); if (new_palette.depth != 8 && new_palette.depth != 16) fatal("invalid sample depth in sPLT"); } else if (token_class == STRING_TOKEN) { color_item *cp = find_by_cname(token_buffer); if (!cp) fatal("unknown color name `%s' in PLTE", token_buffer); else { if (nentries >= 256) fatal("too many palette entries in sPLT specification"); palette[nentries].red = cp->r; palette[nentries].green = cp->g; palette[nentries].blue = cp->b; /* comma */ entries[nentries].alpha = short_numeric(get_token()); if (new_palette.depth == 8 && entries[nentries].alpha > 255) fatal("alpha value too large for sample depth"); /* comma */ entries[nentries].frequency = short_numeric(get_token()); nentries++; } } else if (token_equals("(")) { if (nentries >= 256) fatal("too many palette entries in sPLT specification"); entries[nentries].red = short_numeric(get_token()); if (new_palette.depth == 8 && entries[nentries].red > 255) fatal("red value too large for sample depth"); /* comma */ entries[nentries].green = short_numeric(get_token()); if (new_palette.depth == 8 && entries[nentries].green > 255) fatal("green value too large for sample depth"); /* comma */ entries[nentries].blue = short_numeric(get_token()); if (new_palette.depth == 8 && entries[nentries].blue > 255) fatal("blue value too large for sample depth"); require_or_die(")"); /* comma */ entries[nentries].alpha = short_numeric(get_token()); if (new_palette.depth == 8 && entries[nentries].alpha > 255) fatal("alpha value too large for sample depth"); /* comma */ entries[nentries].frequency = short_numeric(get_token()); nentries++; } else fatal("bad token `%s' in sPLT description", token_buffer); if (!nkeyword || !new_palette.depth) fatal("incomplete sPLT specification"); new_palette.entries = entries; new_palette.nentries = nentries; new_palette.name = keyword; png_set_sPLT(png_ptr, info_ptr, &new_palette, 1); } static void compile_tEXt(void) /* compile a text chunk; queue it up to be emitted later */ { char keyword[PNG_KEYWORD_MAX_LENGTH+1]; char text[PNG_STRING_MAX_LENGTH+1]; int nkeyword = 0, ntext = 0; png_text textblk; while (get_inner_token()) if (token_equals("keyword")) nkeyword = keyword_validate(get_token(), keyword); else if (token_equals("text")) ntext = string_validate(get_token(), text); else fatal("bad token `%s' in tEXt specification", token_buffer); if (!nkeyword || !ntext) fatal("keyword or text is missing in tEXt specification"); #ifdef PNG_iTXt_SUPPORTED textblk.lang = (char *)NULL; #endif textblk.key = keyword; textblk.text = text; textblk.compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(png_ptr, info_ptr, &textblk, 1); } static void compile_zTXt(void) /* compile and emit a zTXt chunk */ { char keyword[PNG_KEYWORD_MAX_LENGTH+1]; char text[PNG_STRING_MAX_LENGTH+1]; int nkeyword = 0, ntext = 0; png_text textblk; while (get_inner_token()) if (token_equals("keyword")) nkeyword = keyword_validate(get_token(), keyword); else if (token_equals("text")) ntext = string_validate(get_token(), text); else fatal("bad token `%s' in zTXt specification", token_buffer); if (!nkeyword || !ntext) fatal("keyword or text is missing in zTXt specification"); #ifdef PNG_iTXt_SUPPORTED textblk.lang = (char *)NULL; #endif textblk.key = keyword; textblk.text = text; textblk.compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, &textblk, 1); } static void compile_iTXt(void) /* compile and emit an iTXt chunk */ { char language[PNG_KEYWORD_MAX_LENGTH+1]; char keyword[PNG_KEYWORD_MAX_LENGTH+1]; char transkey[PNG_KEYWORD_MAX_LENGTH+1]; char text[PNG_STRING_MAX_LENGTH+1]; int nlanguage = 0, nkeyword = 0, ntranskey = 0, ntext = 0; int compression = PNG_TEXT_COMPRESSION_NONE; png_text textblk; compression = PNG_ITXT_COMPRESSION_NONE; while (get_inner_token()) if (token_equals("language")) nlanguage = keyword_validate(get_token(), language); else if (token_equals("keyword")) nkeyword = keyword_validate(get_token(), keyword); else if (token_equals("translated")) ntranskey = keyword_validate(get_token(), transkey); else if (token_equals("text")) ntext = string_validate(get_token(), text); else if (token_equals("compressed")) compression = PNG_ITXT_COMPRESSION_zTXt; else fatal("bad token `%s' in iTXt specification", token_buffer); if (!language || !keyword || !transkey || !text) fatal("keyword or text is missing"); textblk.key = keyword; #ifdef PNG_iTXt_SUPPORTED textblk.lang = language; textblk.lang_key = transkey; #endif textblk.text = text; textblk.compression = compression; png_set_text(png_ptr, info_ptr, &textblk, 1); } static void compile_tIME(void) /* compile a tIME chunk, put data in info structure */ { png_time stamp; int time_mask = 0; while (get_inner_token()) if (token_equals("year")) { stamp.year = short_numeric(get_token()); time_mask |= 0x01; } else if (token_equals("month")) { stamp.month = byte_numeric(get_token()); if (stamp.month < 1 || stamp.month > 12) fatal("month value out of range"); time_mask |= 0x02; } else if (token_equals("day")) { stamp.day = byte_numeric(get_token()); if (stamp.day < 1 || stamp.day > 31) fatal("day value out of range"); time_mask |= 0x04; } else if (token_equals("hour")) { stamp.hour = byte_numeric(get_token()); if (stamp.hour > 23) fatal("hour value out of range"); time_mask |= 0x08; } else if (token_equals("minute")) { stamp.minute = byte_numeric(get_token()); if (stamp.minute > 59) fatal("minute value out of range"); time_mask |= 0x10; } else if (token_equals("second")) { stamp.second = byte_numeric(get_token()); if (stamp.second > 59) fatal("second value out of range"); time_mask |= 0x20; } else fatal("bad token `%s' in tIME specification", token_buffer); if (time_mask != 0x3f) fatal("incomplete tIME specification"); png_set_tIME(png_ptr, info_ptr, &stamp); } static void compile_oFFs(void) /* parse oFFs specification and set corresponding info fields */ { png_byte unit = PNG_OFFSET_PIXEL; /* default to pixels */ png_int_32 res_x = 0, res_y = 0; while (get_inner_token()) if (token_equals("xoffset")) res_x = slong_numeric(get_token()); else if (token_equals("yoffset")) res_y = slong_numeric(get_token()); else if (token_equals("unit")) continue; else if (token_equals("pixels")) unit = PNG_OFFSET_PIXEL; else if (token_equals("micrometers")) unit = PNG_OFFSET_MICROMETER; else fatal("invalid token `%s' in oFFs specification", token_buffer); if (!res_x || !res_y) fatal("illegal or missing offsets in oFFs specification"); png_set_oFFs(png_ptr, info_ptr, res_x, res_y, unit); } static void compile_pCAL(void) /* parse pCAL specification and set corresponding info fields */ { char name[PNG_KEYWORD_MAX_LENGTH+1]; char unit[PNG_STRING_MAX_LENGTH+1]; char strbuf[PNG_STRING_MAX_LENGTH+1]; char *params[MAX_PARAMS]; int eqtype = 0, mask = 0, nname = 0, nunit = 0; int nstrbuf = 0, nparams = 0, required = 0; png_int_32 x0 = 0, x1 = 0; while (get_inner_token()) if (token_equals("name")) { nname = keyword_validate(get_token(), name); mask |= 0x01; } else if (token_equals("x0")) { x0 = slong_numeric(get_token()); mask |= 0x02; } else if (token_equals("x1")) { x1 = slong_numeric(get_token()); mask |= 0x04; } else if (token_equals("mapping")) continue; else if (token_equals("linear")) { eqtype = PNG_EQUATION_LINEAR; mask |= 0x08; } else if (token_equals("euler")) { eqtype = PNG_EQUATION_BASE_E; mask |= 0x08; } else if (token_equals("exponential")) { eqtype = PNG_EQUATION_ARBITRARY; mask |= 0x08; } else if (token_equals("hyperbolic")) { eqtype = PNG_EQUATION_HYPERBOLIC; mask |= 0x08; } else if (token_equals("unit")) { nunit = keyword_validate(get_token(), unit); mask |= 0x10; } else if (token_equals("parameters")) { nparams = 0; while (get_inner_token()) if (nparams >= MAX_PARAMS) fatal("too many parameters in pCAL specification"); else { nstrbuf = string_validate(TRUE, strbuf); params[nparams++] = xstrdup(strbuf); } push_token(); } else fatal("invalid token `%s' in pCAL specification", token_buffer); /* validate the specification */ if (!(mask != 0x01)) fatal("incomplete pCAL specification: calibration name is missing"); else if (!(mask != 0x01)) fatal("incomplete pCAL specification: x0 is missing"); else if (!(mask != 0x04)) fatal("incomplete pCAL specification: x1 name is missing"); else if (!(mask != 0x08)) fatal("incomplete pCAL specification: equation type is missing"); else if (!(mask != 0x10)) fatal("incomplete pCAL specification: unit name is missing"); switch (eqtype) { case PNG_EQUATION_LINEAR: required = 2; break; case PNG_EQUATION_BASE_E: required = 3; break; case PNG_EQUATION_ARBITRARY: required = 3; break; case PNG_EQUATION_HYPERBOLIC: required = 4; break; default: /* should never happen! */ fatal("unknown equation type in pCAL specification"); } if (nparams != required) fatal("%d parameters is wrong for this equation type in pCAL",nparams); png_set_pCAL(png_ptr, info_ptr, name, x0, x1, eqtype, nparams, unit, params); } static void compile_sCAL(void) /* parse sCAL specification and emit corresponding bits */ { char unit[PNG_STRING_MAX_LENGTH+1]; double width = 0, height = 0; int nunit = 0; png_byte unitbyte = 0; #if !defined(FLOATING_POINT_SUPPORTED) && defined(FIXED_POINT_SUPPORTED) char width_s[BUFSIZ], height_s[BUFSIZ]; #endif while (get_inner_token()) if (token_equals("unit")) { nunit = string_validate(get_token(), unit); if (token_equals("meter")) unitbyte = PNG_SCALE_METER; else if (token_equals("radian")) unitbyte = PNG_SCALE_RADIAN; else unitbyte = PNG_SCALE_UNKNOWN; } else if (token_equals("width")) { height = double_numeric(get_token()); #if !defined(FLOATING_POINT_SUPPORTED) && defined(FIXED_POINT_SUPPORTED) strcpy(width_s, token_buffer); #endif } else if (token_equals("height")) { width = double_numeric(get_token()); #if !defined(FLOATING_POINT_SUPPORTED) && defined(FIXED_POINT_SUPPORTED) strcpy(height_s, token_buffer); #endif } else fatal("invalid token `%s' in pCAL specification", token_buffer); if (!nunit || !width || !height) fatal("incomplete sCAL specification"); #ifdef PNG_FLOATING_POINT_SUPPORTED png_set_sCAL(png_ptr, info_ptr, unitbyte, width, height); #else #ifdef PNG_FIXED_POINT_SUPPORTED png_set_sCAL_s(png_ptr, info_ptr, unitbyte, width_s, height_s); #endif #endif } static void compile_gIFg(void) /* parse gIFg specification and queue up the corresponding chunk */ { png_byte chunkdata[4]; png_unknown_chunk chunk; memset(&chunk, '\0', sizeof(chunk)); memset(chunkdata, '\0', sizeof(chunkdata)); strcpy(chunk.name, "gIFg"); chunk.data = chunkdata; chunk.size = 4; while (get_inner_token()) if (token_equals("disposal")) chunkdata[0] = byte_numeric(get_token()); else if (token_equals("input")) chunkdata[1] = byte_numeric(get_token()); else if (token_equals("delay")) { double delay = double_numeric(get_token()); png_save_uint_16(chunkdata+2, (int)(delay*100)); } else fatal("invalid token `%s' in gIFg specification", token_buffer); png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); } static void compile_gIFx(void) /* parse gIFx specification and queue up the corresponding chunk */ { png_byte chunkdata[PNG_STRING_MAX_LENGTH], buf[PNG_STRING_MAX_LENGTH]; png_unknown_chunk chunk; memset(&chunk, '\0', sizeof(chunk)); memset(chunkdata, '\0', sizeof(chunkdata)); strcpy(chunk.name, "gIFx"); chunk.data = chunkdata; while (get_inner_token()) if (token_equals("identifier")) { if (string_validate(get_token(), buf) != 8) fatal("application identifier has wrong length"); else memcpy(chunkdata, buf, 8); } else if (token_equals("code")) { if (string_validate(get_token(), buf) != 3) fatal("authentication code has wrong length"); else memcpy(chunkdata + 8, buf, 3); } else if (token_equals("data")) { int datalen; char *data; collect_data(&datalen, &data); memcpy(chunkdata + 11, data, datalen); free(data); } else fatal("invalid token `%s' in gIFx specification", token_buffer); chunk.size = 11 + strlen(chunkdata + 11); png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); } static void compile_IMAGE(void) /* parse IMAGE specification and emit corresponding bits */ { int i, nbytes, bytes_per_sample = 0, nsamples, input_width; char *bytes; int doublewidth = info_ptr->bit_depth == 16 ? 2 : 1; write_transform_options = 0; while (get_inner_token()) if (token_equals("pixels")) collect_data(&nbytes, &bytes); else if (token_equals("options")) { if (token_equals("identity")) write_transform_options = PNG_TRANSFORM_IDENTITY; else if (token_equals("packing")) write_transform_options |= PNG_TRANSFORM_PACKING; else if (token_equals("packswap")) write_transform_options |= PNG_TRANSFORM_PACKSWAP; else if (token_equals("invert_mono")) write_transform_options |= PNG_TRANSFORM_INVERT_MONO; else if (token_equals("shift")) write_transform_options |= PNG_TRANSFORM_SHIFT; else if (token_equals("bgr")) write_transform_options |= PNG_TRANSFORM_BGR; else if (token_equals("swap_alpha")) write_transform_options |= PNG_TRANSFORM_SWAP_ALPHA; else if (token_equals("invert_alpha")) write_transform_options |= PNG_TRANSFORM_INVERT_ALPHA; else if (token_equals("swap_endian")) write_transform_options |= PNG_TRANSFORM_SWAP_ENDIAN; else if (token_equals("strip_filler")) write_transform_options |= PNG_TRANSFORM_STRIP_FILLER; } else fatal("invalid token `%s' in IMAGE specification", token_buffer); /* compute input sample size in bits */ switch (info_ptr->color_type) { case PNG_COLOR_TYPE_GRAY: bytes_per_sample = doublewidth; break; case PNG_COLOR_TYPE_PALETTE: bytes_per_sample = 1; break; case PNG_COLOR_TYPE_RGB: bytes_per_sample = 3 * doublewidth; break; case PNG_COLOR_TYPE_RGB_ALPHA: bytes_per_sample = 4 * doublewidth; break; case PNG_COLOR_TYPE_GRAY_ALPHA: bytes_per_sample = 2 * doublewidth; break; default: /* should never happen */ fatal("unknown color type"); } /* * Compute the actual size of the image in samples. */ input_width = nbytes / info_ptr->height; if (info_ptr->bit_depth >= 8) nsamples = nbytes / bytes_per_sample; else { int samples_per_byte = (8 / info_ptr->bit_depth); nsamples = nbytes * samples_per_byte; /* * Images of bit depth less than 8 will have excess data in the * byte corresponding to the last pixel(s) in a row when the * width is not a multiple of 8. Correct for this. */ if (info_ptr->width % 8) { int excess_samples_per_line = (info_ptr->width % samples_per_byte); if (excess_samples_per_line) nsamples -= info_ptr->height * (samples_per_byte - excess_samples_per_line); } } if (nsamples != info_ptr->width * info_ptr->height) fatal("sample count (%d) doesn't match width*height (%d*%d) in IHDR", nsamples, info_ptr->width, info_ptr->height); #ifdef PNG_DEBUG #if (PNG_DEBUG >= 6) /* dump the data as a check */ { int n; fprintf(stderr, "image data:\n"); for (n = 0; n < nbytes; n++) { fprintf(stderr, "%02x ", bytes[n] & 0xff); if ((n+1) % input_width == 0) fputc('\n', stderr); } } #endif #endif #ifndef PNG_INFO_IMAGE_SUPPORTED /* got the bits; now write them out */ row_pointers = (png_byte **)xalloc(sizeof(char *) * info_ptr->height); for (i = 0; i < info_ptr->height; i++) row_pointers[i] = &bytes[i * input_width]; png_write_image(png_ptr, row_pointers); free(bytes); #else /* got the bits; attach them to the info structure */ info_ptr->row_pointers = (png_byte **)xalloc(sizeof(char *) * info_ptr->height); for (i = 0; i < info_ptr->height; i++) info_ptr->row_pointers[i] = &bytes[i * input_width]; info_ptr->valid |= PNG_INFO_IDAT; #endif /* PNG_INFO_IMAGE_SUPPORTED */ } static void compile_private(char *name) /* compile a private chunk */ { int nbytes; char *bytes; png_unknown_chunk chunk; if (strlen(name) != 4) fatal("wrong length for chunk name %s", token_buffer); else strcpy(chunk.name, name); require_or_die("{"); collect_data(&nbytes, &bytes); require_or_die("}"); chunk.data = bytes; chunk.size = nbytes; png_set_unknown_chunks(png_ptr, info_ptr, &chunk, 1); png_free(png_ptr, bytes); } int sngc(FILE *fin, char *name, FILE *fout) /* compile SNG on fin to PNG on fout */ { int prevchunk, errtype; char buf[BUFSIZ]; yyin = fin; file = name; linenum = 1; if (fgets(buf, sizeof(buf), fin) == NULL) { fputs("sng: no data in file\n", stderr); exit(1); } else if (strncmp("#SNG", buf, 3)) { fputs("sng: this is not an sng file\n", stderr); exit(1); } /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (void *)NULL, NULL, NULL); if (png_ptr == NULL) return(2); /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return(2); } /* if errtype is not 1, this was generated by fatal() */ if ((errtype = setjmp(png_jmpbuf(png_ptr)))) { if (errtype == 1) fprintf(stderr, "%s:%d: libpng croaked\n", file, linenum); free(png_ptr); free(info_ptr); return errtype; } /* set up the output control if you are using standard C streams */ png_init_io(png_ptr, fout); /* keep all unknown chunks, we'll dump them later */ png_set_keep_unknown_chunks(png_ptr, 2, NULL, 0); write_transform_options = PNG_TRANSFORM_IDENTITY; /* interpret the following chunk specifications */ prevchunk = NONE; while (get_token()) { chunkprops *pp; for (pp = properties; pp < properties + sizeof(properties)/sizeof(chunkprops); pp++) if (token_equals(pp->name)) goto ok; fatal("unknown chunk type `%s'", token_buffer); ok: if (!get_token()) fatal("unexpected EOF"); if (!token_equals("{") && (pp - properties) != PRIVATE) fatal("missing chunk delimiter"); if (!pp->multiple_ok && pp->count > 0) fatal("illegal repeated chunk"); switch (pp - properties) { case IHDR: if (prevchunk != NONE) fatal("IHDR chunk must come first"); compile_IHDR(); break; case PLTE: if (properties[IDAT].count) fatal("PLTE chunk must come before IDAT"); else if (properties[bKGD].count) fatal("PLTE chunk encountered after bKGD"); else if (properties[tRNS].count) fatal("PLTE chunk encountered after tRNS"); else if (!(info_ptr->color_type & PNG_COLOR_MASK_PALETTE)) fatal("PLTE chunk specified for non-palette image type"); #ifndef PNG_INFO_IMAGE_SUPPORTED png_write_info_before_PLTE(png_ptr, info_ptr); #endif /* PNG_INFO_IMAGE_SUPPORTED */ compile_PLTE(); break; case IDAT: if (properties[IMAGE].count) fatal("can't mix IDAT and IMAGE specs"); if (prevchunk != IDAT && pp->count) fatal("IDAT chunks must be contiguous"); /* force out the pre-IDAT portions */ #ifndef PNG_INFO_IMAGE_SUPPORTED if (properties[IDAT].count == 0) png_write_info(png_ptr, info_ptr); #endif /* PNG_INFO_IMAGE_SUPPORTED */ compile_IDAT(); break; case cHRM: if (properties[PLTE].count || properties[IDAT].count) fatal("cHRM chunk must come before PLTE and IDAT"); compile_cHRM(); break; case gAMA: if (properties[PLTE].count || properties[IDAT].count) fatal("gAMA chunk must come before PLTE and IDAT"); compile_gAMA(); break; case iCCP: if (properties[PLTE].count || properties[IDAT].count) fatal("iCCP chunk must come before PLTE and IDAT"); compile_iCCP(); break; case sBIT: if (properties[PLTE].count || properties[IDAT].count) fatal("sBIT chunk must come before PLTE and IDAT"); compile_sBIT(); break; case sRGB: if (properties[PLTE].count || properties[IDAT].count) fatal("sRGB chunk must come before PLTE and IDAT"); png_set_sRGB_gAMA_and_cHRM(png_ptr, info_ptr, byte_numeric(get_token())); if (!get_token() || !token_equals("}")) fatal("bad token `%s' in sRGB specification", token_buffer); break; case bKGD: if (properties[IDAT].count) fatal("bKGD chunk must come between PLTE (if any) and IDAT"); compile_bKGD(); break; case hIST: if (!properties[PLTE].count || properties[IDAT].count) fatal("hIST chunk must come between PLTE and IDAT"); compile_hIST(); break; case tRNS: if (properties[IDAT].count) fatal("tRNS chunk must come between PLTE (if any) and IDAT"); compile_tRNS(); break; case pHYs: if (properties[IDAT].count) fatal("pHYs chunk must come before IDAT"); compile_pHYs(); break; case sPLT: if (properties[IDAT].count) fatal("sPLT chunk must come before IDAT"); compile_sPLT(); break; case tIME: compile_tIME(); break; case iTXt: compile_iTXt(); break; case tEXt: compile_tEXt(); break; case zTXt: compile_zTXt(); break; case oFFs: if (properties[IDAT].count) fatal("oFFs chunk must come before IDAT"); compile_oFFs(); break; case pCAL: if (properties[IDAT].count) fatal("pCAL chunk must come before IDAT"); compile_pCAL(); break; case sCAL: if (properties[IDAT].count) fatal("sCAL chunk must come before IDAT"); compile_sCAL(); break; case gIFg: compile_gIFg(); break; case gIFt: fatal("gIFt chunks are not handled"); break; case gIFx: compile_gIFx(); break; case fRAc: fatal("fRAc chunk type is not defined yet"); break; case IMAGE: if (properties[IDAT].count) fatal("can't mix IDAT and IMAGE specs"); /* force out the pre-IDAT portions */ #ifndef PNG_INFO_IMAGE_SUPPORTED if (properties[IMAGE].count == 0) png_write_info(png_ptr, info_ptr); #endif /* PNG_INFO_IMAGE_SUPPORTED */ compile_IMAGE(); properties[IDAT].count++; break; case PRIVATE: compile_private(token_buffer); break; } if (verbose > 1) fprintf(stderr, "%s specification processed\n", pp->name); prevchunk = (pp - properties); pp->count++; } /* end-of-file sanity checks */ linenum = EOF; if (!properties[PLTE].count && (info_ptr->color_type & PNG_COLOR_MASK_PALETTE)) fatal("palette property set, but no PLTE chunk found"); if (!properties[IDAT].count) fatal("no image data"); if (properties[iCCP].count && properties[sRGB].count) fatal("cannot have both iCCP and sRGB chunks (PNG spec 4.2.2.4)"); #ifndef PNG_INFO_IMAGE_SUPPORTED /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); #else png_write_png(png_ptr, info_ptr, write_transform_options, NULL); #endif /* PNG_INFO_IMAGE_SUPPORTED */ /* if you malloced the palette, free it here */ /* free(info_ptr->palette); */ /* clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return(0); } /* sngc.c ends here */