/* * pngxrtif.c - libpng external I/O: TIFF reader. * Copyright (C) 2006-2012 Cosmin Truta. */ #include "pngxtern.h" #include "pngxutil.h" #include "minitiff.h" #include #include #define PNGX_INTERNAL #include "pngxpriv.h" /* * The TIFF format name. */ static const char tiff_fmt_name[] = "TIFF"; static const char tiff_fmt_long_name[] = "Tagged Image File Format"; int /* PRIVATE */ pngx_sig_is_tiff(png_bytep sig, size_t sig_size, png_const_charpp fmt_name_ptr, png_const_charpp fmt_long_name_ptr) { /* Require at least the TIFF signature. */ if (sig_size < 8) return -1; /* insufficient data */ if (memcmp(sig, minitiff_sig_m, 4) != 0 && memcmp(sig, minitiff_sig_i, 4) != 0) return 0; /* not TIFF */ /* Store the format name. */ if (fmt_name_ptr != NULL) *fmt_name_ptr = tiff_fmt_name; if (fmt_long_name_ptr != NULL) *fmt_long_name_ptr = tiff_fmt_long_name; return 1; /* TIFF */ } /* FIXME: Not thread-safe. */ static png_structp err_png_ptr = NULL; static unsigned int num_extra_images; static void pngx_tiff_error(const char *msg) { png_error(err_png_ptr, msg); } static void pngx_tiff_warning(const char *msg) { /* FIXME: * Inspection of warning messages is fragile, but is * required by the limitations of minitiff version 0.1. */ if (strstr(msg, "multi-image") != NULL) ++num_extra_images; #if 0 /* Metadata is not imported, so warnings need not be shown. */ png_warning(err_png_ptr, msg); #endif } int /* PRIVATE */ pngx_read_tiff(png_structp png_ptr, png_infop info_ptr, FILE *stream) { struct minitiff_info tiff_info; unsigned int width, height, pixel_size, sample_depth, sample_max; int color_type; int sample_overflow; png_bytepp row_pointers; png_bytep row; unsigned int i, j, k; err_png_ptr = png_ptr; num_extra_images = 0; minitiff_init_info(&tiff_info); tiff_info.error_handler = pngx_tiff_error; tiff_info.warning_handler = pngx_tiff_warning; minitiff_read_info(&tiff_info, stream); minitiff_validate_info(&tiff_info); width = (unsigned int)tiff_info.width; height = (unsigned int)tiff_info.height; pixel_size = tiff_info.samples_per_pixel; sample_depth = tiff_info.bits_per_sample; switch (pixel_size) { case 1: color_type = PNG_COLOR_TYPE_GRAY; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: png_error(png_ptr, "Unsupported TIFF color space"); /* NOTREACHED */ return 0; /* avoid "uninitialized color_type" warning */ } if (sample_depth > 16) png_error(png_ptr, "Unsupported TIFF sample depth"); sample_max = (1 << sample_depth) - 1; sample_overflow = 0; png_set_IHDR(png_ptr, info_ptr, width, height, (sample_depth <= 8) ? 8 : 16, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); row_pointers = pngx_malloc_rows(png_ptr, info_ptr, 0); if (sample_depth <= 8) { for (i = 0; i < height; ++i) { row = row_pointers[i]; minitiff_read_row(&tiff_info, row, i, stream); if (sample_depth < 8) { for (j = 0; j < pixel_size * width; ++j) { unsigned int b = row[j]; if (b > sample_max) { b = sample_max; sample_overflow = 1; } row[j] = (png_byte)((b * 255 + sample_max / 2) / sample_max); } } if (tiff_info.photometric == 0) { for (j = 0; j < pixel_size * width; ++j) row[j] = (png_byte)(255 - row[j]); } } } else { for (i = 0; i < height; ++i) { row = row_pointers[i]; minitiff_read_row(&tiff_info, row, i, stream); if (tiff_info.byte_order == 'I') { /* "Intel" byte order => swap row bytes */ for (j = k = 0; j < pixel_size * width; ++j, k+=2) { png_byte b = row[k]; row[k] = row[k + 1]; row[k + 1] = b; } } if (sample_depth < 16) { for (j = k = 0; k < pixel_size * width; ++j, k+=2) { unsigned int b = (row[k] << 8) + row[k + 1]; if (b > sample_max) { b = sample_max; sample_overflow = 1; } b = (b * 65535U + sample_max / 2) / sample_max; row[k] = (png_byte)(b >> 8); row[k + 1] = (png_byte)(b & 255); } } } } if (sample_overflow) png_warning(png_ptr, "Overflow in TIFF samples"); minitiff_destroy_info(&tiff_info); return 1 + num_extra_images; }