This is tiff.c in view mode; [Download] [Up]
/*
tiff - Functions for dealing with tiff images
Copyright (C) 1994, Adam Fedor
Some of this code is derived from tif_getimage, by Sam Leffler. See the
copyright below.
*/
/*
* Copyright (c) 1991, 1992, 1993, 1994 Sam Leffler
* Copyright (c) 1991, 1992, 1993, 1994 Silicon Graphics, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that (i) the above copyright notices and this permission notice appear in
* all copies of the software and related documentation, and (ii) the names of
* Sam Leffler and Silicon Graphics may not be used in any advertising or
* publicity relating to the software without the specific, prior written
* permission of Sam Leffler and Silicon Graphics.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
* ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
* OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
* WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
* LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
* OF THIS SOFTWARE.
*/
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* for L_SET, etc definitions */
#include "streams/streams.h"
#include "appkit/tiff.h"
#include "appkit/stdmacros.h"
/* Define this to use the "old" version 3.0 with patches */
/* Use the Makefile if possible */
/* #define HAVE_LIBTIFF_3_0 1 */
typedef struct {
NXStream *stream;
int my_stream;
int mode;
const char *name;
} chandle_t;
#define MCHECK(m, tif) \
if (!m) { TIFFError(TIFFFileName(tif), "Malloc failed.\n"); return(NULL); }
static const char *
type_for_mode(int mode)
{
char *type = NULL;
if (mode == NX_READONLY)
type = "r";
else if (mode == NX_WRITEONLY)
type = "w";
else if (mode == NX_READWRITE)
type = "wr";
return type;
}
#ifdef HAVE_LIBTIFF
#ifndef HAVE_LIBTIFF_3_0
/* Client functions that provide reading/writing of file data for libtiff */
static tsize_t
HandleTiffRead(thandle_t handle, tdata_t buf, toff_t count)
{
chandle_t *h = (chandle_t *)handle;
return NXRead(h->stream, buf, (size_t)count);
}
static tsize_t
HandleTiffWrite(thandle_t handle, tdata_t buf, toff_t count)
{
chandle_t *h = (chandle_t *)handle;
return NXWrite(h->stream, buf, (size_t)count);
}
static toff_t
HandleTiffSeek(thandle_t handle, toff_t offset, int mode)
{
chandle_t *h = (chandle_t *)handle;
switch(mode) {
case SEEK_SET: mode = NX_FROMSTART; break;
case SEEK_CUR: mode = NX_FROMCURRENT; break;
case SEEK_END: mode = NX_FROMEND; break;
default: mode = NX_FROMSTART;
}
NXSeek(h->stream, offset, mode);
return NXTell(h->stream);
}
static int
HandleTiffClose(thandle_t handle)
{
chandle_t *h = (chandle_t *)handle;
/* If we don't own the stream, don't do anything */
if (h->my_stream == 0)
return 0;
/* If we wrote to the file, we need to save it */
if (h->mode != NX_READONLY)
NXSaveToFile(h->stream, h->name);
NXCloseMemory(h->stream, NX_FREEBUFFER);
/* Presumably, we don't need the handle anymore */
NX_FREE(h);
return 0;
}
static toff_t
HandleTiffSize(thandle_t handle)
{
int pos, len;
chandle_t *h = (chandle_t *)handle;
/* Ugly way to find the size of a file/stream */
pos = NXTell(h->stream);
if (pos < 0)
return 0;
NXSeek(h->stream, 0, NX_FROMEND);
len = NXTell(h->stream);
NXSeek(h->stream, pos, NX_FROMSTART);
return (len > 0) ? len : 0;
}
static int
HandleTiffMap(thandle_t handle, tdata_t *data, toff_t *size)
{
int maxsize;
chandle_t *h = (chandle_t *)handle;
/* If we've got true streams, then we're already mapped, if not, we
don't even try to map the file.
*/
*size = 0;
NXGetMemoryBuffer(h->stream, (char **)data, (int *)size, &maxsize);
return (*size) ? 1 : 0;
}
static void
HandleTiffUnmap(thandle_t handle, tdata_t data, toff_t size)
{
/* Nothing to do. This is handled by HandleTiffClose. */
}
#endif /* not HAVE_LIBTIFF_3_0 */
/* Open a tiff file. Returns NULL if can't read tiff information */
TIFF *
NXOpenTiffFile(const char *filename, int mode)
{
#ifndef HAVE_LIBTIFF_3_0
chandle_t *handle;
NX_MALLOC(handle, chandle_t, 1);
if ((handle->stream = NXMapFile(filename, mode)) == NULL)
return NULL;
handle->my_stream = 1;
handle->mode = mode;
handle->name = filename;
return TIFFClientOpen(filename, (char *)type_for_mode(mode),
(thandle_t)handle,
HandleTiffRead, HandleTiffWrite,
HandleTiffSeek, HandleTiffClose,
HandleTiffSize,
HandleTiffMap, HandleTiffUnmap);
#else
return TIFFOpen((char *)filename, (char *)type_for_mode(mode));
#endif
}
/* Open a tiff from a stream. Returns NULL if can't read tiff information. */
TIFF *
NXOpenTiffStream(NXStream *stream, int mode)
{
#ifndef HAVE_LIBTIFF_3_0
chandle_t *handle;
NX_MALLOC(handle, chandle_t, 1);
handle->stream = stream;
handle->my_stream = 0;
handle->mode = mode;
handle->name = "NXStream";
return TIFFClientOpen("NXStream", (char *)type_for_mode(mode),
(thandle_t)handle,
HandleTiffRead, HandleTiffWrite,
HandleTiffSeek, HandleTiffClose,
HandleTiffSize,
HandleTiffMap, HandleTiffUnmap);
#else
return TIFFFdOpen((int)stream, NULL, (char *)type_for_mode(mode));
#endif
}
int
NXCloseTiff(TIFF *image)
{
TIFFClose(image);
return 0;
}
/* Read some information about the image. Note that currently we don't
determine numImages.
*/
NXTiffInfo *
NXGetTiffInfo(int imageNumber, TIFF *image)
{
NXTiffInfo *info;
if (imageNumber >= 0 && !TIFFSetDirectory(image, imageNumber)) {
return NULL;
}
NX_MALLOC(info, NXTiffInfo, 1);
memset(info, 0, sizeof(NXTiffInfo));
if (imageNumber >= 0)
info->imageNumber = imageNumber;
TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &info->width);
TIFFGetField(image, TIFFTAG_IMAGELENGTH, &info->height);
TIFFGetField(image, TIFFTAG_COMPRESSION, &info->compression);
TIFFGetField(image, TIFFTAG_SUBFILETYPE, &info->subfileType);
/*
* If the following tags aren't present then use the TIFF defaults.
*/
TIFFGetFieldDefaulted(image, TIFFTAG_BITSPERSAMPLE, &info->bitsPerSample);
TIFFGetFieldDefaulted(image, TIFFTAG_SAMPLESPERPIXEL,
&info->samplesPerPixel);
TIFFGetFieldDefaulted(image, TIFFTAG_PLANARCONFIG,
&info->planarConfig);
/*
* If TIFFTAG_PHOTOMETRIC is not present then assign a reasonable default.
* The TIFF 5.0 specification doesn't give a default.
*/
if (!TIFFGetField(image, TIFFTAG_PHOTOMETRIC, &info->photoInterp)) {
switch (info->samplesPerPixel) {
case 1:
info->photoInterp = PHOTOMETRIC_MINISBLACK;
break;
case 3: case 4:
info->photoInterp = PHOTOMETRIC_RGB;
break;
default:
TIFFError(TIFFFileName(image),
"Missing needed \"PhotometricInterpretation\" tag");
return (0);
}
TIFFError(TIFFFileName(image),
"No \"PhotometricInterpretation\" tag, assuming %s\n",
info->photoInterp == PHOTOMETRIC_RGB ? "RGB" : "min-is-black");
}
return info;
}
#define READ_SCANLINE(sample) \
if ( TIFFReadScanline( image, buf, row, sample ) < 0 ) { \
fprintf(stderr, "tiff: bad data read on line %d\n", row ); \
error = 1; \
break; \
} \
inP = buf;
/* Read an image into a data array. The data array is assumed to have been
already allocated to the correct size.
Note that palette images are implicitly coverted to 24-bit contig
direct color images. Thus the data array should be large
enough to hold this information.
*/
void *
NXReadTiff(int imageNumber, TIFF *image, NXTiffInfo *info, void *data)
{
int i;
int row, col;
int maxval;
int size;
int line;
int error = 0;
u_char *inP, *outP;
u_char *buf;
u_char *raster;
NXTiffInfo *newinfo;
NXColormap *map;
if (data == NULL)
return NULL;
/* Make sure we're at the right image */
if ((newinfo = NXGetTiffInfo(imageNumber, image)) == NULL)
return NULL;
if (info)
memcpy(info, newinfo, sizeof(NXTiffInfo));
map = NULL;
if ( newinfo->photoInterp == PHOTOMETRIC_PALETTE) {
map = NXGetColormap(image);
if (!map)
return NULL;
}
maxval = ( 1 << newinfo->bitsPerSample ) - 1;
line = ceil((float)newinfo->width * newinfo->bitsPerSample / 8.0);
size = ceil((float)line * newinfo->height * newinfo->samplesPerPixel );
NX_MALLOC(buf, u_char, TIFFScanlineSize(image));
MCHECK(buf, image);
raster = (u_char *)data;
outP = raster;
switch ( newinfo->photoInterp ) {
case PHOTOMETRIC_MINISBLACK:
case PHOTOMETRIC_MINISWHITE:
if (newinfo->planarConfig == PLANARCONFIG_CONTIG) {
for ( row = 0; row < newinfo->height; ++row ) {
READ_SCANLINE(0)
for ( col = 0; col < line*newinfo->samplesPerPixel; col++)
*outP++ = *inP++;
}
} else {
for (i = 0; i < newinfo->samplesPerPixel; i++)
for ( row = 0; row < newinfo->height; ++row ) {
READ_SCANLINE(i)
for ( col = 0; col < line; col++)
*outP++ = *inP++;
}
}
break;
case PHOTOMETRIC_PALETTE:
{
for ( row = 0; row < newinfo->height; ++row ) {
READ_SCANLINE(0)
for ( col = 0; col < newinfo->width; col++) {
*outP++ = map->red[*inP] / 256;
*outP++ = map->green[*inP] / 256;
*outP++ = map->blue[*inP] / 256;
inP++;
}
}
free(map->red);
free(map->green);
free(map->blue);
free(map);
}
break;
case PHOTOMETRIC_RGB:
if (newinfo->planarConfig == PLANARCONFIG_CONTIG) {
for ( row = 0; row < newinfo->height; ++row ) {
READ_SCANLINE(0)
for ( col = 0; col < newinfo->width; col++)
for (i = 0; i < newinfo->samplesPerPixel; i++)
*outP++ = *inP++;
}
} else {
for (i = 0; i < newinfo->samplesPerPixel; i++)
for ( row = 0; row < newinfo->height; ++row ) {
READ_SCANLINE(i)
for ( col = 0; col < newinfo->width; col++)
*outP++ = *inP++;
}
}
break;
default:
TIFFError(TIFFFileName(image),
"Can't read photometric %d", newinfo->photoInterp);
break;
}
NX_FREE(newinfo);
return (error) ? NULL : raster;
}
int
NXWriteTiff(TIFF *image, NXTiffInfo *info, void *data)
{
return 0;
}
/*------------------------------------------------------------------------*/
/*
* Many programs get TIFF colormaps wrong. They use 8-bit colormaps instead of
* 16-bit colormaps. This function is a heuristic to detect and correct this.
*/
static int
CheckAndCorrectColormap(NXColormap *map)
{
register int i;
for (i = 0; i < map->size; i++)
if ((map->red[i] > 255)||(map->green[i] > 255)||(map->blue[i] > 255))
return 16;
#define CVT(x) (((x) * 255) / ((1L<<16)-1))
for (i = 0; i < map->size; i++) {
map->red[i] = CVT(map->red[i]);
map->green[i] = CVT(map->green[i]);
map->blue[i] = CVT(map->blue[i]);
}
return 8;
}
/* Gets the colormap for the image if there is one. Returns a
NXColormap if one was found.
*/
NXColormap *
NXGetColormap(TIFF *image)
{
NXTiffInfo *info;
NXColormap *map;
/* Re-read the tiff information. We pass -1 as the image number which
means just read the current image.
*/
info = NXGetTiffInfo(-1, image);
if (info->photoInterp != PHOTOMETRIC_PALETTE)
return NULL;
NX_MALLOC(map, NXColormap, 1);
map->size = 1 << info->bitsPerSample;
if (!TIFFGetField(image, TIFFTAG_COLORMAP,
&map->red, &map->green, &map->blue)) {
TIFFError(TIFFFileName(image),
"Missing required \"Colormap\" tag");
NX_FREE(map);
return NULL;
}
if (CheckAndCorrectColormap(map) == 8)
TIFFWarning(TIFFFileName(image), "Assuming 8-bit colormap");
free(info);
return map;
}
#else /* HAVE_LIBTIFF */
TIFF *
NXOpenTiffFile(const char *filename, int mode)
{
return NULL;
}
TIFF *
NXOpenTiffStream(NXStream *stream, int mode)
{
return NULL;
}
int
NXCloseTiff(TIFF *image)
{
return 0;
}
NXTiffInfo *
NXGetTiffInfo(int imageNumber, TIFF *image)
{
return NULL;
}
void *
NXReadTiff(int imageNumber, TIFF *image, NXTiffInfo *info, void *data)
{
return NULL;
}
int
NXWriteTiff(TIFF *image, NXTiffInfo *info, void *data)
{
return -1;
}
NXColormap *
NXGetColormap(TIFF *image)
{
return NULL;
}
#endif /* not HAVE_LIBTIFF */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.