[graphics/krita] /: tiff: support YCbCr + JPEG imports
L. E. Segovia
null at kde.org
Tue Jun 28 18:04:36 BST 2022
Git commit dd4d2b4b793a42b2c7406dfc63fc108ea8905ec5 by L. E. Segovia.
Committed on 28/06/2022 at 16:01.
Pushed by lsegovia into branch 'master'.
tiff: support YCbCr + JPEG imports
This commit enables reading JPEG-compressed TIFFs that use the YCbCr
colour space. This is not possible to do with the standard libtiff API
because of Very Bad Decisions :tm: that were taken in the past, both
during the 6.0 standardization [1] and its implementation [2].
To do so, I've implemented a custom made strip decoder that, when the
combination of COMPRESSION_JPEG and PHOTOMETRIC_YCBCR is detected,
redirects to a (also custom made) buffer stream that performs automatic
interleaving and upsampling of the image planes. This data is shaped
into the form specified in the TIFF 6.0 Specification [3], then fed
to the existing YCbCr readers. The planes are decoded using
libjpeg-turbo (to avoid the mindbending API of libjpeg) into three
separate planes without upsampling.
There's a catch, however. Unlike libjpeg-turbo, libjpeg provides a
direct API to prime the decoder with the coefficient tables that are
provided in TIFFTAG_JPEGTABLES, and which libtiff uses as part of
TIFFjpeg_read_header. To remediate this, I've patched libtiff-turbo to
add a tjReadCoefficientTables API, that takes a reference to the tables
and forwards it to the decoder.
BUG: 454116
CCMAIL: kimageshop at kde.org
[1]: DRAFT TIFF Technical Note #2, 17-Mar-95.
<http://libtiff.maptools.org/TIFFTechNote2.html>
[2]: 2022.06.11 07:23 "Re: [Tiff] How to read JPEG-compressed YCbCr
TIFFs", by Joris Van Damme. <https://www.asmail.be/msg0054881320.html>
[3]: "Ordering of Component Samples". In: TIFF Revision 6.0 Final. June
3, 1992: Adobe Developers Association, pp. 93-94.
M +4 -4 3rdparty/ext_jpeg/CMakeLists.txt
M +14 -0 CMakeLists.txt
A +4 -0 config-jpeg.h.cmake
M +1 -1 plugins/impex/tiff/CMakeLists.txt
M +138 -2 plugins/impex/tiff/kis_buffer_stream.cc
M +57 -1 plugins/impex/tiff/kis_buffer_stream.h
M +177 -6 plugins/impex/tiff/kis_tiff_import.cc
M +1 -0 plugins/impex/tiff/kis_tiff_import.h
D +- -- plugins/impex/tiff/tests/data/quad-jpeg.tif.png
A +- -- plugins/impex/tiff/tests/data/results/quad-jpeg.tif.png
R +- -- plugins/impex/tiff/tests/data/sources/quad-jpeg.tif [from: plugins/impex/tiff/tests/data/quad-jpeg.tif - 100% similarity]
https://invent.kde.org/graphics/krita/commit/dd4d2b4b793a42b2c7406dfc63fc108ea8905ec5
diff --git a/3rdparty/ext_jpeg/CMakeLists.txt b/3rdparty/ext_jpeg/CMakeLists.txt
index 834b5529dd..cd39e1f1b0 100644
--- a/3rdparty/ext_jpeg/CMakeLists.txt
+++ b/3rdparty/ext_jpeg/CMakeLists.txt
@@ -2,8 +2,8 @@ SET(PREFIX_ext_jpeg "${EXTPREFIX}" )
if (ANDROID)
ExternalProject_Add( ext_jpeg
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
- URL https://downloads.sourceforge.net/project/libjpeg-turbo/2.1.3/libjpeg-turbo-2.1.3.tar.gz
- URL_HASH SHA256=467b310903832b033fe56cd37720d1b73a6a3bd0171dbf6ff0b620385f4f76d0
+ URL https://github.com/libjpeg-turbo/libjpeg-turbo/archive/ba22c0f76d88f596e157b6c546c599e21ebbaf64.tar.gz
+ URL_HASH SHA256=a70aef6318f0eb71e04065fb396906efa16338561b12e5ee0c5cd3fcfdf356ca
CMAKE_ARGS -DANDROID_ARM_MODE=arm -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_jpeg} -DENABLE_SHARED=ON -DWITH_SIMD=OFF -DENABLE_STATIC=OFF _DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE}
@@ -12,8 +12,8 @@ ExternalProject_Add( ext_jpeg
else()
ExternalProject_Add( ext_jpeg
DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR}
- URL https://downloads.sourceforge.net/project/libjpeg-turbo/2.1.3/libjpeg-turbo-2.1.3.tar.gz
- URL_HASH SHA256=467b310903832b033fe56cd37720d1b73a6a3bd0171dbf6ff0b620385f4f76d0
+ URL https://github.com/libjpeg-turbo/libjpeg-turbo/archive/ba22c0f76d88f596e157b6c546c599e21ebbaf64.tar.gz
+ URL_HASH SHA256=a70aef6318f0eb71e04065fb396906efa16338561b12e5ee0c5cd3fcfdf356ca
INSTALL_DIR ${PREFIX_ext_jpeg}
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PREFIX_ext_jpeg} -DWITH_SIMD=OFF _DCMAKE_BUILD_TYPE=${GLOBAL_BUILD_TYPE} ${GLOBAL_PROFILE}
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0f8a4dc73a..8c9ec5a9c2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -855,6 +855,20 @@ if (JPEG_FOUND)
macro_bool_to_01(JPEG_FOUND HAVE_JPEG)
endif()
+find_package(libjpeg-turbo 2.1.3)
+set_package_properties(libjpeg-turbo PROPERTIES
+ DESCRIPTION "libjpeg-turbo is a JPEG image codec that uses SIMD instructions (MMX, SSE2, AVX2, Neon, AltiVec) to accelerate baseline JPEG compression and decompression on x86, x86-64, Arm, and PowerPC systems, as well as progressive JPEG compression on x86 and x86-64 systems."
+ URL "https://www.libjpeg-turbo.org"
+ TYPE OPTIONAL
+ PURPOSE "Required by the Krita JPEG and TIFF filters")
+if(libjpeg-turbo_FOUND)
+ get_target_property(JPEG_TURBO_LIBRARY libjpeg-turbo::turbojpeg "LOCATION")
+ set(JPEG_TURBO_LIBRARIES libjpeg-turbo::turbojpeg)
+ list(APPEND ANDROID_EXTRA_LIBS ${JPEG_TURBO_LIBRARY})
+endif()
+macro_bool_to_01(libjpeg-turbo_FOUND HAVE_JPEG_TURBO)
+configure_file(config-jpeg.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-jpeg.h )
+
find_package(GIF)
set_package_properties(GIF PROPERTIES
DESCRIPTION "Library for loading and saving gif files."
diff --git a/config-jpeg.h.cmake b/config-jpeg.h.cmake
new file mode 100644
index 0000000000..0ebf435f7f
--- /dev/null
+++ b/config-jpeg.h.cmake
@@ -0,0 +1,4 @@
+/* config-jpeg.h. Generated by cmake from config-jpeg.h.cmake */
+
+/* Define if libjpeg has the turbo-jpeg API */
+#cmakedefine HAVE_JPEG_TURBO 1
diff --git a/plugins/impex/tiff/CMakeLists.txt b/plugins/impex/tiff/CMakeLists.txt
index 9fa8b50266..bc392c3750 100644
--- a/plugins/impex/tiff/CMakeLists.txt
+++ b/plugins/impex/tiff/CMakeLists.txt
@@ -23,7 +23,7 @@ set(kritatiffimport_SOURCES
kis_add_library(kritatiffimport MODULE ${kritatiffimport_SOURCES})
-target_link_libraries(kritatiffimport kritaui kritaimpex ${KRITATIFFPSD_LIBRARY} ${TIFF_LIBRARIES} LibExiv2::LibExiv2 kritametadata)
+target_link_libraries(kritatiffimport kritaui kritaimpex ${KRITATIFFPSD_LIBRARY} ${TIFF_LIBRARIES} LibExiv2::LibExiv2 kritametadata ${JPEG_TURBO_LIBRARIES})
install(TARGETS kritatiffimport DESTINATION ${KRITA_PLUGIN_INSTALL_DIR})
diff --git a/plugins/impex/tiff/kis_buffer_stream.cc b/plugins/impex/tiff/kis_buffer_stream.cc
index ad69653ccc..5a8129cfc7 100644
--- a/plugins/impex/tiff/kis_buffer_stream.cc
+++ b/plugins/impex/tiff/kis_buffer_stream.cc
@@ -26,15 +26,41 @@ KisBufferStreamContigBase::KisBufferStreamContigBase(uint8_t *src,
void KisBufferStreamContigBase::restart()
{
m_srcIt = m_src;
+ m_lineOffset = 0;
+ m_lineNumber = 0;
m_posinc = 8;
}
void KisBufferStreamContigBase::moveToLine(tsize_t lineNumber)
{
- m_srcIt = m_src + lineNumber * m_lineSize;
+ KIS_ASSERT(lineNumber >= 0);
+ moveToPos(0, lineNumber);
+}
+
+void KisBufferStreamContigBase::moveToPos(tsize_t x, tsize_t y)
+{
+ KIS_ASSERT(x >= 0 && y >= 0);
+ m_lineNumber = y;
+ m_lineOffset = (x * m_depth) / 8;
+ m_srcIt = m_src + y * m_lineSize + m_lineOffset;
m_posinc = 8;
}
+tsize_t KisBufferStreamContigBase::x() const
+{
+ return (m_lineOffset * 8) / m_depth;
+}
+
+tsize_t KisBufferStreamContigBase::y() const
+{
+ return m_lineNumber;
+}
+
+tsize_t KisBufferStreamContigBase::width() const
+{
+ return (m_lineSize * 8) / m_depth;
+}
+
uint32_t KisBufferStreamContigBelow16::nextValue()
{
uint16_t remain = m_depth;
@@ -47,9 +73,14 @@ uint32_t KisBufferStreamContigBelow16::nextValue()
value = (value << toread) | (((*m_srcIt) >> (m_posinc)) & ((1 << toread) - 1));
if (m_posinc == 0) {
m_srcIt++;
+ m_lineOffset++; // we consumed a byte
m_posinc = 8;
}
}
+ if (m_lineOffset >= m_lineSize) {
+ m_lineNumber += 1;
+ m_lineOffset = 0;
+ }
return value;
}
@@ -65,9 +96,14 @@ uint32_t KisBufferStreamContigBelow32::nextValue()
value = (value) | ((((*m_srcIt) >> (m_posinc)) & ((1 << toread) - 1U)) << (m_depth - 8U - remain));
if (m_posinc == 0) {
m_srcIt++;
+ m_lineOffset++; // we consumed a byte
m_posinc = 8U;
}
}
+ if (m_lineOffset >= m_lineSize) {
+ m_lineNumber += 1;
+ m_lineOffset = 0;
+ }
return value;
}
@@ -86,9 +122,14 @@ uint32_t KisBufferStreamContigAbove32::nextValue()
}
if (m_posinc == 0) {
m_srcIt++;
+ m_lineOffset++; // we consumed a byte
m_posinc = 8U;
}
}
+ if (m_lineOffset >= m_lineSize) {
+ m_lineNumber += 1;
+ m_lineOffset = 0;
+ }
return value;
}
@@ -141,8 +182,103 @@ void KisBufferStreamSeparate::restart()
}
void KisBufferStreamSeparate::moveToLine(tsize_t lineNumber)
+{
+ KIS_ASSERT(lineNumber >= 0);
+ moveToPos(0, lineNumber);
+}
+
+void KisBufferStreamSeparate::moveToPos(tsize_t x, tsize_t y)
{
for (const auto &stream : streams) {
- stream->moveToLine(lineNumber);
+ stream->moveToPos(x, y);
}
}
+
+tsize_t KisBufferStreamSeparate::x() const
+{
+ return streams[m_current_sample]->x();
+}
+
+tsize_t KisBufferStreamSeparate::y() const
+{
+ return streams[m_current_sample]->y();
+}
+
+tsize_t KisBufferStreamSeparate::width() const
+{
+ return streams[m_current_sample]->width();
+}
+
+KisBufferStreamInterleaveUpsample::KisBufferStreamInterleaveUpsample(
+ uint8_t **srcs,
+ uint16_t nb_samples,
+ uint16_t depth,
+ tsize_t *lineSize,
+ uint16_t hsubsample,
+ uint16_t vsubsample)
+ : KisBufferStreamSeparate(srcs, nb_samples, depth, lineSize)
+ , m_hsubsample(hsubsample)
+ , m_vsubsample(vsubsample)
+{
+}
+
+uint32_t KisBufferStreamInterleaveUpsample::nextValue()
+{
+ uint32_t value = streams[m_currentPlane]->nextValue();
+ if (m_currentPlane == 0) {
+ m_current_sample++;
+ if (m_current_sample % m_hsubsample == 0) {
+ if (m_current_sample >= m_hsubsample * m_vsubsample) {
+ // Fix up the position of the luminance plane
+ // If it's already 0, the cursor has already looped (correctly)
+ // to the next line
+ if (streams[m_currentPlane]->x() != 0) {
+ streams[m_currentPlane]->moveToPos(
+ streams[m_currentPlane]->x(),
+ streams[m_currentPlane]->y() - m_vsubsample + 1);
+ }
+ // Move to Cb/Cr
+ m_currentPlane += 1;
+ m_current_sample = 0;
+ } else {
+ // Go to next line
+ // If the position is already 0, we need to correct the row
+ // AND column
+ if (streams[m_currentPlane]->x() != 0) {
+ streams[m_currentPlane]->moveToPos(
+ streams[m_currentPlane]->x() - m_hsubsample,
+ streams[m_currentPlane]->y() + 1);
+ } else {
+ streams[m_currentPlane]->moveToPos(
+ streams[m_currentPlane]->width() - m_hsubsample,
+ streams[m_currentPlane]->y());
+ }
+ }
+ }
+ } else if (m_currentPlane < m_nb_samples - 1) {
+ m_currentPlane += 1;
+ } else {
+ m_currentPlane = 0;
+ }
+ return value;
+}
+
+void KisBufferStreamInterleaveUpsample::moveToPos(tsize_t x, tsize_t y)
+{
+ // Needs to subsample
+ for (uint16_t i = 0; i < m_nb_samples; i++) {
+ const tsize_t realX = i == 0 ? x : x / m_hsubsample;
+ const tsize_t realY = i == 0 ? y : y / m_vsubsample;
+ streams.at(i)->moveToPos(realX, realY);
+ }
+}
+
+tsize_t KisBufferStreamInterleaveUpsample::x() const
+{
+ return streams[0]->x();
+}
+
+tsize_t KisBufferStreamInterleaveUpsample::y() const
+{
+ return streams[0]->y();
+}
diff --git a/plugins/impex/tiff/kis_buffer_stream.h b/plugins/impex/tiff/kis_buffer_stream.h
index 706b85de31..2b0575ff93 100644
--- a/plugins/impex/tiff/kis_buffer_stream.h
+++ b/plugins/impex/tiff/kis_buffer_stream.h
@@ -23,7 +23,11 @@ public:
virtual uint32_t nextValue() = 0;
virtual void restart() = 0;
virtual void moveToLine(tsize_t lineNumber) = 0;
- virtual ~KisBufferStreamBase() {}
+ virtual void moveToPos(tsize_t x, tsize_t y) = 0;
+ virtual tsize_t x() const = 0;
+ virtual tsize_t y() const = 0;
+ virtual tsize_t width() const = 0;
+
protected:
uint16_t m_depth;
};
@@ -39,11 +43,21 @@ public:
void moveToLine(tsize_t lineNumber) override;
+ void moveToPos(tsize_t x, tsize_t y) override;
+
+ tsize_t x() const override;
+
+ tsize_t y() const override;
+
+ tsize_t width() const override;
+
protected:
uint8_t *const m_src;
uint8_t *m_srcIt;
uint16_t m_posinc = 0;
const tsize_t m_lineSize;
+ tsize_t m_lineNumber = 0;
+ tsize_t m_lineOffset = 0;
};
class KisBufferStreamContigBelow16 : public KisBufferStreamContigBase
@@ -98,10 +112,52 @@ public:
void moveToLine(tsize_t lineNumber) override;
+ void moveToPos(tsize_t x, tsize_t y) override;
+
+ tsize_t x() const override;
+
+ tsize_t y() const override;
+
+ tsize_t width() const override;
+
protected:
QVector<QSharedPointer<KisBufferStreamBase>> streams;
uint16_t m_current_sample = 0;
uint16_t m_nb_samples;
};
+class KisBufferStreamInterleaveUpsample : public KisBufferStreamSeparate
+{
+public:
+ KisBufferStreamInterleaveUpsample(uint8_t **srcs,
+ uint16_t nb_samples,
+ uint16_t depth,
+ tsize_t *lineSize,
+ uint16_t hsubsample,
+ uint16_t vsubsample);
+
+ uint32_t nextValue() override;
+
+ void moveToPos(tsize_t x, tsize_t y) override;
+
+ tsize_t x() const override;
+
+ tsize_t y() const override;
+
+ tsize_t width() const override
+ {
+ return streams[0]->width();
+ }
+
+ void restart() override
+ {
+ KisBufferStreamSeparate::restart();
+ m_currentPlane = 0;
+ }
+
+protected:
+ uint16_t m_hsubsample, m_vsubsample;
+ uint16_t m_currentPlane = 0;
+};
+
#endif
diff --git a/plugins/impex/tiff/kis_tiff_import.cc b/plugins/impex/tiff/kis_tiff_import.cc
index d0c56112be..4e819d497e 100644
--- a/plugins/impex/tiff/kis_tiff_import.cc
+++ b/plugins/impex/tiff/kis_tiff_import.cc
@@ -39,6 +39,11 @@
#include "kis_tiff_psd_resource_record.h"
#endif
+#ifdef HAVE_JPEG_TURBO
+#include <turbojpeg.h>
+#endif
+
+#include "kis_buffer_stream.h"
#include "kis_tiff_logger.h"
#include "kis_tiff_reader.h"
#include "kis_tiff_ycbcr_reader.h"
@@ -782,8 +787,7 @@ KisTIFFImport::readImageFromTiff(KisDocument *m_doc,
&vsubsampling);
lineSizeCoeffs[1] = hsubsampling;
lineSizeCoeffs[2] = hsubsampling;
- uint16_t position = 0;
- TIFFGetFieldDefaulted(image, TIFFTAG_YCBCRPOSITIONING, &position);
+ dbgFile << "Subsampling" << 4 << hsubsampling << vsubsampling;
if (dstDepth == 8) {
tiffReader = new KisTIFFYCbCrReader<uint8_t>(
layer->paintDevice(),
@@ -947,6 +951,9 @@ KisTIFFImport::readImageFromTiff(KisDocument *m_doc,
return ImportExportCodes::FileFormatIncorrect;
}
+ uint32_t compression = COMPRESSION_NONE;
+ TIFFGetFieldDefaulted(image, TIFFTAG_COMPRESSION, &compression, COMPRESSION_NONE);
+
if (TIFFIsTiled(image)) {
dbgFile << "tiled image";
uint32_t tileWidth = 0;
@@ -1025,7 +1032,66 @@ KisTIFFImport::readImageFromTiff(KisDocument *m_doc,
qMin(rowsPerStrip,
height); // when TIFFNumberOfStrips(image) == 1 it might happen
// that rowsPerStrip is incorrectly set
- if (planarconfig == PLANARCONFIG_CONTIG) {
+
+#ifdef HAVE_JPEG_TURBO
+ uint32_t hasSplitTables = 0;
+ uint8_t *tables = nullptr;
+ uint32_t sz = 0;
+ QVector<unsigned char> jpegBuf;
+
+ auto handle = [&]() -> std::unique_ptr<void, decltype(&tjDestroy)> {
+ if (planarconfig == PLANARCONFIG_CONTIG
+ && color_type == PHOTOMETRIC_YCBCR
+ && compression == COMPRESSION_JPEG) {
+ return {tjInitDecompress(), &tjDestroy};
+ } else {
+ return {nullptr, &tjDestroy};
+ }
+ }();
+
+ if (color_type == PHOTOMETRIC_YCBCR && compression == COMPRESSION_JPEG
+ && hsubsampling != 1 && vsubsampling != 1) {
+ jpegBuf.resize(stripsize);
+ dbgFile << "Setting up libjpeg-turbo for handling subsampled JPEG "
+ "strips...";
+ if (!TIFFGetFieldDefaulted(image,
+ TIFFTAG_JPEGTABLESMODE,
+ &hasSplitTables)) {
+ errFile << "Error when detecting the JPEG coefficient "
+ "table mode";
+ return ImportExportCodes::FileFormatIncorrect;
+ }
+ if (hasSplitTables) {
+ if (!TIFFGetField(image, TIFFTAG_JPEGTABLES, &sz, &tables)) {
+ errFile
+ << "Unable to retrieve the JPEG abbreviated datastream";
+ return ImportExportCodes::FileFormatIncorrect;
+ }
+ }
+
+ {
+ int width = 0;
+ int height = 0;
+
+ if (hasSplitTables
+ && tjDecompressHeader(handle.get(),
+ tables,
+ sz,
+ &width,
+ &height)
+ != 0) {
+ errFile << tjGetErrorStr2(handle.get());
+ m_doc->setErrorMessage(i18nc("TIFF errors", "This TIFF file is compressed with JPEG, but libjpeg-turbo could not load its coefficient quantization and/or Huffman coding tables. Please upgrade your version of libjpeg-turbo and try again."));
+ return ImportExportCodes::FileFormatIncorrect;
+ }
+ }
+ }
+#endif
+
+ if (planarconfig == PLANARCONFIG_CONTIG
+ && !(color_type == PHOTOMETRIC_YCBCR
+ && compression == COMPRESSION_JPEG && hsubsampling != 1
+ && vsubsampling != 1)) {
buf.reset(_TIFFmalloc(stripsize));
if (depth < 16) {
tiffstream = new KisBufferStreamContigBelow16(
@@ -1043,6 +1109,61 @@ KisTIFFImport::readImageFromTiff(KisDocument *m_doc,
depth,
stripsize / rowsPerStrip);
}
+ } else if (planarconfig == PLANARCONFIG_CONTIG
+ && color_type == PHOTOMETRIC_YCBCR
+ && compression == COMPRESSION_JPEG) {
+#ifdef HAVE_JPEG_TURBO
+ ps_buf.resize(nbchannels);
+ TIFFReadRawStrip(image, 0, jpegBuf.data(), stripsize);
+
+ int width = basicInfo.width;
+ int height = rowsPerStrip;
+ int jpegSubsamp = TJ_444;
+ int jpegColorspace = TJCS_YCbCr;
+
+ if (tjDecompressHeader3(handle.get(),
+ jpegBuf.data(),
+ stripsize,
+ &width,
+ &height,
+ &jpegSubsamp,
+ &jpegColorspace)
+ != 0) {
+ errFile << tjGetErrorStr2(handle.get());
+ return ImportExportCodes::FileFormatIncorrect;
+ }
+
+ QVector<tsize_t> lineSizes(nbchannels);
+ for (uint32_t i = 0; i < nbchannels; i++) {
+ const unsigned long uncompressedStripsize =
+ tjPlaneSizeYUV(i, width, 0, height, jpegColorspace);
+ KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(
+ uncompressedStripsize != (unsigned long)-1,
+ ImportExportCodes::FileFormatIncorrect);
+ dbgFile << QString("Uncompressed strip size (plane %1): %2")
+ .arg(i)
+ .arg(uncompressedStripsize);
+ tsize_t scanLineSize = uncompressedStripsize / rowsPerStrip;
+ dbgFile << QString("scan line size (plane %1): %2")
+ .arg(i)
+ .arg(scanLineSize);
+ ps_buf[i] = _TIFFmalloc(uncompressedStripsize);
+ lineSizes[i] = scanLineSize;
+ }
+ tiffstream = new KisBufferStreamInterleaveUpsample(
+ reinterpret_cast<uint8_t **>(ps_buf.data()),
+ nbchannels,
+ depth,
+ lineSizes.data(),
+ hsubsampling,
+ vsubsampling);
+#else
+ m_doc->setErrorMessage(
+ i18nc("TIFF",
+ "Subsampled YCbCr TIFF files compressed with JPEG cannot "
+ "be loaded."));
+ return ImportExportCodes::FileFormatIncorrect;
+#endif
} else {
ps_buf.resize(nbchannels);
tsize_t scanLineSize = stripsize / rowsPerStrip;
@@ -1067,12 +1188,57 @@ KisTIFFImport::readImageFromTiff(KisDocument *m_doc,
dbgFile << " NbOfStrips =" << TIFFNumberOfStrips(image)
<< " rowsPerStrip =" << rowsPerStrip
<< " stripsize =" << stripsize;
+
for (uint32_t strip = 0; y < height; strip++) {
+#ifdef HAVE_JPEG_TURBO
+ if (planarconfig == PLANARCONFIG_CONTIG
+ && !(color_type == PHOTOMETRIC_YCBCR
+ && compression == COMPRESSION_JPEG && hsubsampling != 1
+ && vsubsampling != 1)) {
+#else
if (planarconfig == PLANARCONFIG_CONTIG) {
+#endif
TIFFReadEncodedStrip(image,
TIFFComputeStrip(image, y, 0),
buf.get(),
(tsize_t)-1);
+#ifdef HAVE_JPEG_TURBO
+ } else if (planarconfig == PLANARCONFIG_CONTIG
+ && (color_type == PHOTOMETRIC_YCBCR
+ && compression == COMPRESSION_JPEG)) {
+ TIFFReadRawStrip(image, strip, jpegBuf.data(), stripsize);
+
+ int width = basicInfo.width;
+ int height = rowsPerStrip;
+ int jpegSubsamp = TJ_444;
+ int jpegColorspace = TJCS_YCbCr;
+
+ if (tjDecompressHeader3(handle.get(),
+ jpegBuf.data(),
+ stripsize,
+ &width,
+ &height,
+ &jpegSubsamp,
+ &jpegColorspace)
+ != 0) {
+ errFile << tjGetErrorStr2(handle.get());
+ return ImportExportCodes::FileFormatIncorrect;
+ }
+
+ if (tjDecompressToYUVPlanes(
+ handle.get(),
+ jpegBuf.data(),
+ stripsize,
+ reinterpret_cast<unsigned char **>(ps_buf.data()),
+ width,
+ nullptr,
+ height,
+ 0)
+ != 0) {
+ errFile << tjGetErrorStr2(handle.get());
+ return ImportExportCodes::FileFormatIncorrect;
+ }
+#endif
} else {
for (uint16_t i = 0; i < nbchannels; i++) {
TIFFReadEncodedStrip(image,
@@ -1301,7 +1467,10 @@ KisImportExportErrorCode KisTIFFImport::readTIFFDirectory(KisDocument *m_doc,
"Chemical proof");
} else if (basicInfo.colorSpaceIdTag.first == LABAColorModelID.id()) {
profile = KoColorSpaceRegistry::instance()->profileByName(
- "Lab identity build-in");
+ "Lab identity built-in");
+ } else if (basicInfo.colorSpaceIdTag.first == YCbCrAColorModelID.id()) {
+ profile = KoColorSpaceRegistry::instance()->profileByName(
+ "ITU-R BT.709-6 YCbCr ICC V4 profile");
}
if (!profile) {
dbgFile << "No suitable default profile found.";
@@ -1429,7 +1598,10 @@ KisImportExportErrorCode KisTIFFImport::readTIFFDirectory(KisDocument *m_doc,
return readImageFromTiff(m_doc, image, basicInfo);
}
-KisImportExportErrorCode KisTIFFImport::convert(KisDocument *document, QIODevice */*io*/, KisPropertiesConfigurationSP /*configuration*/)
+KisImportExportErrorCode
+KisTIFFImport::convert(KisDocument *document,
+ QIODevice * /*io*/,
+ KisPropertiesConfigurationSP /*configuration*/)
{
dbgFile << "Start decoding TIFF File";
@@ -1558,4 +1730,3 @@ KisImportExportErrorCode KisTIFFImport::convert(KisDocument *document, QIODevice
}
#include <kis_tiff_import.moc>
-
diff --git a/plugins/impex/tiff/kis_tiff_import.h b/plugins/impex/tiff/kis_tiff_import.h
index 993f3af3af..0b09d7e9c3 100644
--- a/plugins/impex/tiff/kis_tiff_import.h
+++ b/plugins/impex/tiff/kis_tiff_import.h
@@ -12,6 +12,7 @@
#include <KisImportExportFilter.h>
#include <config-tiff.h>
+#include <config-jpeg.h>
#include <kis_types.h>
class QBuffer;
diff --git a/plugins/impex/tiff/tests/data/quad-jpeg.tif.png b/plugins/impex/tiff/tests/data/quad-jpeg.tif.png
deleted file mode 100644
index 36985d7435..0000000000
Binary files a/plugins/impex/tiff/tests/data/quad-jpeg.tif.png and /dev/null differ
diff --git a/plugins/impex/tiff/tests/data/results/quad-jpeg.tif.png b/plugins/impex/tiff/tests/data/results/quad-jpeg.tif.png
new file mode 100644
index 0000000000..a6f592cad8
Binary files /dev/null and b/plugins/impex/tiff/tests/data/results/quad-jpeg.tif.png differ
diff --git a/plugins/impex/tiff/tests/data/quad-jpeg.tif b/plugins/impex/tiff/tests/data/sources/quad-jpeg.tif
similarity index 100%
rename from plugins/impex/tiff/tests/data/quad-jpeg.tif
rename to plugins/impex/tiff/tests/data/sources/quad-jpeg.tif
More information about the kimageshop
mailing list