libvips/libvips/foreign/vips2tiff.c
John Cupitt acbb77ac40 add "rgbjpeg" flag to tiffsave
add a secret "rgbjpeg" flag to tiffsave to help IIPImage server

iipimage server 0.9.9 and earlier don't handle YCbCr-coded JPEG tiffs
correctly ... setting --rgbjpeg makes vips output RGB coded JPEG tiff
which iipsrv can handle.

Using this flag makes files 2x to 3x larger with no quality improvement,
so it is tagged as deprecated to keep it hidden.
2014-01-26 10:43:32 +00:00

1622 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
* 4/2/10
* - gtkdoc
* 26/2/10
* - option to turn on bigtiff output
* 16/4/10
* - use vips_sink_*() instead of threadgroup and friends
* 22/6/10
* - make no-owner regions for the tile cache, since we share these
* between threads
* 12/7/11
* - use im__temp_name() for intermediates rather than polluting the
* output directory
* 5/9/11
* - enable YCbCr compression for jpeg write
* 23/11/11
* - set reduced-resolution subfile type on pyramid layers
* 2/12/11
* - make into a simple function call ready to be wrapped as a new-style
* VipsForeign class
* 21/3/12
* - bump max layer buffer up
* 2/6/12
* - copy jpeg pyramid in gather in RGB mode ... tiff4 doesn't do ycbcr
* mode
* 7/8/12
* - be more cautious enabling YCbCr mode
* 24/9/13
* - support many more vips formats, eg. complex, 32-bit int, any number
* of bands, etc., see the tiff loader
* 26/1/14
* - add RGB as well as YCbCr 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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
/*
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
*/
/*
#define DEBUG_VERBOSE
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#ifdef HAVE_TIFF
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif /*HAVE_UNISTD_H*/
#include <string.h>
#include <vips/vips.h>
#include <vips/internal.h>
#include <tiffio.h>
#include "tiff.h"
/* Max no of tiles we buffer in a layer. Enough to buffer a line of 64x64
* tiles on a 100k pixel across image.
*/
#define MAX_LAYER_BUFFER (10000)
/* 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 {
VipsRegion *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 */
VipsPel *tbuf; /* TIFF output buffer */
PyramidTile tiles[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 {
VipsImage *im; /* Original input image */
char *name; /* Final name we write to */
/* Read from im with these.
*/
VipsRegion *reg;
char *bname; /* Name for base layer */
TIFF *tif; /* Image we write to */
PyramidLayer *layer; /* Top of pyramid, if in use */
VipsPel *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 */
char *icc_profile; /* Profile to embed */
int bigtiff; /* True for bigtiff write */
int rgbjpeg; /* True for RGB not YCbCr */
GMutex *write_lock; /* Lock TIFF*() calls with this */
} TiffWrite;
/* Open TIFF for output.
*/
static TIFF *
tiff_openout( TiffWrite *tw, const char *name )
{
TIFF *tif;
const char *mode = tw->bigtiff ? "w8" : "w";
#ifdef DEBUG
printf( "TIFFOpen( \"%s\", \"%s\" )\n", name, mode );
#endif /*DEBUG*/
if( !(tif = TIFFOpen( name, mode )) ) {
vips_error( "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" )) ) {
vips_error( "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( VipsPel *q, VipsPel *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( VipsPel *q, VipsPel *p, int n )
{
int x;
VipsPel 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( VipsPel *q, VipsPel *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, VipsRegion *in, VipsPel *q, VipsRect *area )
{
int y;
for( y = area->top; y < VIPS_RECT_BOTTOM( area ); y++ ) {
VipsPel *p = (VipsPel *) VIPS_REGION_ADDR( in, area->left, y );
if( in->im->Coding == VIPS_CODING_LABQ )
LabQ2LabC( q, p, area->width );
else if( tw->onebit )
eightbit2onebit( q, p, area->width );
else if( in->im->BandFmt == VIPS_FORMAT_SHORT &&
in->im->Type == VIPS_INTERPRETATION_LABS )
LabS2Lab16( q, p, area->width );
else
memcpy( q, p,
area->width * VIPS_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 = vips__file_read_name( profile, VIPS_ICC_DIR, &length )) )
return( -1 );
TIFFSetField( tif, TIFFTAG_ICCPROFILE, length, buffer );
vips_free( buffer );
#ifdef DEBUG
printf( "vips2tiff: attached profile \"%s\"\n", profile );
#endif /*DEBUG*/
return( 0 );
}
/* Embed an ICC profile from VipsImage metadata.
*/
static int
embed_profile_meta( TIFF *tif, VipsImage *im )
{
void *data;
size_t data_length;
if( vips_image_get_blob( im, VIPS_META_ICC_NAME, &data, &data_length ) )
return( -1 );
TIFFSetField( tif, TIFFTAG_ICCPROFILE, data_length, data );
#ifdef DEBUG
printf( "vips2tiff: attached profile from meta\n" );
#endif /*DEBUG*/
return( 0 );
}
static int
embed_profile( TiffWrite *tw, TIFF *tif )
{
if( tw->icc_profile &&
strcmp( tw->icc_profile, "none" ) != 0 &&
embed_profile_file( tif, tw->icc_profile ) )
return( -1 );
if( !tw->icc_profile &&
vips_image_get_typeof( tw->im, VIPS_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 VipsImage we are
* writing (may have been shrunk!).
*/
static int
write_tiff_header( TiffWrite *tw, TIFF *tif, int width, int height )
{
uint16 v[1];
int format;
/* 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 );
if( tw->compression == COMPRESSION_JPEG )
TIFFSetField( tif, TIFFTAG_JPEGQUALITY, tw->jpqual );
if( tw->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
TIFFSetField( tif, TIFFTAG_PREDICTOR, tw->predictor );
/* Don't write mad resolutions (eg. zero), it confuses some programs.
*/
TIFFSetField( tif, TIFFTAG_RESOLUTIONUNIT, tw->resunit );
TIFFSetField( tif, TIFFTAG_XRESOLUTION,
VIPS_CLIP( 0.01, tw->xres, 10000 ) );
TIFFSetField( tif, TIFFTAG_YRESOLUTION,
VIPS_CLIP( 0.01, tw->yres, 10000 ) );
/* Attach ICC profile.
*/
if( embed_profile( tw, tif ) )
return( -1 );
/* And colour fields.
*/
if( tw->im->Coding == VIPS_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,
vips_format_sizeof( tw->im->BandFmt ) << 3 );
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:
/* could be: RGB, RGBA, CMYK, LAB, LABA, generic
* multi-band image.
*/
if( tw->im->Type == VIPS_INTERPRETATION_LAB ||
tw->im->Type == VIPS_INTERPRETATION_LABS )
photometric = PHOTOMETRIC_CIELAB;
else if( tw->im->Type == VIPS_INTERPRETATION_CMYK ) {
photometric = PHOTOMETRIC_SEPARATED;
TIFFSetField( tif,
TIFFTAG_INKSET, INKSET_CMYK );
}
else if( tw->compression == COMPRESSION_JPEG &&
tw->im->Bands == 3 &&
tw->im->BandFmt == VIPS_FORMAT_UCHAR &&
!tw->rgbjpeg ) {
/* This signals to libjpeg that it can do
* YCbCr chrominance subsampling from RGB, not
* that we will supply the image as YCbCr.
*/
photometric = PHOTOMETRIC_YCBCR;
TIFFSetField( tif, TIFFTAG_JPEGCOLORMODE,
JPEGCOLORMODE_RGB );
}
else
photometric = PHOTOMETRIC_RGB;
if( tw->im->Type != VIPS_INTERPRETATION_CMYK &&
tw->im->Bands == 4 ) {
v[0] = EXTRASAMPLE_ASSOCALPHA;
TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v );
}
break;
case 5:
/* Only CMYKA
*/
photometric = PHOTOMETRIC_SEPARATED;
TIFFSetField( tif, TIFFTAG_INKSET, INKSET_CMYK );
v[0] = EXTRASAMPLE_ASSOCALPHA;
TIFFSetField( tif, TIFFTAG_EXTRASAMPLES, 1, v );
break;
default:
/* Who knows. Just call it RGB.
*/
photometric = PHOTOMETRIC_RGB;
break;
}
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 );
if( tif != tw->tif ) {
/* Pyramid layer.
*/
TIFFSetField( tif, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE );
}
/* Sample format.
*/
format = SAMPLEFORMAT_UINT;
if( vips_band_format_isuint( tw->im->BandFmt ) )
format = SAMPLEFORMAT_UINT;
else if( vips_band_format_isint( tw->im->BandFmt ) )
format = SAMPLEFORMAT_INT;
else if( vips_band_format_isfloat( tw->im->BandFmt ) )
format = SAMPLEFORMAT_IEEEFP;
else if( vips_band_format_iscomplex( tw->im->BandFmt ) )
format = SAMPLEFORMAT_COMPLEXIEEEFP;
TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, format );
return( 0 );
}
/* Free a pyramid layer.
*/
static void
free_layer( PyramidLayer *layer )
{
int i;
for( i = 0; i < MAX_LAYER_BUFFER; i++ )
VIPS_FREEF( g_object_unref, layer->tiles[i].tile );
/* And close the TIFF file we are writing to.
*/
VIPS_FREEF( vips_free, layer->tbuf );
VIPS_FREEF( TIFFClose, layer->tif );
}
/* Free an entire pyramid.
*/
static void
free_pyramid( PyramidLayer *layer )
{
if( layer->below )
free_pyramid( layer->below );
free_layer( layer );
}
/* 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 = VIPS_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 < 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 = vips__temp_name( "%s.tif" )) )
return( -1 );
/* Make output image.
*/
if( !(layer->tif = tiff_openout( tw, 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 = vips_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;
/* Existing buffer we have finished with?
*/
for( i = 0; i < MAX_LAYER_BUFFER; i++ )
if( layer->tiles[i].bits == PYR_ALL )
return( i );
/* Have to make a new one.
*/
for( i = 0; i < MAX_LAYER_BUFFER; i++ )
if( !layer->tiles[i].tile ) {
if( !(layer->tiles[i].tile =
vips_region_new( layer->tw->im )) )
return( -1 );
return( i );
}
/* Out of space!
*/
vips_error( "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, VipsRect *pos )
{
int i;
VipsRect quad;
VipsRect image;
VipsRect inter;
/* Do we have a VipsRegion for this position?
*/
for( i = 0; i < MAX_LAYER_BUFFER; i++ ) {
VipsRegion *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( vips_region_buffer( layer->tiles[i].tile, pos ) )
return( -1 );
vips__region_no_ownership( layer->tiles[i].tile );
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;
vips_rect_intersectrect( &quad, &image, &inter );
if( vips_rect_isempty( &inter ) )
layer->tiles[i].bits |= PYR_TL;
quad.left = pos->left + quad.width;
quad.top = pos->top;
vips_rect_intersectrect( &quad, &image, &inter );
if( vips_rect_isempty( &inter ) )
layer->tiles[i].bits |= PYR_TR;
quad.left = pos->left;
quad.top = pos->top + quad.height;
vips_rect_intersectrect( &quad, &image, &inter );
if( vips_rect_isempty( &inter ) )
layer->tiles[i].bits |= PYR_BL;
quad.left = pos->left + quad.width;
quad.top = pos->top + quad.height;
vips_rect_intersectrect( &quad, &image, &inter );
if( vips_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. VIPS_CODING_LABQ only.
*/
static void
shrink_region_labpack( VipsRegion *from, VipsRect *area,
VipsRegion *to, int xoff, int yoff )
{
int ls = VIPS_REGION_LSKIP( from );
VipsRect *t = &to->valid;
int x, y;
VipsRect 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++ ) {
VipsPel *p = VIPS_REGION_ADDR( from,
area->left, area->top + y * 2 );
VipsPel *q = VIPS_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( VipsRegion *from, VipsRect *area,
VipsRegion *to, int xoff, int yoff )
{
int ls = VIPS_REGION_LSKIP( from );
int ps = VIPS_IMAGE_SIZEOF_PEL( from->im );
int nb = from->im->Bands;
VipsRect *t = &to->valid;
int x, y, z;
VipsRect 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++ ) {
VipsPel *p = VIPS_REGION_ADDR( from,
area->left, area->top + y * 2 );
VipsPel *q = VIPS_REGION_ADDR( to,
out.left, out.top + y );
/* Process this line of pels.
*/
switch( from->im->BandFmt ) {
case VIPS_FORMAT_UCHAR:
SHRINK_TYPE_INT( unsigned char ); break;
case VIPS_FORMAT_CHAR:
SHRINK_TYPE_INT( signed char ); break;
case VIPS_FORMAT_USHORT:
SHRINK_TYPE_INT( unsigned short ); break;
case VIPS_FORMAT_SHORT:
SHRINK_TYPE_INT( signed short ); break;
case VIPS_FORMAT_UINT:
SHRINK_TYPE_INT( unsigned int ); break;
case VIPS_FORMAT_INT:
SHRINK_TYPE_INT( signed int ); break;
case VIPS_FORMAT_FLOAT:
SHRINK_TYPE_FLOAT( float ); break;
case VIPS_FORMAT_DOUBLE:
SHRINK_TYPE_FLOAT( double ); break;
default:
g_assert( 0 );
}
}
}
/* Write a tile from a layer.
*/
static int
save_tile( TiffWrite *tw,
TIFF *tif, VipsPel *tbuf, VipsRegion *reg, VipsRect *area )
{
/* Have to repack pixels.
*/
pack2tiff( tw, reg, tbuf, area );
#ifdef DEBUG_VERBOSE
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_VERBOSE*/
/* Write to TIFF! easy.
*/
if( TIFFWriteTile( tif, tbuf, area->left, area->top, 0, 0 ) < 0 ) {
vips_error( "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, VipsRegion *tile, VipsRect *area )
{
TiffWrite *tw = layer->tw;
int xoff, yoff;
int t, ri, bo;
VipsRect 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( vips_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 = VIPS_MIN( layer->width, out.left + layer->tw->tilew );
bo = VIPS_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 == VIPS_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 ) {
vips_error( "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. This is called by vips_sink_tile() for every tile
* generated.
*/
static int
write_tif_tile( VipsRegion *out, void *seq, void *a, void *b, gboolean *stop )
{
TiffWrite *tw = (TiffWrite *) a;
g_mutex_lock( tw->write_lock );
/* Write to TIFF.
*/
if( save_tile( tw, tw->tif, tw->tbuf, out, &out->valid ) ) {
g_mutex_unlock( tw->write_lock );
return( -1 );
}
/* Is there a pyramid? Write to that too.
*/
if( tw->layer &&
new_tile( tw->layer, out, &out->valid ) ) {
g_mutex_unlock( tw->write_lock );
return( -1 );
}
g_mutex_unlock( tw->write_lock );
return( 0 );
}
/* Write as tiles.
*/
static int
write_tif_tilewise( TiffWrite *tw )
{
VipsImage *im = tw->im;
/* Double check: buffers should match in size, except for onebit and
* labq modes.
*/
{
size_t vips_tile_size =
VIPS_IMAGE_SIZEOF_PEL( im ) * tw->tilew * tw->tileh;
if( tw->im->Coding != VIPS_CODING_LABQ &&
!tw->onebit &&
TIFFTileSize( tw->tif ) != vips_tile_size ) {
vips_error( "vips2tiff",
"%s", _( "unsupported image format" ) );
return( -1 );
}
}
g_assert( !tw->tbuf );
if( !(tw->tbuf = vips_malloc( NULL, TIFFTileSize( tw->tif ) )) )
return( -1 );
g_assert( !tw->write_lock );
tw->write_lock = vips_g_mutex_new();
/* 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 );
if( vips_sink_tile( im, tw->tilew, tw->tileh,
NULL, write_tif_tile, NULL, tw, NULL ) )
return( -1 );
return( 0 );
}
static int
write_tif_block( VipsRegion *region, VipsRect *area, void *a )
{
TiffWrite *tw = (TiffWrite *) a;
VipsImage *im = tw->im;
int y;
for( y = 0; y < area->height; y++ ) {
VipsPel *p = VIPS_REGION_ADDR( region, 0, area->top + y );
/* Any repacking necessary.
*/
if( im->Coding == VIPS_CODING_LABQ ) {
LabQ2LabC( tw->tbuf, p, im->Xsize );
p = tw->tbuf;
}
else if( im->BandFmt == VIPS_FORMAT_SHORT &&
im->Type == VIPS_INTERPRETATION_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_stripwise( TiffWrite *tw )
{
g_assert( !tw->tbuf );
/* Double check: buffers should match in size, except for onebit and
* labq modes.
*/
if( tw->im->Coding != VIPS_CODING_LABQ &&
!tw->onebit &&
TIFFScanlineSize( tw->tif ) !=
VIPS_IMAGE_SIZEOF_LINE( tw->im ) ) {
vips_error( "vips2tiff",
"%s", _( "unsupported image format" ) );
return( -1 );
}
if( !(tw->tbuf = vips_malloc( NULL, TIFFScanlineSize( tw->tif ) )) )
return( -1 );
if( vips_sink_disc( tw->im, write_tif_block, tw ) )
return( -1 );
return( 0 );
}
/* Delete any temp files we wrote.
*/
static void
delete_files( TiffWrite *tw )
{
PyramidLayer *layer;
if( tw->bname ) {
#ifndef DEBUG
unlink( tw->bname );
#else
printf( "delete_files: leaving %s\n", tw->bname );
#endif /*DEBUG*/
tw->bname = NULL;
}
for( layer = tw->layer; layer; layer = layer->below )
if( layer->lname ) {
#ifndef DEBUG
unlink( layer->lname );
#else
printf( "delete_files: leaving %s\n", layer->lname );
#endif /*DEBUG*/
layer->lname = NULL;
}
}
/* Free a TiffWrite.
*/
static void
free_tiff_write( TiffWrite *tw )
{
delete_files( tw );
VIPS_FREEF( TIFFClose, tw->tif );
VIPS_FREEF( vips_free, tw->tbuf );
VIPS_FREEF( vips_g_mutex_free, tw->write_lock );
VIPS_FREEF( free_pyramid, tw->layer );
VIPS_FREEF( vips_free, tw->icc_profile );
}
/* 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) ))
static int
get_compression( VipsForeignTiffCompression compression )
{
switch( compression ) {
case VIPS_FOREIGN_TIFF_COMPRESSION_NONE:
return( COMPRESSION_NONE );
case VIPS_FOREIGN_TIFF_COMPRESSION_JPEG:
return( COMPRESSION_JPEG );
case VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE:
return( COMPRESSION_ADOBE_DEFLATE );
case VIPS_FOREIGN_TIFF_COMPRESSION_PACKBITS:
return( COMPRESSION_PACKBITS );
case VIPS_FOREIGN_TIFF_COMPRESSION_CCITTFAX4:
return( COMPRESSION_CCITTFAX4 );
case VIPS_FOREIGN_TIFF_COMPRESSION_LZW:
return( COMPRESSION_LZW );
default:
g_assert( 0 );
}
/* Keep -Wall happy.
*/
return( -1 );
}
static int
get_resunit( VipsForeignTiffResunit resunit )
{
switch( resunit ) {
case VIPS_FOREIGN_TIFF_RESUNIT_CM:
return( RESUNIT_CENTIMETER );
case VIPS_FOREIGN_TIFF_RESUNIT_INCH:
return( RESUNIT_INCH );
default:
g_assert( 0 );
}
/* Keep -Wall happy.
*/
return( -1 );
}
/* Make and init a TiffWrite.
*/
static TiffWrite *
make_tiff_write( VipsImage *im, const char *filename,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
char *profile,
gboolean tile, int tile_width, int tile_height,
gboolean pyramid,
gboolean squash,
VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff,
gboolean rgbjpeg )
{
TiffWrite *tw;
if( !(tw = VIPS_NEW( im, TiffWrite )) )
return( NULL );
tw->im = im;
tw->name = vips_strdup( VIPS_OBJECT( im ), filename );
tw->bname = NULL;
tw->tif = NULL;
tw->layer = NULL;
tw->tbuf = NULL;
tw->compression = get_compression( compression );
tw->jpqual = Q;
tw->predictor = predictor;
tw->tile = tile;
tw->tilew = tile_width;
tw->tileh = tile_height;
tw->pyramid = pyramid;
tw->onebit = squash;
tw->icc_profile = profile;
tw->bigtiff = bigtiff;
tw->rgbjpeg = rgbjpeg;
tw->write_lock = NULL;
tw->resunit = get_resunit( resunit );
tw->xres = xres;
tw->yres = yres;
if( (tw->tilew & 0xf) != 0 ||
(tw->tileh & 0xf) != 0 ) {
vips_error( "vips2tiff",
"%s", _( "tile size not a multiple of 16" ) );
return( NULL );
}
if( !tw->tile && tw->pyramid ) {
vips_warn( "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 == VIPS_CODING_NONE &&
vips_bandfmt_iscomplex( im->BandFmt ) ) {
vips_error( "vips2tiff",
"%s", _( "can only pyramid LABQ and "
"non-complex images" ) );
return( NULL );
}
}
/* Only 1-bit-ize 8 bit mono images.
*/
if( tw->onebit &&
(im->Coding != VIPS_CODING_NONE ||
im->BandFmt != VIPS_FORMAT_UCHAR ||
im->Bands != 1) ) {
vips_warn( "vips2tiff",
"%s", _( "can only squash 1 band uchar images -- "
"disabling squash" ) );
tw->onebit = 0;
}
if( tw->onebit &&
tw->compression == COMPRESSION_JPEG ) {
vips_warn( "vips2tiff",
"%s", _( "can't have 1-bit JPEG -- disabling JPEG" ) );
tw->compression = COMPRESSION_NONE;
}
/* Sizeof a line of bytes in the TIFF tile.
*/
if( im->Coding == VIPS_CODING_LABQ )
tw->tls = tw->tilew * 3;
else if( tw->onebit )
tw->tls = ROUND_UP( tw->tilew, 8 ) / 8;
else
tw->tls = VIPS_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 );
CopyField( TIFFTAG_SUBFILETYPE, i32 );
if( tw->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
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 );
/* Only for three-band, 8-bit images.
*/
if( tw->im->Bands == 3 &&
tw->im->BandFmt == VIPS_FORMAT_UCHAR ) {
/* Enable rgb->ycbcr conversion in the jpeg write.
*/
TIFFSetField( out,
TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
/* And we want ycbcr expanded to rgb on read. Otherwise
* TIFFTileSize() will give us the size of a chrominance
* subsampled tile.
*/
TIFFSetField( in,
TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RGB );
}
}
/* We can't copy profiles :( Set again from TiffWrite.
*/
if( embed_profile( tw, out ) )
return( -1 );
buf = vips_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, -1 );
if( len < 0 ||
TIFFWriteEncodedTile( out, tile, buf, len ) < 0 ) {
vips_free( buf );
return( -1 );
}
}
vips_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, 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
vips__tiff_write( VipsImage *in, const char *filename,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
char *profile,
gboolean tile, int tile_width, int tile_height,
gboolean pyramid,
gboolean squash,
VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff,
gboolean rgbjpeg )
{
TiffWrite *tw;
int res;
#ifdef DEBUG
printf( "tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() );
#endif /*DEBUG*/
vips__tiff_init();
/* Check input image.
*/
if( vips_check_coding_known( "vips2tiff", in ) )
return( -1 );
/* Make output image. If this is a pyramid, write the base image to
* tmp/xx.tif rather than fred.tif.
*/
if( !(tw = make_tiff_write( in, filename,
compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, squash,
resunit, xres, yres, bigtiff, rgbjpeg )) )
return( -1 );
if( tw->pyramid ) {
if( !(tw->bname = vips__temp_name( "%s.tif" )) ||
!(tw->tif = tiff_openout( tw, tw->bname )) ) {
free_tiff_write( tw );
return( -1 );
}
}
else {
/* No pyramid ... write straight to name.
*/
if( !(tw->tif = tiff_openout( tw, tw->name )) ) {
free_tiff_write( tw );
return( -1 );
}
}
/* Write the TIFF header for the full-res file.
*/
if( write_tiff_header( tw, tw->tif, in->Xsize, in->Ysize ) ) {
free_tiff_write( tw );
return( -1 );
}
if( tw->tile )
res = write_tif_tilewise( tw );
else
res = write_tif_stripwise( 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*/