kimgio: fix TIFF images reading and implement write

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2023-05-22 07:54:18 +03:00
parent c9e7213de4
commit b3e9998d8c
4 changed files with 119 additions and 14 deletions

View file

@ -27,9 +27,9 @@
* @li WEBP \<read\> \<write\> * @li WEBP \<read\> \<write\>
* @li JPEG \<read\> \<write\> * @li JPEG \<read\> \<write\>
* @li ICO \<read\> \<write\> * @li ICO \<read\> \<write\>
* @li TIFF \<read\> \<write\>
* @li JP2 \<read\> * @li JP2 \<read\>
* @li RAW \<read\> * @li RAW \<read\>
* @li TIFF \<read\>
*/ */
namespace KImageIO namespace KImageIO
{ {

View file

@ -6,9 +6,9 @@ Current formats include:
WEBP <read> <write> WEBP <read> <write>
JPEG <read> <write> JPEG <read> <write>
ICO <read> <write> ICO <read> <write>
TIFF <read> <write>
JP2 <read> JP2 <read>
RAW <read> RAW <read>
TIFF <read>
If you want to contribute plugin for image format and there is no solid If you want to contribute plugin for image format and there is no solid
C/C++ library for it (like there is for WebP) then it is unlikely it will be C/C++ library for it (like there is for WebP) then it is unlikely it will be

View file

@ -68,11 +68,8 @@ static tmsize_t tiff_read_proc(thandle_t tiffhandle, void* tiffptr, tmsize_t tif
static tmsize_t tiff_write_proc(thandle_t tiffhandle, void* tiffptr, tmsize_t tiffsize) static tmsize_t tiff_write_proc(thandle_t tiffhandle, void* tiffptr, tmsize_t tiffsize)
{ {
// dummy QIODevice* device = static_cast<QIODevice*>(tiffhandle);
Q_UNUSED(tiffhandle); return device->write(static_cast<char*>(tiffptr), tiffsize);
Q_UNUSED(tiffptr);
Q_UNUSED(tiffsize);
return 0;
} }
static toff_t tiff_seek_proc(thandle_t tiffhandle, toff_t tiffoffset, int tiffwhence) static toff_t tiff_seek_proc(thandle_t tiffhandle, toff_t tiffoffset, int tiffwhence)
@ -174,7 +171,7 @@ bool TIFFHandler::read(QImage *image)
} }
// NOTE: TIFFReadRGBA* functions do internal conversion (e.g. YCbCr to RGBA) which does not // NOTE: TIFFReadRGBA* functions do internal conversion (e.g. YCbCr to RGBA) which does not
// work for all images // work for all images (see man TIFFReadRGBAImage)
char tifferror[1024]; char tifferror[1024];
::memset(tifferror, '\0', sizeof(tifferror)); ::memset(tifferror, '\0', sizeof(tifferror));
int tiffresult = TIFFRGBAImageOK(tiffclient, tifferror); int tiffresult = TIFFRGBAImageOK(tiffclient, tifferror);
@ -230,6 +227,8 @@ bool TIFFHandler::read(QImage *image)
return false; return false;
} }
*image = image->rgbSwapped();
TIFFClose(tiffclient); TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler); TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler); TIFFSetWarningHandler(s_tiffwarninghandler);
@ -238,8 +237,110 @@ bool TIFFHandler::read(QImage *image)
bool TIFFHandler::write(const QImage &image) bool TIFFHandler::write(const QImage &image)
{ {
// this plugin is a read-only kind of plugin s_tifferrorhandler = TIFFSetErrorHandler(tiff_error_handler);
s_tiffwarninghandler = TIFFSetWarningHandler(tiff_warning_handler);
TIFF* tiffclient = TIFFClientOpen(
"TIFFHandler", "w",
device(),
tiff_read_proc,
tiff_write_proc,
tiff_seek_proc,
tiff_close_proc,
tiff_size_proc,
tiff_mapfile_proc,
tiff_unmapfile_proc
);
if (!Q_UNLIKELY(tiffclient)) {
kWarning() << "Could not open client";
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false; return false;
}
QImage image32 = image.convertToFormat(QImage::Format_ARGB32).rgbSwapped();
const int width = image32.width();
int tiffresult = TIFFSetField(tiffclient, TIFFTAG_IMAGEWIDTH, width);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set width field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
const int height = image32.height();
tiffresult = TIFFSetField(tiffclient, TIFFTAG_IMAGELENGTH, height);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set height field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
tiffresult = TIFFSetField(tiffclient, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set planar config field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
tiffresult = TIFFSetField(tiffclient, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set photometric field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
tiffresult = TIFFSetField(tiffclient, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set compression field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
tiffresult = TIFFSetField(tiffclient, TIFFTAG_SAMPLESPERPIXEL, 4);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set samples per-pixel field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
tiffresult = TIFFSetField(tiffclient, TIFFTAG_BITSPERSAMPLE, 8);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not set bits per-pixel field";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
for (int i = 0; i < height; i++) {
uchar* scanline = image32.scanLine(i);
tiffresult = TIFFWriteScanline(tiffclient, scanline, i);
if (Q_UNLIKELY(tiffresult != 1)) {
kWarning() << "Could not write scanline";
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return false;
}
}
TIFFClose(tiffclient);
TIFFSetErrorHandler(s_tifferrorhandler);
TIFFSetWarningHandler(s_tiffwarninghandler);
return true;
} }
QByteArray TIFFHandler::name() const QByteArray TIFFHandler::name() const
@ -277,15 +378,19 @@ QList<QByteArray> TIFFPlugin::mimeTypes() const
QImageIOPlugin::Capabilities TIFFPlugin::capabilities(QIODevice *device, const QByteArray &format) const QImageIOPlugin::Capabilities TIFFPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{ {
if (format == s_tiffpluginformat) { if (format == s_tiffpluginformat) {
return QImageIOPlugin::Capabilities(QImageIOPlugin::CanRead); return QImageIOPlugin::Capabilities(QImageIOPlugin::CanRead | QImageIOPlugin::CanWrite);
} }
if (!device || !device->isOpen()) { if (!device || !device->isOpen()) {
return 0; return 0;
} }
QImageIOPlugin::Capabilities cap;
if (device->isReadable() && TIFFHandler::canRead(device)) { if (device->isReadable() && TIFFHandler::canRead(device)) {
return QImageIOPlugin::Capabilities(QImageIOPlugin::CanRead); cap |= QImageIOPlugin::CanRead;
} }
return 0; if (format == s_tiffpluginformat && device->isWritable()) {
cap |= QImageIOPlugin::CanWrite;
}
return cap;
} }
QImageIOHandler *TIFFPlugin::create(QIODevice *device, const QByteArray &format) const QImageIOHandler *TIFFPlugin::create(QIODevice *device, const QByteArray &format) const

View file

@ -136,7 +136,7 @@ bool WebPHandler::write(const QImage &image)
return false; return false;
} }
QImage image32 = image.convertToFormat(QImage::Format_ARGB32); const QImage image32 = image.convertToFormat(QImage::Format_ARGB32);
uint8_t *webpoutput = nullptr; uint8_t *webpoutput = nullptr;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN #if Q_BYTE_ORDER == Q_BIG_ENDIAN