File indexing completed on 2024-05-26 04:33:36

0001 /*
0002  *  SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
0003  *
0004  *  SPDX-License-Identifier: GPL-2.0-or-later
0005  */
0006 #include "psd_saver.h"
0007 
0008 #include <KoColorSpace.h>
0009 #include <KoColorModelStandardIds.h>
0010 #include <KoColorProfile.h>
0011 #include <KoCompositeOp.h>
0012 #include <KoUnit.h>
0013 
0014 #include <QFileInfo>
0015 
0016 #include <kis_annotation.h>
0017 #include <kis_types.h>
0018 #include <kis_paint_layer.h>
0019 #include "kis_painter.h"
0020 #include <KisDocument.h>
0021 #include <kis_image.h>
0022 #include <kis_group_layer.h>
0023 #include <kis_paint_device.h>
0024 #include <kis_transaction.h>
0025 #include <kis_debug.h>
0026 
0027 #include "psd.h"
0028 #include "psd_header.h"
0029 #include "psd_colormode_block.h"
0030 #include "psd_utils.h"
0031 #include "psd_resource_section.h"
0032 #include "psd_layer_section.h"
0033 #include "psd_resource_block.h"
0034 #include "psd_image_data.h"
0035 
0036 
0037 
0038 
0039 
0040 QPair<psd_color_mode, quint16> colormodelid_to_psd_colormode(const QString &colorSpaceId, const QString &colorDepthId)
0041 {
0042     psd_color_mode colorMode = COLORMODE_UNKNOWN;
0043     if (colorSpaceId == RGBAColorModelID.id()) {
0044         colorMode = RGB;
0045     }
0046     else if (colorSpaceId == CMYKAColorModelID.id()) {
0047         colorMode = CMYK;
0048     }
0049     else if (colorSpaceId == GrayAColorModelID.id()) {
0050         colorMode = Grayscale;
0051     }
0052     else if (colorSpaceId == LABAColorModelID.id()) {
0053         colorMode = Lab;
0054     }
0055 
0056     quint16 depth = 0;
0057 
0058     if (colorDepthId ==  Integer8BitsColorDepthID.id()) {
0059         depth = 8;
0060     }
0061     else if (colorDepthId == Integer16BitsColorDepthID.id()) {
0062         depth = 16;
0063     }
0064     else if (colorDepthId == Float16BitsColorDepthID.id()) {
0065         depth = 32;
0066     }
0067     else if (colorDepthId == Float32BitsColorDepthID.id()) {
0068         depth = 32;
0069     }
0070 
0071     return QPair<psd_color_mode, quint16>(colorMode, depth);
0072 }
0073 
0074 
0075 
0076 PSDSaver::PSDSaver(KisDocument *doc)
0077     : m_image(doc->savingImage())
0078     , m_doc(doc)
0079     , m_stop(false)
0080 {
0081 }
0082 
0083 PSDSaver::~PSDSaver()
0084 {
0085 }
0086 
0087 KisImageSP PSDSaver::image()
0088 {
0089     return m_image;
0090 }
0091 
0092 KisImportExportErrorCode PSDSaver::buildFile(QIODevice &io)
0093 {
0094     KIS_ASSERT_RECOVER_RETURN_VALUE(m_image, ImportExportCodes::InternalError);
0095 
0096     if (m_image->width() > MAX_PSD_SIZE || m_image->height() > MAX_PSD_SIZE) {
0097         return ImportExportCodes::Failure;
0098     }
0099 
0100     const bool haveLayers = m_image->rootLayer()->childCount() > 1 ||
0101         m_image->rootLayer()->firstChild()->childCount() > 0 ||
0102         // check if "oblige child" mechanism is in action, then forbid collapsing
0103         m_image->rootLayer()->projection() != m_image->rootLayer()->firstChild()->projection() ||
0104         KisPainter::checkDeviceHasTransparency(
0105                 m_image->rootLayer()->firstChild()->projection());
0106 
0107     // HEADER
0108     PSDHeader header;
0109     header.signature = "8BPS";
0110     header.version = 1;
0111     header.nChannels = haveLayers ?
0112         m_image->colorSpace()->channelCount() :
0113         m_image->colorSpace()->colorChannelCount();
0114 
0115     header.width = m_image->width();
0116     header.height = m_image->height();
0117 
0118     QPair<psd_color_mode, quint16> colordef = colormodelid_to_psd_colormode(m_image->colorSpace()->colorModelId().id(),
0119                                                                           m_image->colorSpace()->colorDepthId().id());
0120 
0121     if (colordef.first == COLORMODE_UNKNOWN || colordef.second == 0 || colordef.second == 32) {
0122         m_image->convertImageColorSpace(KoColorSpaceRegistry::instance()->rgb16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
0123         m_image->waitForDone();
0124         colordef = colormodelid_to_psd_colormode(m_image->colorSpace()->colorModelId().id(), m_image->colorSpace()->colorDepthId().id());
0125     }
0126     header.colormode = colordef.first;
0127     header.channelDepth = colordef.second;
0128 
0129     dbgFile << "header" << header << io.pos();
0130 
0131     if (!header.write(io)) {
0132         dbgFile << "Failed to write header. Error:" << header.error << io.pos();
0133         return ImportExportCodes::ErrorWhileWriting;
0134     }
0135 
0136     // COLORMODE BlOCK
0137     PSDColorModeBlock colorModeBlock(header.colormode);
0138     // XXX: check for annotations that contain the duotone spec
0139 
0140     KisAnnotationSP annotation = m_image->annotation("DuotoneColormodeBlock");
0141     if (annotation) {
0142         colorModeBlock.duotoneSpecification = annotation->annotation();
0143     }
0144 
0145     dbgFile << "colormode block" << io.pos();
0146     if (!colorModeBlock.write(io)) {
0147         dbgFile << "Failed to write colormode block. Error:" << colorModeBlock.error << io.pos();
0148         return ImportExportCodes::ErrorWhileWriting;
0149     }
0150 
0151     // IMAGE RESOURCES SECTION
0152     PSDImageResourceSection resourceSection;
0153 
0154     vKisAnnotationSP_it it = m_image->beginAnnotations();
0155     vKisAnnotationSP_it endIt = m_image->endAnnotations();
0156     while (it != endIt) {
0157         KisAnnotationSP annotation = (*it);
0158         if (!annotation || annotation->type().isEmpty()) {
0159             dbgFile << "Warning: empty annotation";
0160             it++;
0161             continue;
0162         }
0163 
0164         dbgFile << "Annotation:" << annotation->type() << annotation->description();
0165 
0166         if (annotation->type().startsWith(QString("PSD Resource Block:"))) { //
0167             PSDResourceBlock *resourceBlock = dynamic_cast<PSDResourceBlock*>(annotation.data());
0168             if (resourceBlock) {
0169                 dbgFile << "Adding PSD Resource Block" << resourceBlock->identifier;
0170                 resourceSection.resources[(PSDImageResourceSection::PSDResourceID)resourceBlock->identifier] = resourceBlock;
0171             }
0172         }
0173 
0174         it++;
0175     }
0176 
0177     // Add resolution block
0178     {
0179         RESN_INFO_1005 *resInfo = new RESN_INFO_1005;
0180         resInfo->hRes = INCH_TO_POINT(m_image->xRes());
0181         resInfo->vRes = INCH_TO_POINT(m_image->yRes());
0182         PSDResourceBlock *block = new PSDResourceBlock;
0183         block->identifier = PSDImageResourceSection::RESN_INFO;
0184         block->resource = resInfo;
0185         resourceSection.resources[PSDImageResourceSection::RESN_INFO] = block;
0186     }
0187 
0188     // Add icc block
0189     {
0190         ICC_PROFILE_1039 *profileInfo = new ICC_PROFILE_1039;
0191         profileInfo->icc = m_image->profile()->rawData();
0192         PSDResourceBlock *block = new PSDResourceBlock;
0193         block->identifier = PSDImageResourceSection::ICC_PROFILE;
0194         block->resource = profileInfo;
0195         resourceSection.resources[PSDImageResourceSection::ICC_PROFILE] = block;
0196 
0197     }
0198 
0199     dbgFile << "resource section" << io.pos();
0200     if (!resourceSection.write(io)) {
0201         dbgFile << "Failed to write resource section. Error:" << resourceSection.error << io.pos();
0202         return ImportExportCodes::ErrorWhileWriting;
0203     }
0204 
0205     // LAYER AND MASK DATA
0206     // Only save layers and masks if there is more than one layer
0207     dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << io.pos();
0208 
0209     if (haveLayers) {
0210 
0211         PSDLayerMaskSection layerSection(header);
0212         layerSection.hasTransparency = true;
0213 
0214         if (!layerSection.write(io, m_image->rootLayer(), psd_compression_type::RLE)) {
0215             dbgFile << "failed to write layer section. Error:" << layerSection.error << io.pos();
0216             return ImportExportCodes::ErrorWhileWriting;
0217         }
0218     }
0219     else {
0220         // else write a zero length block
0221         dbgFile << "No layers, saving empty layers/mask block" << io.pos();
0222         psdwrite(io, (quint32)0);
0223     }
0224 
0225     // IMAGE DATA
0226     dbgFile << "Saving composited image" << io.pos();
0227     PSDImageData imagedata(&header);
0228     // Photoshop compresses layer data by default with RLE.
0229     if (!imagedata.write(io, m_image->projection(), haveLayers, psd_compression_type::RLE)) {
0230         dbgFile << "Failed to write image data. Error:"  << imagedata.error;
0231         return ImportExportCodes::ErrorWhileWriting;
0232     }
0233 
0234     return ImportExportCodes::OK;
0235 }
0236 
0237 
0238 void PSDSaver::cancel()
0239 {
0240     m_stop = true;
0241 }
0242 
0243