File indexing completed on 2024-05-26 04:33:43
0001 /* 0002 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net> 0003 * SPDX-FileCopyrightText: 2021 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 <KoConfig.h> 0019 #include <KoID.h> 0020 #include <KoUnit.h> 0021 #include <kis_group_layer.h> 0022 #include <kis_image.h> 0023 #include <kis_iterator_ng.h> 0024 #include <kis_painter.h> 0025 #include <kis_tiff_psd_resource_record.h> 0026 #include <psd_resource_block.h> 0027 0028 #ifdef HAVE_OPENEXR 0029 #include <half.h> 0030 #endif 0031 0032 #include "kis_tiff_converter.h" 0033 #include "kis_tiff_psd_layer_record.h" 0034 #include "kis_tiff_psd_writer_visitor.h" 0035 0036 KisTiffPsdWriter::KisTiffPsdWriter(TIFF *image, KisTIFFOptions *options) 0037 : KisTIFFBaseWriter(image, options) 0038 { 0039 } 0040 0041 KisTiffPsdWriter::~KisTiffPsdWriter() = default; 0042 0043 KisImportExportErrorCode KisTiffPsdWriter::writeImage(KisGroupLayerSP layer) 0044 { 0045 dbgFile << "Starting write of Photoshop layer data"; 0046 0047 /** 0048 * For Photoshop tiff files, first thing is to write the 0049 * projection of the image file. 0050 */ 0051 0052 if (layer->image()->width() > MAX_PSD_SIZE || layer->image()->height() > MAX_PSD_SIZE) { 0053 dbgFile << "This TIFF file is too big to be represented as a PSD blob!"; 0054 return ImportExportCodes::Failure; 0055 } 0056 0057 dbgFile << "Writing root layer projection"; 0058 KisPaintDeviceSP pd = layer->projection(); 0059 0060 uint16_t color_type = 0; 0061 uint16_t sample_format = SAMPLEFORMAT_UINT; 0062 const KoColorSpace *destColorSpace = nullptr; 0063 // Check colorspace 0064 if (!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format, destColorSpace)) { // unsupported colorspace 0065 if (!destColorSpace) { 0066 dbgFile << "Unsupported colorspace" << pd->colorSpace()->name(); 0067 return ImportExportCodes::FormatColorSpaceUnsupported; 0068 } 0069 pd.attach(new KisPaintDevice(*pd)); 0070 pd->convertTo(destColorSpace); 0071 } 0072 0073 { 0074 // WORKAROUND: block any attempts to use YCbCr with alpha channels. 0075 // This should not happen because alpha is disabled by default 0076 // and the checkbox is blocked for YCbCr and CMYK. 0077 KIS_SAFE_ASSERT_RECOVER(color_type != PHOTOMETRIC_YCBCR 0078 || !m_options->alpha) 0079 { 0080 warnFile << "TIFF does not support exporting alpha channels with " 0081 "YCbCr. Skipping..."; 0082 m_options->alpha = false; 0083 } 0084 } 0085 0086 // Save depth 0087 uint32_t depth = 8 * pd->pixelSize() / pd->channelCount(); 0088 TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth); 0089 0090 { 0091 // WORKAROUND: block any attempts to use JPEG with >= 8 bits 0092 0093 if (m_options->compressionType == COMPRESSION_JPEG && depth != 8) { 0094 warnFile << "Attempt to export JPEG with multi-byte depth, " 0095 "disabling compression"; 0096 m_options->compressionType = COMPRESSION_NONE; 0097 } 0098 } 0099 0100 // Save number of samples 0101 if (m_options->alpha) { 0102 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount()); 0103 const std::array<uint16_t, 1> sampleinfo = {EXTRASAMPLE_UNASSALPHA}; 0104 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo.data()); 0105 } else { 0106 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount() - 1); 0107 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0); 0108 } 0109 0110 // Save colorspace information 0111 TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type); 0112 TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format); 0113 TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width()); 0114 TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height()); 0115 0116 // Set the compression options 0117 TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType); 0118 if (m_options->compressionType == COMPRESSION_JPEG) { 0119 TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality); 0120 } else if (m_options->compressionType == COMPRESSION_ADOBE_DEFLATE) { 0121 TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress); 0122 } else if (m_options->compressionType == COMPRESSION_PIXARLOG) { 0123 TIFFSetField(image(), 0124 TIFFTAG_PIXARLOGQUALITY, 0125 m_options->pixarLogCompress); 0126 } 0127 0128 // Set the predictor 0129 if (m_options->compressionType == COMPRESSION_LZW 0130 || m_options->compressionType == COMPRESSION_ADOBE_DEFLATE) 0131 TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor); 0132 0133 // Use contiguous configuration 0134 TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 0135 0136 // Do not set the rowsperstrip, as it's incompatible with JPEG 0137 0138 // But do set YCbCr 4:4:4 if applicable 0139 if (color_type == PHOTOMETRIC_YCBCR) { 0140 TIFFSetField(image(), TIFFTAG_YCBCRSUBSAMPLING, 1, 1); 0141 TIFFSetField(image(), TIFFTAG_YCBCRPOSITIONING, YCBCRPOSITION_CENTERED); 0142 if (m_options->compressionType == COMPRESSION_JPEG) { 0143 TIFFSetField(image(), TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW); 0144 } 0145 } 0146 0147 // Save profile 0148 if (m_options->saveProfile) { 0149 const KoColorProfile *profile = pd->colorSpace()->profile(); 0150 if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) { 0151 QByteArray ba = profile->rawData(); 0152 TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(), ba.constData()); 0153 } 0154 } 0155 tsize_t stripsize = TIFFStripSize(image()); 0156 std::unique_ptr<std::remove_pointer_t<tdata_t>, decltype(&_TIFFfree)> buff( 0157 _TIFFmalloc(stripsize), 0158 &_TIFFfree); 0159 KIS_ASSERT_RECOVER_RETURN_VALUE( 0160 buff && "Unable to allocate buffer for TIFF!", 0161 ImportExportCodes::InsufficientMemory); 0162 qint32 height = layer->image()->height(); 0163 qint32 width = layer->image()->width(); 0164 bool r = true; 0165 for (qint32 y = 0; y < height; y++) { 0166 KisHLineConstIteratorSP it = pd->createHLineConstIteratorNG(0, y, width); 0167 switch (color_type) { 0168 case PHOTOMETRIC_MINISBLACK: { 0169 const std::array<quint8, 5> poses = {0, 1}; 0170 r = copyDataToStrips(it, 0171 buff.get(), 0172 depth, 0173 sample_format, 0174 1, 0175 poses); 0176 } break; 0177 case PHOTOMETRIC_RGB: { 0178 const auto poses = [&]() -> std::array<quint8, 5> { 0179 if (sample_format == SAMPLEFORMAT_IEEEFP) { 0180 return {0, 1, 2, 3}; 0181 } else { 0182 return {2, 1, 0, 3}; 0183 } 0184 }(); 0185 r = copyDataToStrips(it, 0186 buff.get(), 0187 depth, 0188 sample_format, 0189 3, 0190 poses); 0191 } break; 0192 case PHOTOMETRIC_SEPARATED: { 0193 const std::array<quint8, 5> poses = {0, 1, 2, 3, 4}; 0194 r = copyDataToStrips(it, 0195 buff.get(), 0196 depth, 0197 sample_format, 0198 4, 0199 poses); 0200 } break; 0201 case PHOTOMETRIC_ICCLAB: 0202 case PHOTOMETRIC_YCBCR: { 0203 const std::array<quint8, 5> poses = {0, 1, 2, 3}; 0204 r = copyDataToStrips(it, 0205 buff.get(), 0206 depth, 0207 sample_format, 0208 3, 0209 poses); 0210 } break; 0211 } 0212 if (!r) 0213 return ImportExportCodes::InternalError; 0214 TIFFWriteScanline(image(), 0215 buff.get(), 0216 static_cast<quint32>(y), 0217 (tsample_t)-1); 0218 } 0219 buff.reset(); 0220 0221 ///* BEGIN PHOTOSHOP SPECIFIC HANDLING CODE */// 0222 0223 /** 0224 * Synthesize the PSD file into the TIFFTAG_IMAGESOURCEDATA field. 0225 */ 0226 0227 { 0228 const bool haveLayers = layer->childCount() > 1 || KisPainter::checkDeviceHasTransparency(layer->firstChild()->projection()); 0229 0230 QBuffer buf; 0231 buf.open(QIODevice::WriteOnly); 0232 0233 dbgFile << "m_image->rootLayer->childCount" << layer->childCount() << buf.pos(); 0234 0235 if (haveLayers) { 0236 KisTiffPsdLayerRecord layerSection( 0237 TIFFIsBigEndian(image()), 0238 static_cast<uint32_t>(width), 0239 static_cast<uint32_t>(height), 0240 static_cast<uint16_t>(depth), 0241 static_cast<uint16_t>(pd->channelCount()), 0242 color_type, 0243 true); 0244 0245 if (!layerSection.write(buf, layer, static_cast<psd_compression_type>(m_options->psdCompressionType))) { 0246 dbgFile << "failed to write layer section. Error:" << layerSection.record()->error << buf.pos(); 0247 return ImportExportCodes::ErrorWhileWriting; 0248 } 0249 } else { 0250 // else write a zero length block 0251 dbgFile << "No layers, saving empty layers/mask block" << buf.pos(); 0252 psdwrite(buf, (quint32)0); 0253 } 0254 0255 buf.close(); 0256 buf.open(QIODevice::ReadOnly); 0257 0258 if (!TIFFSetField(image(), TIFFTAG_IMAGESOURCEDATA, static_cast<uint32_t>(buf.size()), buf.data().constData())) { 0259 dbgFile << "Failed to write the PSD image block to the TIFF field"; 0260 return ImportExportCodes::ErrorWhileWriting; 0261 } 0262 } 0263 0264 /** 0265 * Write all the annotations into the TIFFTAG_PHOTOSHOP field. 0266 */ 0267 0268 { 0269 // IMAGE RESOURCES SECTION 0270 KisTiffPsdResourceRecord resourceSection; 0271 0272 for (vKisAnnotationSP_it it = layer->image()->beginAnnotations(); it != layer->image()->endAnnotations(); ++it) { 0273 KisAnnotationSP annotation = (*it); 0274 if (!annotation || annotation->type().isEmpty()) { 0275 dbgFile << "Warning: empty annotation"; 0276 continue; 0277 } 0278 0279 dbgFile << "Annotation:" << annotation->type() << annotation->description(); 0280 0281 if (annotation->type().startsWith(QString("PSD Resource Block:"))) { // 0282 PSDResourceBlock *resourceBlock = 0283 dynamic_cast<PSDResourceBlock *>(annotation.data()); 0284 if (resourceBlock) { 0285 dbgFile << "Adding PSD Resource Block" << resourceBlock->identifier; 0286 resourceSection.resources[(KisTiffPsdResourceRecord::PSDResourceID)resourceBlock->identifier] = resourceBlock; 0287 } 0288 } 0289 } 0290 0291 // Add resolution block 0292 { 0293 auto *resInfo = new RESN_INFO_1005(); 0294 resInfo->hRes = static_cast<int>(INCH_TO_POINT(layer->image()->xRes())); 0295 resInfo->vRes = static_cast<int>(INCH_TO_POINT(layer->image()->yRes())); 0296 auto *block = new PSDResourceBlock(); 0297 block->identifier = KisTiffPsdResourceRecord::RESN_INFO; 0298 block->resource = resInfo; 0299 resourceSection.resources[KisTiffPsdResourceRecord::RESN_INFO] = block; 0300 } 0301 0302 // Add icc block 0303 { 0304 auto *profileInfo = new ICC_PROFILE_1039(); 0305 profileInfo->icc = layer->image()->profile()->rawData(); 0306 auto *block = new PSDResourceBlock(); 0307 block->identifier = KisTiffPsdResourceRecord::ICC_PROFILE; 0308 block->resource = profileInfo; 0309 resourceSection.resources[KisTiffPsdResourceRecord::ICC_PROFILE] = block; 0310 } 0311 0312 dbgFile << "Resource section ready to write"; 0313 0314 QBuffer buf; 0315 buf.open(QIODevice::WriteOnly); 0316 0317 if (!resourceSection.write(buf)) { 0318 dbgFile << "Failed to write resource section. Error:" << resourceSection.error << buf.pos(); 0319 return ImportExportCodes::ErrorWhileWriting; 0320 } 0321 0322 buf.close(); 0323 buf.open(QIODevice::WriteOnly); 0324 0325 if (!TIFFSetField(image(), TIFFTAG_PHOTOSHOP, static_cast<uint32_t>(buf.size()), buf.data().data())) { 0326 dbgFile << "Failed to write the PSD resource block to the TIFF field"; 0327 return ImportExportCodes::ErrorWhileWriting; 0328 } 0329 } 0330 0331 /** 0332 * Freshly baked Photoshoppy TIFF file! 0333 */ 0334 0335 ///* END PHOTOSHOP SPECIFIC HANDLING CODE */// 0336 0337 TIFFWriteDirectory(image()); 0338 return ImportExportCodes::OK; 0339 }