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 }