Krita Source Code Documentation
Loading...
Searching...
No Matches
KisTiffPsdWriter Class Reference

#include <kis_tiff_psd_writer_visitor.h>

+ Inheritance diagram for KisTiffPsdWriter:

Public Member Functions

 KisTiffPsdWriter (TIFF *image, KisTIFFOptions *options)
 
KisImportExportErrorCode writeImage (KisGroupLayerSP rootLayer)
 
 ~KisTiffPsdWriter () override
 

Additional Inherited Members

- Protected Member Functions inherited from KisTIFFBaseWriter
bool copyDataToStrips (KisHLineConstIteratorSP it, tdata_t buff, uint32_t depth, uint16_t sample_format, uint8_t nbcolorssamples, const std::array< quint8, 5 > &poses)
 
TIFF * image ()
 
 KisTIFFBaseWriter (TIFF *image, KisTIFFOptions *options)
 
 ~KisTIFFBaseWriter ()=default
 
- Static Protected Member Functions inherited from KisTIFFBaseWriter
static bool isBitDepthFloat (const KoID depth)
 
static bool writeColorSpaceInformation (TIFF *image, const KoColorSpace *cs, uint16_t &color_type, uint16_t &sample_format, const KoColorSpace *&destColorSpace)
 
- Protected Attributes inherited from KisTIFFBaseWriter
TIFF * m_image
 
KisTIFFOptionsm_options
 

Detailed Description

Definition at line 22 of file kis_tiff_psd_writer_visitor.h.

Constructor & Destructor Documentation

◆ KisTiffPsdWriter()

KisTiffPsdWriter::KisTiffPsdWriter ( TIFF * image,
KisTIFFOptions * options )

Definition at line 36 of file kis_tiff_psd_writer_visitor.cpp.

37 : KisTIFFBaseWriter(image, options)
38{
39}
KisTIFFBaseWriter(TIFF *image, KisTIFFOptions *options)

◆ ~KisTiffPsdWriter()

KisTiffPsdWriter::~KisTiffPsdWriter ( )
overridedefault

Member Function Documentation

◆ writeImage()

KisImportExportErrorCode KisTiffPsdWriter::writeImage ( KisGroupLayerSP rootLayer)

For Photoshop tiff files, first thing is to write the projection of the image file.

  • BEGIN PHOTOSHOP SPECIFIC HANDLING CODE *‍///

Synthesize the PSD file into the TIFFTAG_IMAGESOURCEDATA field.

Write all the annotations into the TIFFTAG_PHOTOSHOP field.

Freshly baked Photoshoppy TIFF file!

  • END PHOTOSHOP SPECIFIC HANDLING CODE *‍///

Definition at line 43 of file kis_tiff_psd_writer_visitor.cpp.

44{
45 dbgFile << "Starting write of Photoshop layer data";
46
52 if (layer->image()->width() > MAX_PSD_SIZE || layer->image()->height() > MAX_PSD_SIZE) {
53 dbgFile << "This TIFF file is too big to be represented as a PSD blob!";
55 }
56
57 dbgFile << "Writing root layer projection";
58 KisPaintDeviceSP pd = layer->projection();
59
60 uint16_t color_type = 0;
61 uint16_t sample_format = SAMPLEFORMAT_UINT;
62 const KoColorSpace *destColorSpace = nullptr;
63 // Check colorspace
64 if (!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format, destColorSpace)) { // unsupported colorspace
65 if (!destColorSpace) {
66 dbgFile << "Unsupported colorspace" << pd->colorSpace()->name();
68 }
69 pd.attach(new KisPaintDevice(*pd));
70 pd->convertTo(destColorSpace);
71 }
72
73 {
74 // WORKAROUND: block any attempts to use YCbCr with alpha channels.
75 // This should not happen because alpha is disabled by default
76 // and the checkbox is blocked for YCbCr and CMYK.
77 KIS_SAFE_ASSERT_RECOVER(color_type != PHOTOMETRIC_YCBCR
78 || !m_options->alpha)
79 {
80 warnFile << "TIFF does not support exporting alpha channels with "
81 "YCbCr. Skipping...";
82 m_options->alpha = false;
83 }
84 }
85
86 // Save depth
87 uint32_t depth = 8 * pd->pixelSize() / pd->channelCount();
88 TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth);
89
90 {
91 // WORKAROUND: block any attempts to use JPEG with >= 8 bits
92
93 if (m_options->compressionType == COMPRESSION_JPEG && depth != 8) {
94 warnFile << "Attempt to export JPEG with multi-byte depth, "
95 "disabling compression";
96 m_options->compressionType = COMPRESSION_NONE;
97 }
98 }
99
100 // Save number of samples
101 if (m_options->alpha) {
102 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount());
103 const std::array<uint16_t, 1> sampleinfo = {EXTRASAMPLE_UNASSALPHA};
104 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo.data());
105 } else {
106 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount() - 1);
107 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0);
108 }
109
110 // Save colorspace information
111 TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type);
112 TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format);
113 TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width());
114 TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height());
115
116 // Set the compression options
117 TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType);
118 if (m_options->compressionType == COMPRESSION_JPEG) {
119 TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality);
120 } else if (m_options->compressionType == COMPRESSION_ADOBE_DEFLATE) {
121 TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress);
122 } else if (m_options->compressionType == COMPRESSION_PIXARLOG) {
123 TIFFSetField(image(),
124 TIFFTAG_PIXARLOGQUALITY,
126 }
127
128 // Set the predictor
129 if (m_options->compressionType == COMPRESSION_LZW
130 || m_options->compressionType == COMPRESSION_ADOBE_DEFLATE)
131 TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor);
132
133 // Use contiguous configuration
134 TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
135
136 // Do not set the rowsperstrip, as it's incompatible with JPEG
137
138 // But do set YCbCr 4:4:4 if applicable
139 if (color_type == PHOTOMETRIC_YCBCR) {
140 TIFFSetField(image(), TIFFTAG_YCBCRSUBSAMPLING, 1, 1);
141 TIFFSetField(image(), TIFFTAG_YCBCRPOSITIONING, YCBCRPOSITION_CENTERED);
142 if (m_options->compressionType == COMPRESSION_JPEG) {
143 TIFFSetField(image(), TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW);
144 }
145 }
146
147 // Save profile
148 if (m_options->saveProfile) {
149 const KoColorProfile *profile = pd->colorSpace()->profile();
150 if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) {
151 QByteArray ba = profile->rawData();
152 TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(), ba.constData());
153 }
154 }
155 tsize_t stripsize = TIFFStripSize(image());
156 std::unique_ptr<std::remove_pointer_t<tdata_t>, decltype(&_TIFFfree)> buff(
157 _TIFFmalloc(stripsize),
158 &_TIFFfree);
160 buff && "Unable to allocate buffer for TIFF!",
162 qint32 height = layer->image()->height();
163 qint32 width = layer->image()->width();
164 bool r = true;
165 for (qint32 y = 0; y < height; y++) {
167 switch (color_type) {
168 case PHOTOMETRIC_MINISBLACK: {
169 const std::array<quint8, 5> poses = {0, 1};
170 r = copyDataToStrips(it,
171 buff.get(),
172 depth,
173 sample_format,
174 1,
175 poses);
176 } break;
177 case PHOTOMETRIC_RGB: {
178 const auto poses = [&]() -> std::array<quint8, 5> {
179 if (sample_format == SAMPLEFORMAT_IEEEFP) {
180 return {0, 1, 2, 3};
181 } else {
182 return {2, 1, 0, 3};
183 }
184 }();
185 r = copyDataToStrips(it,
186 buff.get(),
187 depth,
188 sample_format,
189 3,
190 poses);
191 } break;
192 case PHOTOMETRIC_SEPARATED: {
193 const std::array<quint8, 5> poses = {0, 1, 2, 3, 4};
194 r = copyDataToStrips(it,
195 buff.get(),
196 depth,
197 sample_format,
198 4,
199 poses);
200 } break;
201 case PHOTOMETRIC_ICCLAB:
202 case PHOTOMETRIC_YCBCR: {
203 const std::array<quint8, 5> poses = {0, 1, 2, 3};
204 r = copyDataToStrips(it,
205 buff.get(),
206 depth,
207 sample_format,
208 3,
209 poses);
210 } break;
211 }
212 if (!r)
214 TIFFWriteScanline(image(),
215 buff.get(),
216 static_cast<quint32>(y),
217 (tsample_t)-1);
218 }
219 buff.reset();
220
222
227 {
228 const bool haveLayers = layer->childCount() > 1 || KisPainter::checkDeviceHasTransparency(layer->firstChild()->projection());
229
230 QBuffer buf;
231 buf.open(QIODevice::WriteOnly);
232
233 dbgFile << "m_image->rootLayer->childCount" << layer->childCount() << buf.pos();
234
235 if (haveLayers) {
236 KisTiffPsdLayerRecord layerSection(
237 TIFFIsBigEndian(image()),
238 static_cast<uint32_t>(width),
239 static_cast<uint32_t>(height),
240 static_cast<uint16_t>(depth),
241 static_cast<uint16_t>(pd->channelCount()),
242 color_type,
243 true);
244
245 if (!layerSection.write(buf, layer, static_cast<psd_compression_type>(m_options->psdCompressionType))) {
246 dbgFile << "failed to write layer section. Error:" << layerSection.record()->error << buf.pos();
248 }
249 } else {
250 // else write a zero length block
251 dbgFile << "No layers, saving empty layers/mask block" << buf.pos();
252 psdwrite(buf, (quint32)0);
253 }
254
255 buf.close();
256 buf.open(QIODevice::ReadOnly);
257
258 if (!TIFFSetField(image(), TIFFTAG_IMAGESOURCEDATA, static_cast<uint32_t>(buf.size()), buf.data().constData())) {
259 dbgFile << "Failed to write the PSD image block to the TIFF field";
261 }
262 }
263
268 {
269 // IMAGE RESOURCES SECTION
270 KisTiffPsdResourceRecord resourceSection;
271
272 for (vKisAnnotationSP_it it = layer->image()->beginAnnotations(); it != layer->image()->endAnnotations(); ++it) {
273 KisAnnotationSP annotation = (*it);
274 if (!annotation || annotation->type().isEmpty()) {
275 dbgFile << "Warning: empty annotation";
276 continue;
277 }
278
279 dbgFile << "Annotation:" << annotation->type() << annotation->description();
280
281 if (annotation->type().startsWith(QString("PSD Resource Block:"))) { //
282 PSDResourceBlock *resourceBlock =
283 dynamic_cast<PSDResourceBlock *>(annotation.data());
284 if (resourceBlock) {
285 dbgFile << "Adding PSD Resource Block" << resourceBlock->identifier;
286 resourceSection.resources[(KisTiffPsdResourceRecord::PSDResourceID)resourceBlock->identifier] = resourceBlock;
287 }
288 }
289 }
290
291 // Add resolution block
292 {
293 auto *resInfo = new RESN_INFO_1005();
294 resInfo->hRes = static_cast<int>(INCH_TO_POINT(layer->image()->xRes()));
295 resInfo->vRes = static_cast<int>(INCH_TO_POINT(layer->image()->yRes()));
296 auto *block = new PSDResourceBlock();
298 block->resource = resInfo;
300 }
301
302 // Add icc block
303 {
304 auto *profileInfo = new ICC_PROFILE_1039();
305 profileInfo->icc = layer->image()->profile()->rawData();
306 auto *block = new PSDResourceBlock();
308 block->resource = profileInfo;
310 }
311
312 dbgFile << "Resource section ready to write";
313
314 QBuffer buf;
315 buf.open(QIODevice::WriteOnly);
316
317 if (!resourceSection.write(buf)) {
318 dbgFile << "Failed to write resource section. Error:" << resourceSection.error << buf.pos();
320 }
321
322 buf.close();
323 buf.open(QIODevice::WriteOnly);
324
325 if (!TIFFSetField(image(), TIFFTAG_PHOTOSHOP, static_cast<uint32_t>(buf.size()), buf.data().data())) {
326 dbgFile << "Failed to write the PSD resource block to the TIFF field";
328 }
329 }
330
336
337 TIFFWriteDirectory(image());
339}
constexpr qreal INCH_TO_POINT(qreal inch)
Definition KoUnit.h:38
quint32 pixelSize() const
quint32 channelCount() const
const KoColorSpace * colorSpace() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
KisHLineConstIteratorSP createHLineConstIteratorNG(qint32 x, qint32 y, qint32 w) const
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev)
void attach(T *p)
bool copyDataToStrips(KisHLineConstIteratorSP it, tdata_t buff, uint32_t depth, uint16_t sample_format, uint8_t nbcolorssamples, const std::array< quint8, 5 > &poses)
KisTIFFOptions * m_options
static bool writeColorSpaceInformation(TIFF *image, const KoColorSpace *cs, uint16_t &color_type, uint16_t &sample_format, const KoColorSpace *&destColorSpace)
QMap< PSDResourceID, PSDResourceBlock * > resources
virtual const KoColorProfile * profile() const =0
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:85
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define warnFile
Definition kis_debug.h:95
#define dbgFile
Definition kis_debug.h:53
vKisAnnotationSP::iterator vKisAnnotationSP_it
Definition kis_types.h:181
int depth(typename Forest< T >::const_child_iterator beginIt, typename Forest< T >::const_child_iterator endIt)
Definition KisForest.h:1213
psd_compression_type
Definition psd.h:39
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
virtual QByteArray rawData() const
virtual QString type() const

References KisTIFFOptions::alpha, KisSharedPtr< T >::attach(), KisImage::beginAnnotations(), KisPaintDevice::channelCount(), KisPainter::checkDeviceHasTransparency(), KisNode::childCount(), KisPaintDevice::colorSpace(), KisTIFFOptions::compressionType, KisPaintDevice::convertTo(), KisTIFFBaseWriter::copyDataToStrips(), KisPaintDevice::createHLineConstIteratorNG(), KisSharedPtr< T >::data(), dbgFile, KisTIFFOptions::deflateCompress, KisImage::endAnnotations(), KisTiffPsdResourceRecord::error, ImportExportCodes::ErrorWhileWriting, ImportExportCodes::Failure, KisNode::firstChild(), ImportExportCodes::FormatColorSpaceUnsupported, KisImage::height(), KisTiffPsdResourceRecord::ICC_PROFILE, PSDResourceBlock::identifier, KisBaseNode::image, KisTIFFBaseWriter::image(), INCH_TO_POINT(), ImportExportCodes::InsufficientMemory, ImportExportCodes::InternalError, KisTIFFOptions::jpegQuality, KIS_ASSERT_RECOVER_RETURN_VALUE, KIS_SAFE_ASSERT_RECOVER, KisTIFFBaseWriter::m_options, MAX_PSD_SIZE, KoColorSpace::name, ImportExportCodes::OK, KisTIFFOptions::pixarLogCompress, KisPaintDevice::pixelSize(), KisTIFFOptions::predictor, KisImage::profile(), KoColorSpace::profile(), KisBaseNode::projection(), KisLayer::projection(), KisTIFFOptions::psdCompressionType, psdwrite(), KoColorProfile::rawData(), KisTiffPsdLayerRecord::record(), KisTiffPsdResourceRecord::RESN_INFO, KisTiffPsdResourceRecord::resources, KisTIFFOptions::saveProfile, KoColorProfile::type(), warnFile, KisImage::width(), KisTiffPsdResourceRecord::write(), KisTiffPsdLayerRecord::write(), KisTIFFBaseWriter::writeColorSpaceInformation(), KisImage::xRes(), and KisImage::yRes().


The documentation for this class was generated from the following files: