File indexing completed on 2024-12-22 04:15:58
0001 /* 0002 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> 0004 * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include <QBuffer> 0009 0010 #include <memory> 0011 0012 #include <tiff.h> 0013 0014 #include <KoColorModelStandardIds.h> 0015 #include <KoColorProfile.h> 0016 #include <KoColorSpace.h> 0017 #include <KoColorSpaceRegistry.h> 0018 #include <KoID.h> 0019 #include <kis_assert.h> 0020 #include <kis_meta_data_backend_registry.h> 0021 0022 #include <KoConfig.h> 0023 #ifdef HAVE_OPENEXR 0024 #include <half.h> 0025 #endif 0026 0027 #include "kis_tiff_converter.h" 0028 #include "kis_tiff_writer_visitor.h" 0029 0030 KisTIFFWriterVisitor::KisTIFFWriterVisitor(TIFF*image, KisTIFFOptions* options) 0031 : KisTIFFBaseWriter(image, options) 0032 { 0033 } 0034 0035 KisTIFFWriterVisitor::~KisTIFFWriterVisitor() = default; 0036 0037 bool KisTIFFWriterVisitor::saveLayerProjection(KisLayer *layer) 0038 { 0039 dbgFile << "visiting on layer" << layer->name() << ""; 0040 KisPaintDeviceSP pd = layer->projection(); 0041 0042 uint16_t color_type = 0; 0043 uint16_t sample_format = SAMPLEFORMAT_UINT; 0044 const KoColorSpace *destColorSpace = nullptr; 0045 // Check colorspace 0046 if (!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format, destColorSpace)) { // unsupported colorspace 0047 if (!destColorSpace) { 0048 return false; 0049 } 0050 pd.attach(new KisPaintDevice(*pd)); 0051 pd->convertTo(destColorSpace); 0052 } 0053 0054 { 0055 // WORKAROUND: block any attempts to use YCbCr with alpha channels. 0056 // This should not happen because alpha is disabled by default 0057 // and the checkbox is blocked for YCbCr and CMYK. 0058 KIS_SAFE_ASSERT_RECOVER(color_type != PHOTOMETRIC_YCBCR 0059 || !m_options->alpha) 0060 { 0061 warnFile << "TIFF does not support exporting alpha channels with " 0062 "YCbCr. Skipping..."; 0063 m_options->alpha = false; 0064 } 0065 } 0066 0067 // Save depth 0068 uint32_t depth = 8 * pd->pixelSize() / pd->channelCount(); 0069 TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth); 0070 0071 { 0072 // WORKAROUND: block any attempts to use JPEG with >= 8 bits 0073 0074 if (m_options->compressionType == COMPRESSION_JPEG && depth != 8) { 0075 warnFile << "Attempt to export JPEG with multi-byte depth, " 0076 "disabling compression"; 0077 m_options->compressionType = COMPRESSION_NONE; 0078 } 0079 } 0080 0081 // Save number of samples 0082 if (m_options->alpha) { 0083 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount()); 0084 const std::array<uint16_t, 1> sampleinfo = {EXTRASAMPLE_UNASSALPHA}; 0085 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo.data()); 0086 } else { 0087 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount() - 1); 0088 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0); 0089 } 0090 0091 // Save colorspace information 0092 TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type); 0093 TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format); 0094 TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width()); 0095 TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height()); 0096 0097 // Set the compression options 0098 TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType); 0099 if (m_options->compressionType == COMPRESSION_JPEG) { 0100 TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality); 0101 } else if (m_options->compressionType == COMPRESSION_ADOBE_DEFLATE) { 0102 TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress); 0103 } else if (m_options->compressionType == COMPRESSION_PIXARLOG) { 0104 TIFFSetField(image(), 0105 TIFFTAG_PIXARLOGQUALITY, 0106 m_options->pixarLogCompress); 0107 } 0108 0109 // Set the predictor 0110 if (m_options->compressionType == COMPRESSION_LZW 0111 || m_options->compressionType == COMPRESSION_ADOBE_DEFLATE) 0112 TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor); 0113 0114 // Use contiguous configuration 0115 TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 0116 0117 // Do not set the rowsperstrip, as it's incompatible with JPEG 0118 0119 // But do set YCbCr 4:4:4 if applicable 0120 if (color_type == PHOTOMETRIC_YCBCR) { 0121 TIFFSetField(image(), TIFFTAG_YCBCRSUBSAMPLING, 1, 1); 0122 TIFFSetField(image(), TIFFTAG_YCBCRPOSITIONING, YCBCRPOSITION_CENTERED); 0123 if (m_options->compressionType == COMPRESSION_JPEG) { 0124 TIFFSetField(image(), TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW); 0125 } 0126 } 0127 0128 // Save profile 0129 if (m_options->saveProfile) { 0130 const KoColorProfile* profile = pd->colorSpace()->profile(); 0131 if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) { 0132 QByteArray ba = profile->rawData(); 0133 TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(), ba.constData()); 0134 } 0135 } 0136 0137 { 0138 // IPTC 0139 KisMetaData::IOBackend *io = 0140 KisMetadataBackendRegistry::instance()->value("iptc"); 0141 QBuffer buf; 0142 io->saveTo(layer->metaData(), &buf, KisMetaData::IOBackend::NoHeader); 0143 0144 if (buf.size() 0145 && !TIFFSetField(image(), 0146 TIFFTAG_RICHTIFFIPTC, 0147 static_cast<uint32_t>(buf.size()), 0148 buf.data().data())) { 0149 dbgFile << "Failed to write the IPTC metadata to the TIFF field"; 0150 } 0151 } 0152 0153 { 0154 // XMP 0155 KisMetaData::IOBackend *io = 0156 KisMetadataBackendRegistry::instance()->value("xmp"); 0157 QBuffer buf; 0158 io->saveTo(layer->metaData(), &buf, KisMetaData::IOBackend::NoHeader); 0159 0160 if (buf.size() 0161 && !TIFFSetField(image(), 0162 TIFFTAG_XMLPACKET, 0163 static_cast<uint32_t>(buf.size()), 0164 buf.data().data())) { 0165 dbgFile << "Failed to write the XMP metadata to the TIFF field"; 0166 } 0167 } 0168 0169 tsize_t stripsize = TIFFStripSize(image()); 0170 std::unique_ptr<std::remove_pointer_t<tdata_t>, decltype(&_TIFFfree)> buff( 0171 _TIFFmalloc(stripsize), 0172 &_TIFFfree); 0173 KIS_ASSERT_RECOVER_RETURN_VALUE( 0174 buff && "Unable to allocate buffer for TIFF!", 0175 false); 0176 qint32 height = layer->image()->height(); 0177 qint32 width = layer->image()->width(); 0178 bool r = true; 0179 for (int y = 0; y < height; y++) { 0180 KisHLineConstIteratorSP it = pd->createHLineConstIteratorNG(0, y, width); 0181 switch (color_type) { 0182 case PHOTOMETRIC_MINISBLACK: { 0183 const std::array<quint8, 5> poses = {0, 1}; 0184 r = copyDataToStrips(it, 0185 buff.get(), 0186 depth, 0187 sample_format, 0188 1, 0189 poses); 0190 } 0191 break; 0192 case PHOTOMETRIC_RGB: { 0193 const auto poses = [&]() -> std::array<quint8, 5> { 0194 if (sample_format == SAMPLEFORMAT_IEEEFP) { 0195 return {0, 1, 2, 3}; 0196 } else { 0197 return {2, 1, 0, 3}; 0198 } 0199 }(); 0200 r = copyDataToStrips(it, 0201 buff.get(), 0202 depth, 0203 sample_format, 0204 3, 0205 poses); 0206 } 0207 break; 0208 case PHOTOMETRIC_SEPARATED: { 0209 const std::array<quint8, 5> poses = {0, 1, 2, 3, 4}; 0210 r = copyDataToStrips(it, 0211 buff.get(), 0212 depth, 0213 sample_format, 0214 4, 0215 poses); 0216 } 0217 break; 0218 case PHOTOMETRIC_ICCLAB: 0219 case PHOTOMETRIC_YCBCR: { 0220 const std::array<quint8, 5> poses = {0, 1, 2, 3}; 0221 r = copyDataToStrips(it, 0222 buff.get(), 0223 depth, 0224 sample_format, 0225 3, 0226 poses); 0227 } break; 0228 } 0229 if (!r) return false; 0230 TIFFWriteScanline(image(), 0231 buff.get(), 0232 static_cast<uint32_t>(y), 0233 (tsample_t)-1); 0234 } 0235 buff.reset(); 0236 0237 return TIFFWriteDirectory(image()); 0238 }