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