1680 lines
38 KiB
C
1680 lines
38 KiB
C
/* TIFF PARTS:
|
|
* Copyright (c) 1988, 1990 by Sam Leffler.
|
|
* All rights reserved.
|
|
*
|
|
* This file is provided for unrestricted use provided that this
|
|
* legend is included on all tape media and as a part of the
|
|
* software program in whole or part. Users may copy, modify or
|
|
* distribute this file at will.
|
|
|
|
MODIFICATION FOR VIPS Copyright 1991, K.Martinez
|
|
* software may be distributed FREE, with these copyright notices
|
|
* no responsibility/warantee is implied or given
|
|
*
|
|
*
|
|
* Modified and added im_LabQ2LabC() function. It can write IM_TYPE_LABQ image
|
|
* in vips format to LAB in tiff format.
|
|
* Copyright 1994 Ahmed Abbood.
|
|
*
|
|
* 19/9/95 JC
|
|
* - calls TIFFClose() more reliably
|
|
* - tidied up
|
|
* 12/4/97 JC
|
|
* - thrown away and rewritten for TIFF 6 lib
|
|
* 22/4/97 JC
|
|
* - writes a pyramid!
|
|
* - to separate TIFF files tho'
|
|
* 23/4/97 JC
|
|
* - does 2nd gather pass to put pyramid into a single TIFF file
|
|
* - ... and shrinks IM_CODING_LABQ too
|
|
* 26/10/98 JC
|
|
* - binary open for stupid systems
|
|
* 7/6/99 JC
|
|
* - 16bit TIFF write too
|
|
* 9/7/99 JC
|
|
* - ZIP tiff added
|
|
* 11/5/00 JC
|
|
* - removed TIFFmalloc/TIFFfree
|
|
* 5/8/00 JC
|
|
* - mode string now part of filename
|
|
* 23/4/01 JC
|
|
* - HAVE_TIFF turns on TIFFness
|
|
* 19/3/02 ruven
|
|
* - pyramid stops at tile size, not 64x64
|
|
* 29/4/02 JC
|
|
* - write any number of bands (but still with photometric RGB, so not
|
|
* very useful)
|
|
* 10/9/02 JC
|
|
* - oops, handle TIFF errors better
|
|
* - now writes CMYK correctly
|
|
* 13/2/03 JC
|
|
* - tries not to write mad resolutions
|
|
* 7/5/03 JC
|
|
* - only write CMYK if Type == CMYK
|
|
* - writes EXTRASAMPLES ALPHA for bands == 2 or 4 (if we're writing RGB)
|
|
* 17/11/03 JC
|
|
* - write float too
|
|
* 28/11/03 JC
|
|
* - read via a "p" so we work from mmap window images
|
|
* - uses threadgroups for speedup
|
|
* 9/3/04 JC
|
|
* - 1 bit write mode added
|
|
* 5/4/04
|
|
* - better handling of edge tiles (thanks Ruven)
|
|
* 18/5/04 Andrey Kiselev
|
|
* - added res_inch/res_cm option
|
|
* 20/5/04 JC
|
|
* - allow single res number too
|
|
* 19/7/04
|
|
* - write several scanlines at once, good speed up for some cases
|
|
* 22/9/04
|
|
* - got rid of wrapper image so nip gets progress feedback
|
|
* - fixed tiny read-beyond-buffer issue for edge tiles
|
|
* 7/10/04
|
|
* - added ICC profile embedding
|
|
* 13/12/04
|
|
* - can now pyramid any non-complex type (thanks Ruven)
|
|
* 27/1/05
|
|
* - added ccittfax4 as a compression option
|
|
* 9/3/05
|
|
* - set PHOTOMETRIC_CIELAB for vips TYPE_LAB images ... so we can write
|
|
* float LAB as well as float RGB
|
|
* - also LABS images
|
|
* 22/6/05
|
|
* - 16 bit LAB write was broken
|
|
* 9/9/05
|
|
* - write any icc profile from meta
|
|
* 3/3/06
|
|
* - raise tile buffer limit (thanks Ruven)
|
|
* 11/11/06
|
|
* - set ORIENTATION_TOPLEFT (thanks Josef)
|
|
* 18/7/07 Andrey Kiselev
|
|
* - remove "b" option on TIFFOpen()
|
|
* - support TIFFTAG_PREDICTOR types for lzw and deflate compression
|
|
* 3/11/07
|
|
* - use im_wbuffer() for background writes
|
|
* 15/2/08
|
|
* - set TIFFTAG_JPEGQUALITY explicitly when we copy TIFF files, since
|
|
* libtiff doesn't keep this in the header (thanks Joe)
|
|
* 20/2/08
|
|
* - use tiff error handler from im_tiff2vips.c
|
|
* 27/2/08
|
|
* - don't try to copy icc profiles when building pyramids (thanks Joe)
|
|
* 9/4/08
|
|
* - use IM_META_RESOLUTION_UNIT to set default resunit
|
|
* 17/4/08
|
|
* - allow CMYKA (thanks Doron)
|
|
* 5/9/08
|
|
* - trigger eval callbacks during tile write
|
|
*/
|
|
|
|
/*
|
|
|
|
This file is part of VIPS.
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
/* Turn on IM_REGION_ADDR() range checks, don't delete intermediates.
|
|
#define DEBUG
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <vips/intl.h>
|
|
|
|
#ifndef HAVE_TIFF
|
|
|
|
#include <vips/vips.h>
|
|
|
|
int
|
|
im_vips2tiff( IMAGE *im, const char *filename )
|
|
{
|
|
im_error( "im_vips2tiff", "%s",
|
|
_( "TIFF support disabled" ) );
|
|
|
|
return( -1 );
|
|
}
|
|
|
|
#else /*HAVE_TIFF*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif /*HAVE_UNISTD_H*/
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <vips/vips.h>
|
|
#include <vips/internal.h>
|
|
|
|
#include <tiffio.h>
|
|
|
|
#ifdef WITH_DMALLOC
|
|
#include <dmalloc.h>
|
|
#endif /*WITH_DMALLOC*/
|
|
|
|
/* Max no of tiles we buffer in a layer. Enough to buffer a line of 64x64
|
|
* tiles on a 100k pixel across image.
|
|
*/
|
|
#define IM_MAX_LAYER_BUFFER (1000)
|
|
|
|
/* Bits we OR together for quadrants in a tile.
|
|
*/
|
|
typedef enum pyramid_bits {
|
|
PYR_TL = 1, /* Top-left etc. */
|
|
PYR_TR = 2,
|
|
PYR_BL = 4,
|
|
PYR_BR = 8,
|
|
PYR_ALL = 15,
|
|
PYR_NONE = 0
|
|
} PyramidBits;
|
|
|
|
/* A tile in our pyramid.
|
|
*/
|
|
typedef struct pyramid_tile {
|
|
REGION *tile;
|
|
PyramidBits bits;
|
|
} PyramidTile;
|
|
|
|
/* A layer in the pyramid.
|
|
*/
|
|
typedef struct pyramid_layer {
|
|
/* Parameters.
|
|
*/
|
|
struct tiff_write *tw; /* Main TIFF write struct */
|
|
int width, height; /* Layer size */
|
|
int sub; /* Subsample factor for this layer */
|
|
|
|
char *lname; /* Name of this TIFF file */
|
|
TIFF *tif; /* TIFF file we write this layer to */
|
|
PEL *tbuf; /* TIFF output buffer */
|
|
PyramidTile tiles[IM_MAX_LAYER_BUFFER];
|
|
|
|
struct pyramid_layer *below; /* Tiles go to here */
|
|
struct pyramid_layer *above; /* Tiles come from here */
|
|
} PyramidLayer;
|
|
|
|
/* A TIFF image in the process of being written.
|
|
*/
|
|
typedef struct tiff_write {
|
|
IMAGE *im; /* Original input image */
|
|
char *name; /* Final name we write to */
|
|
char *mode; /* Mode string */
|
|
|
|
/* Read from im with these.
|
|
*/
|
|
REGION *reg;
|
|
im_threadgroup_t *tg;
|
|
|
|
char *bname; /* Name for base layer */
|
|
TIFF *tif; /* Image we write to */
|
|
|
|
PyramidLayer *layer; /* Top of pyramid, if in use */
|
|
PEL *tbuf; /* TIFF output buffer */
|
|
int tls; /* Tile line size */
|
|
|
|
int compression; /* Compression type */
|
|
int jpqual; /* JPEG q-factor */
|
|
int predictor; /* Predictor value */
|
|
int tile; /* Tile or not */
|
|
int tilew, tileh; /* Tile size */
|
|
int pyramid; /* Write pyramid */
|
|
int onebit; /* Write as 1-bit TIFF */
|
|
int resunit; /* Resolution unit (inches or cm) */
|
|
float xres; /* Resolution in X */
|
|
float yres; /* Resolution in Y */
|
|
int embed; /* Embed ICC profile */
|
|
char *icc_profile; /* Profile to embed */
|
|
} TiffWrite;
|
|
|
|
/* Use these from im_tiff2vips().
|
|
*/
|
|
void im__thandler_error( char *module, char *fmt, va_list ap );
|
|
void im__thandler_warning( char *module, char *fmt, va_list ap );
|
|
|
|
/* Open TIFF for output.
|
|
*/
|
|
static TIFF *
|
|
tiff_openout( const char *name )
|
|
{
|
|
TIFF *tif;
|
|
|
|
if( !(tif = TIFFOpen( name, "w" )) ) {
|
|
im_error( "im_vips2tiff",
|
|
_( "unable to open \"%s\" for output" ), name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( tif );
|
|
}
|
|
|
|
/* Open TIFF for input.
|
|
*/
|
|
static TIFF *
|
|
tiff_openin( const char *name )
|
|
{
|
|
TIFF *tif;
|
|
|
|
if( !(tif = TIFFOpen( name, "r" )) ) {
|
|
im_error( "im_vips2tiff",
|
|
_( "unable to open \"%s\" for input" ), name );
|
|
return( NULL );
|
|
}
|
|
|
|
return( tif );
|
|
}
|
|
|
|
/* Convert VIPS LabQ to TIFF LAB. Just take the first three bands.
|
|
*/
|
|
static void
|
|
LabQ2LabC( PEL *q, PEL *p, int n )
|
|
{
|
|
int x;
|
|
|
|
for( x = 0; x < n; x++ ) {
|
|
/* Get most significant 8 bits of lab.
|
|
*/
|
|
q[0] = p[0];
|
|
q[1] = p[1];
|
|
q[2] = p[2];
|
|
|
|
p += 4;
|
|
q += 3;
|
|
}
|
|
}
|
|
|
|
/* Pack 8 bit VIPS to 1 bit TIFF.
|
|
*/
|
|
static void
|
|
eightbit2onebit( PEL *q, PEL *p, int n )
|
|
{
|
|
int x;
|
|
PEL bits;
|
|
|
|
bits = 0;
|
|
for( x = 0; x < n; x++ ) {
|
|
bits <<= 1;
|
|
if( p[x] )
|
|
bits |= 1;
|
|
|
|
if( (x & 0x7) == 0x7 ) {
|
|
*q++ = bits;
|
|
bits = 0;
|
|
}
|
|
}
|
|
|
|
/* Any left-over bits? Need to be left-aligned.
|
|
*/
|
|
if( (x & 0x7) != 0 )
|
|
*q++ = bits << (8 - (x & 0x7));
|
|
}
|
|
|
|
/* Convert VIPS LABS to TIFF 16 bit LAB.
|
|
*/
|
|
static void
|
|
LabS2Lab16( PEL *q, PEL *p, int n )
|
|
{
|
|
int x;
|
|
short *p1 = (short *) p;
|
|
unsigned short *q1 = (unsigned short *) q;
|
|
|
|
for( x = 0; x < n; x++ ) {
|
|
/* TIFF uses unsigned 16 bit ... move zero, scale up L.
|
|
*/
|
|
q1[0] = (int) p1[0] << 1;
|
|
q1[1] = p1[1];
|
|
q1[2] = p1[2];
|
|
|
|
p1 += 3;
|
|
q1 += 3;
|
|
}
|
|
}
|
|
|
|
/* Pack a VIPS region into a TIFF tile buffer.
|
|
*/
|
|
static void
|
|
pack2tiff( TiffWrite *tw, REGION *in, PEL *q, Rect *area )
|
|
{
|
|
int y;
|
|
|
|
for( y = area->top; y < IM_RECT_BOTTOM( area ); y++ ) {
|
|
PEL *p = (PEL *) IM_REGION_ADDR( in, area->left, y );
|
|
|
|
if( in->im->Coding == IM_CODING_LABQ )
|
|
LabQ2LabC( q, p, area->width );
|
|
else if( tw->onebit )
|
|
eightbit2onebit( q, p, area->width );
|
|
else if( in->im->BandFmt == IM_BANDFMT_SHORT &&
|
|
in->im->Type == IM_TYPE_LABS )
|
|
LabS2Lab16( q, p, area->width );
|
|
else
|
|
memcpy( q, p,
|
|
area->width * IM_IMAGE_SIZEOF_PEL( in->im ) );
|
|
|
|
q += tw->tls;
|
|
}
|
|
}
|
|
|
|
/* Embed an ICC profile from a file.
|
|
*/
|
|
static int
|
|
embed_profile_file( TIFF *tif, const char *profile )
|
|
{
|
|
char *buffer;
|
|
unsigned int length;
|
|
|
|
if( !(buffer = im__file_read_name( profile, &length )) )
|
|
return( -1 );
|
|
TIFFSetField( tif, TIFFTAG_ICCPROFILE, length, buffer );
|
|
im_free( buffer );
|
|
|
|
#ifdef DEBUG
|
|
printf( "im_vips2tiff: attached profile \"%s\"\n", profile );
|
|
#endif /*DEBUG*/
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Embed an ICC profile from IMAGE metadata.
|
|
*/
|
|
static int
|
|
embed_profile_meta( TIFF *tif, IMAGE *im )
|
|
{
|
|
void *data;
|
|
size_t data_length;
|
|
|
|
if( im_meta_get_blob( im, IM_META_ICC_NAME, &data, &data_length ) )
|
|
return( -1 );
|
|
TIFFSetField( tif, TIFFTAG_ICCPROFILE, data_length, data );
|
|
|
|
#ifdef DEBUG
|
|
printf( "im_vips2tiff: attached profile from meta\n" );
|
|
#endif /*DEBUG*/
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
embed_profile( TiffWrite *tw, TIFF *tif )
|
|
{
|
|
if( tw->embed && embed_profile_file( tif, tw->icc_profile ) )
|
|
return( -1 );
|
|
if( !tw->embed && im_header_get_type( tw->im, IM_META_ICC_NAME ) &&
|
|
embed_profile_meta( tif, tw->im ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Write a TIFF header. width and height are the size of the IMAGE we are
|
|
* writing (may have been shrunk!).
|
|
*/
|
|
static int
|
|
write_tiff_header( TiffWrite *tw, TIFF *tif, int width, int height )
|
|
{
|
|
uint16 v[1];
|
|
|
|
/* Output base header fields.
|
|
*/
|
|
TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, width );
|
|
TIFFSetField( tif, TIFFTAG_IMAGELENGTH, height );
|
|
TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
|
|
TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT );
|
|
TIFFSetField( tif, TIFFTAG_COMPRESSION, tw->compression );
|
|
|
|
/* Don't write mad resolutions (eg. zero), it confuses some programs.
|
|
*/
|
|
TIFFSetField( tif, TIFFTAG_RESOLUTIONUNIT, tw->resunit );
|
|
TIFFSetField( tif, TIFFTAG_XRESOLUTION,
|
|
IM_CLIP( 0.01, tw->xres, 10000 ) );
|
|
TIFFSetField( tif, TIFFTAG_YRESOLUTION,
|
|
IM_CLIP( 0.01, tw->yres, 10000 ) );
|
|
|
|
if( tw->compression == COMPRESSION_JPEG )
|
|
TIFFSetField( tif, TIFFTAG_JPEGQUALITY, tw->jpqual );
|
|
|
|
if( tw->predictor != -1 )
|
|
TIFFSetField( tif, TIFFTAG_PREDICTOR, tw->predictor );
|
|
|
|
/* Attach ICC profile.
|
|
*/
|
|
if( embed_profile( tw, tif ) )
|
|
return( -1 );
|
|
|
|
/* And colour fields.
|
|
*/
|
|
if( tw->im->Coding == IM_CODING_LABQ ) {
|
|
TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 3 );
|
|
TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 8 );
|
|
TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB );
|
|
}
|
|
else if( tw->onebit ) {
|
|
TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 1 );
|
|
TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 1 );
|
|
TIFFSetField( tif,
|
|
TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK );
|
|
}
|
|
else {
|
|
int photometric;
|
|
|
|
TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, tw->im->Bands );
|
|
TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, tw->im->Bbits );
|
|
|
|
switch( tw->im->Bands ) {
|
|
case 1:
|
|
case 2:
|
|
photometric = PHOTOMETRIC_MINISBLACK;
|
|
if( tw->im->Bands == 2 ) {
|
|
v[0] = EXTRASAMPLE_ASSOCALPHA;
|
|
TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v );
|
|
}
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
if( tw->im->Type == IM_TYPE_LAB ||
|
|
tw->im->Type == IM_TYPE_LABS )
|
|
photometric = PHOTOMETRIC_CIELAB;
|
|
else if( tw->im->Type == IM_TYPE_CMYK ) {
|
|
photometric = PHOTOMETRIC_SEPARATED;
|
|
TIFFSetField( tif,
|
|
TIFFTAG_INKSET, INKSET_CMYK );
|
|
}
|
|
else
|
|
photometric = PHOTOMETRIC_RGB;
|
|
|
|
if( tw->im->Type != IM_TYPE_CMYK &&
|
|
tw->im->Bands == 4 ) {
|
|
v[0] = EXTRASAMPLE_ASSOCALPHA;
|
|
TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v );
|
|
}
|
|
break;
|
|
|
|
case 5:
|
|
if( tw->im->Type == IM_TYPE_CMYK ) {
|
|
photometric = PHOTOMETRIC_SEPARATED;
|
|
TIFFSetField( tif,
|
|
TIFFTAG_INKSET, INKSET_CMYK );
|
|
}
|
|
break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
|
|
TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric );
|
|
}
|
|
|
|
/* Layout.
|
|
*/
|
|
if( tw->tile ) {
|
|
TIFFSetField( tif, TIFFTAG_TILEWIDTH, tw->tilew );
|
|
TIFFSetField( tif, TIFFTAG_TILELENGTH, tw->tileh );
|
|
}
|
|
else
|
|
TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, 16 );
|
|
|
|
/* Sample format ... for float, we write IEEE.
|
|
*/
|
|
if( tw->im->BandFmt == IM_BANDFMT_FLOAT )
|
|
TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Free a pyramid layer.
|
|
*/
|
|
static void
|
|
free_layer( PyramidLayer *layer )
|
|
{
|
|
int i;
|
|
|
|
for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ )
|
|
if( layer->tiles[i].tile ) {
|
|
im_region_free( layer->tiles[i].tile );
|
|
layer->tiles[i].tile = NULL;
|
|
}
|
|
|
|
/* And close the TIFF file we are writing to.
|
|
*/
|
|
if( layer->tbuf ) {
|
|
im_free( layer->tbuf );
|
|
layer->tbuf = NULL;
|
|
}
|
|
if( layer->tif ) {
|
|
TIFFClose( layer->tif );
|
|
layer->tif = NULL;
|
|
}
|
|
}
|
|
|
|
/* Free an entire pyramid.
|
|
*/
|
|
static void
|
|
free_pyramid( PyramidLayer *layer )
|
|
{
|
|
if( layer->below )
|
|
free_pyramid( layer->below );
|
|
|
|
free_layer( layer );
|
|
}
|
|
|
|
/* Make a name for a new TIFF layer. Base it on sub factor.
|
|
*/
|
|
static char *
|
|
new_tiff_name( TiffWrite *tw, char *name, int sub )
|
|
{
|
|
char buf[FILENAME_MAX];
|
|
char buf2[FILENAME_MAX];
|
|
|
|
/* Remove existing .tif/.tiff suffix, if any.
|
|
*/
|
|
strcpy( buf, name );
|
|
if( im_ispostfix( buf, ".tif" ) )
|
|
buf[strlen( buf ) - 4] = '\0';
|
|
if( im_ispostfix( buf, ".tiff" ) )
|
|
buf[strlen( buf ) - 5] = '\0';
|
|
|
|
im_snprintf( buf2, FILENAME_MAX, "%s.%d.tif", buf, sub );
|
|
|
|
return( im_strdup( tw->im, buf2 ) );
|
|
}
|
|
|
|
/* Build a pyramid. w & h are size of layer above this layer. Write new layer
|
|
* struct into *zap, return 0/-1 for success/fail.
|
|
*/
|
|
static int
|
|
build_pyramid( TiffWrite *tw, PyramidLayer *above,
|
|
PyramidLayer **zap, int w, int h )
|
|
{
|
|
PyramidLayer *layer = IM_NEW( tw->im, PyramidLayer );
|
|
int i;
|
|
|
|
if( !layer )
|
|
return( -1 );
|
|
layer->tw = tw;
|
|
layer->width = w / 2;
|
|
layer->height = h / 2;
|
|
|
|
if( !above )
|
|
/* Top of pyramid.
|
|
*/
|
|
layer->sub = 2;
|
|
else
|
|
layer->sub = above->sub * 2;
|
|
|
|
layer->lname = NULL;
|
|
layer->tif = NULL;
|
|
layer->tbuf = NULL;
|
|
|
|
for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) {
|
|
layer->tiles[i].tile = NULL;
|
|
layer->tiles[i].bits = PYR_NONE;
|
|
}
|
|
|
|
layer->below = NULL;
|
|
layer->above = above;
|
|
|
|
/* Save layer, to make sure it gets freed properly.
|
|
*/
|
|
*zap = layer;
|
|
|
|
if( layer->width > tw->tilew || layer->height > tw->tileh )
|
|
if( build_pyramid( tw, layer,
|
|
&layer->below, layer->width, layer->height ) )
|
|
return( -1 );
|
|
|
|
if( !(layer->lname = new_tiff_name( tw, tw->name, layer->sub )) )
|
|
return( -1 );
|
|
|
|
/* Make output image.
|
|
*/
|
|
if( !(layer->tif = tiff_openout( layer->lname )) )
|
|
return( -1 );
|
|
|
|
/* Write the TIFF header for this layer.
|
|
*/
|
|
if( write_tiff_header( tw, layer->tif, layer->width, layer->height ) )
|
|
return( -1 );
|
|
|
|
if( !(layer->tbuf = im_malloc( NULL, TIFFTileSize( layer->tif ) )) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Pick a new tile to write to in this layer. Either reuse a tile we have
|
|
* previously filled, or make a new one.
|
|
*/
|
|
static int
|
|
find_new_tile( PyramidLayer *layer )
|
|
{
|
|
int i;
|
|
|
|
/* Exisiting buffer we have finished with?
|
|
*/
|
|
for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ )
|
|
if( layer->tiles[i].bits == PYR_ALL )
|
|
return( i );
|
|
|
|
/* Have to make a new one.
|
|
*/
|
|
for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ )
|
|
if( !layer->tiles[i].tile ) {
|
|
if( !(layer->tiles[i].tile =
|
|
im_region_create( layer->tw->im )) )
|
|
return( -1 );
|
|
return( i );
|
|
}
|
|
|
|
/* Out of space!
|
|
*/
|
|
im_error( "im_vips2tiff", "%s", _( "layer buffer exhausted -- "
|
|
"try making TIFF output tiles smaller" ) );
|
|
|
|
return( -1 );
|
|
}
|
|
|
|
/* Find a tile in the layer buffer - if it's not there, make a new one.
|
|
*/
|
|
static int
|
|
find_tile( PyramidLayer *layer, Rect *pos )
|
|
{
|
|
int i;
|
|
Rect quad;
|
|
Rect image;
|
|
Rect inter;
|
|
|
|
/* Do we have a REGION for this position?
|
|
*/
|
|
for( i = 0; i < IM_MAX_LAYER_BUFFER; i++ ) {
|
|
REGION *reg = layer->tiles[i].tile;
|
|
|
|
if( reg && reg->valid.left == pos->left &&
|
|
reg->valid.top == pos->top )
|
|
return( i );
|
|
}
|
|
|
|
/* Make a new one.
|
|
*/
|
|
if( (i = find_new_tile( layer )) < 0 )
|
|
return( -1 );
|
|
if( im_region_buffer( layer->tiles[i].tile, pos ) )
|
|
return( -1 );
|
|
layer->tiles[i].bits = PYR_NONE;
|
|
|
|
/* Do any quadrants of this tile fall entirely outside the image?
|
|
* If they do, set their bits now.
|
|
*/
|
|
quad.width = layer->tw->tilew / 2;
|
|
quad.height = layer->tw->tileh / 2;
|
|
image.left = 0;
|
|
image.top = 0;
|
|
image.width = layer->width;
|
|
image.height = layer->height;
|
|
|
|
quad.left = pos->left;
|
|
quad.top = pos->top;
|
|
im_rect_intersectrect( &quad, &image, &inter );
|
|
if( im_rect_isempty( &inter ) )
|
|
layer->tiles[i].bits |= PYR_TL;
|
|
|
|
quad.left = pos->left + quad.width;
|
|
quad.top = pos->top;
|
|
im_rect_intersectrect( &quad, &image, &inter );
|
|
if( im_rect_isempty( &inter ) )
|
|
layer->tiles[i].bits |= PYR_TR;
|
|
|
|
quad.left = pos->left;
|
|
quad.top = pos->top + quad.height;
|
|
im_rect_intersectrect( &quad, &image, &inter );
|
|
if( im_rect_isempty( &inter ) )
|
|
layer->tiles[i].bits |= PYR_BL;
|
|
|
|
quad.left = pos->left + quad.width;
|
|
quad.top = pos->top + quad.height;
|
|
im_rect_intersectrect( &quad, &image, &inter );
|
|
if( im_rect_isempty( &inter ) )
|
|
layer->tiles[i].bits |= PYR_BR;
|
|
|
|
return( i );
|
|
}
|
|
|
|
/* Shrink a region by a factor of two, writing the result to a specified
|
|
* offset in another region. IM_CODING_LABQ only.
|
|
*/
|
|
static void
|
|
shrink_region_labpack( REGION *from, Rect *area,
|
|
REGION *to, int xoff, int yoff )
|
|
{
|
|
int ls = IM_REGION_LSKIP( from );
|
|
Rect *t = &to->valid;
|
|
|
|
int x, y;
|
|
Rect out;
|
|
|
|
/* Calculate output size and position.
|
|
*/
|
|
out.left = t->left + xoff;
|
|
out.top = t->top + yoff;
|
|
out.width = area->width / 2;
|
|
out.height = area->height / 2;
|
|
|
|
/* Shrink ... ignore the extension byte for speed.
|
|
*/
|
|
for( y = 0; y < out.height; y++ ) {
|
|
PEL *p = (PEL *)
|
|
IM_REGION_ADDR( from, area->left, area->top + y * 2 );
|
|
PEL *q = (PEL *)
|
|
IM_REGION_ADDR( to, out.left, out.top + y );
|
|
|
|
for( x = 0; x < out.width; x++ ) {
|
|
signed char *sp = (signed char *) p;
|
|
unsigned char *up = (unsigned char *) p;
|
|
|
|
int l = up[0] + up[4] +
|
|
up[ls] + up[ls + 4];
|
|
int a = sp[1] + sp[5] +
|
|
sp[ls + 1] + sp[ls + 5];
|
|
int b = sp[2] + sp[6] +
|
|
sp[ls + 2] + sp[ls + 6];
|
|
|
|
q[0] = l >> 2;
|
|
q[1] = a >> 2;
|
|
q[2] = b >> 2;
|
|
q[3] = 0;
|
|
|
|
q += 4;
|
|
p += 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
#define SHRINK_TYPE_INT( TYPE ) \
|
|
for( x = 0; x < out.width; x++ ) { \
|
|
TYPE *tp = (TYPE *) p; \
|
|
TYPE *tp1 = (TYPE *) (p + ls); \
|
|
TYPE *tq = (TYPE *) q; \
|
|
\
|
|
for( z = 0; z < nb; z++ ) { \
|
|
int tot = tp[z] + tp[z + nb] + \
|
|
tp1[z] + tp1[z + nb]; \
|
|
\
|
|
tq[z] = tot >> 2; \
|
|
} \
|
|
\
|
|
/* Move on two pels in input. \
|
|
*/ \
|
|
p += ps << 1; \
|
|
q += ps; \
|
|
}
|
|
|
|
#define SHRINK_TYPE_FLOAT( TYPE ) \
|
|
for( x = 0; x < out.width; x++ ) { \
|
|
TYPE *tp = (TYPE *) p; \
|
|
TYPE *tp1 = (TYPE *) (p + ls); \
|
|
TYPE *tq = (TYPE *) q; \
|
|
\
|
|
for( z = 0; z < nb; z++ ) { \
|
|
double tot = (double) tp[z] + tp[z + nb] + \
|
|
tp1[z] + tp1[z + nb]; \
|
|
\
|
|
tq[z] = tot / 4; \
|
|
} \
|
|
\
|
|
/* Move on two pels in input. \
|
|
*/ \
|
|
p += ps << 1; \
|
|
q += ps; \
|
|
}
|
|
|
|
/* Shrink a region by a factor of two, writing the result to a specified
|
|
* offset in another region. n-band, non-complex.
|
|
*/
|
|
static void
|
|
shrink_region( REGION *from, Rect *area,
|
|
REGION *to, int xoff, int yoff )
|
|
{
|
|
int ls = IM_REGION_LSKIP( from );
|
|
int ps = IM_IMAGE_SIZEOF_PEL( from->im );
|
|
int nb = from->im->Bands;
|
|
Rect *t = &to->valid;
|
|
|
|
int x, y, z;
|
|
Rect out;
|
|
|
|
/* Calculate output size and position.
|
|
*/
|
|
out.left = t->left + xoff;
|
|
out.top = t->top + yoff;
|
|
out.width = area->width / 2;
|
|
out.height = area->height / 2;
|
|
|
|
for( y = 0; y < out.height; y++ ) {
|
|
PEL *p = (PEL *)
|
|
IM_REGION_ADDR( from, area->left, area->top + y * 2 );
|
|
PEL *q = (PEL *)
|
|
IM_REGION_ADDR( to, out.left, out.top + y );
|
|
|
|
/* Process this line of pels.
|
|
*/
|
|
switch( from->im->BandFmt ) {
|
|
case IM_BANDFMT_UCHAR:
|
|
SHRINK_TYPE_INT( unsigned char ); break;
|
|
case IM_BANDFMT_CHAR:
|
|
SHRINK_TYPE_INT( signed char ); break;
|
|
case IM_BANDFMT_USHORT:
|
|
SHRINK_TYPE_INT( unsigned short ); break;
|
|
case IM_BANDFMT_SHORT:
|
|
SHRINK_TYPE_INT( signed short ); break;
|
|
case IM_BANDFMT_UINT:
|
|
SHRINK_TYPE_INT( unsigned int ); break;
|
|
case IM_BANDFMT_INT:
|
|
SHRINK_TYPE_INT( signed int ); break;
|
|
case IM_BANDFMT_FLOAT:
|
|
SHRINK_TYPE_FLOAT( float ); break;
|
|
case IM_BANDFMT_DOUBLE:
|
|
SHRINK_TYPE_FLOAT( double ); break;
|
|
|
|
default:
|
|
assert( 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Write a tile from a layer.
|
|
*/
|
|
static int
|
|
save_tile( TiffWrite *tw, TIFF *tif, PEL *tbuf, REGION *reg, Rect *area )
|
|
{
|
|
/* Have to repack pixels.
|
|
*/
|
|
pack2tiff( tw, reg, tbuf, area );
|
|
|
|
#ifdef DEBUG
|
|
printf( "Writing %dx%d pixels at position %dx%d to image %s\n",
|
|
tw->tilew, tw->tileh, area->left, area->top,
|
|
TIFFFileName( tif ) );
|
|
#endif /*DEBUG*/
|
|
|
|
/* Write to TIFF! easy.
|
|
*/
|
|
if( TIFFWriteTile( tif, tbuf, area->left, area->top, 0, 0 ) < 0 ) {
|
|
im_error( "im_vips2tiff", "%s", _( "TIFF write tile failed" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* A new tile has arrived! Shrink into this layer, if we fill a region, write
|
|
* it and recurse.
|
|
*/
|
|
static int
|
|
new_tile( PyramidLayer *layer, REGION *tile, Rect *area )
|
|
{
|
|
TiffWrite *tw = layer->tw;
|
|
int xoff, yoff;
|
|
|
|
int t, ri, bo;
|
|
Rect out, new;
|
|
PyramidBits bit;
|
|
|
|
/* Calculate pos and size of new pixels we make inside this layer.
|
|
*/
|
|
new.left = area->left / 2;
|
|
new.top = area->top / 2;
|
|
new.width = area->width / 2;
|
|
new.height = area->height / 2;
|
|
|
|
/* Has size fallen to zero? Can happen if this is a one-pixel-wide
|
|
* strip.
|
|
*/
|
|
if( im_rect_isempty( &new ) )
|
|
return( 0 );
|
|
|
|
/* Offset into this tile ... ie. which quadrant we are writing.
|
|
*/
|
|
xoff = new.left % layer->tw->tilew;
|
|
yoff = new.top % layer->tw->tileh;
|
|
|
|
/* Calculate pos for tile we shrink into in this layer.
|
|
*/
|
|
out.left = new.left - xoff;
|
|
out.top = new.top - yoff;
|
|
|
|
/* Clip against edge of image.
|
|
*/
|
|
ri = IM_MIN( layer->width, out.left + layer->tw->tilew );
|
|
bo = IM_MIN( layer->height, out.top + layer->tw->tileh );
|
|
out.width = ri - out.left;
|
|
out.height = bo - out.top;
|
|
|
|
if( (t = find_tile( layer, &out )) < 0 )
|
|
return( -1 );
|
|
|
|
/* Shrink into place.
|
|
*/
|
|
if( tw->im->Coding == IM_CODING_NONE )
|
|
shrink_region( tile, area,
|
|
layer->tiles[t].tile, xoff, yoff );
|
|
else
|
|
shrink_region_labpack( tile, area,
|
|
layer->tiles[t].tile, xoff, yoff );
|
|
|
|
/* Set that bit.
|
|
*/
|
|
if( xoff )
|
|
if( yoff )
|
|
bit = PYR_BR;
|
|
else
|
|
bit = PYR_TR;
|
|
else
|
|
if( yoff )
|
|
bit = PYR_BL;
|
|
else
|
|
bit = PYR_TL;
|
|
if( layer->tiles[t].bits & bit ) {
|
|
im_error( "im_vips2tiff",
|
|
"%s", _( "internal error #9876345" ) );
|
|
return( -1 );
|
|
}
|
|
layer->tiles[t].bits |= bit;
|
|
|
|
if( layer->tiles[t].bits == PYR_ALL ) {
|
|
/* Save this complete tile.
|
|
*/
|
|
if( save_tile( tw, layer->tif, layer->tbuf,
|
|
layer->tiles[t].tile, &layer->tiles[t].tile->valid ) )
|
|
return( -1 );
|
|
|
|
/* And recurse down the pyramid!
|
|
*/
|
|
if( layer->below &&
|
|
new_tile( layer->below,
|
|
layer->tiles[t].tile,
|
|
&layer->tiles[t].tile->valid ) )
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Write as tiles.
|
|
*/
|
|
static int
|
|
write_tif_tile( TiffWrite *tw )
|
|
{
|
|
IMAGE *im = tw->im;
|
|
Rect area;
|
|
int x, y;
|
|
|
|
if( !(tw->tbuf = im_malloc( NULL, TIFFTileSize( tw->tif ) )) )
|
|
return( -1 );
|
|
|
|
/* Write pyramid too? Only bother if bigger than tile size.
|
|
*/
|
|
if( tw->pyramid &&
|
|
(im->Xsize > tw->tilew || im->Ysize > tw->tileh) &&
|
|
build_pyramid( tw, NULL, &tw->layer, im->Xsize, im->Ysize ) )
|
|
return( -1 );
|
|
|
|
for( y = 0; y < im->Ysize; y += tw->tileh )
|
|
for( x = 0; x < im->Xsize; x += tw->tilew ) {
|
|
/* Set up rect we write.
|
|
*/
|
|
area.left = x;
|
|
area.top = y;
|
|
area.width = IM_MIN( tw->tilew, im->Xsize - x );
|
|
area.height = IM_MIN( tw->tileh, im->Ysize - y );
|
|
|
|
if( im_prepare_thread( tw->tg, tw->reg, &area ) )
|
|
return( -1 );
|
|
|
|
/* Write to TIFF.
|
|
*/
|
|
if( save_tile( tw, tw->tif, tw->tbuf, tw->reg, &area ) )
|
|
return( -1 );
|
|
|
|
/* Is there a pyramid? Write to that too.
|
|
*/
|
|
if( tw->layer && new_tile( tw->layer, tw->reg, &area ) )
|
|
return( -1 );
|
|
|
|
/* Trigger any eval callbacks on our source image and
|
|
* check for errors.
|
|
*/
|
|
if( im__handle_eval( im, area.width, area.height ) ||
|
|
im_threadgroup_iserror( tw->tg ) )
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static int
|
|
write_tif_block( REGION *region, Rect *area, void *a, void *b )
|
|
{
|
|
TiffWrite *tw = (TiffWrite *) a;
|
|
IMAGE *im = tw->im;
|
|
|
|
int y;
|
|
|
|
for( y = 0; y < area->height; y++ ) {
|
|
PEL *p = (PEL *) IM_REGION_ADDR( region, 0, area->top + y );
|
|
|
|
/* Any repacking necessary.
|
|
*/
|
|
if( im->Coding == IM_CODING_LABQ ) {
|
|
LabQ2LabC( tw->tbuf, p, im->Xsize );
|
|
p = tw->tbuf;
|
|
}
|
|
else if( im->BandFmt == IM_BANDFMT_SHORT &&
|
|
im->Type == IM_TYPE_LABS ) {
|
|
LabS2Lab16( tw->tbuf, p, im->Xsize );
|
|
p = tw->tbuf;
|
|
}
|
|
else if( tw->onebit ) {
|
|
eightbit2onebit( tw->tbuf, p, im->Xsize );
|
|
p = tw->tbuf;
|
|
}
|
|
|
|
if( TIFFWriteScanline( tw->tif, p, area->top + y, 0 ) < 0 )
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Write as scan-lines.
|
|
*/
|
|
static int
|
|
write_tif_strip( TiffWrite *tw )
|
|
{
|
|
g_assert( !tw->tbuf );
|
|
|
|
if( !(tw->tbuf = im_malloc( NULL, TIFFScanlineSize( tw->tif ) )) )
|
|
return( -1 );
|
|
|
|
if( im_wbuffer( tw->tg, write_tif_block, tw, NULL ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Delete any temp files we wrote.
|
|
*/
|
|
static void
|
|
delete_files( TiffWrite *tw )
|
|
{
|
|
PyramidLayer *layer = tw->layer;
|
|
|
|
if( tw->bname ) {
|
|
unlink( tw->bname );
|
|
tw->bname = NULL;
|
|
}
|
|
|
|
for( layer = tw->layer; layer; layer = layer->below )
|
|
if( layer->lname ) {
|
|
unlink( layer->lname );
|
|
layer->lname = NULL;
|
|
}
|
|
}
|
|
|
|
/* Free a TiffWrite.
|
|
*/
|
|
static void
|
|
free_tiff_write( TiffWrite *tw )
|
|
{
|
|
#ifndef DEBUG
|
|
delete_files( tw );
|
|
#endif /*DEBUG*/
|
|
|
|
if( tw->tg ) {
|
|
im_threadgroup_free( tw->tg );
|
|
tw->tg = NULL;
|
|
}
|
|
if( tw->reg ) {
|
|
im_region_free( tw->reg );
|
|
tw->reg = NULL;
|
|
}
|
|
if( tw->tif ) {
|
|
TIFFClose( tw->tif );
|
|
tw->tif = NULL;
|
|
}
|
|
if( tw->tbuf ) {
|
|
im_free( tw->tbuf );
|
|
tw->tbuf = NULL;
|
|
}
|
|
if( tw->layer )
|
|
free_pyramid( tw->layer );
|
|
if( tw->icc_profile ) {
|
|
im_free( tw->icc_profile );
|
|
tw->icc_profile = NULL;
|
|
}
|
|
}
|
|
|
|
/* Round N down to P boundary.
|
|
*/
|
|
#define ROUND_DOWN(N,P) ((N) - ((N) % P))
|
|
|
|
/* Round N up to P boundary.
|
|
*/
|
|
#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
|
|
|
/* Make and init a TiffWrite.
|
|
*/
|
|
static TiffWrite *
|
|
make_tiff_write( IMAGE *im, const char *filename )
|
|
{
|
|
TiffWrite *tw;
|
|
char *p, *q, *r;
|
|
char name[FILENAME_MAX];
|
|
char mode[FILENAME_MAX];
|
|
char buf[FILENAME_MAX];
|
|
|
|
if( !(tw = IM_NEW( im, TiffWrite )) )
|
|
return( NULL );
|
|
tw->im = im;
|
|
im_filename_split( filename, name, mode );
|
|
tw->name = im_strdup( im, name );
|
|
tw->mode = im_strdup( im, mode );
|
|
tw->tg = NULL;
|
|
tw->reg = NULL;
|
|
tw->bname = NULL;
|
|
tw->tif = NULL;
|
|
tw->layer = NULL;
|
|
tw->tbuf = NULL;
|
|
tw->compression = COMPRESSION_NONE;
|
|
tw->jpqual = 75;
|
|
tw->predictor = -1;
|
|
tw->tile = 0;
|
|
tw->tilew = 128;
|
|
tw->tileh = 128;
|
|
tw->pyramid = 0;
|
|
tw->onebit = 0;
|
|
tw->embed = 0;
|
|
tw->icc_profile = NULL;
|
|
|
|
/* Output resolution settings ... default to VIPS-alike.
|
|
*/
|
|
tw->resunit = RESUNIT_CENTIMETER;
|
|
tw->xres = im->Xres * 10;
|
|
tw->yres = im->Yres * 10;
|
|
if( !im_meta_get_string( im, IM_META_RESOLUTION_UNIT, &p ) &&
|
|
strcmp( p, "in" ) == 0 ) {
|
|
tw->resunit = RESUNIT_INCH;
|
|
tw->xres *= 2.54;
|
|
tw->yres *= 2.54;
|
|
}
|
|
|
|
/* Parse mode string.
|
|
*/
|
|
strcpy( buf, mode );
|
|
p = &buf[0];
|
|
if( (q = im_getnextoption( &p )) ) {
|
|
if( im_isprefix( "none", q ) )
|
|
tw->compression = COMPRESSION_NONE;
|
|
else if( im_isprefix( "packbits", q ) )
|
|
tw->compression = COMPRESSION_PACKBITS;
|
|
else if( im_isprefix( "ccittfax4", q ) )
|
|
tw->compression = COMPRESSION_CCITTFAX4;
|
|
else if( im_isprefix( "lzw", q ) ) {
|
|
tw->compression = COMPRESSION_LZW;
|
|
|
|
if( (r = im_getsuboption( q )) )
|
|
if( sscanf( r, "%d", &tw->predictor ) != 1 ) {
|
|
im_error( "im_vips2tiff",
|
|
"%s", _( "bad predictor "
|
|
"parameter" ) );
|
|
return( NULL );
|
|
}
|
|
}
|
|
else if( im_isprefix( "deflate", q ) ) {
|
|
tw->compression = COMPRESSION_ADOBE_DEFLATE;
|
|
|
|
if( (r = im_getsuboption( q )) )
|
|
if( sscanf( r, "%d", &tw->predictor ) != 1 ) {
|
|
im_error( "im_vips2tiff",
|
|
"%s", _( "bad predictor "
|
|
"parameter" ) );
|
|
return( NULL );
|
|
}
|
|
}
|
|
else if( im_isprefix( "jpeg", q ) ) {
|
|
tw->compression = COMPRESSION_JPEG;
|
|
|
|
if( (r = im_getsuboption( q )) )
|
|
if( sscanf( r, "%d", &tw->jpqual ) != 1 ) {
|
|
im_error( "im_vips2tiff",
|
|
"%s", _( "bad JPEG quality "
|
|
"parameter" ) );
|
|
return( NULL );
|
|
}
|
|
}
|
|
else {
|
|
im_error( "im_vips2tiff", _( "unknown compression mode "
|
|
"\"%s\"\nshould be one of \"none\", "
|
|
"\"packbits\", \"ccittfax4\", \"lzw\", "
|
|
"\"deflate\" or \"jpeg\"" ), q );
|
|
return( NULL );
|
|
}
|
|
}
|
|
if( (q = im_getnextoption( &p )) ) {
|
|
if( im_isprefix( "tile", q ) ) {
|
|
tw->tile = 1;
|
|
|
|
if( (r = im_getsuboption( q )) ) {
|
|
if( sscanf( r, "%dx%d",
|
|
&tw->tilew, &tw->tileh ) != 2 ) {
|
|
im_error( "im_vips2tiff", "%s",
|
|
_( "bad tile sizes" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
if( tw->tilew < 10 || tw->tileh < 10 ||
|
|
tw->tilew > 1000 || tw->tileh > 1000 ) {
|
|
im_error( "im_vips2tiff",
|
|
_( "bad tile size %dx%d" ),
|
|
tw->tilew, tw->tileh );
|
|
return( NULL );
|
|
}
|
|
|
|
if( (tw->tilew & 0xf) != 0 ||
|
|
(tw->tileh & 0xf) != 0 ) {
|
|
im_error( "im_vips2tiff", "%s",
|
|
_( "tile size not a "
|
|
"multiple of 16" ) );
|
|
return( NULL );
|
|
}
|
|
}
|
|
}
|
|
else if( im_isprefix( "strip", q ) )
|
|
tw->tile = 0;
|
|
else {
|
|
im_error( "im_vips2tiff", _( "unknown layout mode "
|
|
"\"%s\"\nshould be one of \"tile\" or "
|
|
"\"strip\"" ), q );
|
|
return( NULL );
|
|
}
|
|
}
|
|
if( (q = im_getnextoption( &p )) ) {
|
|
if( im_isprefix( "pyramid", q ) )
|
|
tw->pyramid = 1;
|
|
else if( im_isprefix( "flat", q ) )
|
|
tw->pyramid = 0;
|
|
else {
|
|
im_error( "im_vips2tiff", _( "unknown multi-res mode "
|
|
"\"%s\"\nshould be one of \"flat\" or "
|
|
"\"pyramid\"" ), q );
|
|
return( NULL );
|
|
}
|
|
}
|
|
if( (q = im_getnextoption( &p )) ) {
|
|
if( im_isprefix( "onebit", q ) )
|
|
tw->onebit = 1;
|
|
else if( im_isprefix( "manybit", q ) )
|
|
tw->onebit = 0;
|
|
else {
|
|
im_error( "im_vips2tiff", _( "unknown format "
|
|
"\"%s\"\nshould be one of \"onebit\" or "
|
|
"\"manybit\"" ), q );
|
|
return( NULL );
|
|
}
|
|
}
|
|
if( (q = im_getnextoption( &p )) ) {
|
|
if( im_isprefix( "res_cm", q ) ) {
|
|
if( tw->resunit == RESUNIT_INCH ) {
|
|
tw->xres /= 2.54;
|
|
tw->yres /= 2.54;
|
|
}
|
|
tw->resunit = RESUNIT_CENTIMETER;
|
|
}
|
|
else if( im_isprefix( "res_inch", q ) ) {
|
|
if( tw->resunit == RESUNIT_CENTIMETER ) {
|
|
tw->xres *= 2.54;
|
|
tw->yres *= 2.54;
|
|
}
|
|
tw->resunit = RESUNIT_INCH;
|
|
}
|
|
else {
|
|
im_error( "im_vips2tiff", _( "unknown resolution unit "
|
|
"\"%s\"\nshould be one of \"res_cm\" or "
|
|
"\"res_inch\"" ), q );
|
|
return( NULL );
|
|
}
|
|
|
|
if( (r = im_getsuboption( q )) ) {
|
|
if( sscanf( r, "%fx%f", &tw->xres, &tw->yres ) != 2 ) {
|
|
if( sscanf( r, "%f", &tw->xres ) != 1 ) {
|
|
im_error( "im_vips2tiff", "%s",
|
|
_( "bad resolution values" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
tw->yres = tw->xres;
|
|
}
|
|
}
|
|
}
|
|
if( (q = im_getnextoption( &p )) && strcmp( q, "" ) != 0 ) {
|
|
tw->embed = 1;
|
|
tw->icc_profile = im_strdup( NULL, q );
|
|
}
|
|
if( (q = im_getnextoption( &p )) ) {
|
|
im_error( "im_vips2tiff",
|
|
_( "unknown extra options \"%s\"" ), q );
|
|
return( NULL );
|
|
}
|
|
if( !tw->tile && tw->pyramid ) {
|
|
im_warn( "im_vips2tiff", "%s", _( "can't have strip pyramid -- "
|
|
"enabling tiling" ) );
|
|
tw->tile = 1;
|
|
}
|
|
|
|
/* We can only pyramid LABQ and non-complex images.
|
|
*/
|
|
if( tw->pyramid ) {
|
|
if( im->Coding == IM_CODING_NONE && im_iscomplex( im ) ) {
|
|
im_error( "im_vips2tiff",
|
|
"%s", _( "can only pyramid LABQ and "
|
|
"non-complex images" ) );
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
/* Only 1-bit-ize 8 bit mono images.
|
|
*/
|
|
if( tw->onebit ) {
|
|
if( im->Coding != IM_CODING_NONE ||
|
|
im->BandFmt != IM_BANDFMT_UCHAR ||
|
|
im->Bands != 1 )
|
|
tw->onebit = 0;
|
|
}
|
|
|
|
if( tw->onebit && tw->compression == COMPRESSION_JPEG ) {
|
|
im_warn( "im_vips2tiff", "%s", _( "can't have 1-bit JPEG -- "
|
|
"disabling JPEG" ) );
|
|
tw->compression = COMPRESSION_NONE;
|
|
}
|
|
|
|
/* Make region and threadgroup.
|
|
*/
|
|
if( !(tw->reg = im_region_create( tw->im )) ||
|
|
!(tw->tg = im_threadgroup_create( tw->im )) ) {
|
|
free_tiff_write( tw );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Sizeof a line of bytes in the TIFF tile.
|
|
*/
|
|
if( im->Coding == IM_CODING_LABQ )
|
|
tw->tls = tw->tilew * 3;
|
|
else if( tw->onebit )
|
|
tw->tls = ROUND_UP( tw->tilew, 8 ) / 8;
|
|
else
|
|
tw->tls = IM_IMAGE_SIZEOF_PEL( im ) * tw->tilew;
|
|
|
|
return( tw );
|
|
}
|
|
|
|
/* Copy fields.
|
|
*/
|
|
#define CopyField( tag, v ) \
|
|
if( TIFFGetField( in, tag, &v ) ) TIFFSetField( out, tag, v )
|
|
|
|
/* Copy a TIFF file ... we know we wrote it, so just copy the tags we know
|
|
* we might have set.
|
|
*/
|
|
static int
|
|
tiff_copy( TiffWrite *tw, TIFF *out, TIFF *in )
|
|
{
|
|
uint32 i32;
|
|
uint16 i16;
|
|
float f;
|
|
tdata_t buf;
|
|
ttile_t tile;
|
|
ttile_t n;
|
|
|
|
/* All the fields we might have set.
|
|
*/
|
|
CopyField( TIFFTAG_IMAGEWIDTH, i32 );
|
|
CopyField( TIFFTAG_IMAGELENGTH, i32 );
|
|
CopyField( TIFFTAG_PLANARCONFIG, i16 );
|
|
CopyField( TIFFTAG_ORIENTATION, i16 );
|
|
CopyField( TIFFTAG_XRESOLUTION, f );
|
|
CopyField( TIFFTAG_YRESOLUTION, f );
|
|
CopyField( TIFFTAG_RESOLUTIONUNIT, i16 );
|
|
CopyField( TIFFTAG_COMPRESSION, i16 );
|
|
CopyField( TIFFTAG_SAMPLESPERPIXEL, i16 );
|
|
CopyField( TIFFTAG_BITSPERSAMPLE, i16 );
|
|
CopyField( TIFFTAG_PHOTOMETRIC, i16 );
|
|
CopyField( TIFFTAG_TILEWIDTH, i32 );
|
|
CopyField( TIFFTAG_TILELENGTH, i32 );
|
|
CopyField( TIFFTAG_ROWSPERSTRIP, i32 );
|
|
|
|
if( tw->predictor != -1 )
|
|
TIFFSetField( out, TIFFTAG_PREDICTOR, tw->predictor );
|
|
|
|
/* TIFFTAG_JPEGQUALITY is a pesudo-tag, so we can't copy it.
|
|
* Set explicitly from TiffWrite.
|
|
*/
|
|
if( tw->compression == COMPRESSION_JPEG )
|
|
TIFFSetField( out, TIFFTAG_JPEGQUALITY, tw->jpqual );
|
|
|
|
/* We can't copy profiles :( Set again from TiffWrite.
|
|
*/
|
|
if( embed_profile( tw, out ) )
|
|
return( -1 );
|
|
|
|
buf = im_malloc( NULL, TIFFTileSize( in ) );
|
|
n = TIFFNumberOfTiles( in );
|
|
for( tile = 0; tile < n; tile++ ) {
|
|
tsize_t len;
|
|
|
|
/* It'd be good to use TIFFReadRawTile()/TIFFWriteRawTile()
|
|
* here to save compression/decompression, but sadly it seems
|
|
* not to work :-( investigate at some point.
|
|
*/
|
|
len = TIFFReadEncodedTile( in, tile, buf, (tsize_t) -1 );
|
|
if( len < 0 ||
|
|
TIFFWriteEncodedTile( out, tile, buf, len ) < 0 ) {
|
|
im_free( buf );
|
|
return( -1 );
|
|
}
|
|
}
|
|
im_free( buf );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Append a file to a TIFF file.
|
|
*/
|
|
static int
|
|
tiff_append( TiffWrite *tw, TIFF *out, const char *name )
|
|
{
|
|
TIFF *in;
|
|
|
|
if( !(in = tiff_openin( name )) )
|
|
return( -1 );
|
|
|
|
if( tiff_copy( tw, out, in ) ) {
|
|
TIFFClose( in );
|
|
return( -1 );
|
|
}
|
|
TIFFClose( in );
|
|
|
|
if( !TIFFWriteDirectory( out ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Gather all of the files we wrote into single output file.
|
|
*/
|
|
static int
|
|
gather_pyramid( TiffWrite *tw )
|
|
{
|
|
PyramidLayer *layer;
|
|
TIFF *out;
|
|
|
|
#ifdef DEBUG
|
|
printf( "Starting pyramid gather ...\n" );
|
|
#endif /*DEBUG*/
|
|
|
|
if( !(out = tiff_openout( tw->name )) )
|
|
return( -1 );
|
|
|
|
if( tiff_append( tw, out, tw->bname ) ) {
|
|
TIFFClose( out );
|
|
return( -1 );
|
|
}
|
|
|
|
for( layer = tw->layer; layer; layer = layer->below )
|
|
if( tiff_append( tw, out, layer->lname ) ) {
|
|
TIFFClose( out );
|
|
return( -1 );
|
|
}
|
|
|
|
TIFFClose( out );
|
|
|
|
#ifdef DEBUG
|
|
printf( "Pyramid built\n" );
|
|
#endif /*DEBUG*/
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
int
|
|
im_vips2tiff( IMAGE *im, const char *filename )
|
|
{
|
|
TiffWrite *tw;
|
|
int res;
|
|
|
|
#ifdef DEBUG
|
|
printf( "im_tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() );
|
|
#endif /*DEBUG*/
|
|
|
|
/* Override the default TIFF error handler.
|
|
*/
|
|
TIFFSetErrorHandler( (TIFFErrorHandler) im__thandler_error );
|
|
TIFFSetWarningHandler( (TIFFErrorHandler) im__thandler_warning );
|
|
|
|
/* Check input image.
|
|
*/
|
|
if( im_pincheck( im ) )
|
|
return( -1 );
|
|
if( im->Coding != IM_CODING_LABQ && im->Coding != IM_CODING_NONE ) {
|
|
im_error( "im_vips2tiff", "%s", _( "unknown coding type" ) );
|
|
return( -1 );
|
|
}
|
|
if( im->BandFmt != IM_BANDFMT_UCHAR &&
|
|
!(im->BandFmt == IM_BANDFMT_SHORT &&
|
|
im->Type == IM_TYPE_LABS) &&
|
|
im->BandFmt != IM_BANDFMT_USHORT &&
|
|
im->BandFmt != IM_BANDFMT_FLOAT ) {
|
|
im_error( "im_vips2tiff", "%s",
|
|
_( "unsigned 8-bit int, 16-bit int, "
|
|
"and 32-bit float only" ) );
|
|
return( -1 );
|
|
}
|
|
if( im->Coding == IM_CODING_NONE ) {
|
|
if( im->Bands < 1 || im->Bands > 5 ) {
|
|
im_error( "im_vips2tiff", "%s",
|
|
_( "1 to 5 bands only" ) );
|
|
return( -1 );
|
|
}
|
|
}
|
|
|
|
/* Make output image. If this is a pyramid, write the base image to
|
|
* fred.1.tif rather than fred.tif.
|
|
*/
|
|
if( !(tw = make_tiff_write( im, filename )) )
|
|
return( -1 );
|
|
if( tw->pyramid ) {
|
|
if( !(tw->bname = new_tiff_name( tw, tw->name, 1 )) ||
|
|
!(tw->tif = tiff_openout( tw->bname )) ) {
|
|
free_tiff_write( tw );
|
|
return( -1 );
|
|
}
|
|
}
|
|
else {
|
|
/* No pyramid ... write straight to name.
|
|
*/
|
|
if( !(tw->tif = tiff_openout( tw->name )) ) {
|
|
free_tiff_write( tw );
|
|
return( -1 );
|
|
}
|
|
}
|
|
|
|
/* Write the TIFF header for the full-res file.
|
|
*/
|
|
if( write_tiff_header( tw, tw->tif, im->Xsize, im->Ysize ) ) {
|
|
free_tiff_write( tw );
|
|
return( -1 );
|
|
}
|
|
|
|
|
|
if( tw->tile ) {
|
|
/* The strip writer uses wbuffer and does start/end for us, we
|
|
* have our own loop for tile writing and so we need to call
|
|
* ourselves.
|
|
*/
|
|
if( im__start_eval( im ) ) {
|
|
free_tiff_write( tw );
|
|
return( -1 );
|
|
}
|
|
res = write_tif_tile( tw );
|
|
im__end_eval( im );
|
|
}
|
|
else
|
|
res = write_tif_strip( tw );
|
|
if( res ) {
|
|
free_tiff_write( tw );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Free pyramid resources ... this will TIFFClose() the intermediates,
|
|
* ready for us to read from them again.
|
|
*/
|
|
if( tw->layer )
|
|
free_pyramid( tw->layer );
|
|
if( tw->tif ) {
|
|
TIFFClose( tw->tif );
|
|
tw->tif = NULL;
|
|
}
|
|
|
|
/* Gather layers together into final pyramid file.
|
|
*/
|
|
if( tw->pyramid && gather_pyramid( tw ) ) {
|
|
free_tiff_write( tw );
|
|
return( -1 );
|
|
}
|
|
|
|
free_tiff_write( tw );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#endif /*HAVE_TIFF*/
|