22#include <exiv2/jpgimage.hpp>
23#include <exiv2/version.hpp>
24#if EXIV2_TEST_VERSION(0,28,0)
25#include <exiv2/photoshop.hpp>
30#include <QApplication>
32#include <klocalizedstring.h>
58#define ICC_MARKER (JPEG_APP0 + 2)
59#define ICC_OVERHEAD_LEN 14
60#define MAX_BYTES_IN_MARKER 65533
61#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
66const char xmpMarker[] =
"http://ns.adobe.com/xap/1.0/\0";
72void jpegErrorExit (j_common_ptr cinfo)
74 char jpegLastErrorMsg[JMSG_LENGTH_MAX];
76 ( *( cinfo->err->format_message ) ) ( cinfo, jpegLastErrorMsg );
78 throw std::runtime_error(jpegLastErrorMsg);
81J_COLOR_SPACE getColorTypeforColorSpace(
const KoColorSpace * cs)
83 if (
KoID(cs->
id()) ==
KoID(
"GRAYA") || cs->
id() ==
"GRAYAU16" || cs->
id() ==
"GRAYA16") {
95QString getColorSpaceModelForColorType(J_COLOR_SPACE color_type)
97 dbgFile <<
"color_type =" << color_type;
98 if (color_type == JCS_GRAYSCALE) {
100 }
else if (color_type == JCS_RGB) {
102 }
else if (color_type == JCS_CMYK) {
125 : m_d(new
Private(doc, batchMode))
135 struct jpeg_decompress_struct cinfo;
136 struct jpeg_error_mgr jerr;
138 cinfo.err = jpeg_std_error(&jerr);
139 jerr.error_exit = jpegErrorExit;
142 jpeg_create_decompress(&cinfo);
146 jpeg_save_markers(&cinfo, JPEG_COM, 0xFFFF);
148 for (
int m = 0; m < 16; m++)
149 jpeg_save_markers(&cinfo, JPEG_APP0 + m, 0xFFFF);
154 jpeg_read_header(&cinfo, (
boolean)
true);
157 jpeg_start_decompress(&cinfo);
160 QString modelId = getColorSpaceModelForColorType(cinfo.out_color_space);
161 if (modelId.isEmpty()) {
162 dbgFile <<
"unsupported colorspace :" << cinfo.out_color_space;
163 jpeg_destroy_decompress(&cinfo);
167 uchar* profile_data = 0;
168 uint profile_len = 0;
170 QByteArray profile_rawdata;
172 profile_rawdata.resize(profile_len);
173 memcpy(profile_rawdata.data(), profile_data, profile_len);
174 cmsHPROFILE hProfile = cmsOpenProfileFromMem(profile_data, profile_len);
176 if (hProfile != (cmsHPROFILE) 0) {
178 Q_CHECK_PTR(profile);
179 dbgFile <<
"profile name:" << profile->
name() <<
" product information:" << profile->
info();
181 dbgFile <<
"the profile is not suitable for output and therefore cannot be used in krita, we need to convert the image to a standard profile";
188 const QString colorSpaceId =
193 warnFile <<
"The profile " << profile->
name() <<
" is not compatible with the color space model " << modelId;
200 dbgFile <<
"image has embedded profile:" << profile -> name() <<
"";
206 dbgFile <<
"unknown colorspace";
207 jpeg_destroy_decompress(&cinfo);
219 if (transform && !transform->
isValid()) {
226 m_d->image =
new KisImage(
m_d->doc->createUndoStore(), cinfo.image_width, cinfo.image_height, cs,
"built image");
227 Q_CHECK_PTR(
m_d->image);
231 double xres = 72, yres = 72;
232 if (cinfo.density_unit == 1) {
233 xres = cinfo.X_density;
234 yres = cinfo.Y_density;
235 }
else if (cinfo.density_unit == 2) {
236 xres = cinfo.X_density * 2.54;
237 yres = cinfo.Y_density * 2.54;
251 JSAMPROW row_pointer =
new JSAMPLE[cinfo.image_width*cinfo.num_components];
253 for (; cinfo.output_scanline < cinfo.image_height;) {
255 jpeg_read_scanlines(&cinfo, &row_pointer, 1);
256 quint8 *src = row_pointer;
257 switch (cinfo.out_color_space) {
260 quint8 *d = it->rawData();
262 if (transform) transform->
transform(d, d, 1);
265 }
while (it->nextPixel());
269 quint8 *d = it->rawData();
273 if (transform) transform->
transform(d, d, 1);
276 }
while (it->nextPixel());
280 quint8 *d = it->rawData();
285 if (transform) transform->
transform(d, d, 1);
288 }
while (it->nextPixel());
299 dbgFile <<
"Looking for exif information";
301 for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) {
302 dbgFile <<
"Marker is" << marker->marker;
303 if (marker->marker != (JOCTET)(JPEG_APP0 + 1)
304 || marker->data_length < 14) {
308 if (GETJOCTET(marker->data[0]) != (JOCTET) 0x45 ||
309 GETJOCTET(marker->data[1]) != (JOCTET) 0x78 ||
310 GETJOCTET(marker->data[2]) != (JOCTET) 0x69 ||
311 GETJOCTET(marker->data[3]) != (JOCTET) 0x66 ||
312 GETJOCTET(marker->data[4]) != (JOCTET) 0x00 ||
313 GETJOCTET(marker->data[5]) != (JOCTET) 0x00)
316 dbgFile <<
"Found exif information of length :" << marker->data_length;
319 QByteArray byteArray((
const char*)marker->data + 6, marker->data_length - 6);
320 QBuffer buf(&byteArray);
359 dbgFile <<
"Looking for IPTC information";
361 for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) {
362 dbgFile <<
"Marker is" << marker->marker;
363 if (marker->marker != (JOCTET)(JPEG_APP0 + 13) || marker->data_length < 14) {
367 for (
int i = 0; i < 14; i++) {
370 dbgFile <<
"No photoshop marker";
374 dbgFile <<
"Found Photoshop information of length :" << marker->data_length;
377 const Exiv2::byte *record = 0;
378 uint32_t sizeIptc = 0;
379 uint32_t sizeHdr = 0;
381 if (!Exiv2::Photoshop::locateIptcIrb((Exiv2::byte*)(marker->data + 14),
382#
if EXIV2_TEST_VERSION(0,28,0)
383 marker->data_length - 14, &record, sizeHdr, sizeIptc)) {
385 marker->data_length - 14, &record, &sizeHdr, &sizeIptc)) {
389 QByteArray byteArray((
const char*)(record + sizeHdr), sizeIptc);
390 QBuffer buf(&byteArray);
393 dbgFile <<
"IPTC Not found in Photoshop marker";
399 dbgFile <<
"Looking for XMP information";
401 for (jpeg_saved_marker_ptr marker = cinfo.marker_list; marker != 0; marker = marker->next) {
402 dbgFile <<
"Marker is" << marker->marker;
403 if (marker->marker != (JOCTET)(JPEG_APP0 + 1) || marker->data_length < 31) {
406 if (memcmp(marker->data,
xmpMarker, 29) != 0) {
410 dbgFile <<
"Found XMP Marker of length " << marker->data_length;
411 QByteArray byteArray((
const char*)marker->data + 29, marker->data_length - 29);
425 if (xres != 0 && yres != 0) {
431 jpeg_finish_decompress(&cinfo);
432 jpeg_destroy_decompress(&cinfo);
433 delete [] row_pointer;
436 catch( std::runtime_error &) {
437 jpeg_destroy_decompress(&cinfo);
464 J_COLOR_SPACE color_type = getColorTypeforColorSpace(cs);
466 if (color_type == JCS_UNKNOWN) {
469 color_type = JCS_RGB;
476 color_type = JCS_RGB;
482 struct jpeg_compress_struct cinfo;
484 struct jpeg_error_mgr jerr;
485 cinfo.err = jpeg_std_error(&jerr);
486 jerr.error_exit = jpegErrorExit;
491 jpeg_create_compress(&cinfo);
495 cinfo.image_width = width;
496 cinfo.image_height = height;
498 cinfo.in_color_space = color_type;
501 jpeg_set_defaults(&cinfo);
506 jpeg_simple_progression(&cinfo);
509 cinfo.optimize_coding = (boolean)options.
optimize;
512 cinfo.smoothing_factor = (boolean)options.
smooth;
518 cinfo.comp_info[0].h_samp_factor = 2;
519 cinfo.comp_info[0].v_samp_factor = 2;
520 cinfo.comp_info[1].h_samp_factor = 1;
521 cinfo.comp_info[1].v_samp_factor = 1;
522 cinfo.comp_info[2].h_samp_factor = 1;
523 cinfo.comp_info[2].v_samp_factor = 1;
528 cinfo.comp_info[0].h_samp_factor = 2;
529 cinfo.comp_info[0].v_samp_factor = 1;
530 cinfo.comp_info[1].h_samp_factor = 1;
531 cinfo.comp_info[1].v_samp_factor = 1;
532 cinfo.comp_info[2].h_samp_factor = 1;
533 cinfo.comp_info[2].v_samp_factor = 1;
537 cinfo.comp_info[0].h_samp_factor = 1;
538 cinfo.comp_info[0].v_samp_factor = 2;
539 cinfo.comp_info[1].h_samp_factor = 1;
540 cinfo.comp_info[1].v_samp_factor = 1;
541 cinfo.comp_info[2].h_samp_factor = 1;
542 cinfo.comp_info[2].v_samp_factor = 1;
546 cinfo.comp_info[0].h_samp_factor = 1;
547 cinfo.comp_info[0].v_samp_factor = 1;
548 cinfo.comp_info[1].h_samp_factor = 1;
549 cinfo.comp_info[1].v_samp_factor = 1;
550 cinfo.comp_info[2].h_samp_factor = 1;
551 cinfo.comp_info[2].v_samp_factor = 1;
559 cinfo.density_unit = 1;
560 cinfo.write_JFIF_header = (boolean)
true;
563 jpeg_start_compress(&cinfo, (
boolean)
true);
566 if (metaData && !metaData->
empty()) {
570 dbgFile <<
"Trying to save exif information";
578 dbgFile <<
"Exif information size is" << buffer.data().size();
579 QByteArray data = buffer.data();
581 jpeg_write_marker(&cinfo, JPEG_APP0 + 1, (
const JOCTET*)data.data(), data.size());
583 dbgFile <<
"EXIF information could not be saved.";
588 dbgFile <<
"Trying to save exif information";
595 dbgFile <<
"IPTC information size is" << buffer.data().size();
596 QByteArray data = buffer.data();
598 jpeg_write_marker(&cinfo, JPEG_APP0 + 13, (
const JOCTET*)data.data(), data.size());
600 dbgFile <<
"IPTC information could not be saved.";
605 dbgFile <<
"Trying to save XMP information";
612 dbgFile <<
"XMP information size is" << buffer.data().size();
613 QByteArray data = buffer.data();
615 jpeg_write_marker(&cinfo, JPEG_APP0 + 14, (
const JOCTET*)data.data(), data.size());
617 dbgFile <<
"XMP information could not be saved.";
625 dev->
fill(QRect(0, 0, width, height), c);
633 QByteArray colorProfileData = colorProfile->
rawData();
634 write_icc_profile(& cinfo, (uchar*) colorProfileData.data(), colorProfileData.size());
639 JSAMPROW row_pointer =
new JSAMPLE[width*cinfo.input_components];
642 for (; cinfo.next_scanline < height;) {
644 quint8 *dst = row_pointer;
645 switch (color_type) {
647 if (color_nb_bits == 16) {
663 if (color_nb_bits == 16) {
683 if (color_nb_bits == 16) {
705 delete [] row_pointer;
706 jpeg_destroy_compress(&cinfo);
709 jpeg_write_scanlines(&cinfo, &row_pointer, 1);
714 jpeg_finish_compress(&cinfo);
716 delete [] row_pointer;
718 jpeg_destroy_compress(&cinfo);
722 }
catch( std::runtime_error &) {
723 jpeg_destroy_compress(&cinfo);
const KoID GrayAColorModelID("GRAYA", ki18n("Grayscale/Alpha"))
const KoID Integer8BitsColorDepthID("U8", ki18n("8-bit integer/channel"))
const KoID CMYKAColorModelID("CMYKA", ki18n("CMYK/Alpha"))
const KoID RGBAColorModelID("RGBA", ki18n("RGB/Alpha"))
constexpr qreal POINT_TO_INCH(qreal px)
constexpr qreal INCH_TO_POINT(qreal inch)
virtual const quint8 * oldRawData() const =0
virtual bool nextPixel()=0
KisGroupLayerSP rootLayer() const
void rotateImage(double radians)
start asynchronous operation on rotating the image
KisImportExportErrorCode buildImage(QIODevice *io)
QScopedPointer< Private > m_d
~KisJPEGConverter() override
KisJPEGConverter(KisDocument *doc, bool batchMode=false)
KisImportExportErrorCode buildFile(QIODevice *io, KisPaintLayerSP layer, KisJPEGOptions options, KisMetaData::Store *metaData)
KisImportExportErrorCode decode(QIODevice *io)
const KisMetaData::Value & value() const
quint32 pixelSize() const
quint32 channelCount() const
KisHLineIteratorSP createHLineIteratorNG(qint32 x, qint32 y, qint32 w)
void fill(const QRect &rc, const KoColor &color)
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 bitBlt(qint32 dstX, qint32 dstY, const KisPaintDeviceSP srcDev, qint32 srcX, qint32 srcY, qint32 srcWidth, qint32 srcHeight)
virtual quint8 scaleToU8(const quint8 *srcPixel, qint32 channelPos) const =0
virtual KoID colorDepthId() const =0
virtual quint32 colorChannelCount() const =0
virtual const KoColorProfile * profile() const =0
virtual KoColorConversionTransformation * createColorConverter(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const
const T value(const QString &id) const
boolean read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, unsigned int *icc_data_len)
void write_icc_profile(j_compress_ptr cinfo, const JOCTET *icc_data_ptr, unsigned int icc_data_len)
#define MAX_DATA_BYTES_IN_MARKER
#define KIS_ASSERT_RECOVER_RETURN_VALUE(cond, val)
const uint16_t photoshopIptc
const char photoshopMarker[]
const QByteArray photoshopIptc_((char *)&photoshopIptc, 2)
KisSharedPtr< KisPaintLayer > KisPaintLayerSP
KisSharedPtr< KisImage > KisImageSP
KisSharedPtr< KisNode > KisNodeSP
@ FormatFeaturesUnsupported
@ FormatColorSpaceUnsupported
void setDestination(j_compress_ptr cinfo, QIODevice *destinationDevice)
void setSource(j_decompress_ptr cinfo, QIODevice *inputDevice)
Private(KisDocument *doc, bool batchMode)
QList< const KisMetaData::Filter * > filters
QColor transparencyFillColor
KisMetaData::Store * metaData()
const KoColorSpace * colorSpace() const override
returns the image's colorSpace or null, if there is no image
KisPaintDeviceSP paintDevice
virtual bool isSuitableForOutput() const =0
virtual QByteArray rawData() const
QString colorSpaceId(const QString &colorModelId, const QString &colorDepthId) const
const KoColorSpace * colorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile)
static KoColorSpaceRegistry * instance()
const KoColorSpace * rgb8(const QString &profileName=QString())
const KoColorProfile * createColorProfile(const QString &colorModelId, const QString &colorDepthId, const QByteArray &rawData)