kis_tiff_import.cc (krita-5.1.3.tar.xz) | : | kis_tiff_import.cc (krita-5.1.4.tar.xz) | ||
---|---|---|---|---|
/* | /* | |||
* SPDX-FileCopyrightText: 2005 Cyrille Berger <cberger@cberger.net> | * SPDX-FileCopyrightText: 2005 Cyrille Berger <cberger@cberger.net> | |||
* SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> | * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me> | |||
* | * | |||
* SPDX-License-Identifier: GPL-2.0-or-later | * SPDX-License-Identifier: GPL-2.0-or-later | |||
*/ | */ | |||
#include "kis_tiff_import.h" | #include "kis_tiff_import.h" | |||
#include "KisImportExportErrorCode.h" | ||||
#include "kis_assert.h" | ||||
#include <QBuffer> | #include <QBuffer> | |||
#include <QFileInfo> | #include <QFileInfo> | |||
#include <QPair> | #include <QPair> | |||
#include <QSharedPointer> | #include <QSharedPointer> | |||
#include <QStack> | #include <QStack> | |||
#include <array> | #include <array> | |||
#include <exiv2/exiv2.hpp> | #include <exiv2/exiv2.hpp> | |||
#include <kpluginfactory.h> | #include <kpluginfactory.h> | |||
#ifdef Q_OS_WIN | ||||
#include <io.h> | ||||
#endif | ||||
#include <tiffio.h> | #include <tiffio.h> | |||
#include <KisDocument.h> | #include <KisDocument.h> | |||
#include <KisImportExportAdditionalChecks.h> | #include <KisImportExportAdditionalChecks.h> | |||
#include <KisViewManager.h> | #include <KisViewManager.h> | |||
#include <KoColorProfile.h> | #include <KoColorProfile.h> | |||
#include <KoDocumentInfo.h> | #include <KoDocumentInfo.h> | |||
#include <KoUnit.h> | #include <KoUnit.h> | |||
#include <KisExiv2IODevice.h> | ||||
#include <kis_group_layer.h> | #include <kis_group_layer.h> | |||
#include <kis_image.h> | #include <kis_image.h> | |||
#include <kis_meta_data_backend_registry.h> | #include <kis_meta_data_backend_registry.h> | |||
#include <kis_meta_data_tags.h> | #include <kis_meta_data_tags.h> | |||
#include <kis_paint_layer.h> | #include <kis_paint_layer.h> | |||
#include <kis_transform_worker.h> | #include <kis_transform_worker.h> | |||
#include <kis_transparency_mask.h> | #include <kis_transparency_mask.h> | |||
#ifdef TIFF_HAS_PSD_TAGS | #ifdef TIFF_HAS_PSD_TAGS | |||
#include <psd_resource_block.h> | #include <psd_resource_block.h> | |||
skipping to change at line 52 | skipping to change at line 58 | |||
#ifdef HAVE_JPEG_TURBO | #ifdef HAVE_JPEG_TURBO | |||
#include <turbojpeg.h> | #include <turbojpeg.h> | |||
#endif | #endif | |||
#include "kis_buffer_stream.h" | #include "kis_buffer_stream.h" | |||
#include "kis_tiff_logger.h" | #include "kis_tiff_logger.h" | |||
#include "kis_tiff_reader.h" | #include "kis_tiff_reader.h" | |||
#include "kis_tiff_ycbcr_reader.h" | #include "kis_tiff_ycbcr_reader.h" | |||
enum class TiffResolution : quint8 { | ||||
NONE = RESUNIT_NONE, | ||||
INCH = RESUNIT_INCH, | ||||
CM = RESUNIT_CENTIMETER, | ||||
}; | ||||
struct KisTiffBasicInfo { | struct KisTiffBasicInfo { | |||
uint32_t width{}; | uint32_t width{}; | |||
uint32_t height{}; | uint32_t height{}; | |||
float x{}; | float x{}; | |||
float y{}; | float y{}; | |||
float xres{}; | float xres{}; | |||
float yres{}; | float yres{}; | |||
uint16_t depth{}; | uint16_t depth{}; | |||
uint16_t sampletype{}; | uint16_t sampletype{}; | |||
uint16_t nbchannels{}; | uint16_t nbchannels{}; | |||
uint16_t color_type{}; | uint16_t color_type{}; | |||
uint16_t *sampleinfo = nullptr; | uint16_t *sampleinfo = nullptr; | |||
uint16_t extrasamplescount = 0; | uint16_t extrasamplescount = 0; | |||
const KoColorSpace *cs = nullptr; | const KoColorSpace *cs = nullptr; | |||
QPair<QString, QString> colorSpaceIdTag; | QPair<QString, QString> colorSpaceIdTag; | |||
KoColorTransformation *transform = nullptr; | KoColorTransformation *transform = nullptr; | |||
uint8_t dstDepth{}; | uint8_t dstDepth{}; | |||
TiffResolution resolution = TiffResolution::NONE; | ||||
}; | }; | |||
K_PLUGIN_FACTORY_WITH_JSON(TIFFImportFactory, | K_PLUGIN_FACTORY_WITH_JSON(TIFFImportFactory, | |||
"krita_tiff_import.json", | "krita_tiff_import.json", | |||
registerPlugin<KisTIFFImport>();) | registerPlugin<KisTIFFImport>();) | |||
QPair<QString, QString> getColorSpaceForColorType(uint16_t sampletype, | QPair<QString, QString> getColorSpaceForColorType(uint16_t sampletype, | |||
uint16_t color_type, | uint16_t color_type, | |||
uint16_t color_nb_bits, | uint16_t color_nb_bits, | |||
TIFF *image, | TIFF *image, | |||
skipping to change at line 431 | skipping to change at line 444 | |||
* probably, it is just a single layered group. | * probably, it is just a single layered group. | |||
*/ | */ | |||
KisNodeSP lastAddedLayer; | KisNodeSP lastAddedLayer; | |||
using LayerStyleMapping = QPair<QDomDocument, KisLayerSP>; | using LayerStyleMapping = QPair<QDomDocument, KisLayerSP>; | |||
QVector<LayerStyleMapping> allStylesXml; | QVector<LayerStyleMapping> allStylesXml; | |||
const std::shared_ptr<PSDLayerMaskSection> &layerSection = | const std::shared_ptr<PSDLayerMaskSection> &layerSection = | |||
photoshopLayerRecord.record(); | photoshopLayerRecord.record(); | |||
KIS_SAFE_ASSERT_RECOVER(layerSection->nLayers != 0) | ||||
{ | ||||
return ImportExportCodes::FileFormatIncorrect; | ||||
} | ||||
for (int i = 0; i != layerSection->nLayers; i++) { | for (int i = 0; i != layerSection->nLayers; i++) { | |||
PSDLayerRecord *layerRecord = layerSection->layers.at(i); | PSDLayerRecord *layerRecord = layerSection->layers.at(i); | |||
dbgFile << "Going to read channels for layer" << i | dbgFile << "Going to read channels for layer" << i | |||
<< layerRecord->layerName; | << layerRecord->layerName; | |||
KisLayerSP newLayer; | KisLayerSP newLayer; | |||
if (layerRecord->infoBlocks.keys.contains("lsct") | if (layerRecord->infoBlocks.keys.contains("lsct") | |||
&& layerRecord->infoBlocks.sectionDividerType != psd_other) { | && layerRecord->infoBlocks.sectionDividerType != psd_other) { | |||
if (layerRecord->infoBlocks.sectionDividerType | if (layerRecord->infoBlocks.sectionDividerType | |||
== psd_bounding_divider | == psd_bounding_divider | |||
&& !groupStack.isEmpty()) { | && !groupStack.isEmpty()) { | |||
skipping to change at line 689 | skipping to change at line 707 | |||
// Creating the KisImageSP | // Creating the KisImageSP | |||
if (!m_image) { | if (!m_image) { | |||
m_image = new KisImage(m_doc->createUndoStore(), | m_image = new KisImage(m_doc->createUndoStore(), | |||
static_cast<qint32>(width), | static_cast<qint32>(width), | |||
static_cast<qint32>(height), | static_cast<qint32>(height), | |||
cs, | cs, | |||
"built image"); | "built image"); | |||
KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE( | KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE( | |||
m_image, | m_image, | |||
ImportExportCodes::InsufficientMemory); | ImportExportCodes::InsufficientMemory); | |||
m_image->setResolution( | // It is the "invert" macro because we | |||
POINT_TO_INCH(static_cast<qreal>(xres)), | // convert from pointer-per-unit to points | |||
POINT_TO_INCH(static_cast<qreal>( | if (basicInfo.resolution == TiffResolution::INCH) { | |||
yres))); // It is the "invert" macro because we convert from | m_image->setResolution(POINT_TO_INCH(static_cast<qreal>(xres)), POIN | |||
// pointer-per-inchs to points | T_TO_INCH(static_cast<qreal>(yres))); | |||
} else { | ||||
m_image->setResolution(POINT_TO_CM(static_cast<qreal>(xres)), POINT_ | ||||
TO_CM(static_cast<qreal>(yres))); | ||||
} | ||||
} else { | } else { | |||
if (m_image->width() < static_cast<qint32>(width) | if (m_image->width() < static_cast<qint32>(width) | |||
|| m_image->height() < static_cast<qint32>(height)) { | || m_image->height() < static_cast<qint32>(height)) { | |||
qint32 newwidth = (m_image->width() < static_cast<qint32>(width)) | qint32 newwidth = (m_image->width() < static_cast<qint32>(width)) | |||
? static_cast<qint32>(width) | ? static_cast<qint32>(width) | |||
: m_image->width(); | : m_image->width(); | |||
qint32 newheight = (m_image->height() < static_cast<qint32>(height)) | qint32 newheight = (m_image->height() < static_cast<qint32>(height)) | |||
? static_cast<qint32>(height) | ? static_cast<qint32>(height) | |||
: m_image->height(); | : m_image->height(); | |||
m_image->resizeImage(QRect(0, 0, newwidth, newheight)); | m_image->resizeImage(QRect(0, 0, newwidth, newheight)); | |||
skipping to change at line 1510 | skipping to change at line 1530 | |||
// but we don't stop | // but we don't stop | |||
basicInfo.xres = 100; | basicInfo.xres = 100; | |||
} | } | |||
if (TIFFGetField(image, TIFFTAG_YRESOLUTION, &basicInfo.yres) == 0) { | if (TIFFGetField(image, TIFFTAG_YRESOLUTION, &basicInfo.yres) == 0) { | |||
dbgFile << "Image does not define y resolution"; | dbgFile << "Image does not define y resolution"; | |||
// but we don't stop | // but we don't stop | |||
basicInfo.yres = 100; | basicInfo.yres = 100; | |||
} | } | |||
if (TIFFGetField(image, TIFFTAG_RESOLUTIONUNIT, &basicInfo.resolution) == 0) | ||||
{ | ||||
dbgFile << "Image does not define resolution unit"; | ||||
// but we don't stop | ||||
basicInfo.resolution = TiffResolution::INCH; | ||||
} | ||||
if (TIFFGetField(image, TIFFTAG_XPOSITION, &basicInfo.x) == 0) { | if (TIFFGetField(image, TIFFTAG_XPOSITION, &basicInfo.x) == 0) { | |||
dbgFile << "Image does not define a horizontal offset"; | dbgFile << "Image does not define a horizontal offset"; | |||
basicInfo.x = 0; | basicInfo.x = 0; | |||
} | } | |||
if (TIFFGetField(image, TIFFTAG_YPOSITION, &basicInfo.y) == 0) { | if (TIFFGetField(image, TIFFTAG_YPOSITION, &basicInfo.y) == 0) { | |||
dbgFile << "Image does not define a vertical offset"; | dbgFile << "Image does not define a vertical offset"; | |||
basicInfo.y = 0; | basicInfo.y = 0; | |||
} | } | |||
skipping to change at line 1767 | skipping to change at line 1793 | |||
{ | { | |||
dbgFile << "Start decoding TIFF File"; | dbgFile << "Start decoding TIFF File"; | |||
if (!KisImportExportAdditionalChecks::doesFileExist(filename())) { | if (!KisImportExportAdditionalChecks::doesFileExist(filename())) { | |||
return ImportExportCodes::FileNotExist; | return ImportExportCodes::FileNotExist; | |||
} | } | |||
if (!KisImportExportAdditionalChecks::isFileReadable(filename())) { | if (!KisImportExportAdditionalChecks::isFileReadable(filename())) { | |||
return ImportExportCodes::NoAccessToRead; | return ImportExportCodes::NoAccessToRead; | |||
} | } | |||
QFile file(filename()); | ||||
if (!file.open(QFile::ReadOnly)) { | ||||
return KisImportExportErrorCode(KisImportExportErrorCannotRead(file.erro | ||||
r())); | ||||
} | ||||
// Open the TIFF file | // Open the TIFF file | |||
const QByteArray encodedFilename = QFile::encodeName(filename()); | const QByteArray encodedFilename = QFile::encodeName(filename()); | |||
std::unique_ptr<TIFF, decltype(&TIFFClose)> image( | ||||
TIFFOpen(encodedFilename.data(), "r"), | // https://gitlab.com/libtiff/libtiff/-/issues/173 | |||
&TIFFClose); | #ifdef Q_OS_WIN | |||
const intptr_t handle = _get_osfhandle(file.handle()); | ||||
#else | ||||
const int handle = file.handle(); | ||||
#endif | ||||
std::unique_ptr<TIFF, decltype(&TIFFCleanup)> image(TIFFFdOpen(handle, encod | ||||
edFilename.data(), "r"), &TIFFCleanup); | ||||
if (!image) { | if (!image) { | |||
dbgFile << "Could not open the file, either it does not exist, either " | dbgFile << "Could not open the file, either it does not exist, either " | |||
"it is not a TIFF :" | "it is not a TIFF :" | |||
<< filename(); | << filename(); | |||
return (ImportExportCodes::FileFormatIncorrect); | return (ImportExportCodes::FileFormatIncorrect); | |||
} | } | |||
dbgFile << "Reading first image descriptor"; | dbgFile << "Reading first image descriptor"; | |||
KisImportExportErrorCode result = readTIFFDirectory(document, image.get()); | KisImportExportErrorCode result = readTIFFDirectory(document, image.get()); | |||
if (!result.isOk()) { | if (!result.isOk()) { | |||
skipping to change at line 1798 | skipping to change at line 1835 | |||
m_photoshopBlockParsed = true; | m_photoshopBlockParsed = true; | |||
while (TIFFReadDirectory(image.get())) { | while (TIFFReadDirectory(image.get())) { | |||
result = readTIFFDirectory(document, image.get()); | result = readTIFFDirectory(document, image.get()); | |||
if (!result.isOk()) { | if (!result.isOk()) { | |||
return result; | return result; | |||
} | } | |||
} | } | |||
} | } | |||
// Freeing memory | // Freeing memory | |||
image.reset(); | image.reset(); | |||
file.close(); | ||||
{ | { | |||
// HACK!! Externally parse the Exif metadata | // HACK!! Externally parse the Exif metadata | |||
// libtiff has no way to access the fields wholesale | // libtiff has no way to access the fields wholesale | |||
try { | try { | |||
const std::string encodedFilename = | KisExiv2IODevice::ptr_type basicIoDevice(new KisExiv2IODevice(filena | |||
QFile::encodeName(filename()).toStdString(); | me())); | |||
const std::unique_ptr<Exiv2::Image> readImg( | const std::unique_ptr<Exiv2::Image> readImg(Exiv2::ImageFactory::ope | |||
Exiv2::ImageFactory::open(encodedFilename).release()); | n(basicIoDevice).release()); | |||
readImg->readMetadata(); | readImg->readMetadata(); | |||
const KisMetaData::IOBackend *io = | const KisMetaData::IOBackend *io = | |||
KisMetadataBackendRegistry::instance()->value("exif"); | KisMetadataBackendRegistry::instance()->value("exif"); | |||
// All IFDs are paint layer children of root | // All IFDs are paint layer children of root | |||
KisNodeSP node = m_image->rootLayer()->firstChild(); | KisNodeSP node = m_image->rootLayer()->firstChild(); | |||
QBuffer ioDevice; | QBuffer ioDevice; | |||
skipping to change at line 1867 | skipping to change at line 1903 | |||
Exiv2::littleEndian, | Exiv2::littleEndian, | |||
tempData); | tempData); | |||
// Reencode into Qt land | // Reencode into Qt land | |||
ioDevice.setData(reinterpret_cast<char *>(tempBlob.data()), | ioDevice.setData(reinterpret_cast<char *>(tempBlob.data()), | |||
static_cast<int>(tempBlob.size())); | static_cast<int>(tempBlob.size())); | |||
} | } | |||
// Get layer | // Get layer | |||
KisLayer *layer = qobject_cast<KisLayer *>(node.data()); | KisLayer *layer = qobject_cast<KisLayer *>(node.data()); | |||
Q_ASSERT(layer); | KIS_ASSERT_RECOVER(layer) | |||
{ | ||||
errFile << "Attempted to import metadata on an empty document"; | ||||
return ImportExportCodes::InternalError; | ||||
} | ||||
// Inject the data as any other IOBackend | // Inject the data as any other IOBackend | |||
io->loadFrom(layer->metaData(), &ioDevice); | io->loadFrom(layer->metaData(), &ioDevice); | |||
} catch (Exiv2::AnyError &e) { | } catch (Exiv2::AnyError &e) { | |||
errFile << "Failed metadata import:" << e.code() << e.what(); | errFile << "Failed metadata import:" << e.code() << e.what(); | |||
} | } | |||
} | } | |||
document->setCurrentImage(m_image); | document->setCurrentImage(m_image); | |||
End of changes. 14 change blocks. | ||||
13 lines changed or deleted | 60 lines changed or added |