_PNG: THE PORTABLE NETWORK GRAPHIC FORMAT_ by Lee Daniel Crocker Listing One /* ptot.h -- Header file for PNG to TIFF converter. * HISTORY: 95-03-10 Created by Lee Daniel Crocker * */ #ifdef _X86_ /* Intel i86 family */ # define LITTLE_ENDIAN #endif #ifdef _SPARC_ # define BIG_ENDIAN # ifndef FILENAME_MAX /* Work around stupid header file bugs */ # define FILENAME_MAX 1024 # endif # ifndef SEEK_SET # define SEEK_SET 0 # endif # ifndef min # define min(x,y) (((x)<(y))?(x):(y)) # endif # ifndef max # define max(x,y) (((y)<(x))?(x):(y)) # endif #endif /* Some types and macros for easier porting. Byte swapping is the major issue * because we have to convert big-endiang PNG to native-endian TIFF on * whatever architecture we're compiled on. Code depends heavily on endianness * definition above. Functions would be a lot simpler than macros here, but are * less likely to be optimized down to simple inline byte swaps. Some of these * macros evaluate the address twice, so don't pass "*p++" to them! */ typedef signed char S8; typedef unsigned char U8; typedef signed short S16; typedef unsigned short U16; typedef signed long S32; typedef unsigned long U32; #ifndef TRUE # define TRUE 1 # define FALSE 0 #endif #define LOBYTE(w) ((U8)((w)&0xFF)) #define HIBYTE(w) ((U8)(((w)>>8)&0xFF)) #define LOWORD(d) ((U16)((d)&0xFFFF)) #define HIWORD(d) ((U16)(((d)>>16)&0xFFFF)) #define PUT16(p,w) (*(U16*)(p)=(w)) /* Native byte order */ #define GET16(p) (*(U16*)(p)) #define PUT32(p,d) (*(U32*)(p)=(d)) #define GET32(p) (*(U32*)(p)) #if !defined(BIG_ENDIAN) && !defined(LITTLE_ENDIAN) # error "No byte order defined" #endif #ifdef BIG_ENDIAN # define BE_GET16(p) GET16(p) # define BE_PUT16(p,w) PUT16((p),(w)) # define BE_GET32(p) GET32(p) # define BE_PUT32(p,d) PUT32((p),(d)) # define LE_GET16(p) ((U16)(*(U8*)(p)&0xFF)|\ (*((U8*)(p)+1)<<8)) # define LE_PUT16(p,w) (((*(U8*)(p))=LOBYTE(w)),\ ((*((U8*)(p)+1))=HIBYTE(w))) # define LE_GET32(p) (((U32)LE_GET16(p))|\ LE_GET16((U8*)(p)+2)<<16) # define LE_PUT32(p,d) (LE_PUT16((p),LOWORD(d)),\ LE_PUT16((U8*)(p)+2,HIWORD(d))) #else # define BE_GET16(p) ((U16)(*(U8*)(p)<<8)|\ (*((U8*)(p)+1)&0xFF)) # define BE_PUT16(p,w) (((*(U8*)(p))=HIBYTE(w)),\ ((*((U8*)(p)+1))=LOBYTE(w))) # define BE_GET32(p) (((U32)BE_GET16(p)<<16)|\ BE_GET16((U8*)(p)+2)) # define BE_PUT32(p,d) (BE_PUT16((p),HIWORD(d)),\ BE_PUT16((U8*)(p)+2,LOWORD(d))) # define LE_GET16(p) GET16(p) # define LE_PUT16(p,w) PUT16((p),(w)) # define LE_GET32(p) GET32(p) # define LE_PUT32(p,d) PUT32((p),(d)) #endif /* Miscellaneous PNG definitions. */ #define PNG_Signature "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A" #define PNG_MaxChunkLength 0x7FFFFFFFL #define PNG_CN_IHDR 0x49484452L /* Chunk names */ #define PNG_CN_PLTE 0x504C5445L #define PNG_CN_IDAT 0x49444154L #define PNG_CN_IEND 0x49454E44L #define PNG_CN_gAMA 0x67414D41L #define PNG_CN_sBIT 0x73424954L #define PNG_CN_cHRM 0x6348524DL #define PNG_CN_tRNS 0x74524E53L #define PNG_CN_bKGD 0x624B4744L #define PNG_CN_hIST 0x68495354L #define PNG_CN_tEXt 0x74455874L #define PNG_CN_zTXt 0x7A545874L #define PNG_CN_pHYs 0x70485973L #define PNG_CN_oFFs 0x6F464673L #define PNG_CN_tIME 0x74494D45L #define PNG_CN_sCAL 0x7343414CL #define PNG_CF_Ancillary 0x20000000L /* Chunk flags */ #define PNG_CF_Private 0x00200000L #define PNG_CF_CopySafe 0x00000020L #define PNG_FT_Adaptive 0 /* Filtering type */ #define PNG_CT_Deflate 0 /* Compression type */ #define PNG_IT_None 0 /* Interlace types */ #define PNG_IT_Costello 1 #define PNG_CB_Palette 0x01 /* Colortype bits */ #define PNG_CB_Color 0x02 #define PNG_CB_Alpha 0x04 #define PNG_MU_None 0 /* Measurement units */ #define PNG_MU_Pixel 0 #define PNG_MU_Meter 1 #define PNG_MU_Micrometer 1 #define PNG_MU_Radian 2 #define PNG_PF_None 0 /* Prediction filters */ #define PNG_PF_Sub 1 #define PNG_PF_Up 2 #define PNG_PF_Average 3 #define PNG_PF_Paeth 4 /* Miscellaneous TIFF definitions. This is a small subset of the tags, data * types, and values available in TIFF--only those needed for PNG conversion. * For example, we are not doing any TIFF compression, so the only TIFF * compression type listed is "None". */ #define TIFF_BO_Intel 0x4949 /* Byte order identifiers */ #define TIFF_BO_Motorola 0x4D4D #define TIFF_MagicNumber 42 #define TIFF_DT_BYTE 1 /* Data types */ #define TIFF_DT_ASCII 2 #define TIFF_DT_SHORT 3 #define TIFF_DT_LONG 4 #define TIFF_DT_RATIONAL 5 #define TIFF_DT_UNDEFINED 7 #define TIFF_TAG_ImageWidth 256 /* Tag values */ #define TIFF_TAG_ImageLength 257 #define TIFF_TAG_BitsPerSample 258 #define TIFF_TAG_Compression 259 #define TIFF_TAG_PhotometricInterpretation 262 #define TIFF_TAG_ImageDescription 270 #define TIFF_TAG_Make 271 #define TIFF_TAG_Model 272 #define TIFF_TAG_StripOffsets 273 #define TIFF_TAG_SamplesPerPixel 277 #define TIFF_TAG_RowsPerStrip 278 #define TIFF_TAG_StripByteCounts 279 #define TIFF_TAG_XResolution 282 #define TIFF_TAG_YResolution 283 #define TIFF_TAG_PlanarConfiguration 284 #define TIFF_TAG_XPosition 286 #define TIFF_TAG_YPosition 287 #define TIFF_TAG_ResolutionUnit 296 #define TIFF_TAG_TransferFunction 301 #define TIFF_TAG_Software 305 #define TIFF_TAG_DateTime 306 #define TIFF_TAG_Artist 315 #define TIFF_TAG_HostComputer 316 #define TIFF_TAG_WhitePoint 318 #define TIFF_TAG_PrimaryChromaticities 319 #define TIFF_TAG_ColorMap 320 #define TIFF_TAG_ExtraSamples 338 #define TIFF_TAG_Copyright 33432 /* This last tag is registered to me specifically for this program and its * companion TIFF-to-PNG (not included in the DDJ code), so that they can be * invertable. I encourage you to use it for the same purpose--just make sure * you stay compatible with this program. There is no equivalent PNG chunk for * TIFF data; the known TIFF tags can be either translated to functionally * equivalent PNG chunks or encoded in tEXt chunks. Unknown * ones are not copy-safe (according to the TIFF spec). */ #define TIFF_TAG_PNGChunks 34865 #define TIFF_CT_NONE 1 /* Compression type */ #define TIFF_PI_GRAY 1 /* Photometric interpretations */ #define TIFF_PI_RGB 2 #define TIFF_PI_PLTE 3 #define TIFF_PC_CONTIG 1 /* Planar configurations */ #define TIFF_RU_NONE 1 /* Resolution units */ #define TIFF_RU_CM 3 #define TIFF_ES_UNASSOC 2 /* Extra sample type */ /* Structure for holding miscellaneous image information. Conversion program * will read an image into this structure, then pass it to the output function. * In this implementation, image data bytes are stored in a file, which is * pointed to by this structure. This is so the code will work on small-memory * architectures like MS-DOS. On Unix, Win32 (NT/Chicago), and other systems, * it might make more sense to allocate one big chunk of memory for the image * and replace image_data_file string with an image_data_buffer pointer. */ #define N_KEYWORDS 5 typedef struct _image_info { U32 width, height; U32 xoffset, yoffset; U32 xres, yres; double xscale, yscale; double source_gamma; U32 chromaticities[8]; /* Fixed point x 100000 */ int resolution_unit; /* Units as in PNG */ int offset_unit, scale_unit; int samples_per_pixel; int bits_per_sample; int significant_bits[4]; int background_color[4]; int is_color, has_alpha, has_trns; int is_interlaced, is_palette; int palette_size; U8 palette[3 * 256]; U16 trans_values[3]; U8 palette_trans_bytes[256]; char *keywords[N_KEYWORDS]; char *pixel_data_file; /* Where to find the pixels */ U32 png_data_size; char *png_data_file; /* Untranslatable PNG chunks */ } IMG_INFO; #define IMG_SIZE (sizeof (struct _image_info)) extern char *keyword_table[N_KEYWORDS]; extern U16 ASCII_tags[N_KEYWORDS]; /* Local ASSERT macro. Assumes the function Assert() is defined somewhere in * the calling program (in this case, it's in ptot.c). */ #ifndef NDEBUG # define ASSERT(x) ((x)?(void)0:Assert(__FILE__,__LINE__)) # define TRACE_STR(x) (fprintf(stderr,"TR: %s\n",(x)),\ fflush(stderr)) # define TRACE_INT(x) (fprintf(stderr,"TR: %ld\n",(long)(x)),\ fflush(stderr)) #else # define ASSERT(x) # define TRACE_STR(x) # define TRACE_INT(x) #endif /* Prototypes */ U32 update_crc(U32, U8 *, U32); int main(int argc, char *argv[]); void print_warning(int); void error_exit(int); void Assert(char *, int); int read_PNG(FILE *, IMG_INFO *); int get_chunk_header(void); U32 get_chunk_data(U32); int verify_chunk_crc(void); int decode_IDAT(void); U8 fill_buf(void); void flush_window(U32); int decode_text(void); int copy_unknown_chunk_data(void); size_t new_line_size(IMG_INFO *, int, int); int get_local_byte_order(void); int write_TIFF(FILE *, IMG_INFO *); int create_tempfile(int); int open_tempfile(int); void close_all_tempfiles(void); void remove_all_tempfiles(void); /* Interface to Mark Adler's inflate.c */ int inflate(void); typedef unsigned char uch; typedef unsigned short ush; typedef unsigned long ulg; typedef void *voidp; #define slide (ps.inflate_window) #define WSIZE ((size_t)(ps.inflate_window_size)) #define NEXTBYTE ((--ps.bytes_in_buf>=0)?(*ps.bufp++):fill_buf()) #define FLUSH(n) flush_window(n) #define memzero(a,s) memset((a),0,(s)) #define qflag 1 /* A state structure is used to store all needed info about the reading process * so that we don't have to pass 4 or 5 arguments to every function in ptot.c. * This is also used to share data with inflate.c. */ #define IOBUF_SIZE 8192 /* Must be at least 768 for PLTE */ typedef struct _png_state { FILE *inf, *tf[7]; char *tfnames[7]; IMG_INFO *image; U8 *buf, *bufp; U32 crc, bytes_remaining; U32 inflated_chunk_size; U32 current_chunk_name; S32 bytes_in_buf; /* Must be signed! */ U32 inflate_window_size; U8 *inflate_window; U16 inflate_flags; U16 sum1, sum2; U8 *last_line, *this_line; size_t byte_offset; size_t line_size, line_x; int interlace_pass; U32 current_row, current_col; int cur_filter; int got_first_chunk; int got_first_idat; } PNG_STATE; Listing Two /* ptot.c -- Convert PNG (Portable Network Graphic) file to TIFF. * Takes a filename argument on the command line. * HISTORY: 95-03-10 Created by Lee Daniel Crocker * http://www.piclab.com/piclab/index.html */ #include #include #include #include #include #include #include "ptot.h" #define DEFINE_ENUMS #include "errors.h" #define DEFINE_STRINGS #include "errors.h" PNG_STATE ps = {0}; /* Referenced by tempfile.c, etc. */ char *keyword_table[N_KEYWORDS] = { "Author", "Copyright", "Software", "Source", "Title" }; /* Local definitions and statics */ static int decode_chunk(void); static int decode_IHDR(void); static int decode_PLTE(void); static int decode_gAMA(void); static int decode_tRNS(void); static int decode_cHRM(void); static int decode_pHYs(void); static int decode_oFFs(void); static int decode_sCAL(void); static int skip_chunk_data(void); static int validate_image(IMG_INFO *); /* Main for PTOT. Get filename from command line, massage the * extensions as necessary, and call the read/write routines. */ int main( int argc, char *argv[]) { int err; FILE *fp; char *cp, infname[FILENAME_MAX], outfname[FILENAME_MAX]; IMG_INFO *image; image = (IMG_INFO *)malloc((size_t)IMG_SIZE); if (NULL == image) error_exit(ERR_MEMORY); if (argc < 2) error_exit(ERR_USAGE); strcpy(infname, argv[1]); strcpy(outfname, argv[1]); if (NULL == (cp = strrchr(outfname, '.'))) { strcat(infname, ".png"); } else (*cp = '\0'); strcat(outfname, ".tif"); if (NULL == (fp = fopen(infname, "rb"))) error_exit(ERR_READ); err = read_PNG(fp, image); fclose(fp); if (0 != err) error_exit(err); if (NULL == (fp = fopen(outfname, "wb"))) error_exit(ERR_WRITE); err = write_TIFF(fp, image); fclose(fp); if (0 != err) error_exit(err); return 0; } /* Print warning, but continue. A bad code should never be * passed here, so that causes an assertion failure and exit. */ void print_warning( int code) { ASSERT(PTOT_NMESSAGES > 0); ASSERT(code >= 0 && code < PTOT_NMESSAGES); fprintf(stderr, "WARNING: %s.\n", ptot_error_messages[code]); fflush(stderr); } /* Print fatal error and exit. */ void error_exit( int code) { int msgindex; ASSERT(PTOT_NMESSAGES > 0); if (code < 0 || code >= PTOT_NMESSAGES) msgindex = 0; else msgindex = code; fprintf(stderr, "ERROR: %s.\n", ptot_error_messages[msgindex]); fflush(stderr); if (0 == code) exit(1); else exit(code); } void Assert( char *filename, int lineno) { fprintf(stderr, "ASSERTION FAILURE: " "Line %d of file \"%s\".\n", lineno, filename); fflush(stderr); exit(2); } /* PNG-specific code begins here. read_PNG() reads the PNG file into the * passed IMG_INFO struct. Returns 0 on success. */ int read_PNG( FILE *inf, IMG_INFO *image) { int err; ASSERT(NULL != inf); ASSERT(NULL != image); memset(image, 0, IMG_SIZE); memset(&ps, 0, sizeof ps); ps.inf = inf; ps.image = image; if (NULL == (ps.buf = (U8 *)malloc(IOBUF_SIZE))) return ERR_MEMORY; /* Skip signature and possible MacBinary header; verify signature. * A more robust implementation might search for file signature * anywhere in first 1k bytes or so, but in practice, the method * shown is adequate or file I/O applications. */ fread(ps.buf, 1, 8, inf); ps.buf[8] = '\0'; if (0 != memcmp(ps.buf, PNG_Signature, 8)) { fread(ps.buf, 1, 128, inf); ps.buf[128] = '\0'; if (0 != memcmp(ps.buf+120, PNG_Signature, 8)) { err = ERR_BAD_PNG; goto err_out; } } ps.got_first_chunk = ps.got_first_idat = FALSE; do { if (0 != (err = get_chunk_header())) goto err_out; if (0 != (err = decode_chunk())) goto err_out; /* IHDR must be the first chunk. */ if (!ps.got_first_chunk && (PNG_CN_IHDR != ps.current_chunk_name)) print_warning(WARN_BAD_PNG); ps.got_first_chunk = TRUE; /* Extra unused bytes in chunk? */ if (0 != ps.bytes_remaining) { print_warning(WARN_EXTRA_BYTES); if (0 != (err = skip_chunk_data())) goto err_out; } if (0 != (err = verify_chunk_crc())) goto err_out; } while (PNG_CN_IEND != ps.current_chunk_name); if (!ps.got_first_idat) { err = ERR_NO_IDAT; goto err_out; } if (0 != (err = validate_image(image))) goto err_out; ASSERT(0 == ps.bytes_remaining); if (EOF != getc(inf)) print_warning(WARN_EXTRA_BYTES); err = 0; err_out: ASSERT(NULL != ps.buf); free(ps.buf); return err; } /* decode_chunk() is just a dispatcher, shunting the work of decoding incoming * chunk (whose header we have just read) to the appropriate handler. */ static int decode_chunk( void) { /* Every case in the switch below should set err. We set it * here to gurantee that we hear about it if we don't. */ int err = ERR_ASSERT; switch (ps.current_chunk_name) { case PNG_CN_IHDR: err = decode_IHDR(); break; case PNG_CN_gAMA: err = decode_gAMA(); break; case PNG_CN_IDAT: err = decode_IDAT(); break; /* PNG allows a suggested colormap for 24-bit images. TIFF * does not, and PLTE is not copy-safe, so we discard it. */ case PNG_CN_PLTE: if (ps.image->is_palette) err = decode_PLTE(); else err = skip_chunk_data(); break; case PNG_CN_tRNS: err = decode_tRNS(); break; case PNG_CN_cHRM: err = decode_cHRM(); break; case PNG_CN_pHYs: err = decode_pHYs(); break; case PNG_CN_oFFs: err = decode_oFFs(); break; case PNG_CN_sCAL: err = decode_sCAL(); break; case PNG_CN_tEXt: err = decode_text(); break; case PNG_CN_zTXt: err = decode_text(); break; case PNG_CN_tIME: /* Will be recreated */ case PNG_CN_hIST: /* Not safe to copy */ case PNG_CN_bKGD: err = skip_chunk_data(); break; case PNG_CN_IEND: /* We're done */ err = 0; break; /* Note: sBIT does not have the "copy-safe" bit set, but that really only * applies to unknown chunks. We know what it is just like PLTE and that it * is probably safe to put in the output file. hIST and bKGD aren't * (modifications to the output file might invalidate them), so we leave * them out. */ case PNG_CN_sBIT: err = copy_unknown_chunk_data(); break; default: if (0 == (ps.current_chunk_name & PNG_CF_CopySafe)) err = skip_chunk_data(); else err = copy_unknown_chunk_data(); break; } return err; } /* get_chunk_header() reads the first 8 bytes of each chunk, which include the * length and ID fields. Returns 0 on success. The crc argument is * preconditioned and then updated with the chunk name read. */ int get_chunk_header( void) { int byte; ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); if (8 != fread(ps.buf, 1, 8, ps.inf)) return ERR_READ; ps.bytes_remaining = BE_GET32(ps.buf); ps.current_chunk_name= BE_GET32(ps.buf+4); ps.bytes_in_buf = 0; if (ps.bytes_remaining > PNG_MaxChunkLength) print_warning(WARN_BAD_PNG); for (byte = 4; byte < 8; ++byte) if (!isalpha(ps.buf[byte])) return ERR_BAD_PNG; ps.crc = update_crc(0xFFFFFFFFL, ps.buf+4, 4); return 0; } /* get_chunk_data() reads chunk data into the buffer, returning number of bytes * actually read. Do not use this for IDAT chunks; they are dealt with * specially by the fill_buf() function. */ U32 get_chunk_data( U32 bytes_requested) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ps.bytes_in_buf = (U32)fread(ps.buf, 1, (size_t)min(IOBUF_SIZE, bytes_requested), ps.inf); ASSERT((S32)(ps.bytes_remaining) >= ps.bytes_in_buf); ps.bytes_remaining -= ps.bytes_in_buf; ps.crc = update_crc(ps.crc, ps.buf, ps.bytes_in_buf); return ps.bytes_in_buf; } /* Assuming we have read a chunk header and all the chunk data, now check to * see that CRC stored at end of the chunk matches the one we've calculated. */ int verify_chunk_crc( void) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); if (4 != fread(ps.buf, 1, 4, ps.inf)) return ERR_READ; if ((ps.crc ^ 0xFFFFFFFFL) != BE_GET32(ps.buf)) { print_warning(WARN_BAD_CRC); } return 0; } /* Read and decode IHDR. Errors that would probably cause the IDAT reader to * fail are returned as errors; less serious errors generate a warning but * continue anyway. */ static int decode_IHDR( void) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (ps.bytes_remaining < 13) return ERR_BAD_PNG; if (13 != get_chunk_data(13)) return ERR_READ; ps.image->width = BE_GET32(ps.buf); ps.image->height = BE_GET32(ps.buf+4); if (0 != ps.buf[10] || 0 != ps.buf[11]) return ERR_BAD_PNG; /* Compression & filter type */ ps.image->is_interlaced = ps.buf[12]; if (!(0 == ps.image->is_interlaced || 1 == ps.image->is_interlaced)) return ERR_BAD_PNG; ps.image->is_color = (0 != (ps.buf[9] & PNG_CB_Color)); ps.image->is_palette = (0 != (ps.buf[9] & PNG_CB_Palette)); ps.image->has_alpha = (0 != (ps.buf[9] & PNG_CB_Alpha)); ps.image->samples_per_pixel = 1; if (ps.image->is_color && !ps.image->is_palette) ps.image->samples_per_pixel = 3; if (ps.image->has_alpha) ++ps.image->samples_per_pixel; if (ps.image->is_palette && ps.image->has_alpha) print_warning(WARN_BAD_PNG); /* Check for invalid bit depths. If a bitdepth is not one we can read, * abort processing. If we can read it, but it is illegal, issue a * warning and continue anyway. */ ps.image->bits_per_sample = ps.buf[8]; if (!(1 == ps.buf[8] || 2 == ps.buf[8] || 4 == ps.buf[8] || 8 == ps.buf[8] || 16 == ps.buf[8])) return ERR_BAD_PNG; if ((ps.buf[8] > 8) && ps.image->is_palette) print_warning(WARN_BAD_PNG); if ((ps.buf[8] < 8) && (2 == ps.buf[9] || 4 == ps.buf[9] || 6 == ps.buf[9])) return ERR_BAD_PNG; return 0; } /* Decode gAMA chunk. */ static int decode_gAMA( void) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (0 != ps.image->palette_size) print_warning(WARN_LATE_GAMA); if (ps.bytes_remaining < 4) return ERR_BAD_PNG; if (4 != get_chunk_data(4)) return ERR_READ; ps.image->source_gamma = (double)BE_GET32(ps.buf) / 100000.0; return 0; } /* Decode PLTE chunk. Number of entries is determined by chunk length. A * non-multiple of 3 is technically an error; issue a warning in that case. * IOBUF_SIZE must be 768 or greater, so we check that at compile time here. */ #if (IOBUF_SIZE < 768) # error "IOBUF_SIZE must be >= 768" #endif static int decode_PLTE( void) { U32 bytes_read; ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (!ps.image->is_color) print_warning(WARN_PLTE_GRAY); if (0 != ps.image->palette_size) { print_warning(WARN_MULTI_PLTE); return skip_chunk_data(); } ps.image->palette_size = min(256, (int)(ps.bytes_remaining / 3)); if (0 == ps.image->palette_size) return ERR_BAD_PNG; bytes_read = get_chunk_data(3 * ps.image->palette_size); if (bytes_read < (U32)(3 * ps.image->palette_size)) return ERR_READ; memcpy(ps.image->palette, ps.buf, 3 * ps.image->palette_size); ASSERT(0 != ps.image->palette_size); return 0; } /* Copy transparency data into structure. We will later expand the * TIFF data into full alpha to account for its lack of this data. */ static int decode_tRNS( void) { int i; U32 bytes_read; ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (ps.image->has_trns) print_warning(WARN_MULTI_TRNS); ps.image->has_trns = TRUE; if (ps.image->is_palette) { if (0 == ps.image->palette_size) { print_warning(WARN_LATE_TRNS); } bytes_read = get_chunk_data(ps.bytes_remaining); memcpy(ps.image->palette_trans_bytes, ps.buf, (size_t)bytes_read); for (i = bytes_read; i < ps.image->palette_size; ++i) ps.image->palette_trans_bytes[i] = 255; } else if (ps.image->is_color) { if (ps.bytes_remaining < 6) return ERR_BAD_PNG; bytes_read = get_chunk_data(6); for (i = 0; i < 3; ++i) ps.image->trans_values[i] = BE_GET16(ps.buf + 2 * i); } else { if (ps.bytes_remaining < 2) return ERR_BAD_PNG; ps.image->trans_values[0] = BE_GET16(ps.buf); } return 0; } static int decode_cHRM( void) { int i; ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (ps.bytes_remaining < 32) return ERR_BAD_PNG; if (32 != get_chunk_data(32)) return ERR_READ; for (i = 0; i < 8; ++i) ps.image->chromaticities[i] = BE_GET32(ps.buf + 4 * i); return 0; } static int decode_pHYs( void) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (ps.bytes_remaining < 9) return ERR_BAD_PNG; if (9 != get_chunk_data(9)) return ERR_READ; ps.image->resolution_unit = ps.buf[8]; if (ps.buf[8] > PNG_MU_Meter) print_warning(WARN_BAD_VAL); ps.image->xres = BE_GET32(ps.buf); ps.image->yres = BE_GET32(ps.buf + 4); return 0; } static int decode_oFFs( void) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); if (ps.bytes_remaining < 9) return ERR_BAD_PNG; if (9 != get_chunk_data(9)) return ERR_READ; ps.image->offset_unit = ps.buf[8]; if (ps.buf[8] > PNG_MU_Micrometer) print_warning(WARN_BAD_VAL); ps.image->xoffset = BE_GET32(ps.buf); ps.image->yoffset = BE_GET32(ps.buf + 4); return 0; } /* Decode sCAL chunk. Note: as of this writing, this is not an official PNG * chunk. It probably will be by the time you read this, but it might possibly * change in some way. You have been warned. It also has no TIFF equivalent, so * this only gets read into the structure. */ static int decode_sCAL( void) { ASSERT(NULL != ps.inf); ASSERT(NULL != ps.buf); ASSERT(NULL != ps.image); get_chunk_data(ps.bytes_remaining); if (ps.bytes_in_buf == IOBUF_SIZE) { --ps.bytes_in_buf; print_warning(WARN_BAD_PNG); } ps.buf[ps.bytes_in_buf] = '\0'; ps.image->scale_unit = ps.buf[0]; if (ps.buf[0] < PNG_MU_Meter || ps.buf[0] > PNG_MU_Radian) print_warning(WARN_BAD_VAL); ps.image->xscale = atof(ps.buf+1); ps.image->yscale = atof(ps.buf + (strlen(ps.buf+1)) + 2); return 0; } /* Skip all remaining data in current chunk. */ static int skip_chunk_data( void) { U32 bytes_read; do { bytes_read = get_chunk_data(ps.bytes_remaining); } while (0 != bytes_read); return 0; } /* Ensure that the image structure we have created by reading the input PNG is * compatible with whatever we intend to do with it. In this case, TIFF can * handle anything, so we use this as a sanity check on basic assumptions. */ static int validate_image( IMG_INFO *image) { if (0 == image->width || 0 == image->height) return ERR_BAD_IMAGE; if (image->samples_per_pixel < 1 || image->samples_per_pixel > 4) return ERR_BAD_IMAGE; if (image->is_palette && (image->palette_size < 1 || image->palette_size > 256)) return ERR_BAD_IMAGE; if (NULL == image->pixel_data_file) return ERR_BAD_IMAGE; return 0; } /* End of ptot.c. */