For Photoshop tiff files, first thing is to write the projection of the image file.
Synthesize the PSD file into the TIFFTAG_IMAGESOURCEDATA field.
Write all the annotations into the TIFFTAG_PHOTOSHOP field.
44{
45 dbgFile <<
"Starting write of Photoshop layer data";
46
53 dbgFile <<
"This TIFF file is too big to be represented as a PSD blob!";
55 }
56
57 dbgFile <<
"Writing root layer projection";
59
60 uint16_t color_type = 0;
61 uint16_t sample_format = SAMPLEFORMAT_UINT;
63
65 if (!destColorSpace) {
68 }
71 }
72
73 {
74
75
76
79 {
80 warnFile <<
"TIFF does not support exporting alpha channels with "
81 "YCbCr. Skipping...";
83 }
84 }
85
86
88 TIFFSetField(
image(), TIFFTAG_BITSPERSAMPLE, depth);
89
90 {
91
92
94 warnFile <<
"Attempt to export JPEG with multi-byte depth, "
95 "disabling compression";
97 }
98 }
99
100
103 const std::array<uint16_t, 1> sampleinfo = {EXTRASAMPLE_UNASSALPHA};
104 TIFFSetField(
image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo.data());
105 } else {
107 TIFFSetField(
image(), TIFFTAG_EXTRASAMPLES, 0);
108 }
109
110
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
123 TIFFSetField(
image(),
124 TIFFTAG_PIXARLOGQUALITY,
126 }
127
128
132
133
134 TIFFSetField(
image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
135
136
137
138
139 if (color_type == PHOTOMETRIC_YCBCR) {
140 TIFFSetField(
image(), TIFFTAG_YCBCRSUBSAMPLING, 1, 1);
141 TIFFSetField(
image(), TIFFTAG_YCBCRPOSITIONING, YCBCRPOSITION_CENTERED);
143 TIFFSetField(
image(), TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW);
144 }
145 }
146
147
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();
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};
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 }();
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};
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};
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 {
229
230 QBuffer buf;
231 buf.open(QIODevice::WriteOnly);
232
233 dbgFile <<
"m_image->rootLayer->childCount" << layer->childCount() << buf.pos();
234
235 if (haveLayers) {
237 TIFFIsBigEndian(
image()),
238 static_cast<uint32_t>(width),
239 static_cast<uint32_t>(height),
240 static_cast<uint16_t>(depth),
242 color_type,
243 true);
244
246 dbgFile <<
"failed to write layer section. Error:" << layerSection.record()->error << buf.pos();
248 }
249 } else {
250
251 dbgFile <<
"No layers, saving empty layers/mask block" << buf.pos();
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
271
272 for (
vKisAnnotationSP_it it = layer->image()->beginAnnotations(); it != layer->image()->endAnnotations(); ++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:"))) {
284 if (resourceBlock) {
287 }
288 }
289 }
290
291
292 {
294 resInfo->hRes =
static_cast<int>(
INCH_TO_POINT(layer->image()->xRes()));
295 resInfo->vRes =
static_cast<int>(
INCH_TO_POINT(layer->image()->yRes()));
298 block->resource = resInfo;
300 }
301
302
303 {
305 profileInfo->icc = layer->image()->profile()->rawData();
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)
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)
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
bool write(QIODevice &io)
virtual const KoColorProfile * profile() const =0
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
#define KIS_SAFE_ASSERT_RECOVER(cond)
vKisAnnotationSP::iterator vKisAnnotationSP_it
@ FormatColorSpaceUnsupported
int depth(typename Forest< T >::const_child_iterator beginIt, typename Forest< T >::const_child_iterator endIt)
std::enable_if_t< std::is_arithmetic< T >::value, bool > psdwrite(QIODevice &io, T v)
quint16 psdCompressionType
virtual QByteArray rawData() const
virtual QString type() const