From 95b6cf26837dba0ac215db552ce47031f3abfc6e Mon Sep 17 00:00:00 2001 From: Jake Petroules <jake.petroules@petroules.com> Date: Mon, 6 Jan 2014 07:52:54 -0500 Subject: [PATCH] Add JPEG 2000 plugin. It is moving from Qt Solutions. Change-Id: Ie0dc44d258597f871544fa43238528f42628b799 Reviewed-by: Jake Petroules <jake.petroules@petroules.com> Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com> --- dist/changes-5.3.0 | 1 + .../doc/qtimageformats-dita.qdocconf | 2 +- src/imageformats/doc/src/qtimageformats.qdoc | 1 + src/plugins/imageformats/imageformats.pro | 11 +- src/plugins/imageformats/jp2/jp2.json | 4 + src/plugins/imageformats/jp2/jp2.pro | 9 + src/plugins/imageformats/jp2/main.cpp | 100 ++ src/plugins/imageformats/jp2/qjp2handler.cpp | 1218 +++++++++++++++++ src/plugins/imageformats/jp2/qjp2handler.pri | 10 + src/plugins/imageformats/jp2/qjp2handler_p.h | 78 ++ tests/auto/auto.pro | 3 +- tests/auto/jp2/jp2.pro | 8 + tests/auto/jp2/tst_qjp2.cpp | 88 ++ tests/shared/images/jp2.qrc | 6 + tests/shared/images/jp2/logo.bmp | Bin 0 -> 119734 bytes tests/shared/images/jp2/logo.jp2 | Bin 0 -> 27570 bytes 16 files changed, 1533 insertions(+), 6 deletions(-) create mode 100644 src/plugins/imageformats/jp2/jp2.json create mode 100644 src/plugins/imageformats/jp2/jp2.pro create mode 100644 src/plugins/imageformats/jp2/main.cpp create mode 100644 src/plugins/imageformats/jp2/qjp2handler.cpp create mode 100644 src/plugins/imageformats/jp2/qjp2handler.pri create mode 100644 src/plugins/imageformats/jp2/qjp2handler_p.h create mode 100644 tests/auto/jp2/jp2.pro create mode 100644 tests/auto/jp2/tst_qjp2.cpp create mode 100644 tests/shared/images/jp2.qrc create mode 100644 tests/shared/images/jp2/logo.bmp create mode 100644 tests/shared/images/jp2/logo.jp2 diff --git a/dist/changes-5.3.0 b/dist/changes-5.3.0 index 5a7a19b6..026d568d 100644 --- a/dist/changes-5.3.0 +++ b/dist/changes-5.3.0 @@ -21,3 +21,4 @@ information about a particular change. - Add read/write support for Direct Draw Surface images. - Add read/write support for ICNS images. + - Add read/write support for JPEG 2000 images. diff --git a/src/imageformats/doc/qtimageformats-dita.qdocconf b/src/imageformats/doc/qtimageformats-dita.qdocconf index 82f9fdca..053e02a5 100644 --- a/src/imageformats/doc/qtimageformats-dita.qdocconf +++ b/src/imageformats/doc/qtimageformats-dita.qdocconf @@ -15,7 +15,7 @@ exampledirs += ../examples HTML.nobreadcrumbs = "true" examples.fileextensions = "*.cpp *.h *.js *.svg *.xml *.ui *.qml" -examples.imageextensions = "*.png *.jpeg *.jpg *.gif *.mng" +examples.imageextensions = "*.png *.jp2 *.jpeg *.jpg *.gif *.mng" headers.fileextensions = "*.h *.ch *.h++ *.hh *.hpp *.hxx" sources.fileextensions = "*.cpp *.qdoc *.mm *.qml" diff --git a/src/imageformats/doc/src/qtimageformats.qdoc b/src/imageformats/doc/src/qtimageformats.qdoc index 2c9f8a32..86cdd53e 100644 --- a/src/imageformats/doc/src/qtimageformats.qdoc +++ b/src/imageformats/doc/src/qtimageformats.qdoc @@ -54,6 +54,7 @@ libraries. If not found, it may fall back on using a bundled copy (in \header \li Format \li Description \li Support \li 3rd party codec \row \li DDS \li Direct Draw Surface \li Read/write \li No \row \li ICNS \li Apple Icon Image \li Read/write \li No +\row \li JP2 \li Joint Photographic Experts Group 2000 \li Read/write \li Yes (bundled) \row \li MNG \li Multiple-image Network Graphics \li Read \li Yes (bundled) \row \li TGA \li Truevision Graphics Adapter \li Read \li No \row \li TIFF \li Tagged Image File Format \li Read/write \li Yes (bundled) diff --git a/src/plugins/imageformats/imageformats.pro b/src/plugins/imageformats/imageformats.pro index c6aa4098..01d2ac02 100644 --- a/src/plugins/imageformats/imageformats.pro +++ b/src/plugins/imageformats/imageformats.pro @@ -1,11 +1,14 @@ TEMPLATE = subdirs SUBDIRS = \ - tga \ - wbmp \ + dds \ + icns \ + jp2 \ mng \ + tga \ tiff \ - dds \ - icns + wbmp + +wince:SUBDIRS -= jp2 winrt { SUBDIRS -= tiff \ diff --git a/src/plugins/imageformats/jp2/jp2.json b/src/plugins/imageformats/jp2/jp2.json new file mode 100644 index 00000000..e3d7fe30 --- /dev/null +++ b/src/plugins/imageformats/jp2/jp2.json @@ -0,0 +1,4 @@ +{ + "Keys": [ "jp2" ], + "MimeTypes": [ "image/jp2", "image/jpx", "image/jpm", "video/mj2" ] +} diff --git a/src/plugins/imageformats/jp2/jp2.pro b/src/plugins/imageformats/jp2/jp2.pro new file mode 100644 index 00000000..864ec269 --- /dev/null +++ b/src/plugins/imageformats/jp2/jp2.pro @@ -0,0 +1,9 @@ +TARGET = qjp2 + +PLUGIN_TYPE = imageformats +PLUGIN_CLASS_NAME = QJp2Plugin +load(qt_plugin) + +include(qjp2handler.pri) +SOURCES += main.cpp +OTHER_FILES += jp2.json diff --git a/src/plugins/imageformats/jp2/main.cpp b/src/plugins/imageformats/jp2/main.cpp new file mode 100644 index 00000000..700e96ed --- /dev/null +++ b/src/plugins/imageformats/jp2/main.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Petroules Corporation. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the JP2 plugins in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qimageiohandler.h> +#include <qstringlist.h> + +#ifndef QT_NO_IMAGEFORMATPLUGIN + +#include "qjp2handler_p.h" + +#include <qiodevice.h> +#include <qbytearray.h> + +QT_BEGIN_NAMESPACE + +class QJp2Plugin : public QImageIOPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QImageIOHandlerFactoryInterface" FILE "jp2.json") + +public: + QStringList keys() const; + Capabilities capabilities(QIODevice *device, const QByteArray &format) const; + QImageIOHandler *create(QIODevice *device, const QByteArray &format = QByteArray()) const; +}; + +QStringList QJp2Plugin::keys() const +{ + return QStringList() << QLatin1String("jp2") << QLatin1String("j2k"); +} + +QImageIOPlugin::Capabilities QJp2Plugin::capabilities(QIODevice *device, const QByteArray &format) const +{ + if (format == "jp2" || format == "j2k") + return Capabilities(CanRead | CanWrite); + if (!format.isEmpty()) + return 0; + if (!device->isOpen()) + return 0; + + Capabilities cap; + if (device->isReadable() && QJp2Handler::canRead(device, 0)) + cap |= CanRead; + if (device->isWritable()) + cap |= CanWrite; + return cap; +} + +QImageIOHandler *QJp2Plugin::create(QIODevice *device, const QByteArray &format) const +{ + QJp2Handler *handler = new QJp2Handler(); + handler->setDevice(device); + handler->setFormat(format); + return handler; +} + +QT_END_NAMESPACE + +#include "main.moc" + +#endif // !QT_NO_IMAGEFORMATPLUGIN diff --git a/src/plugins/imageformats/jp2/qjp2handler.cpp b/src/plugins/imageformats/jp2/qjp2handler.cpp new file mode 100644 index 00000000..3a611d68 --- /dev/null +++ b/src/plugins/imageformats/jp2/qjp2handler.cpp @@ -0,0 +1,1218 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Petroules Corporation. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the JP2 plugins in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qjp2handler_p.h" + +#include "qimage.h" +#include "qvariant.h" +#include "qcolor.h" + +#ifdef Q_CC_MSVC +#define JAS_WIN_MSVC_BUILD +#endif +#include <jasper/jasper.h> + +QT_BEGIN_NAMESPACE + +class QJp2HandlerPrivate +{ + Q_DECLARE_PUBLIC(QJp2Handler) + Q_DISABLE_COPY(QJp2HandlerPrivate) +public: + int writeQuality; + QByteArray subType; + QJp2Handler *q_ptr; + QJp2HandlerPrivate(QJp2Handler *q_ptr); +}; + +enum SubFormat { Jp2Format, J2kFormat }; + +/* + \class Jpeg2000JasperReader + \brief Jpeg2000JasperReader implements reading and writing of JPEG 2000 + image files. + + \internal + + This class is designed to be used together with the an QImageIO IOHandler, + and it should probably not be necessary to instantiate it directly. + + Internally it used the Jasper library for coding the image data. +*/ +class Jpeg2000JasperReader +{ +public: + Jpeg2000JasperReader(QIODevice *iod, const SubFormat format = Jp2Format); + + ~Jpeg2000JasperReader(); + + bool read(QImage *pImage); + bool write(const QImage &image, int quality); +private: + typedef void (Jpeg2000JasperReader::*ScanlineFunc)(jas_seqent_t** const, uchar*); + typedef void (Jpeg2000JasperReader::*ScanlineFuncWrite)(jas_matrix_t**, uchar*); + + void copyJasperQt(ScanlineFunc scanlinecopier); + void copyJasperQtGeneric(); + void copyScanlineJasperQtRGB(jas_seqent_t ** const jasperRow, uchar *qtScanLine); + void copyScanlineJasperQtRGBA(jas_seqent_t ** const jasperRow, uchar *qtScanLine); + void copyScanlineJasperQtGray(jas_seqent_t ** const jasperRow, uchar *qtScanLine); + void copyScanlineJasperQtGrayA(jas_seqent_t ** const jasperRow, uchar *qtScanLine); + + void copyQtJasper(const ScanlineFuncWrite scanlinecopier); + void copyScanlineQtJasperRGB(jas_matrix_t ** jasperRow, uchar *qtScanLine); + void copyScanlineQtJasperRGBA(jas_matrix_t ** jasperRow, uchar *qtScanLine); + void copyScanlineQtJasperColormapRGB(jas_matrix_t ** jasperRow, uchar *qtScanLine); + void copyScanlineQtJasperColormapRGBA(jas_matrix_t ** jasperRow, uchar *qtScanLine); + void copyScanlineQtJasperColormapGrayscale(jas_matrix_t ** jasperRow, uchar *qtScanLine); + void copyScanlineQtJasperColormapGrayscaleA(jas_matrix_t ** jasperRow, uchar *qtScanLine); + + bool attemptColorspaceChange(int wantedColorSpace); + bool createJasperMatrix(jas_matrix_t **&matrix); + bool freeJasperMatrix(jas_matrix_t **matrix); + void printColorSpaceError(); + jas_image_cmptparm_t createComponentMetadata(const int width, const int height); + jas_image_t *newRGBAImage(const int width, const int height, bool alpha); + jas_image_t *newGrayscaleImage(const int width, const int height, bool alpha); + bool decodeColorSpace(int clrspc, QString &family, QString &specific); + void printMetadata(jas_image_t *image); + + bool jasperOk; + + QIODevice *ioDevice; + QImage qtImage; + SubFormat format; + + // Qt image properties + int qtWidth; + int qtHeight; + int qtDepth; + int qtNumComponents; + + jas_image_t *jasper_image; + // jasper image properties + int jasNumComponents; + int jasComponentPrecicion[4]; + int computedComponentWidth ; + int computedComponentHeight; + int computedComponentHorizontalSubsampling; + int computedComponentVerticalSubsampling; + int jasperColorspaceFamily; + // maps color to component (ex: colorComponentMapping[RED] + // gives the component that contains the red color) + int colorComponentMapping[4]; + bool hasAlpha; +}; + +QJp2HandlerPrivate::QJp2HandlerPrivate(QJp2Handler *q_ptr) + : writeQuality(100), subType("jp2"), q_ptr(q_ptr) +{ +} + +/*! + \class QJp2Handler + \brief The QJp2Handler class provides support for reading and writing + JPEG 2000 image files with the Qt plugin system. + Currently, it only supports dynamically-loaded plugins. + + JPEG files comes in two subtypes: the JPEG 2000 file format (\c .jp2) and + the JPEG 2000 code stream format (\c .j2k, \c .jpc, or \c .j2c). + QJp2Handler can read and write both types. + + To select a subtype, use the setOption() function with the + QImageIOHandler::SubType option and a parameter value of "jp2" or "j2k". + + To set the image quality when writing, you can use setOption() with the + QImageIOHandler::Quality option, or QImageWriter::setQuality() if your are + using a QImageWriter object to write the image. The image quality is + specified as an int in the range 0 to 100. 0 means maximum compression and + 99 means minimum compression. 100 selects lossless encoding, and this is the + default value. + + The JPEG handler is only available as a plugin, + and this enables you to use normal QImage and QPixmap functions to read and + write images. For example: + + \code + myLabel->setPixmap(QPixmap("myimage.jp2")); + + QPixmap myPixmap; + myPixmap.save("myimage.jp2", "JP2"); + \endcode +*/ + +/*! + Constructs an instance of QJp2Handler. +*/ +QJp2Handler::QJp2Handler() + : d_ptr(new QJp2HandlerPrivate(this)) +{ +} + +/*! + Destructor for QJp2Handler. +*/ +QJp2Handler::~QJp2Handler() +{ + +} + +/*! + Verifies if some values (magic bytes) are set as expected in the + header of the file. If the magic bytes were found, we assume that we + can read the file. The function will assume that the \a iod is + pointing to the beginning of the JPEG 2000 header. (i.e. it will for + instance not seek to the beginning of a file before reading). + + If \a subType is not 0, it will contain "jp2" or "j2k" upon + successful return. +*/ +bool QJp2Handler::canRead(QIODevice *iod, QByteArray *subType) +{ + bool bCanRead = false; + if (iod) { + const QByteArray header = iod->peek(12); + if (header.startsWith(QByteArrayLiteral("\000\000\000\fjP \r\n\207\n"))) { + // Jp2 is the JPEG 2000 file format + bCanRead = true; + if (subType) + *subType = QByteArray("jp2"); + } else if (header.startsWith(QByteArrayLiteral("\377\117\377\121\000"))) { + // J2c is the JPEG 2000 code stream + bCanRead = true; + if (subType) + *subType = QByteArray("j2k"); + } + } + return bCanRead; +} + +/*! \reimp +*/ +bool QJp2Handler::canRead() const +{ + QByteArray subType; + if (canRead(device(), &subType)) { + setFormat(subType); + return true; + } + return false; +} + +/*! \reimp +*/ +bool QJp2Handler::read(QImage *image) +{ + Jpeg2000JasperReader reader(device()); + return reader.read(image); +} + +/*! \reimp +*/ +bool QJp2Handler::write(const QImage &image) +{ + Q_D(const QJp2Handler); + SubFormat subFormat; + if (d->subType == QByteArray("jp2")) + subFormat = Jp2Format; + else + subFormat = J2kFormat; + + Jpeg2000JasperReader writer(device(), subFormat); + return writer.write(image, d->writeQuality); +} + +/*! + Get the value associated with \a option. + \sa setOption() +*/ +QVariant QJp2Handler::option(ImageOption option) const +{ + Q_D(const QJp2Handler); + if (option == Quality) { + return QVariant(d->writeQuality); + } else if (option == SubType) { + return QVariant(d->subType); + } + return QVariant(); +} + +/*! + The JPEG 2000 handler supports two options. + Set \a option to QImageIOHandler::Quality to balance between quality and the + compression level, where \a value should be an integer in the interval + [0-100]. 0 is maximum compression (low quality) and 100 is lossless + compression (high quality). + + Set \a option to QImageIOHandler::Subtype to choose the subtype, + where \a value should be a string equal to either "jp2" or "j2k" + \sa option() +*/ +void QJp2Handler::setOption(ImageOption option, const QVariant &value) +{ + Q_D(QJp2Handler); + if (option == Quality) { + bool ok; + const int quality = value.toInt(&ok); + if (ok) + d->writeQuality = quality; + } else if (option == SubType) { + const QByteArray subTypeCandidate = value.toByteArray(); + // Test for default Jpeg2000 file format (jp2), or stream format (j2k). + if (subTypeCandidate == QByteArrayLiteral("jp2") || + subTypeCandidate == QByteArrayLiteral("j2k")) + d->subType = subTypeCandidate; + } +} + +/*! + This function will return true if \a option is set to either + QImageIOHandler::Quality or QImageIOHandler::Subtype. +*/ +bool QJp2Handler::supportsOption(ImageOption option) const +{ + return (option == Quality || option == SubType); +} + +/*! + Return the common identifier of the format. + For JPEG 2000 this will return "jp2". + */ +QByteArray QJp2Handler::name() const +{ + return QByteArrayLiteral("jp2"); +} + +/*! + Automatic resource handling for a jas_image_t*. +*/ +class ScopedJasperImage +{ +public: + // Take reference to the pointer here, because the pointer + // may change when we change color spaces. + ScopedJasperImage(jas_image_t *&image):image(image) { } + ~ScopedJasperImage() { jas_image_destroy(image); } +private: + jas_image_t *ℑ +}; + +/*! \internal + Construct a Jpeg2000JasperReader using the provided \a imageIO. + Note that currently the jasper library is initialized in this constructor, + (and freed in the destructor) which means that: + - Only one instance of this class may exist at one time + - No thread safety +*/ +Jpeg2000JasperReader::Jpeg2000JasperReader(QIODevice *iod, SubFormat format) + : jasperOk(true), ioDevice(iod), format(format), hasAlpha(false) +{ + if (jas_init()) { + jasperOk = false; + qDebug("Jasper Library initialization failed"); + } +} + +Jpeg2000JasperReader::~Jpeg2000JasperReader() +{ + if (jasperOk) + jas_cleanup(); +} + +/*! \internal + Opens the file data and attempts to decode it using the Jasper library. + Returns true if successful, false on failure +*/ +bool Jpeg2000JasperReader::read(QImage *pImage) +{ + if (!jasperOk) + return false; + + /* + Reading proceeds approximately as follows: + 1. Open stream and decode using Jasper + 2. Get image metadata + 3. Change colorspace if necessary + 4. Create a QImage of the appropriate type (32-bit for RGB, + 8-bit for grayscale) + 5. Copy image data from Jasper to the QImage + + When copying the image data from the Jasper data structures to the + QImage, a generic copy function (copyJasperQt) iterates through the + scanlines and calls the provided (via the scanlineCopier argument) + scanline copy function for each scanline. The scanline copy function + selected according to image metadata such as color space and the + presence of an alpha channel. + */ + QByteArray fileContents = ioDevice->readAll(); + jas_stream_t *imageData = jas_stream_memopen(fileContents.data(), + fileContents.size()); + jasper_image = jas_image_decode(imageData, jas_image_getfmt(imageData), 0); + jas_stream_close(imageData); + if (!jasper_image) { + qDebug("Jasper library can't decode Jpeg2000 image data"); + return false; + } + ScopedJasperImage scopedImage(jasper_image); + //printMetadata(jasper_image); + + qtWidth = jas_image_width(jasper_image); + qtHeight = jas_image_height(jasper_image); + jasNumComponents = jas_image_numcmpts(jasper_image); + jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image)); + + bool needColorspaceChange = false; + if (jasperColorspaceFamily != JAS_CLRSPC_FAM_RGB && + jasperColorspaceFamily != JAS_CLRSPC_FAM_GRAY) + needColorspaceChange = true; + + // Get per-component data + int c; + for (c = 0; c < jasNumComponents; ++c) { + jasComponentPrecicion[c] = jas_image_cmptprec(jasper_image, c); + + // Test for precision + if (jasComponentPrecicion[c] > 8 || jasComponentPrecicion[c] < 8) + needColorspaceChange = true; + + // Test for subsampling + if (jas_image_cmpthstep(jasper_image, c) != 1 || + jas_image_cmptvstep(jasper_image, c) != 1) + needColorspaceChange = true; + + // Test for signed components + if (jas_image_cmptsgnd(jasper_image, c) != 0) + needColorspaceChange = true; + } + + /* + If we encounter a different color space than RGB + (such as XYZ or YCbCr) we change that to RGB. + Also, if any component has "funny" metadata (such as precicion != 8 bits + or subsampling != 1) we also do a colorspace + change in order to convert it to something we can load. + */ + + bool decodeOk = true; + if (needColorspaceChange) + decodeOk = attemptColorspaceChange(JAS_CLRSPC_SRGB); + + if (!decodeOk) { + printColorSpaceError(); + return false; + } + + // Image metadata may have changed, get from Jasper. + qtWidth = jas_image_width(jasper_image); + qtHeight = jas_image_height(jasper_image); + jasNumComponents = jas_image_numcmpts(jasper_image); + jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image)); + for (c = 0; c < jasNumComponents; ++c) { + jasComponentPrecicion[c] = jas_image_cmptprec(jasper_image, c); + } + + if (jasperColorspaceFamily != JAS_CLRSPC_FAM_RGB && + jasperColorspaceFamily != JAS_CLRSPC_FAM_GRAY) { + qDebug("The Qt JPEG 2000 reader was unable to convert colorspace to RGB or grayscale"); + return false; + } + + // If a component has a subsampling factor != 1, we can't trust + // jas_image_height/width, so we need to figure it out ourselves + bool oddComponentSubsampling = false; + for (c = 0; c < jasNumComponents; ++c) { + if (jas_image_cmpthstep(jasper_image, c) != 1 || + jas_image_cmptvstep(jasper_image, c) != 1) { + oddComponentSubsampling = true; + } + } + + if (oddComponentSubsampling) { + // Check if all components have the same vertical/horizontal dim and + // subsampling + computedComponentWidth = jas_image_cmptwidth(jasper_image, 0); + computedComponentHeight = jas_image_cmptheight(jasper_image, 0); + computedComponentHorizontalSubsampling = jas_image_cmpthstep(jasper_image, 0); + computedComponentVerticalSubsampling = jas_image_cmptvstep(jasper_image, 0); + + for (c = 1; c < jasNumComponents; ++c) { + if (computedComponentWidth != jas_image_cmptwidth(jasper_image, c) || + computedComponentWidth != jas_image_cmptwidth(jasper_image, c) || + computedComponentHorizontalSubsampling != jas_image_cmpthstep(jasper_image, c) || + computedComponentVerticalSubsampling != jas_image_cmptvstep(jasper_image, c)) { + qDebug("The Qt JPEG 2000 reader does not support images where " + "component geometry differs from image geometry"); + return false; + } + } + qtWidth = computedComponentWidth * computedComponentHorizontalSubsampling; + qtHeight = computedComponentHeight * computedComponentVerticalSubsampling; + } + + // Sanity check each component + for (c = 0; c < jasNumComponents; ++c) { + // Test for precision + if (jasComponentPrecicion[c]>8 || jasComponentPrecicion[c]<8) { + qDebug("The Qt JPEG 2000 reader does not support components with " + "precision != 8"); + decodeOk = false; + } +#if 0 + // Test the subsampling factor (space between pixels on the image grid) + if (oddComponentSubsampling) { + qDebug("The Qt JPEG 2000 reader does not support components with " + "a subsampling factor != 1 (yet)"); + decodeOk = false; + } +#endif + // Test for signed components + if (jas_image_cmptsgnd(jasper_image, c) != 0) { + qDebug("Qt JPEG 2000 reader does not support signed components"); + decodeOk = false; + } + + // Test for component/image geomoetry mismach. + // If oddComponentSubsampling, then this is already taken care of above. + if (!oddComponentSubsampling) + if (jas_image_cmpttlx(jasper_image,c) != 0 || + jas_image_cmpttly(jasper_image,c) != 0 || + jas_image_cmptbrx(jasper_image,c) != jas_image_brx(jasper_image) || + jas_image_cmptbry(jasper_image,c) != jas_image_bry(jasper_image) || + jas_image_cmptwidth (jasper_image, c) != jas_image_width (jasper_image) || + jas_image_cmptheight(jasper_image, c) != jas_image_height(jasper_image )) { + qDebug("The Qt JPEG 2000 reader does not support images where " + "component geometry differs from image geometry"); + printMetadata(jasper_image); + decodeOk = false; + } + } + if (!decodeOk) + return false; + + // At this point, the colorspace should be either RGB or grayscale, + // and each component should have eight bits of precision and + // no unsupported geometry. + //printMetadata(jasper_image); + + // Get color components + jasperColorspaceFamily = jas_clrspc_fam(jas_image_clrspc(jasper_image)); + if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) { + if (jasNumComponents > 4) + qDebug("JPEG 2000 reader expected 3 or 4 components, got %d", + jasNumComponents); + + // Set up mapping from R,G,B -> component num. + colorComponentMapping[0] = jas_image_getcmptbytype(jasper_image, + JAS_IMAGE_CT_RGB_R); + colorComponentMapping[1] = jas_image_getcmptbytype(jasper_image, + JAS_IMAGE_CT_RGB_G); + colorComponentMapping[2] = jas_image_getcmptbytype(jasper_image, + JAS_IMAGE_CT_RGB_B); + qtNumComponents = 3; + } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) { + if (jasNumComponents > 2) + qDebug("JPEG 2000 reader expected 1 or 2 components, got %d", + jasNumComponents); + colorComponentMapping[0] = jas_image_getcmptbytype(jasper_image, + JAS_IMAGE_CT_COLOR(JAS_IMAGE_CT_GRAY_Y)); + qtNumComponents = 1; + } else { + printColorSpaceError(); + return false; + } + + // Get alpha component if one exists. Due to the lack of test images, + // loading images with alpha channels is a bit untested. It works + // with images saved with this implementation though. + const int posibleAlphaComponent1 = 3; + const int posibleAlphaComponent2 = 48; + + if (jasNumComponents == qtNumComponents + 1) { + colorComponentMapping[qtNumComponents] = jas_image_getcmptbytype(jasper_image, posibleAlphaComponent1); + if (colorComponentMapping[qtNumComponents] < 0) { + colorComponentMapping[qtNumComponents] = jas_image_getcmptbytype(jasper_image, posibleAlphaComponent2); + } + if (colorComponentMapping[qtNumComponents] > 0) { + hasAlpha = true; + qtNumComponents++; + } + } + + // Check for missing components + for (c = 0; c < qtNumComponents; ++c) { + if (colorComponentMapping[c] < 0) { + qDebug("JPEG 2000 reader missing a color component"); + return false; + } + } + + // Create a QImage of the correct type + if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) { + qtImage = QImage(qtWidth, qtHeight, hasAlpha + ? QImage::Format_ARGB32 + : QImage::Format_RGB32); + } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) { + if (hasAlpha) { + qtImage = QImage(qtWidth, qtHeight, QImage::Format_ARGB32); + } else { + qtImage = QImage(qtWidth, qtHeight, QImage::Format_Indexed8); + qtImage.setColorCount(256); + for (int c = 0; c < 256; ++c) + qtImage.setColor(c, qRgb(c,c,c)); + } + } + + // Copy data + if (oddComponentSubsampling) { + // This is a hack really, copying of data with component subsampling + // != 1 doesn't fit in with the rest of the scanline copying framework. + copyJasperQtGeneric(); + } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) { + if (hasAlpha) + copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtRGBA); + else + copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtRGB); + } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) { + if (hasAlpha) + copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtGrayA); + else + copyJasperQt(&Jpeg2000JasperReader::copyScanlineJasperQtGray); + } + if (decodeOk) + *pImage = qtImage; + + return decodeOk; +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyJasperQtGeneric() +{ + // Create scanline data poinetrs + jas_matrix_t **jasperMatrix; + jas_seqent_t **jasperRow; + createJasperMatrix(jasperMatrix); + jasperRow = (jas_seqent_t**)malloc(jasNumComponents * sizeof(jas_seqent_t *)); + Q_CHECK_PTR(jasperRow); + + int imageY = 0; + for (int componentY = 0; componentY < computedComponentHeight; ++componentY) { + for (int c = 0; c < jasNumComponents; ++c) { + jas_image_readcmpt(jasper_image, colorComponentMapping[c], 0, + componentY, computedComponentWidth, 1, + jasperMatrix[c]); + jasperRow[c] = jas_matrix_getref(jasperMatrix[c], 0, 0); + } + for (int verticalSubsample = 0; + verticalSubsample < computedComponentVerticalSubsampling; + ++verticalSubsample) { + uchar *scanLineUchar = qtImage.scanLine(imageY); + QRgb *scanLineQRgb = reinterpret_cast<QRgb *>(scanLineUchar); + for (int componentX = 0; componentX < computedComponentWidth; + ++componentX) { + for (int horizontalSubsample = 0; + horizontalSubsample < + computedComponentHorizontalSubsampling; + ++horizontalSubsample) { + if (jasperColorspaceFamily == JAS_CLRSPC_FAM_RGB) { + if (hasAlpha) { + *scanLineQRgb++ = (jasperRow[3][componentX] << 24) | + (jasperRow[0][componentX] << 16) | + (jasperRow[1][componentX] << 8) | + jasperRow[2][componentX]; + } else { + *scanLineQRgb++ = (jasperRow[0][componentX] << 16) | + (jasperRow[1][componentX] << 8) | + jasperRow[2][componentX]; + } + } else if (jasperColorspaceFamily == JAS_CLRSPC_FAM_GRAY) { + if (hasAlpha) { + *scanLineQRgb++ = (jasperRow[1][componentX] << 24) | + (jasperRow[0][componentX] << 16) | + (jasperRow[0][componentX] << 8) | + jasperRow[0][componentX]; + } else { + *scanLineUchar++ = jasperRow[0][componentX]; + } + } + } + } + ++imageY; + } + } +} + +/*! + \internal + Copies data from Jasper to QImage. The scanlineCopier parameter specifies + which function to use for handling each scan line. +*/ +void Jpeg2000JasperReader::copyJasperQt(const ScanlineFunc scanlineCopier) +{ + // Create scanline data poinetrs + jas_matrix_t **jasperMatrix; + jas_seqent_t **jasperRow; + + createJasperMatrix(jasperMatrix); + jasperRow = (jas_seqent_t**)malloc(jasNumComponents * sizeof(jas_seqent_t *)); + Q_CHECK_PTR(jasperRow); + + for (int scanline = 0; scanline < qtHeight; ++scanline) { + for (int c = 0; c < jasNumComponents; ++c) { + jas_image_readcmpt(jasper_image, colorComponentMapping[c], 0, + scanline, qtWidth, 1, jasperMatrix[c]); + jasperRow[c] = jas_matrix_getref(jasperMatrix[c], 0, 0); + } + (this->*scanlineCopier)(jasperRow, qtImage.scanLine(scanline)); + } + + freeJasperMatrix(jasperMatrix); + free(jasperRow); +} + +/*! + \internal + Copies RGB data from Jasper to a 32-bit QImage. +*/ +void Jpeg2000JasperReader::copyScanlineJasperQtRGB( + jas_seqent_t ** const jasperRow, uchar *qtScanLine) +{ + QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine); + for (int c = 0; c < qtWidth; ++c) { + *scanLine++ = (0xFF << 24) | + (jasperRow[0][c] << 16) | + (jasperRow[1][c] << 8) | + jasperRow[2][c]; + } +} + +/*! + \internal + Copies RGBA data from Jasper to a 32-bit QImage. +*/ +void Jpeg2000JasperReader::copyScanlineJasperQtRGBA(jas_seqent_t ** const jasperRow, uchar *qtScanLine) +{ + QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine); + for (int c = 0; c < qtWidth; ++c) { + *scanLine++ = (jasperRow[3][c] << 24) | + (jasperRow[0][c] << 16) | + (jasperRow[1][c] << 8) | + jasperRow[2][c]; + } +} + +/*! + \internal + Copies data from a grayscale image to an 8-bit QImage. +*/ +void Jpeg2000JasperReader::copyScanlineJasperQtGray(jas_seqent_t ** const jasperRow, uchar *qtScanLine) +{ + for (int c = 0; c < qtWidth; ++c) { + // *qtScanLine++ = (jasperRow[0][c] >> (jasComponentPrecicion[0] - 8)); + *qtScanLine++ = jasperRow[0][c]; + } +} + +/*! + \internal + Copies data from a grayscale image to a 32-bit QImage. + Note that in this case we use an 32-bit image for grayscale data, since the + alpha value is per-pixel, not per-color (per-color alpha is supported by + 8-bit QImage). +*/ +void Jpeg2000JasperReader::copyScanlineJasperQtGrayA(jas_seqent_t ** const jasperRow, uchar *qtScanLine) +{ + QRgb *scanLine = reinterpret_cast<QRgb *>(qtScanLine); + for (int c = 0; c < qtWidth; ++c) { + *scanLine++ = (jasperRow[1][c] << 24) | + (jasperRow[0][c] << 16) | + (jasperRow[0][c] << 8) | + jasperRow[0][c]; + } +} + +/*! + Opens the file data and attempts to decode it using the Jasper library. + Returns true on success, false on failure. + + 32-bit and color mapped color images are encoded as RGB images, + color mapped grayscale images are encoded as grayscale images +*/ +bool Jpeg2000JasperReader::write(const QImage &image, int quality) +{ + if (!jasperOk) + return false; + + qtImage = image; + + qtHeight = qtImage.height(); + qtWidth = qtImage.width(); + qtDepth = qtImage.depth(); + + if (qtDepth == 32) { // RGB(A) + jasper_image = newRGBAImage(qtWidth, qtHeight, qtImage.hasAlphaChannel()); + if (qtImage.hasAlphaChannel()) + copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperRGBA); + else + copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperRGB); + } else if (qtDepth == 8) { + // Color mapped grayscale + if (qtImage.allGray()) { + jasper_image = newGrayscaleImage(qtWidth, qtHeight, qtImage.hasAlphaChannel()); + if (qtImage.hasAlphaChannel()) + copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscaleA); + else + copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscale); + } else { + // Color mapped color + jasper_image = newRGBAImage(qtWidth, qtHeight, qtImage.hasAlphaChannel()); + if (qtImage.hasAlphaChannel()) + copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapRGBA); + else + copyQtJasper(&Jpeg2000JasperReader::copyScanlineQtJasperColormapRGB); + } + } else { + qDebug("Unable to handle color depth %d", qtDepth); + return false; + } + + int fmtid; + if (format == Jp2Format) + fmtid = jas_image_strtofmt(const_cast<char*>("jp2")); + else /* if (format == J2cFormat) */ + // JasPer refers to the code stream format as jpc + fmtid = jas_image_strtofmt(const_cast<char*>("jpc")); + + const int minQuality = 0; + const int maxQuality = 100; + + if (quality == -1) + quality = 100; + if (quality <= minQuality) + quality = minQuality; + if (quality > maxQuality) + quality = maxQuality; + + // Qt specifies quality as an integer in the range 0..100. Jasper specifies + // compression rate as an real in the range 0..1, where 1 corresponds to no + // compression. Computing the rate from quality is difficult, large images + // get better image quality than small images at the same rate. If the rate + // is too low, Jasper will generate a completely black image. + // minirate is the smallest safe rate value. + const double minRate = 0.001; + + // maxRate specifies maximum target rate, which give the minimum amount + // of compression. Tests show that maxRates higer than 0.3 give no + // additional image quality for most images. Large images could use an even + // smaller maxRate value. + const double maxRate = 0.3; + + // Set jasperRate to a value in the range minRate..maxRate. Distribute the + // quality steps more densely at the lower end if the rate scale. + const double jasperRate = minRate + pow((double(quality) / double(maxQuality)), 2) * maxRate; + + // The Jasper format string contains two options: + // rate: rate=x + // lossy/lossless compression : mode=real/mode=int + QString jasperFormatString; + + // If quality is not maxQuality, we set lossy encoding. + // (lossless is default) + if (quality != maxQuality) { + jasperFormatString += QLatin1String("mode=real"); + jasperFormatString += QString(QLatin1String(" rate=%1")).arg(jasperRate); + } + + // Open an empty jasper stream that grows automatically + jas_stream_t * memory_stream = jas_stream_memopen(0, -1); + + // Jasper wants a non-const string. + char *str = qstrdup(jasperFormatString.toLatin1().constData()); + jas_image_encode(jasper_image, memory_stream, fmtid, str); + delete[] str; + jas_stream_flush(memory_stream); + + // jas_stream_t::obj_ is a void* which points to the stream implementation, + // e.g a file stream or a memory stream. But in our case we know that it is + // a memory stream since we created the object, so we just reiterpret_cast + // here.. + char *buffer = reinterpret_cast<char *>(reinterpret_cast<jas_stream_memobj_t*>(memory_stream->obj_)->buf_); + qint64 length = jas_stream_length(memory_stream); + ioDevice->write(buffer, length); + + jas_stream_close(memory_stream); + jas_image_destroy(jasper_image); + + return true; +} + +/*! + \internal + Copies data from qtImage to JasPer. The scanlineCopier parameter specifies + which function to use for handling each scan line. +*/ +void Jpeg2000JasperReader::copyQtJasper(const ScanlineFuncWrite scanlinecopier) +{ + // Create jasper matrix for holding one scanline + jas_matrix_t **jasperMatrix; + createJasperMatrix(jasperMatrix); + + for (int scanline = 0; scanline < qtHeight; ++scanline) { + (this->*scanlinecopier)(jasperMatrix, qtImage.scanLine(scanline)); + + // Write a scanline of data to jasper_image + for (int c = 0; c < jasNumComponents; ++c) + jas_image_writecmpt(jasper_image, c, 0, scanline, qtWidth, 1, + jasperMatrix[c]); + } + freeJasperMatrix(jasperMatrix); +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyScanlineQtJasperRGB(jas_matrix_t ** jasperRow, + uchar *qtScanLine) +{ + QRgb *scanLineBuffer = reinterpret_cast<QRgb *>(qtScanLine); + for (int col = 0; col < qtWidth; ++col) { + jas_matrix_set(jasperRow[0], 0, col, (*scanLineBuffer & 0xFF0000) >> 16); + jas_matrix_set(jasperRow[1], 0, col, (*scanLineBuffer & 0x00FF00) >> 8); + jas_matrix_set(jasperRow[2], 0, col, *scanLineBuffer & 0x0000FF); + ++scanLineBuffer; + } +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyScanlineQtJasperRGBA(jas_matrix_t ** jasperRow, + uchar *qtScanLine) +{ + QRgb *scanLineBuffer = reinterpret_cast<QRgb *>(qtScanLine); + for (int col = 0; col < qtWidth; ++col) { + jas_matrix_set(jasperRow[3], 0, col, (*scanLineBuffer & 0xFF000000) >> 24); + jas_matrix_set(jasperRow[0], 0, col, (*scanLineBuffer & 0x00FF0000) >> 16); + jas_matrix_set(jasperRow[1], 0, col, (*scanLineBuffer & 0x0000FF00) >> 8); + jas_matrix_set(jasperRow[2], 0, col, *scanLineBuffer & 0x000000FF); + ++scanLineBuffer; + } +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyScanlineQtJasperColormapRGB(jas_matrix_t ** jasperRow, + uchar *qtScanLine) +{ + for (int col = 0; col < qtWidth; ++col) { + QRgb color = qtImage.color(*qtScanLine); + jas_matrix_set(jasperRow[0], 0, col, qRed(color)); + jas_matrix_set(jasperRow[1], 0, col, qGreen(color)); + jas_matrix_set(jasperRow[2], 0, col, qBlue(color)); + ++qtScanLine; + } +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyScanlineQtJasperColormapRGBA(jas_matrix_t ** jasperRow, + uchar *qtScanLine) +{ + for (int col = 0; col < qtWidth; ++col) { + QRgb color = qtImage.color(*qtScanLine); + jas_matrix_set(jasperRow[0], 0, col, qRed(color)); + jas_matrix_set(jasperRow[1], 0, col, qGreen(color)); + jas_matrix_set(jasperRow[2], 0, col, qBlue(color)); + jas_matrix_set(jasperRow[3], 0, col, qAlpha(color)); + ++qtScanLine; + } +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscale(jas_matrix_t ** jasperRow, + uchar *qtScanLine) +{ + for (int col = 0; col < qtWidth; ++col) { + QRgb color = qtImage.color(*qtScanLine); + jas_matrix_set(jasperRow[0], 0, col, qGray(color)); + ++qtScanLine; + } +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::copyScanlineQtJasperColormapGrayscaleA(jas_matrix_t ** jasperRow, + uchar *qtScanLine) +{ + for (int col = 0; col < qtWidth; ++col) { + QRgb color = qtImage.color(*qtScanLine); + jas_matrix_set(jasperRow[0], 0, col, qGray(color)); + jas_matrix_set(jasperRow[1], 0, col, qAlpha(color)); + ++qtScanLine; + } +} + +/*! + \internal + Attempts to change the color space for the image to wantedColorSpace using + the JasPer library +*/ +bool Jpeg2000JasperReader::attemptColorspaceChange(int wantedColorSpace) +{ + //qDebug("Attemting color space change"); + jas_cmprof_t *outprof; + if (!(outprof = jas_cmprof_createfromclrspc(wantedColorSpace))) + return false; + + jas_image_t *newimage; + if (!(newimage = jas_image_chclrspc(jasper_image, outprof, + JAS_CMXFORM_INTENT_PER))) { + jas_cmprof_destroy(outprof); + return false; + } + jas_image_destroy(jasper_image); + jas_cmprof_destroy(outprof); + jasper_image = newimage; + return true; +} + +/*! + \internal + Set up a component with parameters suitable for storing a QImage. +*/ +jas_image_cmptparm_t Jpeg2000JasperReader::createComponentMetadata( + const int width, const int height) +{ + jas_image_cmptparm_t param; + param.tlx = 0; + param.tly = 0; + param.hstep = 1; + param.vstep = 1; + param.width = width; + param.height = height; + param.prec = 8; + param.sgnd = 0; + return param; +} + +/*! + \internal + Create a new RGB JasPer image with a possible alpha channel. +*/ +jas_image_t* Jpeg2000JasperReader::newRGBAImage(const int width, + const int height, bool alpha) +{ + jasNumComponents = alpha ? 4 : 3; + jas_image_cmptparm_t *params = new jas_image_cmptparm_t[jasNumComponents]; + jas_image_cmptparm_t param = createComponentMetadata(width, height); + for (int c=0; c < jasNumComponents; c++) + params[c] = param; + jas_image_t *newImage = jas_image_create(jasNumComponents, params, + JAS_CLRSPC_SRGB); + + jas_image_setcmpttype(newImage, 0, JAS_IMAGE_CT_RGB_R); + jas_image_setcmpttype(newImage, 1, JAS_IMAGE_CT_RGB_G); + jas_image_setcmpttype(newImage, 2, JAS_IMAGE_CT_RGB_B); + + /* + It is unclear how one stores opacity(alpha) components with JasPer, + the following seems to have no effect. The opacity component gets + type id 3 or 48 depending jp2 or j2c format no matter what one puts + in here. + + The symbols are defined as follows: + #define JAS_IMAGE_CT_RGB_R 0 + #define JAS_IMAGE_CT_RGB_G 1 + #define JAS_IMAGE_CT_RGB_B 2 + #define JAS_IMAGE_CT_OPACITY 0x7FFF + */ + if (alpha) + jas_image_setcmpttype(newImage, 3, JAS_IMAGE_CT_OPACITY); + delete[] params; + return newImage; +} + +/*! + \internal + Create a new RGB JasPer image with a possible alpha channel. +*/ +jas_image_t *Jpeg2000JasperReader::newGrayscaleImage(const int width, + const int height, + bool alpha) +{ + jasNumComponents = alpha ? 2 : 1; + jas_image_cmptparm_t param = createComponentMetadata(width, height); + jas_image_t *newImage = jas_image_create(1, ¶m, JAS_CLRSPC_SGRAY); + + jas_image_setcmpttype(newImage, 0, JAS_IMAGE_CT_GRAY_Y); + + // See corresponding comment for newRGBAImage. + if (alpha) + jas_image_setcmpttype(newImage, 1, JAS_IMAGE_CT_OPACITY); + return newImage; +} + +/*! + \internal + Allocate data structures that hold image data during transfer from the + JasPer data structures to QImage. +*/ +bool Jpeg2000JasperReader::createJasperMatrix(jas_matrix_t **&matrix) +{ + matrix = (jas_matrix_t**)malloc(jasNumComponents * sizeof(jas_matrix_t *)); + for (int c = 0; c < jasNumComponents; ++c) + matrix[c] = jas_matrix_create(1, qtWidth); + return true; +} + +/*! + \internal + Free data structures that hold image data during transfer from the + JasPer data structures to QImage. +*/ +bool Jpeg2000JasperReader::freeJasperMatrix(jas_matrix_t **matrix) +{ + for (int c = 0; c < jasNumComponents; ++c) + jas_matrix_destroy(matrix[c]); + free(matrix); + return false; +} + +/*! + \internal +*/ +void Jpeg2000JasperReader::printColorSpaceError() +{ + QString colorspaceFamily, colorspaceSpecific; + decodeColorSpace(jas_image_clrspc(jasper_image), colorspaceFamily, + colorspaceSpecific); + qDebug("Jpeg2000 decoder is not able to handle color space %s - %s", + qPrintable(colorspaceFamily), qPrintable(colorspaceSpecific)); +} +/*! + \internal +*/ +bool Jpeg2000JasperReader::decodeColorSpace(int clrspc, QString &family, + QString &specific) +{ + int fam = jas_clrspc_fam(clrspc); + int mbr = jas_clrspc_mbr(clrspc); + + switch (fam) { + case 0: family = QLatin1String("JAS_CLRSPC_FAM_UNKNOWN"); break; + case 1: family = QLatin1String("JAS_CLRSPC_FAM_XYZ"); break; + case 2: family = QLatin1String("JAS_CLRSPC_FAM_LAB"); break; + case 3: family = QLatin1String("JAS_CLRSPC_FAM_GRAY"); break; + case 4: family = QLatin1String("JAS_CLRSPC_FAM_RGB"); break; + case 5: family = QLatin1String("JAS_CLRSPC_FAM_YCBCR"); break; + default: family = QLatin1String("Unknown"); return false; + } + + switch (mbr) { + case 0: + switch (fam) { + case 1: specific = QLatin1String("JAS_CLRSPC_CIEXYZ"); break; + case 2: specific = QLatin1String("JAS_CLRSPC_CIELAB"); break; + case 3: specific = QLatin1String("JAS_CLRSPC_SGRAY"); break; + case 4: specific = QLatin1String("JAS_CLRSPC_SRGB"); break; + case 5: specific = QLatin1String("JAS_CLRSPC_SYCBCR"); break; + default: specific = QLatin1String("Unknown"); return false; + } + break; + case 1: + switch (fam) { + case 3: specific = QLatin1String("JAS_CLRSPC_GENGRAY"); break; + case 4: specific = QLatin1String("JAS_CLRSPC_GENRGB"); break; + case 5: specific = QLatin1String("JAS_CLRSPC_GENYCBCR"); break; + default: specific = QLatin1String("Unknown"); return false; + } + break; + default: + return false; + } + return true; +} +/*! + \internal +*/ +void Jpeg2000JasperReader::printMetadata(jas_image_t *image) +{ +#ifndef QT_NO_DEBUG + // jas_image_cmptparm_t param + qDebug("Image width: %d", jas_image_width(image)); + qDebug("Image height: %d", jas_image_height(image)); + qDebug("Coordinates on reference grid: (%d,%d) (%d,%d)", + jas_image_tlx(image), jas_image_tly(image), + jas_image_brx(image), jas_image_bry(image)); + qDebug("Number of image components: %d", jas_image_numcmpts(image)); + + QString colorspaceFamily; + QString colorspaceSpecific; + decodeColorSpace(jas_image_clrspc(image), colorspaceFamily, colorspaceSpecific); + qDebug("Color model (space): %d, %s - %s", jas_image_clrspc(image), + qPrintable(colorspaceFamily), qPrintable(colorspaceSpecific)); + + qDebug("Component metadata:"); + + for (int c = 0; c < jas_image_numcmpts(image); ++c) { + qDebug("Component %d:", c); + qDebug(" Component type: %d", jas_image_cmpttype(image, c)); + qDebug(" Width: %d", jas_image_cmptwidth(image, c)); + qDebug(" Height: %d", jas_image_cmptheight(image, c)); + qDebug(" Signedness: %d", jas_image_cmptsgnd(image, c)); + qDebug(" Precision: %d", jas_image_cmptprec(image, c)); + qDebug(" Horizontal subsampling factor: %d",jas_image_cmpthstep(image, c)); + qDebug(" Vertical subsampling factor: %d", jas_image_cmptvstep(image, c)); + qDebug(" Coordinates on reference grid: (%d,%d) (%d,%d)", + jas_image_cmpttlx(image, c), jas_image_cmpttly(image, c), + jas_image_cmptbrx(image, c), jas_image_cmptbry(image, c)); + } +#endif +} + +QT_END_NAMESPACE diff --git a/src/plugins/imageformats/jp2/qjp2handler.pri b/src/plugins/imageformats/jp2/qjp2handler.pri new file mode 100644 index 00000000..539daaab --- /dev/null +++ b/src/plugins/imageformats/jp2/qjp2handler.pri @@ -0,0 +1,10 @@ +# common to plugin and built-in forms +INCLUDEPATH *= $$PWD +HEADERS += $$PWD/qjp2handler_p.h +SOURCES += $$PWD/qjp2handler.cpp +config_jasper { + msvc: LIBS += libjasper.lib + else: LIBS += -ljasper +} else { + include($$PWD/../../../3rdparty/jasper.pri) +} diff --git a/src/plugins/imageformats/jp2/qjp2handler_p.h b/src/plugins/imageformats/jp2/qjp2handler_p.h new file mode 100644 index 00000000..4895bb88 --- /dev/null +++ b/src/plugins/imageformats/jp2/qjp2handler_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Petroules Corporation. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the JP2 plugins in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QJP2HANDLER_H +#define QJP2HANDLER_H + +#include <QtCore/qscopedpointer.h> +#include <QtGui/qimageiohandler.h> + +QT_BEGIN_NAMESPACE + +class QImage; +class QByteArray; +class QIODevice; +class QVariant; +class QJp2HandlerPrivate; + +class QJp2Handler : public QImageIOHandler +{ +public: + QJp2Handler(); + virtual ~QJp2Handler(); + static bool canRead(QIODevice *iod, QByteArray *subType); + virtual bool canRead() const; + virtual bool read(QImage *image); + virtual bool write(const QImage &image); + virtual QVariant option(ImageOption option) const; + virtual void setOption(ImageOption option, const QVariant &value); + virtual bool supportsOption(ImageOption option) const; + virtual QByteArray name() const; + +private: + Q_DECLARE_PRIVATE(QJp2Handler) + QScopedPointer<QJp2HandlerPrivate> d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QJP2HANDLER_P_H diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 109f216a..73f1014a 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -3,6 +3,7 @@ SUBDIRS = \ tga \ wbmp \ dds \ - icns + icns \ + jp2 contains(QT_CONFIG, system-zlib): SUBDIRS += mng tiff diff --git a/tests/auto/jp2/jp2.pro b/tests/auto/jp2/jp2.pro new file mode 100644 index 00000000..26c2f836 --- /dev/null +++ b/tests/auto/jp2/jp2.pro @@ -0,0 +1,8 @@ +TARGET = tst_qjp2 + +QT = core gui testlib +CONFIG -= app_bundle +CONFIG += testcase + +SOURCES += tst_qjp2.cpp +RESOURCES += $$PWD/../../shared/images/jp2.qrc diff --git a/tests/auto/jp2/tst_qjp2.cpp b/tests/auto/jp2/tst_qjp2.cpp new file mode 100644 index 00000000..76087468 --- /dev/null +++ b/tests/auto/jp2/tst_qjp2.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Petroules Corporation. +** Contact: http://www.qt-project.org/legal +** +** This file is part of the MNG autotests in the Qt ImageFormats module. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtTest/QtTest> +#include <QtGui/QtGui> + +class tst_qjp2: public QObject +{ + Q_OBJECT + +private slots: + void readImage_data(); + void readImage(); +}; + +void tst_qjp2::readImage_data() +{ + QTest::addColumn<QString>("fileName"); + QTest::addColumn<QString>("referenceFileName"); + QTest::addColumn<QSize>("size"); + + QTest::newRow("logo") << QString("logo.jp2") << QString("logo.bmp") << QSize(498, 80); +} + +void tst_qjp2::readImage() +{ + QFETCH(QString, fileName); + QFETCH(QString, referenceFileName); + QFETCH(QSize, size); + + QString path = QString(":/jp2/") + fileName; + QImageReader reader(path); + QVERIFY(reader.canRead()); + QImage image = reader.read(); + QVERIFY(!image.isNull()); + QCOMPARE(image.size(), size); + + path = QString(":jp2/") + referenceFileName; + QImageReader referenceReader(path); + QVERIFY(referenceReader.canRead()); + QImage referenceImage = referenceReader.read(); + QVERIFY(!referenceImage.isNull()); + QCOMPARE(referenceImage.size(), size); + + QCOMPARE(image, referenceImage); +} + +QTEST_MAIN(tst_qjp2) +#include "tst_qjp2.moc" diff --git a/tests/shared/images/jp2.qrc b/tests/shared/images/jp2.qrc new file mode 100644 index 00000000..f9520766 --- /dev/null +++ b/tests/shared/images/jp2.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>jp2/logo.bmp</file> + <file>jp2/logo.jp2</file> + </qresource> +</RCC> diff --git a/tests/shared/images/jp2/logo.bmp b/tests/shared/images/jp2/logo.bmp new file mode 100644 index 0000000000000000000000000000000000000000..735ba1b3dc1b353e562b79b4c631d0ab0e8e5de6 GIT binary patch literal 119734 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 YJAY1dj>F*BpR9hpTK1!HC%*pw0fs{%3jhEB literal 0 HcmV?d00001 diff --git a/tests/shared/images/jp2/logo.jp2 b/tests/shared/images/jp2/logo.jp2 new file mode 100644 index 0000000000000000000000000000000000000000..f99343db6d9459cde4f7cbdf4d9144b973cc2d7b GIT binary patch literal 27570 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 literal 0 HcmV?d00001 -- GitLab