Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_tiff_writer_visitor.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2006 Cyrille Berger <cberger@cberger.net>
3 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
8#include <QBuffer>
9
10#include <memory>
11
12#include <tiff.h>
13
15#include <KoColorProfile.h>
16#include <KoColorSpace.h>
18#include <KoID.h>
19#include <kis_assert.h>
21
22#include <KoConfig.h>
23#ifdef HAVE_OPENEXR
24#include <half.h>
25#endif
26
27#include "kis_tiff_converter.h"
29
31 : KisTIFFBaseWriter(image, options)
32{
33}
34
36
38{
39 dbgFile << "visiting on layer" << layer->name() << "";
40 KisPaintDeviceSP pd = layer->projection();
41
42 uint16_t color_type = 0;
43 uint16_t sample_format = SAMPLEFORMAT_UINT;
44 const KoColorSpace *destColorSpace = nullptr;
45 // Check colorspace
46 if (!writeColorSpaceInformation(image(), pd->colorSpace(), color_type, sample_format, destColorSpace)) { // unsupported colorspace
47 if (!destColorSpace) {
48 return false;
49 }
50 pd.attach(new KisPaintDevice(*pd));
51 pd->convertTo(destColorSpace);
52 }
53
54 {
55 // WORKAROUND: block any attempts to use YCbCr with alpha channels.
56 // This should not happen because alpha is disabled by default
57 // and the checkbox is blocked for YCbCr and CMYK.
58 KIS_SAFE_ASSERT_RECOVER(color_type != PHOTOMETRIC_YCBCR
59 || !m_options->alpha)
60 {
61 warnFile << "TIFF does not support exporting alpha channels with "
62 "YCbCr. Skipping...";
63 m_options->alpha = false;
64 }
65 }
66
67 // Save depth
68 uint32_t depth = 8 * pd->pixelSize() / pd->channelCount();
69 TIFFSetField(image(), TIFFTAG_BITSPERSAMPLE, depth);
70
71 {
72 // WORKAROUND: block any attempts to use JPEG with >= 8 bits
73
74 if (m_options->compressionType == COMPRESSION_JPEG && depth != 8) {
75 warnFile << "Attempt to export JPEG with multi-byte depth, "
76 "disabling compression";
77 m_options->compressionType = COMPRESSION_NONE;
78 }
79 }
80
81 // Save number of samples
82 if (m_options->alpha) {
83 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount());
84 const std::array<uint16_t, 1> sampleinfo = {EXTRASAMPLE_UNASSALPHA};
85 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 1, sampleinfo.data());
86 } else {
87 TIFFSetField(image(), TIFFTAG_SAMPLESPERPIXEL, pd->channelCount() - 1);
88 TIFFSetField(image(), TIFFTAG_EXTRASAMPLES, 0);
89 }
90
91 // Save colorspace information
92 TIFFSetField(image(), TIFFTAG_PHOTOMETRIC, color_type);
93 TIFFSetField(image(), TIFFTAG_SAMPLEFORMAT, sample_format);
94 TIFFSetField(image(), TIFFTAG_IMAGEWIDTH, layer->image()->width());
95 TIFFSetField(image(), TIFFTAG_IMAGELENGTH, layer->image()->height());
96
97 // Set the compression options
98 TIFFSetField(image(), TIFFTAG_COMPRESSION, m_options->compressionType);
99 if (m_options->compressionType == COMPRESSION_JPEG) {
100 TIFFSetField(image(), TIFFTAG_JPEGQUALITY, m_options->jpegQuality);
101 } else if (m_options->compressionType == COMPRESSION_ADOBE_DEFLATE) {
102 TIFFSetField(image(), TIFFTAG_ZIPQUALITY, m_options->deflateCompress);
103 } else if (m_options->compressionType == COMPRESSION_PIXARLOG) {
104 TIFFSetField(image(),
105 TIFFTAG_PIXARLOGQUALITY,
107 }
108
109 // Set the predictor
110 if (m_options->compressionType == COMPRESSION_LZW
111 || m_options->compressionType == COMPRESSION_ADOBE_DEFLATE)
112 TIFFSetField(image(), TIFFTAG_PREDICTOR, m_options->predictor);
113
114 // Use contiguous configuration
115 TIFFSetField(image(), TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
116
117 // Do not set the rowsperstrip, as it's incompatible with JPEG
118
119 // But do set YCbCr 4:4:4 if applicable
120 if (color_type == PHOTOMETRIC_YCBCR) {
121 TIFFSetField(image(), TIFFTAG_YCBCRSUBSAMPLING, 1, 1);
122 TIFFSetField(image(), TIFFTAG_YCBCRPOSITIONING, YCBCRPOSITION_CENTERED);
123 if (m_options->compressionType == COMPRESSION_JPEG) {
124 TIFFSetField(image(), TIFFTAG_JPEGCOLORMODE, JPEGCOLORMODE_RAW);
125 }
126 }
127
128 // Save profile
129 if (m_options->saveProfile) {
130 const KoColorProfile* profile = pd->colorSpace()->profile();
131 if (profile && profile->type() == "icc" && !profile->rawData().isEmpty()) {
132 QByteArray ba = profile->rawData();
133 TIFFSetField(image(), TIFFTAG_ICCPROFILE, ba.size(), ba.constData());
134 }
135 }
136
137 {
138 // IPTC
141 QBuffer buf;
143
144 if (buf.size()
145 && !TIFFSetField(image(),
146 TIFFTAG_RICHTIFFIPTC,
147 static_cast<uint32_t>(buf.size()),
148 buf.data().data())) {
149 dbgFile << "Failed to write the IPTC metadata to the TIFF field";
150 }
151 }
152
153 {
154 // XMP
157 QBuffer buf;
159
160 if (buf.size()
161 && !TIFFSetField(image(),
162 TIFFTAG_XMLPACKET,
163 static_cast<uint32_t>(buf.size()),
164 buf.data().data())) {
165 dbgFile << "Failed to write the XMP metadata to the TIFF field";
166 }
167 }
168
169 tsize_t stripsize = TIFFStripSize(image());
170 std::unique_ptr<std::remove_pointer_t<tdata_t>, decltype(&_TIFFfree)> buff(
171 _TIFFmalloc(stripsize),
172 &_TIFFfree);
174 buff && "Unable to allocate buffer for TIFF!",
175 false);
176 qint32 height = layer->image()->height();
177 qint32 width = layer->image()->width();
178 bool r = true;
179 for (int y = 0; y < height; y++) {
181 switch (color_type) {
182 case PHOTOMETRIC_MINISBLACK: {
183 const std::array<quint8, 5> poses = {0, 1};
184 r = copyDataToStrips(it,
185 buff.get(),
186 depth,
187 sample_format,
188 1,
189 poses);
190 }
191 break;
192 case PHOTOMETRIC_RGB: {
193 const auto poses = [&]() -> std::array<quint8, 5> {
194 if (sample_format == SAMPLEFORMAT_IEEEFP) {
195 return {0, 1, 2, 3};
196 } else {
197 return {2, 1, 0, 3};
198 }
199 }();
200 r = copyDataToStrips(it,
201 buff.get(),
202 depth,
203 sample_format,
204 3,
205 poses);
206 }
207 break;
208 case PHOTOMETRIC_SEPARATED: {
209 const std::array<quint8, 5> poses = {0, 1, 2, 3, 4};
210 r = copyDataToStrips(it,
211 buff.get(),
212 depth,
213 sample_format,
214 4,
215 poses);
216 }
217 break;
218 case PHOTOMETRIC_ICCLAB:
219 case PHOTOMETRIC_YCBCR: {
220 const std::array<quint8, 5> poses = {0, 1, 2, 3};
221 r = copyDataToStrips(it,
222 buff.get(),
223 depth,
224 sample_format,
225 3,
226 poses);
227 } break;
228 }
229 if (!r) return false;
230 TIFFWriteScanline(image(),
231 buff.get(),
232 static_cast<uint32_t>(y),
233 (tsample_t)-1);
234 }
235 buff.reset();
236
237 return TIFFWriteDirectory(image());
238}
qint32 width() const
qint32 height() const
@ NoHeader
Don't append any header.
virtual bool saveTo(const Store *store, QIODevice *ioDevice, HeaderType headerType=NoHeader) const =0
static KisMetadataBackendRegistry * instance()
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
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)
~KisTIFFWriterVisitor() override
KisTIFFWriterVisitor(TIFF *image, KisTIFFOptions *options)
virtual const KoColorProfile * profile() const =0
const T value(const QString &id) const
#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
KisImageWSP image
QString name() const
KisPaintDeviceSP projection() const override
Definition kis_layer.cc:820
KisMetaData::Store * metaData()
virtual QByteArray rawData() const
virtual QString type() const