blob: 967a92de9e874888fae4e0b2759574c2752bc1db [file] [log] [blame]
//========================================================================
//
// TiffWriter.cc
//
// This file is licensed under the GPLv2 or later
//
// Copyright (C) 2010, 2012 William Bader <williambader@hotmail.com>
// Copyright (C) 2012, 2021, 2022 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2012, 2017 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2012 Pino Toscano <pino@kde.org>
// Copyright (C) 2014 Steven Lee <roc.sky@gmail.com>
//
//========================================================================
#include "TiffWriter.h"
#ifdef ENABLE_LIBTIFF
# include <cstring>
# ifdef _WIN32
# include <io.h>
# endif
extern "C" {
# include <tiffio.h>
}
# include <cstdint>
struct TiffWriterPrivate
{
TIFF *f; // LibTiff file context
int numRows; // number of rows in the image
int curRow; // number of rows written
const char *compressionString; // compression type
TiffWriter::Format format; // format of image data
};
TiffWriter::~TiffWriter()
{
delete priv;
}
TiffWriter::TiffWriter(Format formatA)
{
priv = new TiffWriterPrivate;
priv->f = nullptr;
priv->numRows = 0;
priv->curRow = 0;
priv->compressionString = nullptr;
priv->format = formatA;
}
// Set the compression type
void TiffWriter::setCompressionString(const char *compressionStringArg)
{
priv->compressionString = compressionStringArg;
}
// Write a TIFF file.
bool TiffWriter::init(FILE *openedFile, int width, int height, double hDPI, double vDPI)
{
unsigned int compression;
uint16_t photometric = 0;
uint32_t rowsperstrip = (uint32_t)-1;
int bitspersample;
uint16_t samplesperpixel = 0;
const struct compression_name_tag
{
const char *compressionName; // name of the compression option from the command line
unsigned int compressionCode; // internal libtiff code
const char *compressionDescription; // descriptive name
} compressionList[] = { { "none", COMPRESSION_NONE, "no compression" },
{ "ccittrle", COMPRESSION_CCITTRLE, "CCITT modified Huffman RLE" },
{ "ccittfax3", COMPRESSION_CCITTFAX3, "CCITT Group 3 fax encoding" },
{ "ccittt4", COMPRESSION_CCITT_T4, "CCITT T.4 (TIFF 6 name)" },
{ "ccittfax4", COMPRESSION_CCITTFAX4, "CCITT Group 4 fax encoding" },
{ "ccittt6", COMPRESSION_CCITT_T6, "CCITT T.6 (TIFF 6 name)" },
{ "lzw", COMPRESSION_LZW, "Lempel-Ziv & Welch" },
{ "ojpeg", COMPRESSION_OJPEG, "!6.0 JPEG" },
{ "jpeg", COMPRESSION_JPEG, "%JPEG DCT compression" },
{ "next", COMPRESSION_NEXT, "NeXT 2-bit RLE" },
{ "packbits", COMPRESSION_PACKBITS, "Macintosh RLE" },
{ "ccittrlew", COMPRESSION_CCITTRLEW, "CCITT modified Huffman RLE w/ word alignment" },
{ "deflate", COMPRESSION_DEFLATE, "Deflate compression" },
{ "adeflate", COMPRESSION_ADOBE_DEFLATE, "Deflate compression, as recognized by Adobe" },
{ "dcs", COMPRESSION_DCS, "Kodak DCS encoding" },
{ "jbig", COMPRESSION_JBIG, "ISO JBIG" },
{ "jp2000", COMPRESSION_JP2000, "Leadtools JPEG2000" },
{ nullptr, 0, nullptr } };
// Initialize
priv->f = nullptr;
priv->curRow = 0;
// Store the number of rows
priv->numRows = height;
// Set the compression
compression = COMPRESSION_NONE;
if (priv->compressionString == nullptr || strcmp(priv->compressionString, "") == 0) {
compression = COMPRESSION_NONE;
} else {
int i;
for (i = 0; compressionList[i].compressionName != nullptr; i++) {
if (strcmp(priv->compressionString, compressionList[i].compressionName) == 0) {
compression = compressionList[i].compressionCode;
break;
}
}
if (compressionList[i].compressionName == nullptr) {
fprintf(stderr, "TiffWriter: Unknown compression type '%.10s', using 'none'.\n", priv->compressionString);
fprintf(stderr, "Known compression types (the tiff library might not support every type)\n");
for (i = 0; compressionList[i].compressionName != nullptr; i++) {
fprintf(stderr, "%10s %s\n", compressionList[i].compressionName, compressionList[i].compressionDescription);
}
}
}
// Set bits per sample, samples per pixel, and photometric type from format
bitspersample = (priv->format == MONOCHROME ? 1 : 8);
switch (priv->format) {
case MONOCHROME:
case GRAY:
samplesperpixel = 1;
photometric = PHOTOMETRIC_MINISBLACK;
break;
case RGB:
samplesperpixel = 3;
photometric = PHOTOMETRIC_RGB;
break;
case RGBA_PREMULTIPLIED:
samplesperpixel = 4;
photometric = PHOTOMETRIC_RGB;
break;
case CMYK:
samplesperpixel = 4;
photometric = PHOTOMETRIC_SEPARATED;
break;
case RGB48:
samplesperpixel = 3;
bitspersample = 16;
photometric = PHOTOMETRIC_RGB;
break;
}
// Open the file
if (openedFile == nullptr) {
fprintf(stderr, "TiffWriter: No output file given.\n");
return false;
}
# ifdef _WIN32
// Convert C Library handle to Win32 Handle
priv->f = TIFFFdOpen(_get_osfhandle(fileno(openedFile)), "-", "w");
# else
priv->f = TIFFFdOpen(fileno(openedFile), "-", "w");
# endif
if (!priv->f) {
return false;
}
// Set TIFF tags
TIFFSetField(priv->f, TIFFTAG_IMAGEWIDTH, width);
TIFFSetField(priv->f, TIFFTAG_IMAGELENGTH, height);
TIFFSetField(priv->f, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
TIFFSetField(priv->f, TIFFTAG_SAMPLESPERPIXEL, samplesperpixel);
TIFFSetField(priv->f, TIFFTAG_BITSPERSAMPLE, bitspersample);
TIFFSetField(priv->f, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
TIFFSetField(priv->f, TIFFTAG_PHOTOMETRIC, photometric);
TIFFSetField(priv->f, TIFFTAG_COMPRESSION, (uint16_t)compression);
TIFFSetField(priv->f, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(priv->f, rowsperstrip));
TIFFSetField(priv->f, TIFFTAG_XRESOLUTION, hDPI);
TIFFSetField(priv->f, TIFFTAG_YRESOLUTION, vDPI);
TIFFSetField(priv->f, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH);
if (priv->format == RGBA_PREMULTIPLIED) {
uint16_t extra = EXTRASAMPLE_ASSOCALPHA;
TIFFSetField(priv->f, TIFFTAG_EXTRASAMPLES, 1, &extra);
}
if (priv->format == CMYK) {
TIFFSetField(priv->f, TIFFTAG_INKSET, INKSET_CMYK);
TIFFSetField(priv->f, TIFFTAG_NUMBEROFINKS, 4);
}
return true;
}
bool TiffWriter::writePointers(unsigned char **rowPointers, int rowCount)
{
// Write all rows to the file
for (int row = 0; row < rowCount; row++) {
if (TIFFWriteScanline(priv->f, rowPointers[row], row, 0) < 0) {
fprintf(stderr, "TiffWriter: Error writing tiff row %d\n", row);
return false;
}
}
return true;
}
bool TiffWriter::writeRow(unsigned char **rowData)
{
// Add a single row
if (TIFFWriteScanline(priv->f, *rowData, priv->curRow, 0) < 0) {
fprintf(stderr, "TiffWriter: Error writing tiff row %d\n", priv->curRow);
return false;
}
priv->curRow++;
return true;
}
bool TiffWriter::close()
{
// Close the file
TIFFClose(priv->f);
return true;
}
#endif