blob: ca968ba0965e772801e1cf1f6eab2e11ac75fc12 [file] [log] [blame]
/*
* pngxrtif.c - libpng external I/O: TIFF reader.
* Copyright (C) 2006-2012 Cosmin Truta.
*/
#include "pngxtern.h"
#include "pngxutil.h"
#include "minitiff.h"
#include <stdio.h>
#include <string.h>
#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;
}