Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_saver.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
6#include "psd_saver.h"
7
8#include <KoColorSpace.h>
10#include <KoColorProfile.h>
11#include <KoCompositeOp.h>
12#include <KoUnit.h>
13
14
15#include <kis_annotation.h>
16#include <kis_types.h>
17#include <kis_paint_layer.h>
18#include "kis_painter.h"
19#include <KisDocument.h>
20#include <kis_image.h>
21#include <kis_group_layer.h>
22#include <kis_paint_device.h>
23#include <kis_transaction.h>
24#include <kis_debug.h>
25#include <kis_guides_config.h>
26
27#include "psd.h"
28#include "psd_header.h"
29#include "psd_colormode_block.h"
30#include "psd_utils.h"
32#include "psd_layer_section.h"
33#include "psd_resource_block.h"
34#include "psd_image_data.h"
35
36
37
38
39
40QPair<psd_color_mode, quint16> colormodelid_to_psd_colormode(const QString &colorSpaceId, const QString &colorDepthId)
41{
43 if (colorSpaceId == RGBAColorModelID.id()) {
44 colorMode = RGB;
45 }
46 else if (colorSpaceId == CMYKAColorModelID.id()) {
47 colorMode = CMYK;
48 }
49 else if (colorSpaceId == GrayAColorModelID.id()) {
50 colorMode = Grayscale;
51 }
52 else if (colorSpaceId == LABAColorModelID.id()) {
53 colorMode = Lab;
54 }
55
56 quint16 depth = 0;
57
58 if (colorDepthId == Integer8BitsColorDepthID.id()) {
59 depth = 8;
60 }
61 else if (colorDepthId == Integer16BitsColorDepthID.id()) {
62 depth = 16;
63 }
64 else if (colorDepthId == Float16BitsColorDepthID.id()) {
65 depth = 32;
66 }
67 else if (colorDepthId == Float32BitsColorDepthID.id()) {
68 depth = 32;
69 }
70
71 return QPair<psd_color_mode, quint16>(colorMode, depth);
72}
73
74
75
77 : m_image(doc->savingImage())
78 , m_doc(doc)
79 , m_stop(false)
80{
81}
82
86
88{
89 return m_image;
90}
91
93{
95
98 }
99
100 const bool haveLayers = m_image->rootLayer()->childCount() > 1 ||
101 m_image->rootLayer()->firstChild()->childCount() > 0 ||
102 // check if "oblige child" mechanism is in action, then forbid collapsing
106
107 // HEADER
108 PSDHeader header;
109 header.signature = "8BPS";
110 header.version = 1;
111 header.nChannels = haveLayers ?
114
115 header.width = m_image->width();
116 header.height = m_image->height();
117
118 QPair<psd_color_mode, quint16> colordef = colormodelid_to_psd_colormode(m_image->colorSpace()->colorModelId().id(),
120
121 if (colordef.first == COLORMODE_UNKNOWN || colordef.second == 0 || colordef.second == 32) {
125 }
126 header.colormode = colordef.first;
127 header.channelDepth = colordef.second;
128
129 dbgFile << "header" << header << io.pos();
130
131 if (!header.write(io)) {
132 dbgFile << "Failed to write header. Error:" << header.error << io.pos();
134 }
135
136 // COLORMODE BlOCK
137 PSDColorModeBlock colorModeBlock(header.colormode);
138 // XXX: check for annotations that contain the duotone spec
139
140 KisAnnotationSP annotation = m_image->annotation("DuotoneColormodeBlock");
141 if (annotation) {
142 colorModeBlock.duotoneSpecification = annotation->annotation();
143 }
144
145 dbgFile << "colormode block" << io.pos();
146 if (!colorModeBlock.write(io)) {
147 dbgFile << "Failed to write colormode block. Error:" << colorModeBlock.error << io.pos();
149 }
150
151 // IMAGE RESOURCES SECTION
152 PSDImageResourceSection resourceSection;
153
156 while (it != endIt) {
157 KisAnnotationSP annotation = (*it);
158 if (!annotation || annotation->type().isEmpty()) {
159 dbgFile << "Warning: empty annotation";
160 it++;
161 continue;
162 }
163
164 dbgFile << "Annotation:" << annotation->type() << annotation->description();
165
166 if (annotation->type().startsWith(QString("PSD Resource Block:"))) { //
167 PSDResourceBlock *resourceBlock = dynamic_cast<PSDResourceBlock*>(annotation.data());
168 if (resourceBlock) {
169 dbgFile << "Adding PSD Resource Block" << resourceBlock->identifier;
170 resourceSection.resources[(PSDImageResourceSection::PSDResourceID)resourceBlock->identifier] = resourceBlock;
171 }
172 }
173
174 it++;
175 }
176
177 // Add resolution block
178 {
179 RESN_INFO_1005 *resInfo = new RESN_INFO_1005;
180 resInfo->hRes = INCH_TO_POINT(m_image->xRes());
181 resInfo->vRes = INCH_TO_POINT(m_image->yRes());
183 block->identifier = PSDImageResourceSection::RESN_INFO;
184 block->resource = resInfo;
185 resourceSection.resources[PSDImageResourceSection::RESN_INFO] = block;
186 }
187
188 // Add grid/guides block
189 {
190 GRID_GUIDE_1032 *gridGuidesInfo = new GRID_GUIDE_1032;
191 QList<quint32> verticalGuides;
192 Q_FOREACH(qreal guide, m_doc->guidesConfig().verticalGuideLines()) {
193 verticalGuides.append(guide * m_image->xRes());
194 }
195 QList<quint32> horizontalGuides;
196 Q_FOREACH(qreal guide, m_doc->guidesConfig().horizontalGuideLines()) {
197 horizontalGuides.append(guide * m_image->xRes());
198 }
199 gridGuidesInfo->verticalGuides = verticalGuides;
200 gridGuidesInfo->horizontalGuides = horizontalGuides;
202 block->identifier = PSDImageResourceSection::GRID_GUIDE;
203 block->resource = gridGuidesInfo;
204 resourceSection.resources[PSDImageResourceSection::GRID_GUIDE] = block;
205 }
206
207 // Add icc block
208 {
209 ICC_PROFILE_1039 *profileInfo = new ICC_PROFILE_1039;
210 profileInfo->icc = m_image->profile()->rawData();
212 block->identifier = PSDImageResourceSection::ICC_PROFILE;
213 block->resource = profileInfo;
214 resourceSection.resources[PSDImageResourceSection::ICC_PROFILE] = block;
215
216 }
217
218 dbgFile << "resource section" << io.pos();
219 if (!resourceSection.write(io)) {
220 dbgFile << "Failed to write resource section. Error:" << resourceSection.error << io.pos();
222 }
223
224 // LAYER AND MASK DATA
225 // Only save layers and masks if there is more than one layer
226 dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << io.pos();
227
228 if (haveLayers) {
229
230 PSDLayerMaskSection layerSection(header);
231 layerSection.hasTransparency = true;
232
233 if (!layerSection.write(io, m_image->rootLayer(), psd_compression_type::RLE)) {
234 dbgFile << "failed to write layer section. Error:" << layerSection.error << io.pos();
236 }
237 }
238 else {
239 // else write a zero length block
240 dbgFile << "No layers, saving empty layers/mask block" << io.pos();
241 psdwrite(io, (quint32)0);
242 }
243
244 // IMAGE DATA
245 dbgFile << "Saving composited image" << io.pos();
246 PSDImageData imagedata(&header);
247 // Photoshop compresses layer data by default with RLE.
248 if (!imagedata.write(io, m_image->projection(), haveLayers, psd_compression_type::RLE)) {
249 dbgFile << "Failed to write image data. Error:" << imagedata.error;
251 }
252
254}
255
256
258{
259 m_stop = true;
260}
261
262
const KoID Float32BitsColorDepthID("F32", ki18n("32-bit float/channel"))
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Float16BitsColorDepthID("F16", ki18n("16-bit float/channel"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID Integer16BitsColorDepthID("U16", ki18n("16-bit integer/channel"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID LABAColorModelID("LABA", ki18n("L*a*b*/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
constexpr qreal INCH_TO_POINT(qreal inch)
Definition KoUnit.h:38
KisGuidesConfig guidesConfig
const QList< qreal > & verticalGuideLines() const
Returns the list of vertical guide lines.
const QList< qreal > & horizontalGuideLines() const
Returns the list of horizontal guide lines.
vKisAnnotationSP_it endAnnotations()
void waitForDone()
KisGroupLayerSP rootLayer() const
const KoColorSpace * colorSpace() const
KisAnnotationSP annotation(const QString &type)
void convertImageColorSpace(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
KisPaintDeviceSP projection() const
qint32 width() const
double xRes() const
double yRes() const
qint32 height() const
const KoColorProfile * profile() const
vKisAnnotationSP_it beginAnnotations()
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev)
virtual KoID colorModelId() const =0
virtual quint32 channelCount() const =0
virtual KoID colorDepthId() const =0
virtual quint32 colorChannelCount() const =0
QString id() const
Definition KoID.cpp:63
QByteArray duotoneSpecification
bool write(QIODevice &io)
psd_color_mode colormode
Definition psd_header.h:48
quint16 channelDepth
Definition psd_header.h:47
quint16 version
Definition psd_header.h:43
quint16 nChannels
Definition psd_header.h:44
quint32 height
Definition psd_header.h:45
bool write(QIODevice &device)
quint32 width
Definition psd_header.h:46
QString error
Definition psd_header.h:52
QString signature
Definition psd_header.h:42
bool write(QIODevice &io, KisPaintDeviceSP dev, bool hasAlpha, psd_compression_type compressionType)
QMap< PSDResourceID, PSDResourceBlock * > resources
bool write(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
~PSDSaver() override
Definition psd_saver.cpp:83
KisImageSP image()
Definition psd_saver.cpp:87
KisImportExportErrorCode buildFile(QIODevice &io)
Definition psd_saver.cpp:92
KisDocument * m_doc
Definition psd_saver.h:44
KisImageSP m_image
Definition psd_saver.h:43
bool m_stop
Definition psd_saver.h:45
PSDSaver(KisDocument *doc)
Definition psd_saver.cpp:76
virtual void cancel()
This file is part of the Krita application in calligra.
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define dbgFile
Definition kis_debug.h:53
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
@ RLE
Definition psd.h:41
psd_color_mode
Definition psd.h:50
@ Lab
Definition psd.h:58
@ RGB
Definition psd.h:54
@ CMYK
Definition psd.h:55
@ COLORMODE_UNKNOWN
Definition psd.h:65
@ Grayscale
Definition psd.h:52
QPair< psd_color_mode, quint16 > colormodelid_to_psd_colormode(const QString &colorSpaceId, const QString &colorDepthId)
Definition psd_saver.cpp:40
const int MAX_PSD_SIZE
Definition psd.h:29
std::enable_if_t< std::is_arithmetic< T >::value, bool > psdwrite(QIODevice &io, T v)
Definition psd_utils.h:170
QList< quint32 > verticalGuides
QList< quint32 > horizontalGuides
virtual KisPaintDeviceSP projection() const =0
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:820
KisNodeSP firstChild() const
Definition kis_node.cpp:361
quint32 childCount() const
Definition kis_node.cpp:414
virtual QByteArray rawData() const
static KoColorSpaceRegistry * instance()