kimgio: new JPEG-specialized plugin

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-10-11 22:58:13 +03:00
parent cdd26390b0
commit 7995231b45
9 changed files with 323 additions and 12 deletions

View file

@ -185,6 +185,14 @@ set_package_properties(LibRaw PROPERTIES
TYPE OPTIONAL
)
kde4_optional_find_package(LibJPEG)
set_package_properties(LibJPEG PROPERTIES
DESCRIPTION "JPEG image codec that uses SIMD instructions"
URL "https://libjpeg-turbo.org/"
PURPOSE "Support for JPEG image format"
TYPE OPTIONAL
)
# v143+ required for udev_monitor_filter_add_match_subsystem_devtype()
kde4_optional_find_package(UDev 143)
set_package_properties(UDev PROPERTIES

View file

@ -0,0 +1,39 @@
# Try to find libjpeg-turbo library, once done this will define:
#
# LIBJPEG_FOUND - system has libjpeg-turbo
# LIBJPEG_INCLUDE_DIR - the libjpeg-turbo include directory
# LIBJPEG_LIBRARIES - the libraries needed to use libjpeg-turbo
# LIBJPEG_DEFINITIONS - compiler switches required for using libjpeg-turbo
#
# Copyright (c) 2022 Ivailo Monev <xakepa10@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
find_package(PkgConfig REQUIRED)
pkg_check_modules(PC_LIBJPEG QUIET libturbojpeg)
set(LIBJPEG_INCLUDE_DIR ${PC_LIBJPEG_INCLUDE_DIRS})
set(LIBJPEG_LIBRARIES ${PC_LIBJPEG_LIBRARIES})
set(LIBJPEG_VERSION ${PC_LIBJPEG_VERSION})
set(LIBJPEG_DEFINITIONS ${PC_LIBJPEG_CFLAGS_OTHER})
if(NOT LIBJPEG_INCLUDE_DIR OR NOT LIBJPEG_LIBRARIES)
find_path(LIBJPEG_INCLUDE_DIR
NAMES turbojpeg.h
HINTS $ENV{LIBJPEGDIR}/include
)
find_library(LIBJPEG_LIBRARIES
NAMES turbojpeg
HINTS $ENV{LIBJPEGDIR}/lib
)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibJPEG
VERSION_VAR LIBJPEG_VERSION
REQUIRED_VARS LIBJPEG_LIBRARIES LIBJPEG_INCLUDE_DIR
)
mark_as_advanced(LIBJPEG_INCLUDE_DIR LIBJPEG_LIBRARIES)

View file

@ -5,7 +5,7 @@
# LIBRAW_LIBRARIES - the libraries needed to use LibRaw
# LIBRAW_DEFINITIONS - compiler switches required for using LibRaw
#
# Copyright (c) 2020 Ivailo Monev <xakepa10@gmail.com>
# Copyright (c) 2022 Ivailo Monev <xakepa10@gmail.com>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.

View file

@ -26,15 +26,13 @@
* Currently supported formats include:
* @li WEBP \<read\> \<write\>
* @li RAW \<read\>
* @li JPEG \<read\>
* @li PGM \<read\>
* @li XBM \<read\>
* @li BMP \<read\>
* @li ICO \<read\>
* @li JPEG \<read\>
* @li JP2 \<read\>
* @li GIF \<read\>
*
* @todo move to kdeui
*/
namespace KImageIO
{

View file

@ -63,3 +63,23 @@ if(LIBRAW_FOUND)
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}/plugins/imageformats
)
endif(LIBRAW_FOUND)
##################################
if(LIBJPEG_FOUND)
include_directories(${LIBJPEG_INCLUDE_DIR})
kde4_add_plugin(kimg_jpeg jpeg.cpp)
target_link_libraries(kimg_jpeg
${KDE4_KDECORE_LIBS}
${QT_QTGUI_LIBRARY}
${LIBJPEG_LIBRARIES}
)
set_target_properties(kimg_jpeg PROPERTIES
OUTPUT_NAME jpeg
)
install(
TARGETS kimg_jpeg
DESTINATION ${KDE4_PLUGIN_INSTALL_DIR}/plugins/imageformats
)
endif(LIBJPEG_FOUND)

View file

@ -5,6 +5,7 @@ Current formats include:
WEBP <read> <write>
RAW <read>
JPEG <read>
along with whatever ImageMagick supports but it is assumed that it supports
reading atleast the following formats (even if they cannot be loaded because
@ -14,7 +15,6 @@ PGM <read>
XBM <read>
BMP <read>
ICO <read>
JPEG <read>
JP2 <read>
GIF <read>

203
kimgio/jpeg.cpp Normal file
View file

@ -0,0 +1,203 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "jpeg.h"
#include <QImage>
#include <kdebug.h>
#include <turbojpeg.h>
static const char* const s_jpegpluginformat = "jpg";
static const ushort s_peekbuffsize = 32;
// for reference:
// https://en.wikipedia.org/wiki/List_of_file_signatures
static const uchar s_jpgjfifheader[] = { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01 };
static const uchar s_jpgheader[] = { 0xFF, 0xD8, 0xFF, 0xE0 };
static const uchar s_jpg2header[] = { 0xFF, 0xD8, 0xFF, 0xEE };
static const uchar s_jpegexifheader[] = { 0xFF, 0xD8, 0xFF, 0xE1 };
static const struct HeadersTblData {
const uchar *header;
const int headersize;
const char *format;
} HeadersTbl[] = {
{ s_jpgjfifheader, 12, "jpg" },
{ s_jpgheader, 4, "jpg" },
{ s_jpg2header, 4, "jpg" },
{ s_jpegexifheader, 4, "jpg" }
};
static const qint16 HeadersTblSize = sizeof(HeadersTbl) / sizeof(HeadersTblData);
JPEGHandler::JPEGHandler()
{
}
JPEGHandler::~JPEGHandler()
{
}
bool JPEGHandler::canRead() const
{
if (canRead(device())) {
setFormat(s_jpegpluginformat);
return true;
}
return false;
}
bool JPEGHandler::read(QImage *image)
{
QByteArray data = device()->readAll();
tjhandle jpegdecomp = tjInitDecompress();
if (!jpegdecomp) {
kWarning() << "Could not initialize decompressor" << tjGetErrorStr();
return false;
}
int jpegwidth = 0;
int jpegheight = 0;
int jpegsubsamp = 0;
int jpegcolorspace = 0;
int jpegstatus = tjDecompressHeader3(
jpegdecomp,
reinterpret_cast<const uchar*>(data.constData()), data.size(),
&jpegwidth, &jpegheight,
&jpegsubsamp, &jpegcolorspace
);
if (jpegstatus != 0) {
kWarning() << "Could not decompress header" << tjGetErrorStr2(jpegdecomp);
(void)tjDestroy(jpegdecomp);
return false;
}
static int s_jpegpixelFormat = TJPF_ARGB;
int jpegbuffersize = (jpegwidth * jpegheight * tjPixelSize[s_jpegpixelFormat]);
unsigned char *jpegbuffer = tjAlloc(jpegbuffersize);
if (!jpegbuffer) {
kWarning() << "Could not allocate buffer" << tjGetErrorStr2(jpegdecomp);
(void)tjDestroy(jpegdecomp);
return false;
}
jpegstatus = tjDecompress2(
jpegdecomp,
reinterpret_cast<const uchar*>(data.constData()), data.size(),
jpegbuffer,
jpegwidth, 0 , jpegheight,
s_jpegpixelFormat,
TJFLAG_FASTDCT
);
if (jpegstatus != 0) {
kWarning() << "Could not decompress" << tjGetErrorStr2(jpegdecomp);
tjFree(jpegbuffer);
(void)tjDestroy(jpegdecomp);
return false;
}
*image = QImage(jpegwidth, jpegheight, QImage::Format_ARGB32);
if (image->isNull()) {
tjFree(jpegbuffer);
(void)tjDestroy(jpegdecomp);
return false;
}
QRgb* imagebits = reinterpret_cast<QRgb*>(image->bits());
for (uint i = 0; i < jpegbuffersize; i += 4) {
*imagebits = qRgba(jpegbuffer[i + 1], jpegbuffer[i + 2], jpegbuffer[i + 3], jpegbuffer[i]);
imagebits++;
}
tjFree(jpegbuffer);
(void)tjDestroy(jpegdecomp);
return true;
}
bool JPEGHandler::write(const QImage &image)
{
// this plugin is a read-only kind of plugin
return false;
}
QByteArray JPEGHandler::name() const
{
return s_jpegpluginformat;
}
bool JPEGHandler::canRead(QIODevice *device)
{
if (Q_UNLIKELY(!device)) {
kWarning() << "Called with no device";
return false;
}
const QByteArray data = device->peek(s_peekbuffsize);
if (Q_UNLIKELY(data.isEmpty())) {
return false;
}
for (int i = 0; i < HeadersTblSize; i++) {
if (qstrncmp(data.constData(), reinterpret_cast<const char*>(HeadersTbl[i].header), HeadersTbl[i].headersize) == 0) {
kDebug() << "Header detected" << HeadersTbl[i].format;
return true;
}
}
return false;
}
QStringList JPEGPlugin::keys() const
{
return QStringList() << s_jpegpluginformat;
}
QList<QByteArray> JPEGPlugin::mimeTypes() const
{
static const QList<QByteArray> list = QList<QByteArray>()
<< "image/jpeg";
return list;
}
QImageIOPlugin::Capabilities JPEGPlugin::capabilities(QIODevice *device, const QByteArray &format) const
{
if (format == s_jpegpluginformat)
return QImageIOPlugin::Capabilities(QImageIOPlugin::CanRead);
if (!format.isEmpty())
return 0;
if (!device->isOpen())
return 0;
QImageIOPlugin::Capabilities cap;
if (device->isReadable() && JPEGHandler::canRead(device))
cap |= QImageIOPlugin::CanRead;
return cap;
}
QImageIOHandler *JPEGPlugin::create(QIODevice *device, const QByteArray &format) const
{
QImageIOHandler *handler = new JPEGHandler();
handler->setDevice(device);
handler->setFormat(format);
return handler;
}
Q_EXPORT_PLUGIN2(jpeg, JPEGPlugin)

50
kimgio/jpeg.h Normal file
View file

@ -0,0 +1,50 @@
/* This file is part of the KDE libraries
Copyright (C) 2022 Ivailo Monev <xakepa10@gmail.com>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License version 2, as published by the Free Software Foundation.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef KIMG_JPEG_H
#define KIMG_JPEG_H
#include <QtCore/qstringlist.h>
#include <QtGui/qimageiohandler.h>
class JPEGHandler : public QImageIOHandler
{
public:
JPEGHandler();
~JPEGHandler();
bool canRead() const final;
bool read(QImage *image) final;
bool write(const QImage &image) final;
QByteArray name() const final;
static bool canRead(QIODevice *device);
};
class JPEGPlugin : public QImageIOPlugin
{
public:
QStringList keys() const;
QList<QByteArray> mimeTypes() const;
Capabilities capabilities(QIODevice *device, const QByteArray &format) const final;
QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const final;
};
#endif // KIMG_JPEG_H

View file

@ -35,12 +35,9 @@ static const ushort s_peekbuffsize = 32;
// for reference:
// https://en.wikipedia.org/wiki/List_of_file_signatures
static const uchar s_jp2header[] = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20, 0x0D, 0x0A, 0x87, 0x0A };
static const uchar s_jpgjfifheader[] = { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01 };
static const uchar s_gif87aheader[] = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
static const uchar s_gif89aheader[] = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
static const uchar s_icoheader[] = { 0x0, 0x0, 0x1, 0x0, 0x0 };
static const uchar s_jpgheader[] = { 0xFF, 0xD8, 0xFF, 0xE0 };
static const uchar s_jpg2header[] = { 0xFF, 0xD8, 0xFF, 0xEE };
static const uchar s_bmpheader[] = { 0x42, 0x4D };
static const struct HeadersTblData {
@ -49,12 +46,9 @@ static const struct HeadersTblData {
const char *format;
} HeadersTbl[] = {
{ s_jp2header, 12, "jp2" },
{ s_jpgjfifheader, 12, "jpg" },
{ s_gif87aheader, 6, "gif" },
{ s_gif89aheader, 6, "gif" },
{ s_icoheader, 5, "ico" },
{ s_jpgheader, 4, "jpg" },
{ s_jpg2header, 4, "jpg" },
{ s_bmpheader , 2, "bmp" }
};
static const qint16 HeadersTblSize = sizeof(HeadersTbl) / sizeof(HeadersTblData);
@ -64,7 +58,6 @@ static QList<std::string> s_whitelist = QList<std::string>()
<< std::string("XBM")
<< std::string("BMP")
<< std::string("ICO")
<< std::string("JPEG")
<< std::string("JP2")
<< std::string("GIF")
<< std::string("PNG");