Alien-FreeImage
view release on metacpan or search on metacpan
src/Source/FreeImage/PluginTIFF.cpp view on Meta::CPAN
}
}
}
// ... or read Photoshop thumbnail
if(!thumbnail) {
uint32 ps_size = 0;
void *ps_data = NULL;
if(TIFFGetField(tiff, TIFFTAG_PHOTOSHOP, &ps_size, &ps_data)) {
FIMEMORY *handle = FreeImage_OpenMemory((BYTE*)ps_data, ps_size);
FreeImageIO io;
SetMemoryIO(&io);
psdParser parser;
parser.ReadImageResources(&io, handle, ps_size);
FreeImage_SetThumbnail(dib, parser.GetThumbnail());
FreeImage_CloseMemory(handle);
}
}
// release thumbnail
FreeImage_Unload(thumbnail);
}
// --------------------------------------------------------------------------
static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
if (!handle || !data ) {
return NULL;
}
TIFF *tif = NULL;
uint32 height = 0;
uint32 width = 0;
uint16 bitspersample = 1;
uint16 samplesperpixel = 1;
uint32 rowsperstrip = (uint32)-1;
uint16 photometric = PHOTOMETRIC_MINISWHITE;
uint16 compression = (uint16)-1;
uint16 planar_config;
FIBITMAP *dib = NULL;
uint32 iccSize = 0; // ICC profile length
void *iccBuf = NULL; // ICC profile data
const BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
try {
fi_TIFFIO *fio = (fi_TIFFIO*)data;
tif = fio->tif;
if (page != -1) {
if (!tif || !TIFFSetDirectory(tif, (uint16)page)) {
throw "Error encountered while opening TIFF file";
}
}
const BOOL asCMYK = (flags & TIFF_CMYK) == TIFF_CMYK;
// first, get the photometric, the compression and basic metadata
// ---------------------------------------------------------------------------------
TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric);
TIFFGetField(tif, TIFFTAG_COMPRESSION, &compression);
// check for HDR formats
// ---------------------------------------------------------------------------------
if(photometric == PHOTOMETRIC_LOGLUV) {
// check the compression
if(compression != COMPRESSION_SGILOG && compression != COMPRESSION_SGILOG24) {
throw "Only support SGILOG compressed LogLuv data";
}
// set decoder to output in IEEE 32-bit float XYZ values
TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT);
}
// ---------------------------------------------------------------------------------
TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width);
TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height);
TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samplesperpixel);
TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bitspersample);
TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rowsperstrip);
TIFFGetField(tif, TIFFTAG_ICCPROFILE, &iccSize, &iccBuf);
TIFFGetFieldDefaulted(tif, TIFFTAG_PLANARCONFIG, &planar_config);
// check for unsupported formats
// ---------------------------------------------------------------------------------
if(IsValidBitsPerSample(photometric, bitspersample) == FALSE) {
FreeImage_OutputMessageProc(s_format_id,
"Unable to handle this format: bitspersample = %d, samplesperpixel = %d, photometric = %d",
(int)bitspersample, (int)samplesperpixel, (int)photometric);
throw (char*)NULL;
}
// ---------------------------------------------------------------------------------
// get image data type
FREE_IMAGE_TYPE image_type = ReadImageType(tif, bitspersample, samplesperpixel);
// get the most appropriate loading method
TIFFLoadMethod loadMethod = FindLoadMethod(tif, image_type, flags);
// ---------------------------------------------------------------------------------
if(loadMethod == LoadAsRBGA) {
// ---------------------------------------------------------------------------------
// RGB[A] loading using the TIFFReadRGBAImage() API
// ---------------------------------------------------------------------------------
BOOL has_alpha = FALSE;
// Read the whole image into one big RGBA buffer and then
// convert it to a DIB. This is using the traditional
// TIFFReadRGBAImage() API that we trust.
uint32 *raster = NULL;
if(!header_only) {
raster = (uint32*)_TIFFmalloc(width * height * sizeof(uint32));
if (raster == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// read the image in one chunk into an RGBA array
if (!TIFFReadRGBAImage(tif, width, height, raster, 1)) {
_TIFFfree(raster);
throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
}
}
// TIFFReadRGBAImage always deliveres 3 or 4 samples per pixel images
// (RGB or RGBA, see below). Cut-off possibly present channels (additional
// alpha channels) from e.g. Photoshop. Any CMYK(A..) is now treated as RGB,
// any additional alpha channel on RGB(AA..) is lost on conversion to RGB(A)
if(samplesperpixel > 4) { // TODO Write to Extra Channels
FreeImage_OutputMessageProc(s_format_id, "Warning: %d additional alpha channel(s) ignored", samplesperpixel-4);
samplesperpixel = 4;
}
// create a new DIB (take care of different samples-per-pixel in case
// of converted CMYK image (RGB conversion is on sample per pixel less)
if (photometric == PHOTOMETRIC_SEPARATED && samplesperpixel == 4) {
samplesperpixel = 3;
}
dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
if (dib == NULL) {
// free the raster pointer and output an error if allocation failed
if(raster) {
_TIFFfree(raster);
}
throw FI_MSG_ERROR_DIB_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
if(!header_only) {
// read the raster lines and save them in the DIB
// with RGB mode, we have to change the order of the 3 samples RGB
// We use macros for extracting components from the packed ABGR
// form returned by TIFFReadRGBAImage.
uint32 *row = &raster[0];
if (samplesperpixel == 4) {
// 32-bit RGBA
for (uint32 y = 0; y < height; y++) {
BYTE *bits = FreeImage_GetScanLine(dib, y);
for (uint32 x = 0; x < width; x++) {
bits[FI_RGBA_BLUE] = (BYTE)TIFFGetB(row[x]);
bits[FI_RGBA_GREEN] = (BYTE)TIFFGetG(row[x]);
bits[FI_RGBA_RED] = (BYTE)TIFFGetR(row[x]);
bits[FI_RGBA_ALPHA] = (BYTE)TIFFGetA(row[x]);
if (bits[FI_RGBA_ALPHA] != 0) {
has_alpha = TRUE;
}
bits += 4;
}
row += width;
}
} else {
// 24-bit RGB
for (uint32 y = 0; y < height; y++) {
BYTE *bits = FreeImage_GetScanLine(dib, y);
for (uint32 x = 0; x < width; x++) {
bits[FI_RGBA_BLUE] = (BYTE)TIFFGetB(row[x]);
bits[FI_RGBA_GREEN] = (BYTE)TIFFGetG(row[x]);
bits[FI_RGBA_RED] = (BYTE)TIFFGetR(row[x]);
bits += 3;
}
row += width;
}
}
_TIFFfree(raster);
}
// ### Not correct when header only
FreeImage_SetTransparent(dib, has_alpha);
} else if(loadMethod == LoadAs8BitTrns) {
// ---------------------------------------------------------------------------------
// 8-bit + 8-bit alpha layer loading
// ---------------------------------------------------------------------------------
// create a new 8-bit DIB
dib = CreateImageType(header_only, image_type, width, height, bitspersample, MIN<uint16>(2, samplesperpixel));
if (dib == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
// set up the colormap based on photometric
ReadPalette(tif, photometric, bitspersample, dib);
// calculate the line + pitch (separate for scr & dest)
const tmsize_t src_line = TIFFScanlineSize(tif);
// here, the pitch is 2x less than the original as we only keep the first layer
int dst_pitch = FreeImage_GetPitch(dib);
// transparency table for 8-bit + 8-bit alpha images
BYTE trns[256];
// clear the transparency table
memset(trns, 0xFF, 256 * sizeof(BYTE));
// In the tiff file the lines are saved from up to down
// In a DIB the lines must be saved from down to up
BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
// read the tiff lines and save them in the DIB
if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
if(buf == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y += rowsperstrip) {
int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
free(buf);
throw FI_MSG_ERROR_PARSING;
}
for (int l = 0; l < nrow; l++) {
BYTE *p = bits;
BYTE *b = buf + l * src_line;
for(uint32 x = 0; x < (uint32)(src_line / samplesperpixel); x++) {
// copy the 8-bit layer
*p = b[0];
// convert the 8-bit alpha layer to a trns table
trns[ b[0] ] = b[1];
p++;
b += samplesperpixel;
}
bits -= dst_pitch;
}
}
free(buf);
}
else if(planar_config == PLANARCONFIG_SEPARATE && !header_only) {
tmsize_t stripsize = TIFFStripSize(tif) * sizeof(BYTE);
BYTE *buf = (BYTE*)malloc(2 * stripsize);
if(buf == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
BYTE *grey = buf;
BYTE *alpha = buf + stripsize;
for (uint32 y = 0; y < height; y += rowsperstrip) {
int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), grey, nrow * src_line) == -1) {
free(buf);
throw FI_MSG_ERROR_PARSING;
}
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 1), alpha, nrow * src_line) == -1) {
free(buf);
throw FI_MSG_ERROR_PARSING;
}
for (int l = 0; l < nrow; l++) {
BYTE *p = bits;
BYTE *g = grey + l * src_line;
BYTE *a = alpha + l * src_line;
for(uint32 x = 0; x < (uint32)(src_line); x++) {
// copy the 8-bit layer
*p = g[0];
// convert the 8-bit alpha layer to a trns table
trns[ g[0] ] = a[0];
p++;
g++;
a++;
}
bits -= dst_pitch;
}
}
free(buf);
}
FreeImage_SetTransparencyTable(dib, &trns[0], 256);
FreeImage_SetTransparent(dib, TRUE);
} else if(loadMethod == LoadAsCMYK) {
// ---------------------------------------------------------------------------------
// CMYK loading
// ---------------------------------------------------------------------------------
// At this place, samplesperpixel could be > 4, esp. when a CMYK(A) format
// is recognized. Where all other formats are handled straight-forward, this
// format has to be handled special
BOOL isCMYKA = (photometric == PHOTOMETRIC_SEPARATED) && (samplesperpixel > 4);
// We use a temp dib to store the alpha for the CMYKA to RGBA conversion
// NOTE this is until we have Extra channels implementation.
// Also then it will be possible to merge LoadAsCMYK with LoadAsGenericStrip
FIBITMAP *alpha = NULL;
unsigned alpha_pitch = 0;
BYTE *alpha_bits = NULL;
unsigned alpha_Bpp = 0;
if(isCMYKA && !asCMYK && !header_only) {
if(bitspersample == 16) {
alpha = FreeImage_AllocateT(FIT_UINT16, width, height);
} else if (bitspersample == 8) {
alpha = FreeImage_Allocate(width, height, 8);
}
if(!alpha) {
FreeImage_OutputMessageProc(s_format_id, "Failed to allocate temporary alpha channel");
} else {
alpha_bits = FreeImage_GetScanLine(alpha, height - 1);
alpha_pitch = FreeImage_GetPitch(alpha);
alpha_Bpp = FreeImage_GetBPP(alpha) / 8;
}
}
// create a new DIB
const uint16 chCount = MIN<uint16>(samplesperpixel, 4);
dib = CreateImageType(header_only, image_type, width, height, bitspersample, chCount);
if (dib == NULL) {
FreeImage_Unload(alpha);
throw FI_MSG_ERROR_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
if(!header_only) {
// calculate the line + pitch (separate for scr & dest)
const tmsize_t src_line = TIFFScanlineSize(tif);
const tmsize_t dst_line = FreeImage_GetLine(dib);
const unsigned dib_pitch = FreeImage_GetPitch(dib);
const unsigned dibBpp = FreeImage_GetBPP(dib) / 8;
const unsigned Bpc = dibBpp / chCount;
const unsigned srcBpp = bitspersample * samplesperpixel / 8;
assert(Bpc <= 2); //< CMYK is only BYTE or SHORT
// In the tiff file the lines are save from up to down
// In a DIB the lines must be saved from down to up
BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
// read the tiff lines and save them in the DIB
BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
if(buf == NULL) {
FreeImage_Unload(alpha);
throw FI_MSG_ERROR_MEMORY;
}
if(planar_config == PLANARCONFIG_CONTIG) {
// - loop for strip blocks -
for (uint32 y = 0; y < height; y += rowsperstrip) {
const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, strips * src_line) == -1) {
free(buf);
FreeImage_Unload(alpha);
throw FI_MSG_ERROR_PARSING;
}
// - loop for strips -
if(src_line != dst_line) {
// CMYKA+
if(alpha) {
for (int l = 0; l < strips; l++) {
for(BYTE *pixel = bits, *al_pixel = alpha_bits, *src_pixel = buf + l * src_line; pixel < bits + dib_pitch; pixel += dibBpp, al_pixel += alpha_Bpp, src_pixel += srcBpp) {
// copy pixel byte by byte
BYTE b = 0;
for( ; b < dibBpp; ++b) {
pixel[b] = src_pixel[b];
}
// TODO write the remaining bytes to extra channel(s)
// HACK write the first alpha to a separate dib (assume BYTE or WORD)
al_pixel[0] = src_pixel[b];
if(Bpc > 1) {
al_pixel[1] = src_pixel[b + 1];
}
}
bits -= dib_pitch;
alpha_bits -= alpha_pitch;
}
}
else {
// alpha/extra channels alloc failed
for (int l = 0; l < strips; l++) {
for(BYTE* pixel = bits, * src_pixel = buf + l * src_line; pixel < bits + dst_line; pixel += dibBpp, src_pixel += srcBpp) {
AssignPixel(pixel, src_pixel, dibBpp);
}
bits -= dib_pitch;
}
}
}
else {
// CMYK to CMYK
for (int l = 0; l < strips; l++) {
BYTE *b = buf + l * src_line;
memcpy(bits, b, src_line);
bits -= dib_pitch;
}
}
} // height
}
else if(planar_config == PLANARCONFIG_SEPARATE) {
BYTE *dib_strip = bits;
BYTE *al_strip = alpha_bits;
// - loop for strip blocks -
for (uint32 y = 0; y < height; y += rowsperstrip) {
const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
// - loop for channels (planes) -
for(uint16 sample = 0; sample < samplesperpixel; sample++) {
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf, strips * src_line) == -1) {
free(buf);
FreeImage_Unload(alpha);
throw FI_MSG_ERROR_PARSING;
}
BYTE *dst_strip = dib_strip;
unsigned dst_pitch = dib_pitch;
uint16 ch = sample;
unsigned Bpp = dibBpp;
if(sample >= chCount) {
// TODO Write to Extra Channel
// HACK redirect write to temp alpha
if(alpha && sample == chCount) {
dst_strip = al_strip;
dst_pitch = alpha_pitch;
ch = 0;
Bpp = alpha_Bpp;
}
else {
break;
}
}
const unsigned channelOffset = ch * Bpc;
// - loop for strips in block -
BYTE *src_line_begin = buf;
BYTE *dst_line_begin = dst_strip;
for (int l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch ) {
// - loop for pixels in strip -
const BYTE* const src_line_end = src_line_begin + src_line;
for (BYTE *src_bits = src_line_begin, * dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) {
AssignPixel(dst_bits + channelOffset, src_bits, Bpc);
} // line
} // strips
} // channels
// done with a strip block, incr to the next
dib_strip -= strips * dib_pitch;
al_strip -= strips * alpha_pitch;
} //< height
}
free(buf);
if(!asCMYK) {
ConvertCMYKtoRGBA(dib);
// The ICC Profile is invalid, clear it
iccSize = 0;
iccBuf = NULL;
if(isCMYKA) {
// HACK until we have Extra channels. (ConvertCMYKtoRGBA will then do the work)
FreeImage_SetChannel(dib, alpha, FICC_ALPHA);
FreeImage_Unload(alpha);
alpha = NULL;
}
else {
FIBITMAP *t = RemoveAlphaChannel(dib);
if(t) {
FreeImage_Unload(dib);
dib = t;
}
else {
FreeImage_OutputMessageProc(s_format_id, "Cannot allocate memory for buffer. CMYK image converted to RGB + pending Alpha");
}
}
}
} // !header_only
} else if(loadMethod == LoadAsGenericStrip) {
// ---------------------------------------------------------------------------------
// Generic loading
// ---------------------------------------------------------------------------------
// create a new DIB
const uint16 chCount = MIN<uint16>(samplesperpixel, 4);
dib = CreateImageType(header_only, image_type, width, height, bitspersample, chCount);
if (dib == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
// set up the colormap based on photometric
ReadPalette(tif, photometric, bitspersample, dib);
if(!header_only) {
// calculate the line + pitch (separate for scr & dest)
const tmsize_t src_line = TIFFScanlineSize(tif);
const tmsize_t dst_line = FreeImage_GetLine(dib);
const unsigned dst_pitch = FreeImage_GetPitch(dib);
const unsigned Bpp = FreeImage_GetBPP(dib) / 8;
const unsigned srcBpp = bitspersample * samplesperpixel / 8;
// In the tiff file the lines are save from up to down
// In a DIB the lines must be saved from down to up
BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
// read the tiff lines and save them in the DIB
BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
if(buf == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
memset(buf, 0, TIFFStripSize(tif) * sizeof(BYTE));
BOOL bThrowMessage = FALSE;
if(planar_config == PLANARCONFIG_CONTIG) {
for (uint32 y = 0; y < height; y += rowsperstrip) {
int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, strips * src_line) == -1) {
// ignore errors as they can be frequent and not really valid errors, especially with fax images
bThrowMessage = TRUE;
/*
free(buf);
throw FI_MSG_ERROR_PARSING;
*/
}
if(src_line == dst_line) {
// channel count match
for (int l = 0; l < strips; l++) {
memcpy(bits, buf + l * src_line, src_line);
bits -= dst_pitch;
}
}
else {
for (int l = 0; l < strips; l++) {
for(BYTE *pixel = bits, *src_pixel = buf + l * src_line; pixel < bits + dst_pitch; pixel += Bpp, src_pixel += srcBpp) {
AssignPixel(pixel, src_pixel, Bpp);
}
bits -= dst_pitch;
}
}
}
}
else if(planar_config == PLANARCONFIG_SEPARATE) {
const unsigned Bpc = bitspersample / 8;
BYTE* dib_strip = bits;
// - loop for strip blocks -
for (uint32 y = 0; y < height; y += rowsperstrip) {
const int32 strips = (y + rowsperstrip > height ? height - y : rowsperstrip);
// - loop for channels (planes) -
for(uint16 sample = 0; sample < samplesperpixel; sample++) {
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, sample), buf, strips * src_line) == -1) {
// ignore errors as they can be frequent and not really valid errors, especially with fax images
bThrowMessage = TRUE;
}
if(sample >= chCount) {
// TODO Write to Extra Channel
break;
}
const unsigned channelOffset = sample * Bpc;
// - loop for strips in block -
BYTE* src_line_begin = buf;
BYTE* dst_line_begin = dib_strip;
for (int l = 0; l < strips; l++, src_line_begin += src_line, dst_line_begin -= dst_pitch ) {
// - loop for pixels in strip -
const BYTE* const src_line_end = src_line_begin + src_line;
for (BYTE* src_bits = src_line_begin, * dst_bits = dst_line_begin; src_bits < src_line_end; src_bits += Bpc, dst_bits += Bpp) {
// actually assigns channel
AssignPixel(dst_bits + channelOffset, src_bits, Bpc);
} // line
} // strips
} // channels
// done with a strip block, incr to the next
dib_strip -= strips * dst_pitch;
} // height
}
free(buf);
if(bThrowMessage) {
FreeImage_OutputMessageProc(s_format_id, "Warning: parsing error. Image may be incomplete or contain invalid data !");
}
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
SwapRedBlue32(dib);
#endif
} // !header only
} else if(loadMethod == LoadAsTiled) {
// ---------------------------------------------------------------------------------
// Tiled image loading
// ---------------------------------------------------------------------------------
uint32 tileWidth, tileHeight;
uint32 src_line = 0;
// create a new DIB
dib = CreateImageType( header_only, image_type, width, height, bitspersample, samplesperpixel);
if (dib == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
// set up the colormap based on photometric
ReadPalette(tif, photometric, bitspersample, dib);
// get the tile geometry
if(!TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tileWidth) || !TIFFGetField(tif, TIFFTAG_TILELENGTH, &tileHeight)) {
throw "Invalid tiled TIFF image";
}
// read the tiff lines and save them in the DIB
if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
// get the maximum number of bytes required to contain a tile
tmsize_t tileSize = TIFFTileSize(tif);
// allocate tile buffer
BYTE *tileBuffer = (BYTE*)malloc(tileSize * sizeof(BYTE));
if(tileBuffer == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// calculate src line and dst pitch
int dst_pitch = FreeImage_GetPitch(dib);
uint32 tileRowSize = (uint32)TIFFTileRowSize(tif);
uint32 imageRowSize = (uint32)TIFFScanlineSize(tif);
// In the tiff file the lines are saved from up to down
// In a DIB the lines must be saved from down to up
BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
for (uint32 y = 0; y < height; y += tileHeight) {
int32 nrows = (y + tileHeight > height ? height - y : tileHeight);
for (uint32 x = 0, rowSize = 0; x < width; x += tileWidth, rowSize += tileRowSize) {
memset(tileBuffer, 0, tileSize);
// read one tile
if (TIFFReadTile(tif, tileBuffer, x, y, 0, 0) < 0) {
free(tileBuffer);
throw "Corrupted tiled TIFF file";
}
// convert to strip
if(x + tileWidth > width) {
src_line = imageRowSize - rowSize;
} else {
src_line = tileRowSize;
}
BYTE *src_bits = tileBuffer;
BYTE *dst_bits = bits + rowSize;
for(int k = 0; k < nrows; k++) {
memcpy(dst_bits, src_bits, src_line);
src_bits += tileRowSize;
dst_bits -= dst_pitch;
}
}
bits -= nrows * dst_pitch;
}
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
SwapRedBlue32(dib);
#endif
free(tileBuffer);
}
else if(planar_config == PLANARCONFIG_SEPARATE) {
throw "Separated tiled TIFF images are not supported";
}
} else if(loadMethod == LoadAsLogLuv) {
// ---------------------------------------------------------------------------------
// RGBF LogLuv compressed loading
// ---------------------------------------------------------------------------------
double stonits; // input conversion to nits
if (!TIFFGetField(tif, TIFFTAG_STONITS, &stonits)) {
stonits = 1;
}
// create a new DIB
dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
if (dib == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
if(planar_config == PLANARCONFIG_CONTIG && !header_only) {
// calculate the line + pitch (separate for scr & dest)
tmsize_t src_line = TIFFScanlineSize(tif);
int dst_pitch = FreeImage_GetPitch(dib);
// In the tiff file the lines are save from up to down
// In a DIB the lines must be saved from down to up
BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
// read the tiff lines and save them in the DIB
BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
if(buf == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y += rowsperstrip) {
int32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
free(buf);
throw FI_MSG_ERROR_PARSING;
}
// convert from XYZ to RGB
for (int l = 0; l < nrow; l++) {
tiff_ConvertLineXYZToRGB(bits, buf + l * src_line, stonits, width);
bits -= dst_pitch;
}
}
free(buf);
}
else if(planar_config == PLANARCONFIG_SEPARATE) {
// this cannot happen according to the LogLuv specification
throw "Unable to handle PLANARCONFIG_SEPARATE LogLuv images";
}
} else if(loadMethod == LoadAsHalfFloat) {
// ---------------------------------------------------------------------------------
// RGBF loading from a half format
// ---------------------------------------------------------------------------------
// create a new DIB
dib = CreateImageType(header_only, image_type, width, height, bitspersample, samplesperpixel);
if (dib == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
// fill in the resolution (english or universal)
ReadResolution(tif, dib);
if(!header_only) {
// calculate the line + pitch (separate for scr & dest)
tmsize_t src_line = TIFFScanlineSize(tif);
unsigned dst_pitch = FreeImage_GetPitch(dib);
// In the tiff file the lines are save from up to down
// In a DIB the lines must be saved from down to up
BYTE *bits = FreeImage_GetScanLine(dib, height - 1);
// read the tiff lines and save them in the DIB
if(planar_config == PLANARCONFIG_CONTIG) {
BYTE *buf = (BYTE*)malloc(TIFFStripSize(tif) * sizeof(BYTE));
if(buf == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y += rowsperstrip) {
uint32 nrow = (y + rowsperstrip > height ? height - y : rowsperstrip);
if (TIFFReadEncodedStrip(tif, TIFFComputeStrip(tif, y, 0), buf, nrow * src_line) == -1) {
free(buf);
throw FI_MSG_ERROR_PARSING;
}
// convert from half (16-bit) to float (32-bit)
// !!! use OpenEXR half helper class
half half_value;
for (uint32 l = 0; l < nrow; l++) {
WORD *src_pixel = (WORD*)(buf + l * src_line);
float *dst_pixel = (float*)bits;
for(tmsize_t x = 0; x < (tmsize_t)(src_line / sizeof(WORD)); x++) {
half_value.setBits(src_pixel[x]);
dst_pixel[x] = half_value;
}
bits -= dst_pitch;
}
}
free(buf);
}
else if(planar_config == PLANARCONFIG_SEPARATE) {
// this use case was never encountered yet
throw "Unable to handle PLANARCONFIG_SEPARATE RGB half float images";
}
} // !header only
} else {
// ---------------------------------------------------------------------------------
// Unknown or unsupported format
// ---------------------------------------------------------------------------------
throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
}
// copy ICC profile data (must be done after FreeImage_Allocate)
FreeImage_CreateICCProfile(dib, iccBuf, iccSize);
if (photometric == PHOTOMETRIC_SEPARATED && asCMYK) {
FreeImage_GetICCProfile(dib)->flags |= FIICC_COLOR_IS_CMYK;
}
// copy TIFF metadata (must be done after FreeImage_Allocate)
ReadMetadata(tif, dib);
// copy TIFF thumbnail (must be done after FreeImage_Allocate)
ReadThumbnail(io, handle, data, tif, dib);
return (FIBITMAP *)dib;
} catch (const char *message) {
if(dib) {
FreeImage_Unload(dib);
}
if(message) {
FreeImage_OutputMessageProc(s_format_id, message);
}
return NULL;
}
}
// --------------------------------------------------------------------------
static BOOL
SaveOneTIFF(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data, unsigned ifd, unsigned ifdCount) {
if (!dib || !handle || !data) {
return FALSE;
}
try {
fi_TIFFIO *fio = (fi_TIFFIO*)data;
TIFF *out = fio->tif;
const FREE_IMAGE_TYPE image_type = FreeImage_GetImageType(dib);
const uint32 width = FreeImage_GetWidth(dib);
const uint32 height = FreeImage_GetHeight(dib);
const uint16 bitsperpixel = (uint16)FreeImage_GetBPP(dib);
const FIICCPROFILE* iccProfile = FreeImage_GetICCProfile(dib);
// setup out-variables based on dib and flag options
uint16 bitspersample;
uint16 samplesperpixel;
uint16 photometric;
if(image_type == FIT_BITMAP) {
// standard image: 1-, 4-, 8-, 16-, 24-, 32-bit
src/Source/FreeImage/PluginTIFF.cpp view on Meta::CPAN
bitspersample = bitsperpixel / samplesperpixel;
photometric = PHOTOMETRIC_RGB;
} else {
// special image type (int, long, double, ...)
samplesperpixel = 1;
bitspersample = bitsperpixel;
photometric = PHOTOMETRIC_MINISBLACK;
}
// set image data type
WriteImageType(out, image_type);
// write possible ICC profile
if (iccProfile->size && iccProfile->data) {
TIFFSetField(out, TIFFTAG_ICCPROFILE, iccProfile->size, iccProfile->data);
}
// handle standard width/height/bpp stuff
TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(out, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, bitspersample);
TIFFSetField(out, TIFFTAG_PHOTOMETRIC, photometric);
TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); // single image plane
TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(out, TIFFTAG_FILLORDER, FILLORDER_MSB2LSB);
TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, (uint32) -1));
// handle metrics
WriteResolution(out, dib);
// multi-paging
if (page >= 0) {
char page_number[20];
sprintf(page_number, "Page %d", page);
TIFFSetField(out, TIFFTAG_SUBFILETYPE, (uint32)FILETYPE_PAGE);
TIFFSetField(out, TIFFTAG_PAGENUMBER, (uint16)page, (uint16)0);
TIFFSetField(out, TIFFTAG_PAGENAME, page_number);
} else {
// is it a thumbnail ?
TIFFSetField(out, TIFFTAG_SUBFILETYPE, (ifd == 0) ? (uint32)0 : (uint32)FILETYPE_REDUCEDIMAGE);
}
// palettes (image colormaps are automatically scaled to 16-bits)
if (photometric == PHOTOMETRIC_PALETTE) {
uint16 *r, *g, *b;
uint16 nColors = (uint16)FreeImage_GetColorsUsed(dib);
RGBQUAD *pal = FreeImage_GetPalette(dib);
r = (uint16 *) _TIFFmalloc(sizeof(uint16) * 3 * nColors);
if(r == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
g = r + nColors;
b = g + nColors;
for (int i = nColors - 1; i >= 0; i--) {
r[i] = SCALE((uint16)pal[i].rgbRed);
g[i] = SCALE((uint16)pal[i].rgbGreen);
b[i] = SCALE((uint16)pal[i].rgbBlue);
}
TIFFSetField(out, TIFFTAG_COLORMAP, r, g, b);
_TIFFfree(r);
}
// compression tag
WriteCompression(out, bitspersample, samplesperpixel, photometric, flags);
// metadata
WriteMetadata(out, dib);
// thumbnail tag
if((ifd == 0) && (ifdCount > 1)) {
uint16 nsubifd = 1;
uint64 subifd[1];
subifd[0] = 0;
TIFFSetField(out, TIFFTAG_SUBIFD, nsubifd, subifd);
}
// read the DIB lines from bottom to top
// and save them in the TIF
// -------------------------------------
const uint32 pitch = FreeImage_GetPitch(dib);
if(image_type == FIT_BITMAP) {
// standard bitmap type
switch(bitsperpixel) {
case 1 :
case 4 :
case 8 :
{
if((bitsperpixel == 8) && FreeImage_IsTransparent(dib)) {
// 8-bit transparent picture : convert to 8-bit + 8-bit alpha
// get the transparency table
BYTE *trns = FreeImage_GetTransparencyTable(dib);
BYTE *buffer = (BYTE *)malloc(2 * width * sizeof(BYTE));
if(buffer == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (int y = height - 1; y >= 0; y--) {
BYTE *bits = FreeImage_GetScanLine(dib, y);
BYTE *p = bits, *b = buffer;
for(uint32 x = 0; x < width; x++) {
// copy the 8-bit layer
b[0] = *p;
// convert the trns table to a 8-bit alpha layer
b[1] = trns[ b[0] ];
p++;
b += samplesperpixel;
}
// write the scanline to disc
TIFFWriteScanline(out, buffer, height - y - 1, 0);
}
free(buffer);
}
else {
// other cases
BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
if(buffer == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y++) {
// get a copy of the scanline
memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
// write the scanline to disc
TIFFWriteScanline(out, buffer, y, 0);
}
free(buffer);
}
break;
}
case 24:
case 32:
{
BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
if(buffer == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y++) {
// get a copy of the scanline
memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
#if FREEIMAGE_COLORORDER == FREEIMAGE_COLORORDER_BGR
if (photometric != PHOTOMETRIC_SEPARATED) {
// TIFFs store color data RGB(A) instead of BGR(A)
BYTE *pBuf = buffer;
for (uint32 x = 0; x < width; x++) {
INPLACESWAP(pBuf[0], pBuf[2]);
pBuf += samplesperpixel;
}
}
#endif
// write the scanline to disc
TIFFWriteScanline(out, buffer, y, 0);
}
free(buffer);
break;
}
}//< switch (bitsperpixel)
} else if(image_type == FIT_RGBF && (flags & TIFF_LOGLUV) == TIFF_LOGLUV) {
// RGBF image => store as XYZ using a LogLuv encoding
BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
if(buffer == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y++) {
// get a copy of the scanline and convert from RGB to XYZ
tiff_ConvertLineRGBToXYZ(buffer, FreeImage_GetScanLine(dib, height - y - 1), width);
// write the scanline to disc
TIFFWriteScanline(out, buffer, y, 0);
}
free(buffer);
} else {
// just dump the dib (tiff supports all dib types)
BYTE *buffer = (BYTE *)malloc(pitch * sizeof(BYTE));
if(buffer == NULL) {
throw FI_MSG_ERROR_MEMORY;
}
for (uint32 y = 0; y < height; y++) {
// get a copy of the scanline
memcpy(buffer, FreeImage_GetScanLine(dib, height - y - 1), pitch);
// write the scanline to disc
TIFFWriteScanline(out, buffer, y, 0);
}
free(buffer);
}
// write out the directory tag if we wrote a page other than -1 or if we have a thumbnail to write later
if( (page >= 0) || ((ifd == 0) && (ifdCount > 1)) ) {
TIFFWriteDirectory(out);
// else: TIFFClose will WriteDirectory
}
return TRUE;
} catch(const char *text) {
FreeImage_OutputMessageProc(s_format_id, text);
return FALSE;
}
}
static BOOL DLL_CALLCONV
Save(FreeImageIO *io, FIBITMAP *dib, fi_handle handle, int page, int flags, void *data) {
BOOL bResult = FALSE;
// handle thumbnail as SubIFD
const BOOL bHasThumbnail = (FreeImage_GetThumbnail(dib) != NULL);
const unsigned ifdCount = bHasThumbnail ? 2 : 1;
FIBITMAP *bitmap = dib;
for(unsigned ifd = 0; ifd < ifdCount; ifd++) {
// redirect dib to thumbnail for the second pass
if(ifd == 1) {
bitmap = FreeImage_GetThumbnail(dib);
}
bResult = SaveOneTIFF(io, bitmap, handle, page, flags, data, ifd, ifdCount);
if(!bResult) {
return FALSE;
}
}
return bResult;
}
// ==========================================================
// Init
// ==========================================================
void DLL_CALLCONV
InitTIFF(Plugin *plugin, int format_id) {
s_format_id = format_id;
// Set up the callback for extended TIFF directory tag support (see XTIFF.cpp)
( run in 0.977 second using v1.01-cache-2.11-cpan-cdf2f3d4e48 )