/* 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
 */

/*

    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 "pforeign.h"
#include "tiff.h"

/* Max number of alpha channels we allow.
 */
#define MAX_ALPHA (64)

typedef struct _Layer Layer;
typedef struct _Wtiff Wtiff;

/* A layer in the pyramid.
 */
struct _Layer {
	Wtiff *wtiff;			/* Main wtiff struct */

	/* The filename for this layer, for file output.
	 */
	char *lname;			

	/* The memory area for this layer, for memory output.
	 */
	void *buf;
	size_t len;

	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;

	/* File to write to, or NULL.
	 */
	char *filename;			/* Name we write to */

	/* Memory area to output, or NULL.
	 */
	void **obuf;
	size_t *olen; 

	Layer *layer;			/* Top of pyramid */
	VipsPel *tbuf;			/* TIFF output buffer */
	int tls;			/* Tile line size */

	int compression;		/* Compression type */
	int Q;				/* JPEG q-factor, webp level */
	int predictor;			/* Predictor value */
	int tile;			/* Tile or not */
	int tilew, tileh;		/* Tile size */
	int pyramid;			/* Wtiff pyramid */
	int squash;			/* Write as small format */
	int miniswhite;			/* Wtiff as 0 == white */
        int resunit;                    /* Resolution unit (inches or cm) */
        double xres;                   	/* Resolution in X */
        double yres;                   	/* Resolution in Y */
	char *icc_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;		/* webp lossless mode */
	VipsForeignDzDepth depth;	/* Pyr depth */

	/* 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;
};

/* 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 Layer *
wtiff_layer_new( Wtiff *wtiff, Layer *above, int width, int height )
{
	Layer *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->lname = NULL;
	layer->buf = NULL;
	layer->len = 0;
	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;

	/*
	printf( "wtiff_layer_new: 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 ) 
			layer->below = wtiff_layer_new( wtiff, layer, 
				width / 2, height / 2 );
	}

	/* The name for the top layer is the output filename.
	 *
	 * We need lname to be freed automatically: it has to stay 
	 * alive until after wtiff_gather().
	 */
	if( wtiff->filename ) { 
		if( !above ) 
			layer->lname = vips_strdup( VIPS_OBJECT( wtiff->ready ),
				wtiff->filename );
		else {
			char *lname;

			lname = vips__temp_name( "%s.tif" );
			layer->lname = vips_strdup( VIPS_OBJECT( wtiff->ready ),
				lname );
			g_free( lname );
		}
	}

	return( layer );
}

static int
wtiff_embed_profile( Wtiff *wtiff, TIFF *tif )
{
	if( wtiff->icc_profile &&
		embed_profile_file( tif, wtiff->icc_profile ) )
		return( -1 );

	if( !wtiff->icc_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 orientation; 

	/* 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 );
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/

	if( (wtiff->compression == VIPS_FOREIGN_TIFF_COMPRESSION_DEFLATE ||
		wtiff->compression == VIPS_FOREIGN_TIFF_COMPRESSION_LZW) &&
		wtiff->predictor != VIPS_FOREIGN_TIFF_PREDICTOR_NONE ) 
		TIFFSetField( tif, TIFFTAG_PREDICTOR, wtiff->predictor );

	/* 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->squash ) {
		TIFFSetField( tif, TIFFTAG_SAMPLESPERPIXEL, 1 );
		TIFFSetField( tif, TIFFTAG_BITSPERSAMPLE, 1 );
		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 ) { 
			uint16 v[MAX_ALPHA];
			int i;

			/* EXTRASAMPLE_UNASSALPHA means generic extra
			 * alpha-like channels. ASSOCALPHA means pre-multipled
			 * alpha only. 
			 */
			for( i = 0; i < alpha_bands; i++ )
				v[i] = 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 );

	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;

	for( layer = wtiff->layer; layer; layer = layer->below ) {
		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 );

		if( wtiff_layer_rewind( wtiff, layer ) )
			return( -1 ); 

		if( layer->lname ) 
			layer->tif = vips__tiff_openout( 
				layer->lname, wtiff->bigtiff );
		else {
			layer->tif = vips__tiff_openout_buffer( wtiff->ready, 
				wtiff->bigtiff, &layer->buf, &layer->len );
		}
		if( !layer->tif ) 
			return( -1 );

		if( wtiff_write_header( wtiff, layer ) )  
			return( -1 );
	}

	return( 0 );
}

/* Delete any temp files we wrote.
 */
static void
wtiff_delete_temps( Wtiff *wtiff )
{
	Layer *layer;

	/* Don't delete the top layer: that's the output file.
	 */
	if( wtiff->layer &&
		wtiff->layer->below )
		for( layer = wtiff->layer->below; layer; layer = layer->below ) 
			if( layer->lname ) {
#ifndef DEBUG
				unlink( layer->lname );
				VIPS_FREE( layer->buf );
#else
				printf( "wtiff_delete_temps: leaving %s\n", 
					layer->lname );
#endif /*DEBUG*/

				layer->lname = NULL;
			}
}

/* Free a single pyramid layer.
 */
static void
layer_free( Layer *layer )
{
	VIPS_UNREF( layer->strip );
	VIPS_UNREF( layer->copy );
	VIPS_UNREF( layer->image );
	VIPS_FREE( layer->buf );
	VIPS_FREEF( TIFFClose, layer->tif );
}

/* 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 )
{
	wtiff_delete_temps( wtiff );

	VIPS_UNREF( wtiff->ready );
	VIPS_FREEF( vips_free, wtiff->tbuf );
	VIPS_FREEF( layer_free_all, wtiff->layer );
	VIPS_FREEF( vips_free, wtiff->icc_profile );
	VIPS_FREE( wtiff->filename );
	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*/
	
	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 )
{
	if( vips_check_coding_known( "vips2tiff", wtiff->input ) )
		return( -1 );

	/* "squash" float LAB down to LABQ.
	 */
	if( wtiff->squash &&
		wtiff->input->Bands == 3 &&
		wtiff->input->BandFmt == VIPS_FORMAT_FLOAT &&
		wtiff->input->Type == VIPS_INTERPRETATION_LAB ) {
		if( vips_Lab2LabQ( wtiff->input, &wtiff->ready, NULL ) )
			return( -1 );
		wtiff->squash = 0;
	}
	else {
		wtiff->ready = wtiff->input;
		g_object_ref( wtiff->ready );
	}

	return( 0 );
}

static Wtiff *
wtiff_new( VipsImage *input, const char *filename, 
	VipsForeignTiffCompression compression, int Q, 
	VipsForeignTiffPredictor predictor,
	char *profile,
	gboolean tile, int tile_width, int tile_height,
	gboolean pyramid,
	gboolean squash,
	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 )
{
	Wtiff *wtiff;

	if( !(wtiff = VIPS_NEW( NULL, Wtiff )) )
		return( NULL );
	wtiff->input = input;
	wtiff->ready = NULL;
	wtiff->filename = filename ? vips_strdup( NULL, filename ) : NULL;
	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->squash = squash;
	wtiff->miniswhite = miniswhite;
	wtiff->resunit = get_resunit( resunit );
	wtiff->xres = xres;
	wtiff->yres = yres;
	wtiff->icc_profile = vips_strdup( NULL, 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->toilet_roll = FALSE;
	wtiff->page_height = vips_image_get_page_height( input );
	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?
	 */
	if( wtiff->page_height < wtiff->ready->Ysize ) {
#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;

		/* We can't pyramid toilet roll images.
		 */
		if( wtiff->pyramid ) {
			g_warning( "%s", 
				_( "can't pyramid multi page images --- "
					"disabling pyramid" ) ); 
			wtiff->pyramid = FALSE;
		}
	}

	/* In strip mode we use tileh to set rowsperstrip, and that does not
	 * have the multiple-of-16 restriction.
	 */
	if( 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 );
		}
	}

	/* 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 );
		}
	}

	/* Can only squash 8 bit mono. 3-band float should have been squashed
	 * above.
	 */
	if( wtiff->squash &&
		!(wtiff->ready->Coding == VIPS_CODING_NONE && 
			wtiff->ready->BandFmt == VIPS_FORMAT_UCHAR &&
			wtiff->ready->Bands == 1) ) { 
		g_warning( "%s",
			_( "can only squash 1-band uchar and "
				"3-band float lab -- disabling squash" ) );
		wtiff->squash = 0;
	}

	if( wtiff->squash && 
		wtiff->compression == COMPRESSION_JPEG ) {
		g_warning( "%s", 
			_( "can't have 1-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;
	}

	/* lossless is for webp only.
	 */
#ifdef HAVE_TIFF_COMPRESSION_WEBP
	if( wtiff->lossless ) {
		if( wtiff->compression == COMPRESSION_NONE )
			wtiff->compression = COMPRESSION_WEBP;

		if( wtiff->compression != COMPRESSION_WEBP ) {
			g_warning( "%s", 
				_( "lossless is for WEBP compression only" ) );
			wtiff->lossless = FALSE;
		}
	}
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/

	/* Sizeof a line of bytes in the TIFF tile.
	 */
	if( wtiff->ready->Coding == VIPS_CODING_LABQ )
		wtiff->tls = wtiff->tilew * 3;
	else if( wtiff->squash )
		wtiff->tls = VIPS_ROUND_UP( wtiff->tilew, 8 ) / 8;
	else
		wtiff->tls = VIPS_IMAGE_SIZEOF_PEL( wtiff->ready ) * 
			wtiff->tilew;

	/* 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 ) { 
		g_warning( "%s", _( "image over 4gb, enabling bigtiff" ) );
		wtiff->bigtiff = TRUE;
	}

	/* Build the pyramid framework.
	 */
	wtiff->layer = wtiff_layer_new( wtiff, NULL, 
		wtiff->ready->Xsize, wtiff->image_height );

	/* Fill all the layers.
	 */
	if( wtiff_allocate_layers( wtiff ) ) {
		wtiff_free( wtiff );
		return( NULL );
	}

	if( tile ) 
		wtiff->tbuf = vips_malloc( NULL, 
			TIFFTileSize( wtiff->layer->tif ) );
	else
		wtiff->tbuf = vips_malloc( NULL, 
			TIFFScanlineSize( wtiff->layer->tif ) );
	if( !wtiff->tbuf ) {
		wtiff_free( wtiff );
		return( NULL );
	}

	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 1 bit TIFF.
 */
static void
eightbit2onebit( Wtiff *wtiff, VipsPel *q, VipsPel *p, int n )
{
        int x;
	VipsPel bits;

	/* Invert in miniswhite mode.
	 */
	int white = wtiff->miniswhite ? 0 : 1;
	int black = white ^ 1;

	bits = 0;
        for( x = 0; x < n; x++ ) {
		bits <<= 1;
		if( p[x] > 128 )
			bits |= white;
		else
			bits |= black;

		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));
}

/* 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->squash ) 
			eightbit2onebit( 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_tile( 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 );

		/* Have to repack pixels.
		 */
		wtiff_pack2tiff( wtiff, layer, strip, &tile, wtiff->tbuf );

#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( 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( "Writing %d pixel strip at height %d to image %s\n",
		height, area->top, 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->squash ) {
			eightbit2onebit( 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( from, to, &target );

		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_tile( 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 strip of image pixels from vips_sink_disc(). Write into the top
 * pyramid layer. 
 */
static int
write_strip( VipsRegion *region, VipsRect *area, void *a )
{
	Wtiff *wtiff = (Wtiff *) a;
	Layer *layer = wtiff->layer; 

#ifdef DEBUG
	printf( "write_strip: strip at %d, height %d\n", 
		area->top, area->height );
#endif/*DEBUG*/

	for(;;) {
		VipsRect *to = &layer->strip->valid;
		VipsRect target;

		/* 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 );

		/* Clip against what we have available.
		 */
		vips_rect_intersectrect( &target, area, &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.
		 */
		vips_region_copy( region, layer->strip, 
			&target, target.left, target.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 )

/* 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 )
{
	uint32 ui32;
	uint16 ui16;
	uint16 ui16_2;
	float f;
	tdata_t buf;
	ttile_t tile;
	ttile_t n;
	uint16 *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 );
#endif /*HAVE_TIFF_COMPRESSION_WEBP*/

	/* 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 );

	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()/TIFFWtiffRawTile() 
		 * 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 all of the lower layers we wrote to the output.
 */
static int
wtiff_gather( Wtiff *wtiff )
{
	Layer *layer;

	if( wtiff->layer &&
		wtiff->layer->below )
		for( layer = wtiff->layer->below; layer; 
			layer = layer->below ) {
			VipsSource *source;
			TIFF *in;

#ifdef DEBUG
			printf( "Appending layer %s ...\n", layer->lname );
#endif /*DEBUG*/

			if( layer->lname ) {
				if( !(source = 
					vips_source_new_from_file( 
						layer->lname )) ) 
					return( -1 );
			}
			else {
				if( !(source = vips_source_new_from_memory(
					layer->buf, layer->len )) )
					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 );
}

/* Three types of write: single image, multipage and pyramid. 
 */
static int
wtiff_write_image( Wtiff *wtiff )
{
	if( wtiff->toilet_roll ) {
		int y;

#ifdef DEBUG
		printf( "wtiff_write_image: toilet-roll mode\n" ); 
#endif /*DEBUG*/

		y = 0;
		for(;;) {
			VipsImage *page;

			if( vips_crop( wtiff->ready, &page, 
				0, y, wtiff->ready->Xsize, wtiff->page_height,
				NULL ) )
				return( -1 ); 
			if( vips_sink_disc( page, write_strip, wtiff ) ) {
				g_object_unref( page );
				return( -1 );
			}
			g_object_unref( page );

			wtiff->page_number += 1;
			y += wtiff->page_height;
			if( y >= wtiff->ready->Ysize )
				break;

			if( !TIFFWriteDirectory( wtiff->layer->tif ) || 
				wtiff_layer_rewind( wtiff, 
					wtiff->layer ) ||
				wtiff_write_header( wtiff, 
					wtiff->layer ) )
				return( -1 );
		}
	}
	else if( wtiff->pyramid ) {
#ifdef DEBUG
		printf( "wtiff_write_image: pyramid mode\n" ); 
#endif /*DEBUG*/

		if( vips_sink_disc( wtiff->ready, write_strip, wtiff ) ) 
			return( -1 );

		if( !TIFFWriteDirectory( wtiff->layer->tif ) ) 
			return( -1 );

		if( wtiff->pyramid ) { 
			/* Free lower pyramid resources ... this will 
			 * TIFFClose() (but not delete) the smaller layers 
			 * ready for us to read from them again.
			 */
			if( wtiff->layer->below )
				layer_free_all( wtiff->layer->below );

			/* Append smaller layers to the main file.
			 */
			if( wtiff_gather( wtiff ) ) 
				return( -1 );
		}
	}
	else {
#ifdef DEBUG
		printf( "wtiff_write_image: single-image mode\n" ); 
#endif /*DEBUG*/

		if( vips_sink_disc( wtiff->ready, write_strip, wtiff ) ) 
			return( -1 );
	}

	return( 0 );
}

int 
vips__tiff_write( VipsImage *input, const char *filename, 
	VipsForeignTiffCompression compression, int Q, 
	VipsForeignTiffPredictor predictor,
	char *profile,
	gboolean tile, int tile_width, int tile_height,
	gboolean pyramid,
	gboolean squash,
	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 )
{
	Wtiff *wtiff;

#ifdef DEBUG
	printf( "tiff2vips: libtiff version is \"%s\"\n", TIFFGetVersion() );
#endif /*DEBUG*/

	vips__tiff_init();

	if( !(wtiff = wtiff_new( input, filename, 
		compression, Q, predictor, profile,
		tile, tile_width, tile_height, pyramid, squash,
		miniswhite, resunit, xres, yres, bigtiff, rgbjpeg, 
		properties, strip, region_shrink, level, lossless, depth )) )
		return( -1 );

	if( wtiff_write_image( wtiff ) ) { 
		wtiff_free( wtiff );
		return( -1 );
	}

	wtiff_free( wtiff );

	return( 0 );
}

int 
vips__tiff_write_buf( VipsImage *input, 
	void **obuf, size_t *olen, 
	VipsForeignTiffCompression compression, int Q, 
	VipsForeignTiffPredictor predictor,
	char *profile,
	gboolean tile, int tile_width, int tile_height,
	gboolean pyramid,
	gboolean squash,
	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 )
{
	Wtiff *wtiff;

	vips__tiff_init();

	if( !(wtiff = wtiff_new( input, NULL, 
		compression, Q, predictor, profile,
		tile, tile_width, tile_height, pyramid, squash,
		miniswhite, resunit, xres, yres, bigtiff, rgbjpeg, 
		properties, strip, region_shrink, level, lossless, depth )) )
		return( -1 );

	wtiff->obuf = obuf;
	wtiff->olen = olen;

	if( wtiff_write_image( wtiff ) ) { 
		wtiff_free( wtiff );
		return( -1 );
	}

	/* Now close the top layer, and we'll get a pointer we can return
	 * to our caller.
	 */
	VIPS_FREEF( TIFFClose, wtiff->layer->tif );

	*obuf = wtiff->layer->buf;
	*olen = wtiff->layer->len;

	/* Now our caller owns it, we must not free it.
	 */
	wtiff->layer->buf = NULL;

	wtiff_free( wtiff );

	return( 0 );
}

#endif /*HAVE_TIFF*/
