ftp.nice.ch/Attic/openStep/implementation/gnustep/sources/objcX-0.87.tgz#/objcX-0.87/appkit/tiff.c

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.