summaryrefslogtreecommitdiffstats
path: root/src/libs/dimg/loaders/jp2kloader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dimg/loaders/jp2kloader.cpp')
-rw-r--r--src/libs/dimg/loaders/jp2kloader.cpp715
1 files changed, 715 insertions, 0 deletions
diff --git a/src/libs/dimg/loaders/jp2kloader.cpp b/src/libs/dimg/loaders/jp2kloader.cpp
new file mode 100644
index 00000000..66351c25
--- /dev/null
+++ b/src/libs/dimg/loaders/jp2kloader.cpp
@@ -0,0 +1,715 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-06-14
+ * Description : A JPEG2000 IO file for DImg framework
+ *
+ * Copyright (C) 2006-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * This implementation use Jasper API
+ * library : http://www.ece.uvic.ca/~mdadams/jasper
+ * Other JPEG2000 encoder-decoder : http://www.openjpeg.org
+ *
+ * Others Linux JPEG2000 Loader implementation using Jasper:
+ * http://cvs.graphicsmagick.org/cgi-bin/cvsweb.cgi/GraphicsMagick/coders/jp2.c
+ * https://subversion.imagemagick.org/subversion/ImageMagick/trunk/coders/jp2.c
+ * http://svn.ghostscript.com:8080/jasper/trunk/src/appl/jasper.c
+ * http://websvn.kde.org/trunk/KDE/tdelibs/kimgio/jp2.cpp
+ *
+ * This program is free software; you can redistribute it
+ * and/or modify it under the terms of the GNU General
+ * Public License as published by the Free Software Foundation;
+ * either version 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * ============================================================ */
+
+// This line must be commented to prevent any latency time
+// when we use threaded image loader interface for each image
+// files io. Uncomment this line only for debugging.
+//#define ENABLE_DEBUG_MESSAGES
+
+// C ANSI includes.
+
+extern "C"
+{
+#if !defined(__STDC_LIMIT_MACROS)
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+}
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqcstring.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "jp2kloader.h"
+
+namespace Digikam
+{
+
+JP2KLoader::JP2KLoader(DImg* image)
+ : DImgLoader(image)
+{
+ m_hasAlpha = false;
+ m_sixteenBit = false;
+}
+
+bool JP2KLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ readMetadata(filePath, DImg::JPEG);
+
+ FILE *file = fopen(TQFile::encodeName(filePath), "rb");
+ if (!file)
+ return false;
+
+ unsigned char header[9];
+
+ if (fread(&header, 9, 1, file) != 1)
+ {
+ fclose(file);
+ return false;
+ }
+
+ unsigned char jp2ID[5] = { 0x6A, 0x50, 0x20, 0x20, 0x0D, };
+ unsigned char jpcID[2] = { 0xFF, 0x4F };
+
+ if (memcmp(&header[4], &jp2ID, 5) != 0 &&
+ memcmp(&header, &jpcID, 2) != 0)
+ {
+ // not a jpeg2000 file
+ fclose(file);
+ return false;
+ }
+
+ fclose(file);
+
+ // -------------------------------------------------------------------
+ // Initialize JPEG 2000 API.
+
+ long i, x, y;
+ int components[4];
+ unsigned int maximum_component_depth, scale[4], x_step[4], y_step[4];
+ unsigned long number_components;
+
+ jas_image_t *jp2_image = 0;
+ jas_stream_t *jp2_stream = 0;
+ jas_matrix_t *pixels[4];
+
+ int init = jas_init();
+ if (init != 0)
+ {
+ DDebug() << "Unable to init JPEG2000 decoder" << endl;
+ return false;
+ }
+
+ jp2_stream = jas_stream_fopen(TQFile::encodeName(filePath), "rb");
+ if (jp2_stream == 0)
+ {
+ DDebug() << "Unable to open JPEG2000 stream" << endl;
+ return false;
+ }
+
+ jp2_image = jas_image_decode(jp2_stream, -1, 0);
+ if (jp2_image == 0)
+ {
+ jas_stream_close(jp2_stream);
+ DDebug() << "Unable to decode JPEG2000 image" << endl;
+ return false;
+ }
+
+ jas_stream_close(jp2_stream);
+
+ // some pseudo-progress
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // -------------------------------------------------------------------
+ // Check color space.
+
+ switch (jas_clrspc_fam(jas_image_clrspc(jp2_image)))
+ {
+ case JAS_CLRSPC_FAM_RGB:
+ {
+ components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_R);
+ components[1] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_G);
+ components[2] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_RGB_B);
+ if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
+ {
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error parsing JPEG2000 image : Missing Image Channel" << endl;
+ return false;
+ }
+
+ number_components = 3;
+ components[3] = jas_image_getcmptbytype(jp2_image, 3);
+ if (components[3] > 0)
+ {
+ m_hasAlpha = true;
+ number_components++;
+ }
+ break;
+ }
+ case JAS_CLRSPC_FAM_GRAY:
+ {
+ components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_GRAY_Y);
+ if (components[0] < 0)
+ {
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error parsing JP2000 image : Missing Image Channel" << endl;
+ return false;
+ }
+ number_components=1;
+ break;
+ }
+ case JAS_CLRSPC_FAM_YCBCR:
+ {
+ components[0] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_Y);
+ components[1] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_CB);
+ components[2] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_YCBCR_CR);
+ if ((components[0] < 0) || (components[1] < 0) || (components[2] < 0))
+ {
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error parsing JP2000 image : Missing Image Channel" << endl;
+ return false;
+ }
+ number_components = 3;
+ components[3] = jas_image_getcmptbytype(jp2_image, JAS_IMAGE_CT_UNKNOWN);
+ if (components[3] > 0)
+ {
+ m_hasAlpha = true;
+ number_components++;
+ }
+ // FIXME : image->colorspace=YCbCrColorspace;
+ break;
+ }
+ default:
+ {
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error parsing JP2000 image : Colorspace Model Is Not Supported" << endl;
+ return false;
+ }
+ }
+
+ // -------------------------------------------------------------------
+ // Check image geometry.
+
+ imageWidth() = jas_image_width(jp2_image);
+ imageHeight() = jas_image_height(jp2_image);
+
+ for (i = 0; i < (long)number_components; i++)
+ {
+ if ((((jas_image_cmptwidth(jp2_image, components[i])*
+ jas_image_cmpthstep(jp2_image, components[i])) != (long)imageWidth())) ||
+ (((jas_image_cmptheight(jp2_image, components[i])*
+ jas_image_cmptvstep(jp2_image, components[i])) != (long)imageHeight())) ||
+ (jas_image_cmpttlx(jp2_image, components[i]) != 0) ||
+ (jas_image_cmpttly(jp2_image, components[i]) != 0) ||
+ (jas_image_cmptsgnd(jp2_image, components[i]) != false))
+ {
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error parsing JPEG2000 image : Irregular Channel Geometry Not Supported" << endl;
+ return false;
+ }
+ x_step[i] = jas_image_cmpthstep(jp2_image, components[i]);
+ y_step[i] = jas_image_cmptvstep(jp2_image, components[i]);
+ }
+
+ // -------------------------------------------------------------------
+ // Convert image data.
+
+ m_hasAlpha = number_components > 3;
+ maximum_component_depth = 0;
+
+ for (i = 0; i < (long)number_components; i++)
+ {
+ maximum_component_depth = TQMAX(jas_image_cmptprec(jp2_image,components[i]),
+ (long)maximum_component_depth);
+ pixels[i] = jas_matrix_create(1, ((unsigned int)imageWidth())/x_step[i]);
+ if (!pixels[i])
+ {
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error decoding JPEG2000 image data : Memory Allocation Failed" << endl;
+ return false;
+ }
+ }
+
+ if (maximum_component_depth > 8)
+ m_sixteenBit = true;
+
+ for (i = 0 ; i < (long)number_components ; i++)
+ {
+ scale[i] = 1;
+ int prec = jas_image_cmptprec(jp2_image, components[i]);
+ if (m_sixteenBit && prec < 16)
+ scale[i] = (1 << (16 - jas_image_cmptprec(jp2_image, components[i])));
+ }
+
+ uchar* data = 0;
+ if (m_sixteenBit) // 16 bits image.
+ data = new uchar[imageWidth()*imageHeight()*8];
+ else
+ data = new uchar[imageWidth()*imageHeight()*4];
+
+ if (!data)
+ {
+ DDebug() << "Error decoding JPEG2000 image data : Memory Allocation Failed" << endl;
+ jas_image_destroy(jp2_image);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+ return false;
+ }
+
+ uint checkPoint = 0;
+ uchar *dst = data;
+ unsigned short *dst16 = (unsigned short *)data;
+
+ for (y = 0 ; y < (long)imageHeight() ; y++)
+ {
+ for (i = 0 ; i < (long)number_components; i++)
+ {
+ int ret = jas_image_readcmpt(jp2_image, (short)components[i], 0,
+ ((unsigned int) y) / y_step[i],
+ ((unsigned int) imageWidth()) / x_step[i],
+ 1, pixels[i]);
+ if (ret != 0)
+ {
+ DDebug() << "Error decoding JPEG2000 image data" << endl;
+ delete [] data;
+ jas_image_destroy(jp2_image);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+ return false;
+ }
+ }
+
+ switch (number_components)
+ {
+ case 1: // Grayscale.
+ {
+ for (x = 0 ; x < (long)imageWidth() ; x++)
+ {
+ dst[0] = (uchar)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0]));
+ dst[1] = dst[0];
+ dst[2] = dst[0];
+ dst[3] = 0xFF;
+
+ dst += 4;
+ }
+ break;
+ }
+ case 3: // RGB.
+ {
+ if (!m_sixteenBit) // 8 bits image.
+ {
+ for (x = 0 ; x < (long)imageWidth() ; x++)
+ {
+ // Blue
+ dst[0] = (uchar)(scale[2]*jas_matrix_getv(pixels[2], x/x_step[2]));
+ // Green
+ dst[1] = (uchar)(scale[1]*jas_matrix_getv(pixels[1], x/x_step[1]));
+ // Red
+ dst[2] = (uchar)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0]));
+ // Alpha
+ dst[3] = 0xFF;
+
+ dst += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ for (x = 0 ; x < (long)imageWidth() ; x++)
+ {
+ // Blue
+ dst16[0] = (unsigned short)(scale[2]*jas_matrix_getv(pixels[2], x/x_step[2]));
+ // Green
+ dst16[1] = (unsigned short)(scale[1]*jas_matrix_getv(pixels[1], x/x_step[1]));
+ // Red
+ dst16[2] = (unsigned short)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0]));
+ // Alpha
+ dst16[3] = 0xFFFF;
+
+ dst16 += 4;
+ }
+ }
+
+ break;
+ }
+ case 4: // RGBA.
+ {
+ if (!m_sixteenBit) // 8 bits image.
+ {
+ for (x = 0 ; x < (long)imageWidth() ; x++)
+ {
+ // Blue
+ dst[0] = (uchar)(scale[2] * jas_matrix_getv(pixels[2], x/x_step[2]));
+ // Green
+ dst[1] = (uchar)(scale[1] * jas_matrix_getv(pixels[1], x/x_step[1]));
+ // Red
+ dst[2] = (uchar)(scale[0] * jas_matrix_getv(pixels[0], x/x_step[0]));
+ // Alpha
+ dst[3] = (uchar)(scale[3] * jas_matrix_getv(pixels[3], x/x_step[3]));
+
+ dst += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ for (x = 0 ; x < (long)imageWidth() ; x++)
+ {
+ // Blue
+ dst16[0] = (unsigned short)(scale[2]*jas_matrix_getv(pixels[2], x/x_step[2]));
+ // Green
+ dst16[1] = (unsigned short)(scale[1]*jas_matrix_getv(pixels[1], x/x_step[1]));
+ // Red
+ dst16[2] = (unsigned short)(scale[0]*jas_matrix_getv(pixels[0], x/x_step[0]));
+ // Alpha
+ dst16[3] = (unsigned short)(scale[3]*jas_matrix_getv(pixels[3], x/x_step[3]));
+
+ dst16 += 4;
+ }
+ }
+
+ break;
+ }
+ }
+
+ // use 0-10% and 90-100% for pseudo-progress
+ if (observer && y >= (long)checkPoint)
+ {
+ checkPoint += granularity(observer, y, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ jas_image_destroy(jp2_image);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)imageHeight()) )));
+ }
+ }
+
+ // -------------------------------------------------------------------
+ // Get ICC color profile.
+
+ jas_iccprof_t *icc_profile = 0;
+ jas_stream_t *icc_stream = 0;
+ jas_cmprof_t *cm_profile = 0;
+
+ cm_profile = jas_image_cmprof(jp2_image);
+ if (cm_profile != 0)
+ icc_profile = jas_iccprof_createfromcmprof(cm_profile);
+
+ if (icc_profile != 0)
+ {
+ icc_stream = jas_stream_memopen(NULL, 0);
+
+ if (icc_stream != 0)
+ {
+ if (jas_iccprof_save(icc_profile, icc_stream) == 0)
+ {
+ if (jas_stream_flush(icc_stream) == 0)
+ {
+ TQMap<int, TQByteArray>& metaData = imageMetaData();
+ jas_stream_memobj_t *blob = (jas_stream_memobj_t *) icc_stream->obj_;
+ TQByteArray profile_rawdata(blob->len_);
+ memcpy(profile_rawdata.data(), blob->buf_, blob->len_);
+ metaData.insert(DImg::ICC, profile_rawdata);
+ jas_stream_close(icc_stream);
+ }
+ }
+ }
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageSetAttribute("format", "JP2K");
+ imageData() = data;
+
+ jas_image_destroy(jp2_image);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+
+ return true;
+}
+
+bool JP2KLoader::save(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ FILE *file = fopen(TQFile::encodeName(filePath), "wb");
+ if (!file)
+ return false;
+
+ fclose(file);
+
+ // -------------------------------------------------------------------
+ // Initialize JPEG 2000 API.
+
+ long i, x, y;
+ unsigned long number_components;
+
+ jas_image_t *jp2_image = 0;
+ jas_stream_t *jp2_stream = 0;
+ jas_matrix_t *pixels[4];
+ jas_image_cmptparm_t component_info[4];
+
+ int init = jas_init();
+ if (init != 0)
+ {
+ DDebug() << "Unable to init JPEG2000 decoder" << endl;
+ return false;
+ }
+
+ jp2_stream = jas_stream_fopen(TQFile::encodeName(filePath), "wb");
+ if (jp2_stream == 0)
+ {
+ DDebug() << "Unable to open JPEG2000 stream" << endl;
+ return false;
+ }
+
+ number_components = imageHasAlpha() ? 4 : 3;
+
+ for (i = 0 ; i < (long)number_components ; i++)
+ {
+ component_info[i].tlx = 0;
+ component_info[i].tly = 0;
+ component_info[i].hstep = 1;
+ component_info[i].vstep = 1;
+ component_info[i].width = imageWidth();
+ component_info[i].height = imageHeight();
+ component_info[i].prec = imageBitsDepth();
+ component_info[i].sgnd = false;
+ }
+
+ jp2_image = jas_image_create(number_components, component_info, JAS_CLRSPC_UNKNOWN);
+ if (jp2_image == 0)
+ {
+ jas_stream_close(jp2_stream);
+ DDebug() << "Unable to create JPEG2000 image" << endl;
+ return false;
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // -------------------------------------------------------------------
+ // Check color space.
+
+ if (number_components >= 3 ) // RGB & RGBA
+ {
+ // Alpha Channel
+ if (number_components == 4 )
+ jas_image_setcmpttype(jp2_image, 3, JAS_IMAGE_CT_OPACITY);
+
+ jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB);
+ jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R));
+ jas_image_setcmpttype(jp2_image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G));
+ jas_image_setcmpttype(jp2_image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B));
+ }
+
+ // -------------------------------------------------------------------
+ // Set ICC color profile.
+
+ // FIXME : doesn't work yet!
+
+ jas_cmprof_t *cm_profile = 0;
+ jas_iccprof_t *icc_profile = 0;
+
+ TQByteArray profile_rawdata = m_image->getICCProfil();
+
+ icc_profile = jas_iccprof_createfrombuf((uchar*)profile_rawdata.data(), profile_rawdata.size());
+ if (icc_profile != 0)
+ {
+ cm_profile = jas_cmprof_createfromiccprof(icc_profile);
+ if (cm_profile != 0)
+ {
+ jas_image_setcmprof(jp2_image, cm_profile);
+ }
+ }
+
+ // -------------------------------------------------------------------
+ // Convert to JPEG 2000 pixels.
+
+ for (i = 0 ; i < (long)number_components ; i++)
+ {
+ pixels[i] = jas_matrix_create(1, (unsigned int)imageWidth());
+ if (pixels[i] == 0)
+ {
+ for (x = 0 ; x < i ; x++)
+ jas_matrix_destroy(pixels[x]);
+
+ jas_image_destroy(jp2_image);
+ DDebug() << "Error encoding JPEG2000 image data : Memory Allocation Failed" << endl;
+ return false;
+ }
+ }
+
+ unsigned char* data = imageData();
+ unsigned char* pixel;
+ unsigned short r, g, b, a=0;
+ uint checkpoint = 0;
+
+ for (y = 0 ; y < (long)imageHeight() ; y++)
+ {
+ if (observer && y == (long)checkpoint)
+ {
+ checkpoint += granularity(observer, imageHeight(), 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ jas_image_destroy(jp2_image);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)imageHeight()) )));
+ }
+
+ for (x = 0 ; x < (long)imageWidth() ; x++)
+ {
+ pixel = &data[((y * imageWidth()) + x) * imageBytesDepth()];
+
+ if ( imageSixteenBit() ) // 16 bits image.
+ {
+ b = (unsigned short)(pixel[0]+256*pixel[1]);
+ g = (unsigned short)(pixel[2]+256*pixel[3]);
+ r = (unsigned short)(pixel[4]+256*pixel[5]);
+
+ if (imageHasAlpha())
+ a = (unsigned short)(pixel[6]+256*pixel[7]);
+ }
+ else // 8 bits image.
+ {
+ b = (unsigned short)pixel[0];
+ g = (unsigned short)pixel[1];
+ r = (unsigned short)pixel[2];
+
+ if (imageHasAlpha())
+ a = (unsigned short)(pixel[3]);
+ }
+
+ jas_matrix_setv(pixels[0], x, r);
+ jas_matrix_setv(pixels[1], x, g);
+ jas_matrix_setv(pixels[2], x, b);
+
+ if (number_components > 3)
+ jas_matrix_setv(pixels[3], x, a);
+ }
+
+ for (i = 0 ; i < (long)number_components ; i++)
+ {
+ int ret = jas_image_writecmpt(jp2_image, (short) i, 0, (unsigned int)y,
+ (unsigned int)imageWidth(), 1, pixels[i]);
+ if (ret != 0)
+ {
+ DDebug() << "Error encoding JPEG2000 image data" << endl;
+
+ jas_image_destroy(jp2_image);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+ return false;
+ }
+ }
+ }
+
+ TQVariant qualityAttr = imageGetAttribute("quality");
+ int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
+
+ if (quality < 0)
+ quality = 90;
+ if (quality > 100)
+ quality = 100;
+
+ TQString rate;
+ TQTextStream ts( &rate, IO_WriteOnly );
+
+ // NOTE: to have a lossless compression use quality=100.
+ // jp2_encode()::optstr:
+ // - rate=#B => the resulting file size is about # bytes
+ // - rate=0.0 .. 1.0 => the resulting file size is about the factor times
+ // the uncompressed size
+ ts << "rate=" << ( quality / 100.0F );
+
+ DDebug() << "JPEG2000 quality: " << quality << endl;
+ DDebug() << "JPEG2000 " << rate << endl;
+
+# if defined(JAS_VERSION_MAJOR) && (JAS_VERSION_MAJOR >= 3)
+ const jas_image_fmtinfo_t *jp2_fmtinfo = jas_image_lookupfmtbyname("jp2");
+ int ret = -1;
+ if (jp2_fmtinfo)
+ {
+ ret = jas_image_encode(jp2_image, jp2_stream, jp2_fmtinfo->id, rate.utf8().data());
+ }
+# else
+ int ret = jp2_encode(jp2_image, jp2_stream, rate.utf8().data());
+# endif
+
+ if (ret != 0)
+ {
+ DDebug() << "Unable to encode JPEG2000 image" << endl;
+
+ jas_image_destroy(jp2_image);
+ jas_stream_close(jp2_stream);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+
+ return false;
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageSetAttribute("savedformat", "JP2K");
+
+ saveMetadata(filePath);
+
+ jas_image_destroy(jp2_image);
+ jas_stream_close(jp2_stream);
+ for (i = 0 ; i < (long)number_components ; i++)
+ jas_matrix_destroy(pixels[i]);
+
+ jas_cleanup();
+
+ return true;
+}
+
+bool JP2KLoader::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+bool JP2KLoader::sixteenBit() const
+{
+ return m_sixteenBit;
+}
+
+} // NameSpace Digikam