Files
test2/source/blender/imbuf/intern/tiff.c
Jesse Yurkovich 38573d515e Cleanup: Remove unused IMB tile cache code
This removes the unused code for the IBM tile cache APIs.  These have
been unused for as far back as I could manage to search.

Since TIFF was used for the cached images, this removal will allow for
an easier review when it comes time to move TIFF to OIIO as part of
T101413.

Differential Revision: https://developer.blender.org/D16587
2022-11-23 19:31:10 -08:00

833 lines
23 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup imbuf
*/
/**
* Provides TIFF file loading and saving for Blender, via libtiff.
*
* The task of loading is complicated somewhat by the fact that Blender has
* already loaded the file into a memory buffer. libtiff is not well
* configured to handle files in memory, so a client wrapper is written to
* surround the memory and turn it into a virtual file. Currently, reading
* of TIFF files is done using libtiff's RGBAImage support. This is a
* high-level routine that loads all images as 32-bit RGBA, handling all the
* required conversions between many different TIFF types internally.
*
* Saving supports RGB, RGBA and BW (gray-scale) images correctly, with
* 8 bits per channel in all cases. The "deflate" compression algorithm is
* used to compress images.
*/
#include <string.h>
#include "imbuf.h"
#include "BLI_endian_defines.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BKE_global.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_filetype.h"
#include "IMB_colormanagement.h"
#include "IMB_colormanagement_intern.h"
#include <tiffio.h>
#ifdef WIN32
# include "utfconv.h"
#endif
/* -------------------------------------------------------------------- */
/** \name Local Declarations
* \{ */
/* Reading and writing of an in-memory TIFF file. */
static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n);
static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n);
static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence);
static int imb_tiff_CloseProc(thandle_t handle);
static toff_t imb_tiff_SizeProc(thandle_t handle);
static int imb_tiff_DummyMapProc(thandle_t fd, tdata_t *pbase, toff_t *psize);
static void imb_tiff_DummyUnmapProc(thandle_t fd, tdata_t base, toff_t size);
/** Structure for in-memory TIFF file. */
typedef struct ImbTIFFMemFile {
/** Location of first byte of TIFF file. */
const uchar *mem;
/** Current offset within the file. */
toff_t offset;
/** Size of the TIFF file. */
tsize_t size;
} ImbTIFFMemFile;
#define IMB_TIFF_GET_MEMFILE(x) ((ImbTIFFMemFile *)(x))
/** \} */
/* -------------------------------------------------------------------- */
/** \name Function Implementations
* \{ */
static void imb_tiff_DummyUnmapProc(
thandle_t fd,
tdata_t base,
/* Cannot be const, because this function implements #TIFFUnmapFileProc.
* NOLINTNEXTLINE: readability-non-const-parameter. */
toff_t size)
{
(void)fd;
(void)base;
(void)size;
}
static int imb_tiff_DummyMapProc(
thandle_t fd,
tdata_t *pbase,
/* Cannot be const, because this function implements #TIFFMapFileProc.
* NOLINTNEXTLINE: readability-non-const-parameter. */
toff_t *psize)
{
(void)fd;
(void)pbase;
(void)psize;
return 0;
}
/**
* Reads data from an in-memory TIFF file.
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
* \param data: Buffer to contain data (treat as (void *)).
* \param n: Number of bytes to read.
*
* \return Number of bytes actually read.
* 0 = EOF.
*/
static tsize_t imb_tiff_ReadProc(thandle_t handle, tdata_t data, tsize_t n)
{
tsize_t nRemaining, nCopy;
ImbTIFFMemFile *mfile;
void *srcAddr;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_ReadProc: !mfile || !mfile->mem!\n");
return 0;
}
/* find the actual number of bytes to read (copy) */
nCopy = n;
if ((tsize_t)mfile->offset >= mfile->size) {
nRemaining = 0;
}
else {
nRemaining = mfile->size - mfile->offset;
}
if (nCopy > nRemaining) {
nCopy = nRemaining;
}
/* on EOF, return immediately and read (copy) nothing */
if (nCopy <= 0) {
return 0;
}
/* all set -> do the read (copy) */
srcAddr = (void *)&(mfile->mem[mfile->offset]);
memcpy((void *)data, srcAddr, nCopy);
mfile->offset += nCopy; /* advance file ptr by copied bytes */
return nCopy;
}
/**
* Writes data to an in-memory TIFF file.
*
* NOTE: The current Blender implementation should not need this function.
* It is simply a stub.
*/
static tsize_t imb_tiff_WriteProc(thandle_t handle, tdata_t data, tsize_t n)
{
(void)handle;
(void)data;
(void)n;
printf("imb_tiff_WriteProc: this function should not be called.\n");
return (-1);
}
/**
* Seeks to a new location in an in-memory TIFF file.
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
* \param ofs: Offset value (interpreted according to whence below).
* \param whence: This can be one of three values:
* SEEK_SET - The offset is set to ofs bytes.
* SEEK_CUR - The offset is set to its current location plus ofs bytes.
* SEEK_END - (This is unsupported and will return -1, indicating an
* error).
*
* \return Resulting offset location within the file, measured in bytes from
* the beginning of the file. (-1) indicates an error.
*/
static toff_t imb_tiff_SeekProc(thandle_t handle, toff_t ofs, int whence)
{
ImbTIFFMemFile *mfile;
toff_t new_offset;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_SeekProc: !mfile || !mfile->mem!\n");
return (-1);
}
/* find the location we plan to seek to */
switch (whence) {
case SEEK_SET:
new_offset = ofs;
break;
case SEEK_CUR:
new_offset = mfile->offset + ofs;
break;
default:
/* no other types are supported - return an error */
fprintf(stderr,
"imb_tiff_SeekProc: "
"Unsupported TIFF SEEK type.\n");
return (-1);
}
/* set the new location */
mfile->offset = new_offset;
return mfile->offset;
}
/**
* Closes (virtually) an in-memory TIFF file.
*
* NOTE: All this function actually does is sets the data pointer within the
* TIFF file to NULL. That should trigger assertion errors if attempts
* are made to access the file after that point. However, no such
* attempts should ever be made (in theory).
*
* \param handle: Handle of the TIFF file (pointer to #ImbTIFFMemFile).
*
* \return 0
*/
static int imb_tiff_CloseProc(thandle_t handle)
{
ImbTIFFMemFile *mfile;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_CloseProc: !mfile || !mfile->mem!\n");
return 0;
}
/* virtually close the file */
mfile->mem = NULL;
mfile->offset = 0;
mfile->size = 0;
return 0;
}
/**
* Returns the size of an in-memory TIFF file in bytes.
*
* \return Size of file (in bytes).
*/
static toff_t imb_tiff_SizeProc(thandle_t handle)
{
ImbTIFFMemFile *mfile;
/* get the pointer to the in-memory file */
mfile = IMB_TIFF_GET_MEMFILE(handle);
if (!mfile || !mfile->mem) {
fprintf(stderr, "imb_tiff_SizeProc: !mfile || !mfile->mem!\n");
return 0;
}
/* return the size */
return (toff_t)(mfile->size);
}
static TIFF *imb_tiff_client_open(ImbTIFFMemFile *memFile, const uchar *mem, size_t size)
{
/* open the TIFF client layer interface to the in-memory file */
memFile->mem = mem;
memFile->offset = 0;
memFile->size = size;
return TIFFClientOpen("(Blender TIFF Interface Layer)",
"r",
(thandle_t)(memFile),
imb_tiff_ReadProc,
imb_tiff_WriteProc,
imb_tiff_SeekProc,
imb_tiff_CloseProc,
imb_tiff_SizeProc,
imb_tiff_DummyMapProc,
imb_tiff_DummyUnmapProc);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Load TIFF
* \{ */
/**
* Checks whether a given memory buffer contains a TIFF file.
*
* This method uses the format identifiers from:
* http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-9.html
* The first four bytes of big-endian and little-endian TIFF files
* respectively are (hex):
* 4d 4d 00 2a
* 49 49 2a 00
* Note that TIFF files on *any* platform can be either big- or little-endian;
* it's not platform-specific.
*
* AFAICT, libtiff doesn't provide a method to do this automatically, and
* hence my manual comparison. - Jonathan Merritt (lancelet) 4th Sept 2005.
*/
#define IMB_TIFF_NCB 4 /* number of comparison bytes used */
bool imb_is_a_tiff(const uchar *buf, size_t size)
{
const char big_endian[IMB_TIFF_NCB] = {0x4d, 0x4d, 0x00, 0x2a};
const char lil_endian[IMB_TIFF_NCB] = {0x49, 0x49, 0x2a, 0x00};
if (size < IMB_TIFF_NCB) {
return false;
}
return ((memcmp(big_endian, buf, IMB_TIFF_NCB) == 0) ||
(memcmp(lil_endian, buf, IMB_TIFF_NCB) == 0));
}
static void scanline_contig_16bit(float *rectf, const ushort *sbuf, int scanline_w, int spp)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + 0] = sbuf[i * spp + 0] / 65535.0;
rectf[i * 4 + 1] = (spp >= 3) ? sbuf[i * spp + 1] / 65535.0 : sbuf[i * spp + 0] / 65535.0;
rectf[i * 4 + 2] = (spp >= 3) ? sbuf[i * spp + 2] / 65535.0 : sbuf[i * spp + 0] / 65535.0;
rectf[i * 4 + 3] = (spp == 4) ? (sbuf[i * spp + 3] / 65535.0) : 1.0;
}
}
static void scanline_contig_32bit(float *rectf, const float *fbuf, int scanline_w, int spp)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + 0] = fbuf[i * spp + 0];
rectf[i * 4 + 1] = (spp >= 3) ? fbuf[i * spp + 1] : fbuf[i * spp + 0];
rectf[i * 4 + 2] = (spp >= 3) ? fbuf[i * spp + 2] : fbuf[i * spp + 0];
rectf[i * 4 + 3] = (spp == 4) ? fbuf[i * spp + 3] : 1.0f;
}
}
static void scanline_separate_16bit(float *rectf, const ushort *sbuf, int scanline_w, int chan)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + chan] = sbuf[i] / 65535.0;
}
}
static void scanline_separate_32bit(float *rectf, const float *fbuf, int scanline_w, int chan)
{
int i;
for (i = 0; i < scanline_w; i++) {
rectf[i * 4 + chan] = fbuf[i];
}
}
static void imb_read_tiff_resolution(ImBuf *ibuf, TIFF *image)
{
uint16_t unit;
float xres;
float yres;
TIFFGetFieldDefaulted(image, TIFFTAG_RESOLUTIONUNIT, &unit);
TIFFGetFieldDefaulted(image, TIFFTAG_XRESOLUTION, &xres);
TIFFGetFieldDefaulted(image, TIFFTAG_YRESOLUTION, &yres);
if (unit == RESUNIT_CENTIMETER) {
ibuf->ppm[0] = (double)xres * 100.0;
ibuf->ppm[1] = (double)yres * 100.0;
}
else {
ibuf->ppm[0] = (double)xres / 0.0254;
ibuf->ppm[1] = (double)yres / 0.0254;
}
}
/*
* Use the libTIFF scanline API to read a TIFF image.
* This method is most flexible and can handle multiple different bit depths
* and RGB channel orderings.
*/
static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image)
{
ImBuf *tmpibuf = NULL;
int success = 0;
short bitspersample, spp, config;
size_t scanline;
int ib_flag = 0, row, chan;
float *fbuf = NULL;
ushort *sbuf = NULL;
TIFFGetField(image, TIFFTAG_BITSPERSAMPLE, &bitspersample);
TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp); /* number of 'channels' */
TIFFGetField(image, TIFFTAG_PLANARCONFIG, &config);
if (spp == 4) {
/* HACK: this is really tricky hack, which is only needed to force libtiff
* do not touch RGB channels when there's alpha channel present
* The thing is: libtiff will premul RGB if alpha mode is set to
* unassociated, which really conflicts with blender's assumptions
*
* Alternative would be to unpremul after load, but it'll be really
* lossy and unwanted behavior
*
* So let's keep this thing here for until proper solution is found (sergey)
*/
ushort extraSampleTypes[1];
extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes);
}
imb_read_tiff_resolution(ibuf, image);
scanline = TIFFScanlineSize(image);
if (bitspersample == 32) {
ib_flag = IB_rectfloat;
fbuf = (float *)_TIFFmalloc(scanline);
if (!fbuf) {
goto cleanup;
}
}
else if (bitspersample == 16) {
ib_flag = IB_rectfloat;
sbuf = (ushort *)_TIFFmalloc(scanline);
if (!sbuf) {
goto cleanup;
}
}
else {
ib_flag = IB_rect;
}
tmpibuf = IMB_allocImBuf(ibuf->x, ibuf->y, ibuf->planes, ib_flag);
if (!tmpibuf) {
goto cleanup;
}
/* simple RGBA image */
if (!ELEM(bitspersample, 32, 16)) {
success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0);
}
/* contiguous channels: RGBRGBRGB */
else if (config == PLANARCONFIG_CONTIG) {
for (row = 0; row < ibuf->y; row++) {
size_t ib_offset = (size_t)ibuf->x * 4 * ((size_t)ibuf->y - ((size_t)row + 1));
if (bitspersample == 32) {
success |= TIFFReadScanline(image, fbuf, row, 0);
scanline_contig_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, spp);
}
else if (bitspersample == 16) {
success |= TIFFReadScanline(image, sbuf, row, 0);
scanline_contig_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, spp);
}
}
/* Separate channels: RRRGGGBBB. */
}
else if (config == PLANARCONFIG_SEPARATE) {
/* imbufs always have 4 channels of data, so we iterate over all of them
* but only fill in from the TIFF scanline where necessary. */
for (chan = 0; chan < 4; chan++) {
for (row = 0; row < ibuf->y; row++) {
size_t ib_offset = (size_t)ibuf->x * 4 * ((size_t)ibuf->y - ((size_t)row + 1));
if (bitspersample == 32) {
if (chan == 3 && spp == 3) { /* fill alpha if only RGB TIFF */
copy_vn_fl(fbuf, ibuf->x, 1.0f);
}
else if (chan >= spp) { /* For gray-scale, duplicate first channel into G and B. */
success |= TIFFReadScanline(image, fbuf, row, 0);
}
else {
success |= TIFFReadScanline(image, fbuf, row, chan);
}
scanline_separate_32bit(tmpibuf->rect_float + ib_offset, fbuf, ibuf->x, chan);
}
else if (bitspersample == 16) {
if (chan == 3 && spp == 3) { /* fill alpha if only RGB TIFF */
copy_vn_ushort(sbuf, ibuf->x, 65535);
}
else if (chan >= spp) { /* For gray-scale, duplicate first channel into G and B. */
success |= TIFFReadScanline(image, fbuf, row, 0);
}
else {
success |= TIFFReadScanline(image, sbuf, row, chan);
}
scanline_separate_16bit(tmpibuf->rect_float + ib_offset, sbuf, ibuf->x, chan);
}
}
}
}
if (success) {
/* Code seems to be not needed for 16 bits TIFF, on PPC G5 OSX (ton) */
if (bitspersample < 16) {
if (ENDIAN_ORDER == B_ENDIAN) {
IMB_convert_rgba_to_abgr(tmpibuf);
}
}
/* assign rect last */
if (tmpibuf->rect_float) {
ibuf->rect_float = tmpibuf->rect_float;
}
else {
ibuf->rect = tmpibuf->rect;
}
ibuf->mall |= ib_flag;
ibuf->flags |= ib_flag;
tmpibuf->mall &= ~ib_flag;
}
cleanup:
if (bitspersample == 32) {
_TIFFfree(fbuf);
}
else if (bitspersample == 16) {
_TIFFfree(sbuf);
}
IMB_freeImBuf(tmpibuf);
return success;
}
void imb_inittiff(void)
{
if (!(G.debug & G_DEBUG)) {
TIFFSetErrorHandler(NULL);
}
}
ImBuf *imb_loadtiff(const uchar *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
TIFF *image = NULL;
ImBuf *ibuf = NULL;
ImbTIFFMemFile memFile;
uint32_t width, height;
short spp;
int ib_depth;
/* Check whether or not we have a TIFF file. */
if (imb_is_a_tiff(mem, size) == 0) {
return NULL;
}
/* both 8 and 16 bit PNGs are default to standard byte colorspace */
colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE);
image = imb_tiff_client_open(&memFile, mem, size);
if (image == NULL) {
printf("imb_loadtiff: could not open TIFF IO layer.\n");
return NULL;
}
/* allocate the image buffer */
TIFFGetField(image, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(image, TIFFTAG_IMAGELENGTH, &height);
TIFFGetField(image, TIFFTAG_SAMPLESPERPIXEL, &spp);
ib_depth = spp * 8;
ibuf = IMB_allocImBuf(width, height, ib_depth, 0);
if (ibuf) {
ibuf->ftype = IMB_FTYPE_TIF;
}
else {
fprintf(stderr,
"imb_loadtiff: could not allocate memory for TIFF "
"image.\n");
TIFFClose(image);
return NULL;
}
/* get alpha mode from file header */
if (flags & IB_alphamode_detect) {
if (spp == 4) {
ushort extra, *extraSampleTypes;
const int found = TIFFGetField(image, TIFFTAG_EXTRASAMPLES, &extra, &extraSampleTypes);
if (found && (extraSampleTypes[0] == EXTRASAMPLE_ASSOCALPHA)) {
ibuf->flags |= IB_alphamode_premul;
}
}
}
/* if testing, we're done */
if (flags & IB_test) {
TIFFClose(image);
return ibuf;
}
/* read pixels */
if (!imb_read_tiff_pixels(ibuf, image)) {
fprintf(stderr, "imb_loadtiff: Failed to read tiff image.\n");
TIFFClose(image);
return NULL;
}
/* close the client layer interface to the in-memory file */
TIFFClose(image);
/* return successfully */
return ibuf;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Save TIFF
* \{ */
bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags)
{
TIFF *image = NULL;
uint16_t samplesperpixel, bitspersample;
size_t npixels;
uchar *pixels = NULL;
uchar *from = NULL, *to = NULL;
ushort *pixels16 = NULL, *to16 = NULL;
float *fromf = NULL;
float xres, yres;
int x, y, from_i, to_i, i;
int compress_mode = COMPRESSION_NONE;
/* check for a valid number of bytes per pixel. Like the PNG writer,
* the TIFF writer supports 1, 3 or 4 bytes per pixel, corresponding
* to gray, RGB, RGBA respectively. */
samplesperpixel = (uint16_t)((ibuf->planes + 7) >> 3);
if ((samplesperpixel > 4) || (samplesperpixel == 2)) {
fprintf(stderr,
"imb_savetiff: unsupported number of bytes per "
"pixel: %d\n",
samplesperpixel);
return 0;
}
if ((ibuf->foptions.flag & TIF_16BIT) && ibuf->rect_float) {
bitspersample = 16;
}
else {
bitspersample = 8;
}
if (ibuf->foptions.flag & TIF_COMPRESS_DEFLATE) {
compress_mode = COMPRESSION_DEFLATE;
}
else if (ibuf->foptions.flag & TIF_COMPRESS_LZW) {
compress_mode = COMPRESSION_LZW;
}
else if (ibuf->foptions.flag & TIF_COMPRESS_PACKBITS) {
compress_mode = COMPRESSION_PACKBITS;
}
/* open TIFF file for writing */
if (flags & IB_mem) {
/* Failed to allocate TIFF in memory. */
fprintf(stderr,
"imb_savetiff: creation of in-memory TIFF files is "
"not yet supported.\n");
return 0;
}
/* create image as a file */
#ifdef WIN32
wchar_t *wname = alloc_utf16_from_8(filepath, 0);
image = TIFFOpenW(wname, "w");
free(wname);
#else
image = TIFFOpen(filepath, "w");
#endif
if (image == NULL) {
fprintf(stderr, "imb_savetiff: could not open TIFF for writing.\n");
return 0;
}
/* allocate array for pixel data */
npixels = ibuf->x * ibuf->y;
if (bitspersample == 16) {
pixels16 = (ushort *)_TIFFmalloc(npixels * samplesperpixel * sizeof(ushort));
}
else {
pixels = (uchar *)_TIFFmalloc(npixels * samplesperpixel * sizeof(uchar));
}
if (pixels == NULL && pixels16 == NULL) {
fprintf(stderr, "imb_savetiff: could not allocate pixels array.\n");
TIFFClose(image);
return 0;
}
/* setup pointers */
if (bitspersample == 16) {
fromf = ibuf->rect_float;
to16 = pixels16;
}
else {
from = (uchar *)ibuf->rect;
to = pixels;
}
/* setup samples per pixel */
TIFFSetField(image, TIFFTAG_BITSPERSAMPLE, bitspersample);
TIFFSetField(image, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
if (samplesperpixel == 4) {
ushort extraSampleTypes[1];
if (bitspersample == 16) {
extraSampleTypes[0] = EXTRASAMPLE_ASSOCALPHA;
}
else {
extraSampleTypes[0] = EXTRASAMPLE_UNASSALPHA;
}
/* RGBA images */
TIFFSetField(image, TIFFTAG_EXTRASAMPLES, 1, extraSampleTypes);
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
}
else if (samplesperpixel == 3) {
/* RGB images */
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
}
else if (samplesperpixel == 1) {
/* Gray-scale images, 1 channel */
TIFFSetField(image, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
}
/* copy pixel data. While copying, we flip the image vertically. */
const int channels_in_float = ibuf->channels ? ibuf->channels : 4;
for (x = 0; x < ibuf->x; x++) {
for (y = 0; y < ibuf->y; y++) {
from_i = ((size_t)channels_in_float) * (y * ibuf->x + x);
to_i = samplesperpixel * ((ibuf->y - y - 1) * ibuf->x + x);
if (pixels16) {
/* convert from float source */
float rgb[4];
if (ELEM(channels_in_float, 3, 4)) {
if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) {
/* Float buffer was managed already, no need in color
* space conversion.
*/
copy_v3_v3(rgb, &fromf[from_i]);
}
else {
/* Standard linear-to-SRGB conversion if float buffer wasn't managed. */
linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]);
}
if (channels_in_float == 4) {
rgb[3] = fromf[from_i + 3];
}
else {
rgb[3] = 1.0f;
}
}
else {
if (ibuf->float_colorspace || (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA)) {
rgb[0] = fromf[from_i];
}
else {
rgb[0] = linearrgb_to_srgb(fromf[from_i]);
}
rgb[1] = rgb[2] = rgb[0];
rgb[3] = 1.0f;
}
for (i = 0; i < samplesperpixel; i++, to_i++) {
to16[to_i] = unit_float_to_ushort_clamp(rgb[i]);
}
}
else {
for (i = 0; i < samplesperpixel; i++, to_i++, from_i++) {
to[to_i] = from[from_i];
}
}
}
}
/* write the actual TIFF file */
TIFFSetField(image, TIFFTAG_IMAGEWIDTH, ibuf->x);
TIFFSetField(image, TIFFTAG_IMAGELENGTH, ibuf->y);
TIFFSetField(image, TIFFTAG_ROWSPERSTRIP, ibuf->y);
TIFFSetField(image, TIFFTAG_COMPRESSION, compress_mode);
TIFFSetField(image, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
TIFFSetField(image, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
if (ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) {
xres = (float)(ibuf->ppm[0] * 0.0254);
yres = (float)(ibuf->ppm[1] * 0.0254);
}
else {
xres = yres = IMB_DPI_DEFAULT;
}
TIFFSetField(image, TIFFTAG_XRESOLUTION, xres);
TIFFSetField(image, TIFFTAG_YRESOLUTION, yres);
TIFFSetField(image, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
if (TIFFWriteEncodedStrip(image,
0,
(bitspersample == 16) ? (uchar *)pixels16 : pixels,
(size_t)ibuf->x * ibuf->y * samplesperpixel * bitspersample / 8) ==
-1) {
fprintf(stderr, "imb_savetiff: Could not write encoded TIFF.\n");
TIFFClose(image);
if (pixels) {
_TIFFfree(pixels);
}
if (pixels16) {
_TIFFfree(pixels16);
}
return 1;
}
/* close the TIFF file */
TIFFClose(image);
if (pixels) {
_TIFFfree(pixels);
}
if (pixels16) {
_TIFFfree(pixels16);
}
return 1;
}
/** \} */