libvips/libvips/foreign/vips2tiff.c

2295 lines
57 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
* 20/11/14
* - cache input in tile write mode to keep us sequential
* 3/12/14
* - embed XMP in output
* 10/12/14
* - zero out edge tile buffers before jpeg wtiff, thanks iwbh15
* 19/1/15
* - disable chroma subsample if Q >= 90
* 13/2/15
* - append later layers, don't copy the base image
* - use the nice dzsave pyramid code, much faster and simpler
* - we now allow strip pyramids
* 27/3/15
* - squash >128 rather than >0, nicer results for shrink
* - add miniswhite option
* 29/9/15
* - try to write IPTC metadata
* - try to write photoshop metadata
* 11/11/15
* - better alpha handling, thanks sadaqatullahn
* 21/12/15
* - write TIFFTAG_IMAGEDESCRIPTION
* 2/6/16
* - support strip option
* 4/7/16
* - tag alpha as UNASSALPHA since it's not pre-multiplied, thanks Peter
* 17/8/16
* - use wchar_t TIFFOpen on Windows
* 14/10/16
* - add buffer output
* 29/1/17
* - enable bigtiff automatically for large, uncompressed writes, thanks
* AndreasSchmid1
* 26/8/17
* - support pyramid creation to buffer, thanks bubba
* 24/10/17
* - no error on page-height not a factor of image height, just don't
* write multipage
* 13/6/18
* - add region_shrink
* 2/7/18
* - copy EXTRASAMPLES to pyramid layers
* 21/12/18
* - stop pyr layers if width or height drop to 1
* 8/7/19
* - add webp and zstd support
* - add @level and @lossless
* 18/12/19
* - "squash" now squashes 3-band float LAB down to LABQ
* 26/1/20
* - add "depth" to set pyr depth
* 27/1/20
* - write XYZ images as logluv
* 7/2/20 [jclavoie-jive]
* - add PAGENUMBER support
* 23/5/20
* - add support for subifd pyramid layers
* 6/6/20 MathemanFlo
* - add bitdepth support for 2 and 4 bit greyscale images
* 29/9/21 LionelArn2
* - loop for the whole output image, rather than per page
* 20/10/21 [jacopoabramo]
* - subifd enables pyramid
* - add support for page_height param
* 11/5/22
* - switch to terget API for output
*/
/*
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 <glib/gi18n-lib.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 "pforeign.h"
#include "tiff.h"
/* TODO:
*
* - add a flag for plane-separate write
*
* At the moment, we write bioformats-style TIFFs by splitting bands up,
* making a toilet-roll image and writing out in pages. The TIFFs we make
* are not tagged as plane-separate and do not have (eg.) RGB photometric
* interpretation. Moreover, when working from an RGB source, we'll end
* up reading the input three times.
*
* A write-plane-separate flag to the TIFF writer could let us set the
* photometric interpretation correctly, and save all planes in a single
* pass before doing a final gather sweep.
*/
/* Max number of alpha channels we allow.
*/
#define MAX_ALPHA (64)
/* Bioformats uses this tag for lossy jp2k compressed tiles.
*/
#define JP2K_LOSSY 33004
/* Compression types we handle ourselves.
*/
static int wtiff_we_compress[] = {
JP2K_LOSSY
};
typedef struct _Layer Layer;
typedef struct _Wtiff Wtiff;
/* A layer in the pyramid.
*/
struct _Layer {
Wtiff *wtiff; /* Main wtiff struct */
/* The temp target for this layer.
*/
VipsTarget *target;
int width, height; /* Layer size */
int sub; /* Subsample factor for this layer */
TIFF *tif; /* TIFF file we write this layer to */
/* The image we build. We only keep a few scanlines of this around in
* @strip.
*/
VipsImage *image;
/* The y position of strip in image.
*/
int y;
/* The next line we write to in strip.
*/
int write_y;
VipsRegion *strip; /* The current strip of pixels */
VipsRegion *copy; /* Pixels we copy to the next strip */
Layer *below; /* The smaller layer below us */
Layer *above; /* The larger layer above */
};
/* A TIFF image in the process of being written.
*/
struct _Wtiff {
VipsImage *input; /* Original input image */
/* Image transformed ready for write.
*/
VipsImage *ready;
/* Target to write to.
*/
VipsTarget *target;
Layer *layer; /* Top of pyramid */
VipsPel *tbuf; /* TIFF output buffer */
int tls; /* Tile line size */
int compression; /* libtiff compression type */
int Q; /* JPEG q-factor, webp level */
int predictor; /* libtiff predictor type */
int tile; /* Tile or not */
int tilew, tileh; /* Tile size */
int pyramid; /* Wtiff pyramid */
int bitdepth; /* Write as 1, 2 or 4 bit */
int miniswhite; /* Wtiff as 0 == white */
int resunit; /* Resolution unit (inches or cm) */
double xres; /* Resolution in X */
double yres; /* Resolution in Y */
const char *profile; /* Profile to embed */
int bigtiff; /* True for bigtiff write */
int rgbjpeg; /* True for RGB not YCbCr */
int properties; /* Set to save XML props */
int strip; /* Don't write metadata */
VipsRegionShrink region_shrink; /* How to shrink regions */
int level; /* zstd compression level */
gboolean lossless; /* lossless mode */
VipsForeignDzDepth depth; /* Pyr depth */
gboolean subifd; /* Write pyr layers into subifds */
gboolean premultiply; /* Premultiply alpha */
/* True if we've detected a toilet-roll image, plus the page height,
* which has been checked to be a factor of im->Ysize. page_number
* starts at zero and ticks up as we write each page.
*/
gboolean toilet_roll;
int page_height;
int page_number;
int n_pages;
/* The height of the TIFF we write. Equal to page_height in toilet
* roll mode.
*/
int image_height;
/* TRUE if the compression type is not supported by libtiff directly
* and we must compress ourselves.
*/
gboolean we_compress;
};
/* Write an ICC Profile from a file into the JPEG stream.
*/
static int
embed_profile_file( TIFF *tif, const char *profile )
{
VipsBlob *blob;
if( vips_profile_load( profile, &blob, NULL ) )
return( -1 );
if( blob ) {
size_t length;
const void *data = vips_blob_get( blob, &length );
TIFFSetField( tif, TIFFTAG_ICCPROFILE, length, data );
#ifdef DEBUG
printf( "vips2tiff: attached profile \"%s\"\n", profile );
#endif /*DEBUG*/
vips_area_unref( (VipsArea *) blob );
}
return( 0 );
}
/* Embed an ICC profile from VipsImage metadata.
*/
static int
embed_profile_meta( TIFF *tif, VipsImage *im )
{
const void *data;
size_t length;
if( vips_image_get_blob( im, VIPS_META_ICC_NAME, &data, &length ) )
return( -1 );
TIFFSetField( tif, TIFFTAG_ICCPROFILE, length, data );
#ifdef DEBUG
printf( "vips2tiff: attached profile from meta\n" );
#endif /*DEBUG*/
return( 0 );
}
static void
wtiff_layer_init( Wtiff *wtiff, Layer **layer, Layer *above,
int width, int height )
{
if( !*layer ) {
*layer = VIPS_NEW( wtiff->ready, Layer );
(*layer)->wtiff = wtiff;
(*layer)->width = width;
(*layer)->height = height;
if( !above )
/* Top of pyramid.
*/
(*layer)->sub = 1;
else
(*layer)->sub = above->sub * 2;
(*layer)->tif = NULL;
(*layer)->image = NULL;
(*layer)->write_y = 0;
(*layer)->y = 0;
(*layer)->strip = NULL;
(*layer)->copy = NULL;
(*layer)->below = NULL;
(*layer)->above = above;
/* The target we write to. The base layer writes to the main
* output, each layer smaller writes to a memory temp.
*/
if( !above ) {
(*layer)->target = wtiff->target;
g_object_ref( (*layer)->target );
}
else
(*layer)->target =
vips_target_new_temp( wtiff->target );
/*
printf( "wtiff_layer_init: sub = %d, width = %d, height = %d\n",
(*layer)->sub, width, height );
*/
}
if( wtiff->pyramid ) {
int limitw, limith;
switch( wtiff->depth ) {
case VIPS_FOREIGN_DZ_DEPTH_ONEPIXEL:
limitw = limith = 1;
break;
case VIPS_FOREIGN_DZ_DEPTH_ONETILE:
limitw = wtiff->tilew;
limith = wtiff->tileh;
break;
case VIPS_FOREIGN_DZ_DEPTH_ONE:
limitw = wtiff->ready->Xsize;
limith = wtiff->ready->Ysize;
break;
default:
g_assert_not_reached();
}
/* We make another layer if the image is too large to fit in a
* single tile, and if neither axis is greater than 1.
*
* Very tall or wide images might end up with a smallest layer
* larger than one tile.
*/
if( ((*layer)->width > limitw ||
(*layer)->height > limith) &&
(*layer)->width > 1 &&
(*layer)->height > 1 )
wtiff_layer_init( wtiff, &(*layer)->below, *layer,
width / 2, height / 2 );
}
}
static int
wtiff_embed_profile( Wtiff *wtiff, TIFF *tif )
{
if( wtiff->profile &&
embed_profile_file( tif, wtiff->profile ) )
return( -1 );
if( !wtiff->profile &&
vips_image_get_typeof( wtiff->ready, VIPS_META_ICC_NAME ) &&
embed_profile_meta( tif, wtiff->ready ) )
return( -1 );
return( 0 );
}
static int
wtiff_embed_xmp( Wtiff *wtiff, TIFF *tif )
{
const void *data;
size_t size;
if( !vips_image_get_typeof( wtiff->ready, VIPS_META_XMP_NAME ) )
return( 0 );
if( vips_image_get_blob( wtiff->ready, VIPS_META_XMP_NAME,
&data, &size ) )
return( -1 );
TIFFSetField( tif, TIFFTAG_XMLPACKET, size, data );
#ifdef DEBUG
printf( "vips2tiff: attached XMP from meta\n" );
#endif /*DEBUG*/
return( 0 );
}
static int
wtiff_embed_iptc( Wtiff *wtiff, TIFF *tif )
{
const void *data;
size_t size;
if( !vips_image_get_typeof( wtiff->ready, VIPS_META_IPTC_NAME ) )
return( 0 );
if( vips_image_get_blob( wtiff->ready, VIPS_META_IPTC_NAME,
&data, &size ) )
return( -1 );
/* For no very good reason, libtiff stores IPTC as an array of
* long, not byte.
*/
if( size & 3 ) {
g_warning( "%s", _( "rounding up IPTC data length" ) );
size /= 4;
size += 1;
}
else
size /= 4;
TIFFSetField( tif, TIFFTAG_RICHTIFFIPTC, size, data );
#ifdef DEBUG
printf( "vips2tiff: attached IPTC from meta\n" );
#endif /*DEBUG*/
return( 0 );
}
static int
wtiff_embed_photoshop( Wtiff *wtiff, TIFF *tif )
{
const void *data;
size_t size;
if( !vips_image_get_typeof( wtiff->ready, VIPS_META_PHOTOSHOP_NAME ) )
return( 0 );
if( vips_image_get_blob( wtiff->ready, VIPS_META_PHOTOSHOP_NAME,
&data, &size ) )
return( -1 );
TIFFSetField( tif, TIFFTAG_PHOTOSHOP, size, data );
#ifdef DEBUG
printf( "vips2tiff: attached photoshop data from meta\n" );
#endif /*DEBUG*/
return( 0 );
}
/* Set IMAGEDESCRIPTION, if it's there. If @properties is TRUE, set from
* vips' metadata.
*/
static int
wtiff_embed_imagedescription( Wtiff *wtiff, TIFF *tif )
{
if( wtiff->properties ) {
char *doc;
if( !(doc = vips__xml_properties( wtiff->ready )) )
return( -1 );
TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, doc );
g_free( doc );
}
else {
const char *imagedescription;
if( !vips_image_get_typeof( wtiff->ready,
VIPS_META_IMAGEDESCRIPTION ) )
return( 0 );
if( vips_image_get_string( wtiff->ready,
VIPS_META_IMAGEDESCRIPTION, &imagedescription ) )
return( -1 );
TIFFSetField( tif, TIFFTAG_IMAGEDESCRIPTION, imagedescription );
}
#ifdef DEBUG
printf( "vips2tiff: attached imagedescription from meta\n" );
#endif /*DEBUG*/
return( 0 );
}
/* Write a TIFF header for this layer.
*/
static int
wtiff_write_header( Wtiff *wtiff, Layer *layer )
{
TIFF *tif = layer->tif;
int i;
int orientation;
#ifdef DEBUG
printf( "wtiff_write_header: sub %d, width %d, height %d\n",
layer->sub, layer->width, layer->height );
#endif /*DEBUG*/
/* Output base header fields.
*/
TIFFSetField( tif, TIFFTAG_IMAGEWIDTH, layer->width );
TIFFSetField( tif, TIFFTAG_IMAGELENGTH, layer->height );
TIFFSetField( tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG );
TIFFSetField( tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT );
TIFFSetField( tif, TIFFTAG_COMPRESSION, wtiff->compression );
if( wtiff->compression == COMPRESSION_JPEG )
TIFFSetField( tif, TIFFTAG_JPEGQUALITY, wtiff->Q );
#ifdef HAVE_TIFF_COMPRESSION_WEBP
if( wtiff->compression == COMPRESSION_WEBP ) {
TIFFSetField( tif, TIFFTAG_WEBP_LEVEL, wtiff->Q );
TIFFSetField( tif, TIFFTAG_WEBP_LOSSLESS, wtiff->lossless );
}
if( wtiff->compression == COMPRESSION_ZSTD ) {
TIFFSetField( tif, TIFFTAG_ZSTD_LEVEL, wtiff->level );
if( wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
TIFFSetField( tif,
TIFFTAG_PREDICTOR, wtiff->predictor );
}
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/
if( (wtiff->compression == COMPRESSION_ADOBE_DEFLATE ||
wtiff->compression == COMPRESSION_LZW) &&
wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
TIFFSetField( tif, TIFFTAG_PREDICTOR, wtiff->predictor );
for( i = 0; i < VIPS_NUMBER( wtiff_we_compress ); i++ )
if( wtiff->compression == wtiff_we_compress[i] ) {
wtiff->we_compress = TRUE;
break;
}
/* Don't write mad resolutions (eg. zero), it confuses some programs.
*/
TIFFSetField( tif, TIFFTAG_RESOLUTIONUNIT, wtiff->resunit );
TIFFSetField( tif, TIFFTAG_XRESOLUTION,
VIPS_FCLIP( 0.01, wtiff->xres, 1000000 ) );
TIFFSetField( tif, TIFFTAG_YRESOLUTION,
VIPS_FCLIP( 0.01, wtiff->yres, 1000000 ) );
if( !wtiff->strip )
if( wtiff_embed_profile( wtiff, tif ) ||
wtiff_embed_xmp( wtiff, tif ) ||
wtiff_embed_iptc( wtiff, tif ) ||
wtiff_embed_photoshop( wtiff, tif ) ||
wtiff_embed_imagedescription( wtiff, tif ) )
return( -1 );
if( vips_image_get_typeof( wtiff->ready, VIPS_META_ORIENTATION ) &&
!vips_image_get_int( wtiff->ready,
VIPS_META_ORIENTATION, &orientation ) )
TIFFSetField( tif, TIFFTAG_ORIENTATION, orientation );
/* And colour fields.
*/
if( wtiff->ready->Coding == VIPS_CODING_LABQ ) {
TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 3 );
TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 8 );
TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CIELAB );
}
else if( wtiff->bitdepth == 1 || wtiff->bitdepth == 2 ||
wtiff->bitdepth == 4 ) {
TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 1 );
TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, wtiff->bitdepth );
TIFFSetField( tif, TIFFTAG_PHOTOMETRIC,
wtiff->miniswhite ?
PHOTOMETRIC_MINISWHITE :
PHOTOMETRIC_MINISBLACK );
}
else {
int photometric;
/* Number of bands that have colour in .. other bands are saved
* as alpha.
*/
int colour_bands;
int alpha_bands;
TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL,
wtiff->ready->Bands );
TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE,
vips_format_sizeof( wtiff->ready->BandFmt ) << 3 );
if( wtiff->ready->Type == VIPS_INTERPRETATION_B_W ||
wtiff->ready->Type == VIPS_INTERPRETATION_GREY16 ||
wtiff->ready->Bands < 3 ) {
/* Mono or mono + alpha.
*/
photometric = wtiff->miniswhite ?
PHOTOMETRIC_MINISWHITE :
PHOTOMETRIC_MINISBLACK;
colour_bands = 1;
}
else if( wtiff->ready->Type == VIPS_INTERPRETATION_LAB ||
wtiff->ready->Type == VIPS_INTERPRETATION_LABS ) {
photometric = PHOTOMETRIC_CIELAB;
colour_bands = 3;
}
else if( wtiff->input->Type == VIPS_INTERPRETATION_XYZ ) {
double stonits;
photometric = PHOTOMETRIC_LOGLUV;
/* Tell libtiff we will write as float XYZ.
*/
TIFFSetField( tif,
TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT );
stonits = 1.0;
if( vips_image_get_typeof( wtiff->ready, "stonits" ) )
vips_image_get_double( wtiff->ready,
"stonits", &stonits );
TIFFSetField( tif, TIFFTAG_STONITS, stonits );
colour_bands = 3;
}
else if( wtiff->ready->Type == VIPS_INTERPRETATION_CMYK &&
wtiff->ready->Bands >= 4 ) {
photometric = PHOTOMETRIC_SEPARATED;
TIFFSetField( tif, TIFFTAG_INKSET, INKSET_CMYK );
colour_bands = 4;
}
else if( wtiff->compression == COMPRESSION_JPEG &&
wtiff->ready->Bands == 3 &&
wtiff->ready->BandFmt == VIPS_FORMAT_UCHAR &&
(!wtiff->rgbjpeg && wtiff->Q < 90) ) {
/* 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 );
colour_bands = 3;
}
else {
/* Some kind of generic multi-band image with three or
* more bands ... save the first three bands as RGB,
* the rest as alpha.
*/
photometric = PHOTOMETRIC_RGB;
colour_bands = 3;
}
alpha_bands = VIPS_CLIP( 0,
wtiff->ready->Bands - colour_bands, MAX_ALPHA );
if( alpha_bands > 0 ) {
guint16 v[MAX_ALPHA];
int i;
/* EXTRASAMPLE_UNASSALPHA means generic extra
* alpha-like channels. ASSOCALPHA means pre-multipled
* alpha only.
*
* Make the first channel the premultiplied alpha, if
* we are premultiplying.
*/
for( i = 0; i < alpha_bands; i++ )
v[i] = i == 0 && wtiff->premultiply ?
EXTRASAMPLE_ASSOCALPHA :
EXTRASAMPLE_UNASSALPHA;
TIFFSetField( tif,
TIFFTAG_EXTRASAMPLES, alpha_bands, v );
}
TIFFSetField( tif, TIFFTAG_PHOTOMETRIC, photometric );
}
/* Layout.
*/
if( wtiff->tile ) {
TIFFSetField( tif, TIFFTAG_TILEWIDTH, wtiff->tilew );
TIFFSetField( tif, TIFFTAG_TILELENGTH, wtiff->tileh );
}
else
TIFFSetField( tif, TIFFTAG_ROWSPERSTRIP, wtiff->tileh );
if( layer->above )
/* Pyramid layer.
*/
TIFFSetField( tif, TIFFTAG_SUBFILETYPE, FILETYPE_REDUCEDIMAGE );
else if( wtiff->toilet_roll ) {
/* One page of many.
*/
TIFFSetField( tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE );
TIFFSetField( tif, TIFFTAG_PAGENUMBER,
wtiff->page_number, wtiff->n_pages );
}
/* Sample format.
*
* Don't set for logluv: libtiff does this for us.
*/
if( wtiff->input->Type != VIPS_INTERPRETATION_XYZ ) {
int format;
format = SAMPLEFORMAT_UINT;
if( vips_band_format_isuint( wtiff->ready->BandFmt ) )
format = SAMPLEFORMAT_UINT;
else if( vips_band_format_isint( wtiff->ready->BandFmt ) )
format = SAMPLEFORMAT_INT;
else if( vips_band_format_isfloat( wtiff->ready->BandFmt ) )
format = SAMPLEFORMAT_IEEEFP;
else if( vips_band_format_iscomplex( wtiff->ready->BandFmt ) )
format = SAMPLEFORMAT_COMPLEXIEEEFP;
TIFFSetField( tif, TIFFTAG_SAMPLEFORMAT, format );
}
return( 0 );
}
static int
wtiff_layer_rewind( Wtiff *wtiff, Layer *layer )
{
VipsRect strip_size;
/* Build a line of tiles here.
*
* Expand the strip if necessary to make sure we have an even
* number of lines.
*/
strip_size.left = 0;
strip_size.top = 0;
strip_size.width = layer->image->Xsize;
strip_size.height = wtiff->tileh;
if( (strip_size.height & 1) == 1 )
strip_size.height += 1;
if( vips_region_buffer( layer->strip, &strip_size ) )
return( -1 );
layer->y = 0;
layer->write_y = 0;
return( 0 );
}
static int
wtiff_allocate_layers( Wtiff *wtiff )
{
Layer *layer;
g_assert( wtiff->layer );
for( layer = wtiff->layer; layer; layer = layer->below ) {
if( !layer->image ) {
layer->image = vips_image_new();
if( vips_image_pipelinev( layer->image,
VIPS_DEMAND_STYLE_ANY, wtiff->ready, NULL ) )
return( -1 );
layer->image->Xsize = layer->width;
layer->image->Ysize = layer->height;
layer->strip = vips_region_new( layer->image );
layer->copy = vips_region_new( layer->image );
/* The regions will get used in the bg thread callback,
* so make sure we don't own them.
*/
vips__region_no_ownership( layer->strip );
vips__region_no_ownership( layer->copy );
layer->tif = vips__tiff_openout_target( layer->target,
wtiff->bigtiff );
if( !layer->tif )
return( -1 );
}
if( wtiff_layer_rewind( wtiff, layer ) )
return( -1 );
if( wtiff_write_header( wtiff, layer ) )
return( -1 );
}
if( !wtiff->tbuf ) {
if( wtiff->tile )
wtiff->tbuf = vips_malloc( NULL,
TIFFTileSize( wtiff->layer->tif ) );
else
wtiff->tbuf = vips_malloc( NULL,
TIFFScanlineSize( wtiff->layer->tif ) );
if( !wtiff->tbuf )
return( -1 );
}
return( 0 );
}
/* Free a single pyramid layer.
*/
static void
layer_free( Layer *layer )
{
/* Don't unref the target for this layer -- we'll need it for gather.
*/
VIPS_FREEF( TIFFClose, layer->tif );
VIPS_UNREF( layer->strip );
VIPS_UNREF( layer->copy );
VIPS_UNREF( layer->image );
}
/* Free an entire pyramid.
*/
static void
layer_free_all( Layer *layer )
{
if( layer->below )
layer_free_all( layer->below );
layer_free( layer );
}
static void
wtiff_free( Wtiff *wtiff )
{
Layer *layer;
/* unref all the targets, including the base layer.
*/
for( layer = wtiff->layer; layer; layer = layer->below )
VIPS_UNREF( layer->target );
VIPS_UNREF( wtiff->ready );
VIPS_FREE( wtiff->tbuf );
VIPS_FREEF( layer_free_all, wtiff->layer );
VIPS_FREE( wtiff );
}
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 );
#ifdef HAVE_TIFF_COMPRESSION_WEBP
case VIPS_FOREIGN_TIFF_COMPRESSION_WEBP:
return( COMPRESSION_WEBP );
case VIPS_FOREIGN_TIFF_COMPRESSION_ZSTD:
return( COMPRESSION_ZSTD );
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/
case VIPS_FOREIGN_TIFF_COMPRESSION_JP2K:
return( JP2K_LOSSY );
default:
return( COMPRESSION_NONE );
}
}
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_not_reached();
}
/* Keep -Wall happy.
*/
return( -1 );
}
/* Get the image ready to be written.
*/
static int
ready_to_write( Wtiff *wtiff )
{
VipsImage *input;
VipsImage *x;
input = wtiff->input;
g_object_ref( input );
if( vips_check_coding_known( "vips2tiff", input ) ) {
VIPS_UNREF( input );
return( -1 );
}
/* Premultiply any alpha, if necessary.
*/
if( wtiff->premultiply &&
vips_image_hasalpha( input ) ) {
VipsBandFormat start_format = input->BandFmt;
if( vips_premultiply( input, &x, NULL ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
input = x;
/* Premultiply always makes a float -- cast back again.
*/
if( vips_cast( input, &x, start_format, NULL ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
input = x;
}
/* "squash" float LAB down to LABQ.
*/
if( wtiff->bitdepth &&
input->Bands == 3 &&
input->BandFmt == VIPS_FORMAT_FLOAT &&
input->Type == VIPS_INTERPRETATION_LAB ) {
if( vips_Lab2LabQ( input, &x, NULL ) ) {
VIPS_UNREF( input );
return( -1 );
}
VIPS_UNREF( input );
input = x;
}
wtiff->ready = input;
return( 0 );
}
static Wtiff *
wtiff_new( VipsImage *input, VipsTarget *target,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
gboolean tile, int tile_width, int tile_height,
gboolean pyramid,
int bitdepth,
gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff,
gboolean rgbjpeg,
gboolean properties,
gboolean strip,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth,
gboolean subifd,
gboolean premultiply,
int page_height )
{
Wtiff *wtiff;
if( !(wtiff = VIPS_NEW( NULL, Wtiff )) )
return( NULL );
wtiff->input = input;
wtiff->ready = NULL;
wtiff->target = target;
wtiff->layer = NULL;
wtiff->tbuf = NULL;
wtiff->compression = get_compression( compression );
wtiff->Q = Q;
wtiff->predictor = predictor;
wtiff->tile = tile;
wtiff->tilew = tile_width;
wtiff->tileh = tile_height;
wtiff->pyramid = pyramid;
wtiff->bitdepth = bitdepth;
wtiff->miniswhite = miniswhite;
wtiff->resunit = get_resunit( resunit );
wtiff->xres = xres;
wtiff->yres = yres;
wtiff->profile = profile;
wtiff->bigtiff = bigtiff;
wtiff->rgbjpeg = rgbjpeg;
wtiff->properties = properties;
wtiff->strip = strip;
wtiff->region_shrink = region_shrink;
wtiff->level = level;
wtiff->lossless = lossless;
wtiff->depth = depth;
wtiff->subifd = subifd;
wtiff->premultiply = premultiply;
wtiff->toilet_roll = FALSE;
wtiff->page_height = page_height;
wtiff->page_number = 0;
wtiff->n_pages = 1;
wtiff->image_height = input->Ysize;
/* Any pre-processing on the image.
*/
if( ready_to_write( wtiff ) ) {
wtiff_free( wtiff );
return( NULL );
}
/* XYZ images are written as libtiff LOGLUV.
*/
if( wtiff->ready->Type == VIPS_INTERPRETATION_XYZ )
wtiff->compression = COMPRESSION_SGILOG;
/* Multipage image? 0 is the default for this argument.
*/
if( wtiff->page_height == 0 )
wtiff->page_height = vips_image_get_page_height( input );
if( wtiff->page_height > 0 &&
wtiff->page_height < wtiff->ready->Ysize &&
wtiff->ready->Ysize % wtiff->page_height == 0 ) {
#ifdef DEBUG
printf( "wtiff_new: detected toilet roll image, "
"page-height=%d\n",
wtiff->page_height );
printf( "wtiff_new: pages=%d\n",
wtiff->ready->Ysize / wtiff->page_height );
#endif/*DEBUG*/
wtiff->toilet_roll = TRUE;
wtiff->image_height = wtiff->page_height;
wtiff->n_pages = wtiff->ready->Ysize / wtiff->page_height;
}
/* subifd turns on pyramid mode.
*/
if( wtiff->subifd )
wtiff->pyramid = TRUE;
/* Pyramid images must be tiled.
*/
if( wtiff->pyramid )
wtiff->tile = TRUE;
/* Multi-page pyramids must be in subifd mode.
*/
if( wtiff->pyramid &&
wtiff->toilet_roll )
wtiff->subifd = TRUE;
/* We can only pyramid LABQ and non-complex images.
*/
if( wtiff->pyramid ) {
if( wtiff->ready->Coding == VIPS_CODING_NONE &&
vips_band_format_iscomplex( wtiff->ready->BandFmt ) ) {
wtiff_free( wtiff );
vips_error( "vips2tiff",
"%s", _( "can only pyramid LABQ and "
"non-complex images" ) );
return( NULL );
}
}
/* If compression is off and we're writing a >4gb image, automatically
* enable bigtiff.
*
* This won't always work. If the image data is just under 4gb but
* there's a lot of metadata, we could be pushed over the 4gb limit.
*/
if( wtiff->compression == COMPRESSION_NONE &&
VIPS_IMAGE_SIZEOF_IMAGE( wtiff->ready ) > UINT_MAX )
wtiff->bigtiff = TRUE;
/* In strip mode we use tileh to set rowsperstrip, and that does not
* have the multiple-of-16 restriction.
*/
if( wtiff->tile ) {
if( (wtiff->tilew & 0xf) != 0 ||
(wtiff->tileh & 0xf) != 0 ) {
wtiff_free( wtiff );
vips_error( "vips2tiff",
"%s", _( "tile size not a multiple of 16" ) );
return( NULL );
}
}
/* Depth 8 is handled above.
*/
if( wtiff->bitdepth &&
!(wtiff->bitdepth == 1 ||
wtiff->bitdepth == 2 ||
wtiff->bitdepth == 4) ) {
g_warning( "%s",
_( "bitdepth 1, 2 or 4 only -- disabling bitdepth") );
wtiff->bitdepth = 0;
}
/* Can only have byte fractional bit depths for 8 bit mono.
* 3-band float should have been packed above.
*/
if( wtiff->bitdepth &&
!(wtiff->ready->Coding == VIPS_CODING_NONE &&
wtiff->ready->BandFmt == VIPS_FORMAT_UCHAR &&
wtiff->ready->Bands == 1) ) {
g_warning( "%s",
( "can only set bitdepth for 1-band uchar and "
"3-band float lab -- disabling bitdepth" ) );
wtiff->bitdepth = 0;
}
if( wtiff->bitdepth &&
wtiff->compression == COMPRESSION_JPEG ) {
g_warning( "%s",
_( "can't have <8 bit JPEG -- disabling JPEG" ) );
wtiff->compression = COMPRESSION_NONE;
}
/* We can only MINISWHITE non-complex images of 1 or 2 bands.
*/
if( wtiff->miniswhite &&
(wtiff->ready->Coding != VIPS_CODING_NONE ||
vips_band_format_iscomplex( wtiff->ready->BandFmt ) ||
wtiff->ready->Bands > 2) ) {
g_warning( "%s",
_( "can only save non-complex greyscale images "
"as miniswhite -- disabling miniswhite" ) );
wtiff->miniswhite = FALSE;
}
/* Sizeof a line of bytes in the TIFF tile.
*/
if( wtiff->ready->Coding == VIPS_CODING_LABQ )
wtiff->tls = wtiff->tilew * 3;
else if( wtiff->bitdepth == 1 )
wtiff->tls = VIPS_ROUND_UP( wtiff->tilew, 8 ) / 8;
else if( wtiff->bitdepth == 2 )
wtiff->tls = VIPS_ROUND_UP( wtiff->tilew, 4 ) / 4;
else if( wtiff->bitdepth == 4 )
wtiff->tls = VIPS_ROUND_UP( wtiff->tilew, 2 ) / 2;
else
wtiff->tls = VIPS_IMAGE_SIZEOF_PEL( wtiff->ready ) *
wtiff->tilew;
return( wtiff );
}
/* 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 N bit TIFF.
*/
static void
eightbit2nbit( Wtiff *wtiff, VipsPel *q, VipsPel *p, int n )
{
/* Invert in miniswhite mode.
*/
VipsPel mask = wtiff->miniswhite ? 255 : 0;
int pixel_mask = 8 / wtiff->bitdepth - 1;
int shift = 8 - wtiff->bitdepth;
VipsPel bits;
int x;
bits = 0;
for( x = 0; x < n; x++ ) {
bits <<= wtiff->bitdepth;
bits |= p[x] >> shift;
if( (x & pixel_mask) == pixel_mask )
*q++ = bits ^ mask;
}
/* Any left-over bits? Need to be left-aligned.
*/
if( (x & pixel_mask) != 0 ) {
/* The number of bits we've collected in bits and must
* left-align and flush.
*/
int collected_bits = (x & pixel_mask) << (wtiff->bitdepth - 1);
*q++ = (bits ^ mask) << (8 - collected_bits);
}
}
/* Swap the sense of the first channel, if necessary.
*/
#define GREY_LOOP( TYPE, MAX ) { \
TYPE *p1; \
TYPE *q1; \
\
p1 = (TYPE *) p; \
q1 = (TYPE *) q; \
for( x = 0; x < n; x++ ) { \
if( invert ) \
q1[0] = MAX - p1[0]; \
else \
q1[0] = p1[0]; \
\
for( i = 1; i < im->Bands; i++ ) \
q1[i] = p1[i]; \
\
q1 += im->Bands; \
p1 += im->Bands; \
} \
}
/* If we're writing a 1 or 2 band image as a greyscale and MINISWHITE, we need
* to swap the sense of the first band. See tiff2vips.c, greyscale_line() for
* the opposite conversion.
*/
static void
invert_band0( Wtiff *wtiff, VipsPel *q, VipsPel *p, int n )
{
VipsImage *im = wtiff->ready;
gboolean invert = wtiff->miniswhite;
int x, i;
switch( im->BandFmt ) {
case VIPS_FORMAT_UCHAR:
case VIPS_FORMAT_CHAR:
GREY_LOOP( guchar, UCHAR_MAX );
break;
case VIPS_FORMAT_SHORT:
GREY_LOOP( gshort, SHRT_MAX );
break;
case VIPS_FORMAT_USHORT:
GREY_LOOP( gushort, USHRT_MAX );
break;
case VIPS_FORMAT_INT:
GREY_LOOP( gint, INT_MAX );
break;
case VIPS_FORMAT_UINT:
GREY_LOOP( guint, UINT_MAX );
break;
case VIPS_FORMAT_FLOAT:
GREY_LOOP( float, 1.0 );
break;
case VIPS_FORMAT_DOUBLE:
GREY_LOOP( double, 1.0 );
break;
default:
g_assert_not_reached();
}
}
/* Convert VIPS LABS to TIFF 16 bit LAB.
*/
static void
LabS2Lab16( VipsPel *q, VipsPel *p, int n, int samples_per_pixel )
{
short *p1 = (short *) p;
unsigned short *q1 = (unsigned short *) q;
int x;
for( x = 0; x < n; x++ ) {
int i;
/* LABS L can be negative.
*/
q1[0] = VIPS_LSHIFT_INT( VIPS_MAX( 0, p1[0] ), 1 );
for( i = 1; i < samples_per_pixel; i++ )
q1[i] = p1[i];
q1 += samples_per_pixel;
p1 += samples_per_pixel;
}
}
/* Convert VIPS D65 XYZ to TIFF scaled float illuminant-free xyz.
*/
static void
XYZ2tiffxyz( VipsPel *q, VipsPel *p, int n, int samples_per_pixel )
{
float *p1 = (float *) p;
float *q1 = (float *) q;
int x;
for( x = 0; x < n; x++ ) {
int i;
q1[0] = p1[0] / VIPS_D65_X0;
q1[1] = p1[1] / VIPS_D65_Y0;
q1[2] = p1[2] / VIPS_D65_Z0;
for( i = 3; i < samples_per_pixel; i++ )
q1[i] = p1[i];
q1 += samples_per_pixel;
p1 += samples_per_pixel;
}
}
/* Pack the pixels in @area from @in into a TIFF tile buffer.
*/
static void
wtiff_pack2tiff( Wtiff *wtiff, Layer *layer,
VipsRegion *in, VipsRect *area, VipsPel *q )
{
int y;
/* JPEG compression can read outside the pixel area for edge tiles. It
* always compresses 8x8 blocks, so if the image width or height is
* not a multiple of 8, it can look beyond the pixels we will write.
*
* Black out the tile first to make sure these edge pixels are always
* zero.
*/
if( wtiff->compression == COMPRESSION_JPEG &&
(area->width < wtiff->tilew ||
area->height < wtiff->tileh) )
memset( q, 0, TIFFTileSize( layer->tif ) );
for( y = area->top; y < VIPS_RECT_BOTTOM( area ); y++ ) {
VipsPel *p = (VipsPel *) VIPS_REGION_ADDR( in, area->left, y );
if( wtiff->ready->Coding == VIPS_CODING_LABQ )
LabQ2LabC( q, p, area->width );
else if( wtiff->bitdepth > 0 )
eightbit2nbit( wtiff, q, p, area->width );
else if( wtiff->input->Type == VIPS_INTERPRETATION_XYZ )
XYZ2tiffxyz( q, p, area->width, in->im->Bands );
else if( (in->im->Bands == 1 || in->im->Bands == 2) &&
wtiff->miniswhite )
invert_band0( wtiff, q, p, area->width );
else if( wtiff->ready->BandFmt == VIPS_FORMAT_SHORT &&
wtiff->ready->Type == VIPS_INTERPRETATION_LABS )
LabS2Lab16( q, p, area->width, in->im->Bands );
else
memcpy( q, p,
area->width *
VIPS_IMAGE_SIZEOF_PEL( wtiff->ready ) );
q += wtiff->tls;
}
}
/* Write a set of tiles across the strip.
*/
static int
wtiff_layer_write_tiles( Wtiff *wtiff, Layer *layer, VipsRegion *strip )
{
VipsImage *im = layer->image;
VipsRect *area = &strip->valid;
VipsRect image;
int x;
image.left = 0;
image.top = 0;
image.width = im->Xsize;
image.height = im->Ysize;
for( x = 0; x < im->Xsize; x += wtiff->tilew ) {
VipsRect tile;
tile.left = x;
tile.top = area->top;
tile.width = wtiff->tilew;
tile.height = wtiff->tileh;
vips_rect_intersectrect( &tile, &image, &tile );
#ifdef DEBUG_VERBOSE
printf( "Writing %dx%d tile at position %dx%d to image %s\n",
tile.width, tile.height, tile.left, tile.top,
TIFFFileName( layer->tif ) );
#endif /*DEBUG_VERBOSE*/
if( wtiff->we_compress ) {
ttile_t tile_no = TIFFComputeTile( layer->tif,
tile.left, tile.top, 0, 0 );
VipsTarget *target;
int result;
unsigned char *buffer;
size_t length;
target = vips_target_new_to_memory();
switch( wtiff->compression ) {
case JP2K_LOSSY:
/* Sadly chroma subsample seems not to work
* for edge tiles in tiff with jp2k
* compression, so we always pass FALSE
* instead of:
*
* !wtiff->rgbjpeg && wtiff->Q < 90,
*
* I've verified that the libvips jp2k
* encode and decode subsample operations fill
* the comps[i].data arrays correctly, so it
* seems to be a openjpeg bug.
*
* FIXME ... try again with openjpeg 2.5,
* when that comes.
*/
result = vips__foreign_load_jp2k_compress(
strip, &tile, target,
wtiff->tilew, wtiff->tileh,
!wtiff->rgbjpeg,
// !wtiff->rgbjpeg && wtiff->Q < 90,
FALSE,
wtiff->lossless,
wtiff->Q );
break;
default:
result = -1;
g_assert_not_reached();
break;
}
if( result ) {
g_object_unref( target );
return( -1 );
}
buffer = vips_target_steal( target, &length );
g_object_unref( target );
result = TIFFWriteRawTile( layer->tif, tile_no,
buffer, length );
g_free( buffer );
if( result < 0 ) {
vips_error( "vips2tiff",
"%s", _( "TIFF write tile failed" ) );
return( -1 );
}
}
else {
/* Have to repack pixels for libtiff.
*/
wtiff_pack2tiff( wtiff,
layer, strip, &tile, wtiff->tbuf );
if( TIFFWriteTile( layer->tif, wtiff->tbuf,
tile.left, tile.top, 0, 0 ) < 0 ) {
vips_error( "vips2tiff",
"%s", _( "TIFF write tile failed" ) );
return( -1 );
}
}
}
return( 0 );
}
/* Write tileh scanlines, less for the last strip.
*/
static int
wtiff_layer_write_strip( Wtiff *wtiff, Layer *layer, VipsRegion *strip )
{
VipsImage *im = layer->image;
VipsRect *area = &strip->valid;
int height = VIPS_MIN( wtiff->tileh, area->height );
int y;
#ifdef DEBUG_VERBOSE
printf( "wtiff_layer_write_strip: top %d, height %d, file %s\n",
area->top, height, TIFFFileName( layer->tif ) );
#endif /*DEBUG_VERBOSE*/
for( y = 0; y < height; y++ ) {
VipsPel *p = VIPS_REGION_ADDR( strip, 0, area->top + y );
/* Any repacking necessary.
*/
if( im->Coding == VIPS_CODING_LABQ ) {
LabQ2LabC( wtiff->tbuf, p, im->Xsize );
p = wtiff->tbuf;
}
else if( im->BandFmt == VIPS_FORMAT_SHORT &&
im->Type == VIPS_INTERPRETATION_LABS ) {
LabS2Lab16( wtiff->tbuf, p, im->Xsize, im->Bands );
p = wtiff->tbuf;
}
else if( wtiff->input->Type == VIPS_INTERPRETATION_XYZ ) {
XYZ2tiffxyz( wtiff->tbuf, p, im->Xsize, im->Bands );
p = wtiff->tbuf;
}
else if( wtiff->bitdepth > 0 ) {
eightbit2nbit( wtiff, wtiff->tbuf, p, im->Xsize );
p = wtiff->tbuf;
}
else if( (im->Bands == 1 || im->Bands == 2) &&
wtiff->miniswhite ) {
invert_band0( wtiff, wtiff->tbuf, p, im->Xsize );
p = wtiff->tbuf;
}
if( TIFFWriteScanline( layer->tif, p, area->top + y, 0 ) < 0 )
return( -1 );
}
return( 0 );
}
static int layer_strip_arrived( Layer *layer );
/* Shrink what pixels we can from this strip into the layer below. If the
* strip below fills, recurse.
*/
static int
layer_strip_shrink( Layer *layer )
{
Layer *below = layer->below;
VipsRegion *from = layer->strip;
VipsRegion *to = below->strip;
VipsRect target;
VipsRect source;
/* Our pixels might cross a strip boundary in the layer below, so we
* have to write repeatedly until we run out of pixels.
*/
for(;;) {
/* The pixels the layer below needs.
*/
target.left = 0;
target.top = below->write_y;
target.width = below->image->Xsize;
target.height = to->valid.height;
vips_rect_intersectrect( &target, &to->valid, &target );
/* Those pixels need this area of this layer.
*/
source.left = target.left * 2;
source.top = target.top * 2;
source.width = target.width * 2;
source.height = target.height * 2;
/* Of which we have these available.
*/
vips_rect_intersectrect( &source, &from->valid, &source );
/* So these are the pixels in the layer below we can provide.
*/
target.left = source.left / 2;
target.top = source.top / 2;
target.width = source.width / 2;
target.height = source.height / 2;
/* None? All done.
*/
if( vips_rect_isempty( &target ) )
break;
(void) vips_region_shrink_method( from, to, &target,
layer->wtiff->region_shrink );
below->write_y += target.height;
/* If we've filled the strip below, let it know.
* We can either fill the region, if it's somewhere half-way
* down the image, or, if it's at the bottom, get to the last
* real line of pixels.
*/
if( below->write_y == VIPS_RECT_BOTTOM( &to->valid ) ||
below->write_y == below->height ) {
if( layer_strip_arrived( below ) )
return( -1 );
}
}
return( 0 );
}
/* A new strip has arrived! The strip has at least enough pixels in to
* write a line of tiles or a set of scanlines.
*
* - write a line of tiles / set of scanlines
* - shrink what we can to the layer below
* - move our strip down by the tile height
* - copy the overlap with the previous strip
*/
static int
layer_strip_arrived( Layer *layer )
{
Wtiff *wtiff = layer->wtiff;
int result;
VipsRect new_strip;
VipsRect overlap;
VipsRect image_area;
if( wtiff->tile )
result = wtiff_layer_write_tiles( wtiff, layer, layer->strip );
else
result = wtiff_layer_write_strip( wtiff, layer, layer->strip );
if( result )
return( -1 );
if( layer->below &&
layer_strip_shrink( layer ) )
return( -1 );
/* Position our strip down the image.
*
* Expand the strip if necessary to make sure we have an even
* number of lines.
*/
layer->y += wtiff->tileh;
new_strip.left = 0;
new_strip.top = layer->y;
new_strip.width = layer->image->Xsize;
new_strip.height = wtiff->tileh;
image_area.left = 0;
image_area.top = 0;
image_area.width = layer->image->Xsize;
image_area.height = layer->image->Ysize;
vips_rect_intersectrect( &new_strip, &image_area, &new_strip );
if( (new_strip.height & 1) == 1 )
new_strip.height += 1;
/* What pixels that we will need do we already have? Save them in
* overlap.
*/
vips_rect_intersectrect( &new_strip, &layer->strip->valid, &overlap );
if( !vips_rect_isempty( &overlap ) ) {
if( vips_region_buffer( layer->copy, &overlap ) )
return( -1 );
vips_region_copy( layer->strip, layer->copy,
&overlap, overlap.left, overlap.top );
}
if( !vips_rect_isempty( &new_strip ) ) {
if( vips_region_buffer( layer->strip, &new_strip ) )
return( -1 );
/* And copy back again.
*/
if( !vips_rect_isempty( &overlap ) )
vips_region_copy( layer->copy, layer->strip,
&overlap, overlap.left, overlap.top );
}
return( 0 );
}
/* Another few scanlines of pixels. We know the scanlines are all within the
* current page.
*/
static int
wtiff_write_lines( Wtiff *wtiff, VipsRegion *region, VipsRect *lines )
{
Layer *layer = wtiff->layer;
int page_top = wtiff->page_number * wtiff->page_height;
#ifdef DEBUG_VERBOSE
printf( "wtiff_write_lines: top %d, height %d\n",
lines->top, lines->height );
#endif/*DEBUG_VERBOSE*/
/* Keep filling the current strip of the top layer. Each time it
* fills, write a chunk of pyramid.
*
* lines is in
*/
for(;;) {
VipsRect *to = &layer->strip->valid;
VipsRect target;
VipsRect page_lines;
/* The bit of strip that needs filling.
*/
target.left = 0;
target.top = layer->write_y;
target.width = layer->image->Xsize;
target.height = to->height;
vips_rect_intersectrect( &target, to, &target );
/* region and lines are in world coordinates, we must subtract
* the top of the current page to get layer coordinates.
*/
page_lines = *lines;
page_lines.top -= page_top;
/* Clip against the lines we've been given.
*/
vips_rect_intersectrect( &target, &page_lines, &target );
/* Are we empty? All done.
*/
if( vips_rect_isempty( &target ) )
break;
/* And copy those pixels in.
*
* FIXME: If the strip fits inside the region we've just
* received, we could skip the copy. Will this happen very
* often? Unclear.
*/
target.top += page_top;
vips_region_copy( region, layer->strip,
&target, target.left, target.top - page_top );
layer->write_y += target.height;
/* We can either fill the strip, if it's somewhere half-way
* down the image, or, if it's at the bottom, get to the last
* real line of pixels.
*/
if( layer->write_y == VIPS_RECT_BOTTOM( to ) ||
layer->write_y == layer->height ) {
if( layer_strip_arrived( layer ) )
return( -1 );
}
}
return( 0 );
}
/* Copy fields.
*/
#define CopyField( tag, v ) \
if( TIFFGetField( in, tag, &v ) ) TIFFSetField( out, tag, v )
static int
wtiff_copy_tiles( Wtiff *wtiff, TIFF *out, TIFF *in )
{
const ttile_t n_tiles = TIFFNumberOfTiles( in );
tsize_t tile_size;
tdata_t buf;
ttile_t i;
if( wtiff->compression == COMPRESSION_JPEG )
tile_size = TIFFTileSize( in );
else
/* If we will be copying raw tiles we need a buffer large
* enough to hold the largest compressed tile in any page.
*
* Allocate a buffer 2x the uncompressed tile size ... much
* simpler than searching every page for the largest tile with
* TIFFTAG_TILEBYTECOUNTS.
*/
tile_size = 2 * wtiff->tls * wtiff->tileh;
buf = vips_malloc( NULL, tile_size );
for( i = 0; i < n_tiles; i++ ) {
tsize_t len;
/* If this is a JPEG-compressed TIFF, we need to decompress
* and recompress, since tiles are actually written in several
* places (coefficients go in the tile, huffman tables go
* elsewhere).
*
* For all other compression types, we can just use
* TIFFReadRawTile()/TIFFWriteRawTile().
*/
if( wtiff->compression == COMPRESSION_JPEG ) {
len = TIFFReadEncodedTile( in, i, buf, tile_size );
if( len <= 0 ||
TIFFWriteEncodedTile( out, i, buf, len ) < 0 ) {
g_free( buf );
return( -1 );
}
}
else {
len = TIFFReadRawTile( in, i, buf, tile_size );
if( len <= 0 ||
TIFFWriteRawTile( out, i, buf, len ) < 0 ) {
g_free( buf );
return( -1 );
}
}
}
g_free( buf );
return( 0 );
}
/* Copy a TIFF file ... we know we wrote it, so just copy the tags we know
* we might have set.
*/
static int
wtiff_copy_tiff( Wtiff *wtiff, TIFF *out, TIFF *in )
{
guint32 ui32;
guint16 ui16;
guint16 ui16_2;
float f;
guint16 *a;
/* All the fields we might have set.
*/
CopyField( TIFFTAG_IMAGEWIDTH, ui32 );
CopyField( TIFFTAG_IMAGELENGTH, ui32 );
CopyField( TIFFTAG_PLANARCONFIG, ui16 );
CopyField( TIFFTAG_ORIENTATION, ui16 );
CopyField( TIFFTAG_XRESOLUTION, f );
CopyField( TIFFTAG_YRESOLUTION, f );
CopyField( TIFFTAG_RESOLUTIONUNIT, ui16 );
CopyField( TIFFTAG_COMPRESSION, ui16 );
CopyField( TIFFTAG_SAMPLESPERPIXEL, ui16 );
CopyField( TIFFTAG_BITSPERSAMPLE, ui16 );
CopyField( TIFFTAG_PHOTOMETRIC, ui16 );
CopyField( TIFFTAG_ORIENTATION, ui16 );
CopyField( TIFFTAG_TILEWIDTH, ui32 );
CopyField( TIFFTAG_TILELENGTH, ui32 );
CopyField( TIFFTAG_ROWSPERSTRIP, ui32 );
CopyField( TIFFTAG_SUBFILETYPE, ui32 );
if( TIFFGetField( in, TIFFTAG_EXTRASAMPLES, &ui16, &a ) )
TIFFSetField( out, TIFFTAG_EXTRASAMPLES, ui16, a );
if( TIFFGetField( in, TIFFTAG_PAGENUMBER, &ui16, &ui16_2 ) )
TIFFSetField( out, TIFFTAG_PAGENUMBER, ui16, ui16_2 );
/* TIFFTAG_JPEGQUALITY is a pesudo-tag, so we can't copy it.
* Set explicitly from Wtiff.
*/
if( wtiff->compression == COMPRESSION_JPEG ) {
TIFFSetField( out, TIFFTAG_JPEGQUALITY, wtiff->Q );
/* Only for three-band, 8-bit images.
*/
if( wtiff->ready->Bands == 3 &&
wtiff->ready->BandFmt == VIPS_FORMAT_UCHAR ) {
/* Enable rgb->ycbcr conversion in the jpeg write.
*/
if( !wtiff->rgbjpeg &&
wtiff->Q < 90 )
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 );
}
}
#ifdef HAVE_TIFF_COMPRESSION_WEBP
/* More pseudotags we can't copy.
*/
if( wtiff->compression == COMPRESSION_WEBP ) {
TIFFSetField( out, TIFFTAG_WEBP_LEVEL, wtiff->Q );
TIFFSetField( out, TIFFTAG_WEBP_LOSSLESS, wtiff->lossless );
}
if( wtiff->compression == COMPRESSION_ZSTD ) {
TIFFSetField( out, TIFFTAG_ZSTD_LEVEL, wtiff->level );
if( wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
TIFFSetField( out,
TIFFTAG_PREDICTOR, wtiff->predictor );
}
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/
if( (wtiff->compression == COMPRESSION_ADOBE_DEFLATE ||
wtiff->compression == COMPRESSION_LZW) &&
wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE )
TIFFSetField( out, TIFFTAG_PREDICTOR, wtiff->predictor );
/* We can't copy profiles or xmp :( Set again from wtiff.
*/
if( !wtiff->strip )
if( wtiff_embed_profile( wtiff, out ) ||
wtiff_embed_xmp( wtiff, out ) ||
wtiff_embed_iptc( wtiff, out ) ||
wtiff_embed_photoshop( wtiff, out ) ||
wtiff_embed_imagedescription( wtiff, out ) )
return( -1 );
if( wtiff_copy_tiles( wtiff, out, in ) )
return( -1 );
return( 0 );
}
/* Append all of the layers we wrote to the output.
*/
static int
wtiff_gather( Wtiff *wtiff )
{
Layer *layer;
#ifdef DEBUG
printf( "wtiff_gather:\n" );
#endif /*DEBUG*/
if( wtiff->layer &&
wtiff->layer->below )
for( layer = wtiff->layer->below; layer;
layer = layer->below ) {
VipsSource *source;
TIFF *in;
#ifdef DEBUG
printf( "appending layer sub = %d ...\n", layer->sub );
#endif /*DEBUG*/
if( !(source =
vips_source_new_from_target( layer->target )) )
return( -1 );
if( !(in = vips__tiff_openin_source( source )) ) {
VIPS_UNREF( source );
return( -1 );
}
VIPS_UNREF( source );
if( wtiff_copy_tiff( wtiff, wtiff->layer->tif, in ) ) {
TIFFClose( in );
return( -1 );
}
TIFFClose( in );
if( !TIFFWriteDirectory( wtiff->layer->tif ) )
return( -1 );
}
return( 0 );
}
static int
wtiff_page_start( Wtiff *wtiff )
{
#ifdef DEBUG
printf( "wtiff_page_start: page %d\n", wtiff->page_number );
#endif /*DEBUG*/
/* Init the pyramid framework for this page. This will just make a
* single layer if we're not pyramiding.
*/
wtiff_layer_init( wtiff, &wtiff->layer, NULL,
wtiff->ready->Xsize, wtiff->page_height );
/* Fill all the layers and write the TIFF headers.
*/
if( wtiff_allocate_layers( wtiff ) )
return( -1 );
/* In ifd mode, we write the pyramid layers as subdirectories of this
* page.
*/
if( wtiff->subifd ) {
int n_layers;
Layer *p;
#ifdef DEBUG
printf( "wtiff_write_page: OME pyr mode\n" );
#endif /*DEBUG*/
for( n_layers = 0, p = wtiff->layer->below; p; p = p->below )
n_layers += 1;
if( n_layers > 0 ) {
toff_t *subifd_offsets;
/* This magic tag makes the n_layers directories we
* write after this one into subdirectories. We set
* the offsets to 0 and libtiff will fill them in
* automatically.
*/
subifd_offsets = VIPS_ARRAY( NULL, n_layers, toff_t );
memset( subifd_offsets,
0, n_layers * sizeof( toff_t ) );
TIFFSetField( wtiff->layer->tif, TIFFTAG_SUBIFD,
n_layers, subifd_offsets );
g_free( subifd_offsets );
}
}
return( 0 );
}
static int
wtiff_page_end( Wtiff *wtiff )
{
#ifdef DEBUG
printf( "wtiff_page_end: page %d\n", wtiff->page_number );
#endif /*DEBUG*/
if( !TIFFWriteDirectory( wtiff->layer->tif ) )
return( -1 );
/* Append any pyr layers, if necessary.
*/
if( wtiff->layer->below ) {
Layer *layer;
/* Free any lower pyramid resources ... this will
* TIFFClose() (but not delete) the smaller layers
* ready for us to read from them again.
*/
layer_free_all( wtiff->layer->below );
/* Append smaller layers to the main file.
*/
if( wtiff_gather( wtiff ) )
return( -1 );
/* unref all the lower targets.
*/
for( layer = wtiff->layer->below; layer; layer = layer->below )
VIPS_UNREF( layer->target );
/* ... ready for the next page.
*/
wtiff->layer->below = NULL;
}
wtiff->page_number += 1;
return( 0 );
}
/* A strip of pixels has come in from libvips. Split these strips into pages,
* and run the page start / end code.
*/
static int
wtiff_sink_disc_strip( VipsRegion *region, VipsRect *area, void *a )
{
Wtiff *wtiff = ( Wtiff *) a;
VipsRect pixels;
#ifdef DEBUG_VERBOSE
printf( "wtiff_sink_disc_strip: top %d, height %d\n",
area->top, area->height );
#endif /*DEBUG_VERBOSE*/
g_assert( area->width == wtiff->ready->Xsize );
/* Loop down this as we write scanlines into pages.
*/
pixels = *area;
do {
VipsRect page;
VipsRect lines;
/* The rect for the current page.
*/
page.left = 0;
page.top = wtiff->page_height * wtiff->page_number;
page.width = wtiff->ready->Xsize;
page.height = wtiff->page_height;
/* The scanlines we have for this page.
*/
vips_rect_intersectrect( &page, &pixels, &lines );
/* At the top of the page? Run the page start code.
*/
if( lines.top == page.top &&
wtiff_page_start( wtiff ) )
return( -1 );
/* Write the scanlines into the page.
*/
if( wtiff_write_lines( wtiff, region, &lines ) )
return( -1 );
/* Hit the end of the page? Run the page end code.
*/
if( VIPS_RECT_BOTTOM( &page ) == VIPS_RECT_BOTTOM( &lines ) &&
wtiff_page_end( wtiff ) )
return( -1 );
/* Remove the pixels we've written and loop if we have some
* still to write.
*/
pixels.top += lines.height;
pixels.height -= lines.height;
} while( !vips_rect_isempty( &pixels ) );
return( 0 );
}
int
vips__tiff_write_target( VipsImage *input, VipsTarget *target,
VipsForeignTiffCompression compression, int Q,
VipsForeignTiffPredictor predictor,
const char *profile,
gboolean tile, int tile_width, int tile_height,
gboolean pyramid,
int bitdepth,
gboolean miniswhite,
VipsForeignTiffResunit resunit, double xres, double yres,
gboolean bigtiff,
gboolean rgbjpeg,
gboolean properties, gboolean strip,
VipsRegionShrink region_shrink,
int level,
gboolean lossless,
VipsForeignDzDepth depth,
gboolean subifd,
gboolean premultiply,
int page_height )
{
Wtiff *wtiff;
vips__tiff_init();
if( !(wtiff = wtiff_new( input, target,
compression, Q, predictor, profile,
tile, tile_width, tile_height, pyramid, bitdepth,
miniswhite, resunit, xres, yres, bigtiff, rgbjpeg,
properties, strip, region_shrink, level, lossless, depth,
subifd, premultiply, page_height )) )
return( -1 );
if( vips_sink_disc( wtiff->ready, wtiff_sink_disc_strip, wtiff ) ) {
wtiff_free( wtiff );
return( -1 );
}
wtiff_free( wtiff );
return( 0 );
}
#endif /*HAVE_TIFF*/