summaryrefslogtreecommitdiffstats
path: root/src/libs/dimg/loaders
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dimg/loaders')
-rw-r--r--src/libs/dimg/loaders/Makefile.am21
-rw-r--r--src/libs/dimg/loaders/README42
-rw-r--r--src/libs/dimg/loaders/dimgloader.cpp200
-rw-r--r--src/libs/dimg/loaders/dimgloader.h97
-rw-r--r--src/libs/dimg/loaders/dimgloaderobserver.h67
-rw-r--r--src/libs/dimg/loaders/iccjpeg.c270
-rw-r--r--src/libs/dimg/loaders/iccjpeg.h101
-rw-r--r--src/libs/dimg/loaders/jp2kloader.cpp715
-rw-r--r--src/libs/dimg/loaders/jp2kloader.h69
-rw-r--r--src/libs/dimg/loaders/jp2ksettings.cpp139
-rw-r--r--src/libs/dimg/loaders/jp2ksettings.h67
-rw-r--r--src/libs/dimg/loaders/jpegloader.cpp676
-rw-r--r--src/libs/dimg/loaders/jpegloader.h80
-rw-r--r--src/libs/dimg/loaders/jpegsettings.cpp154
-rw-r--r--src/libs/dimg/loaders/jpegsettings.h63
-rw-r--r--src/libs/dimg/loaders/pngloader.cpp993
-rw-r--r--src/libs/dimg/loaders/pngloader.h73
-rw-r--r--src/libs/dimg/loaders/pngsettings.cpp102
-rw-r--r--src/libs/dimg/loaders/pngsettings.h60
-rw-r--r--src/libs/dimg/loaders/ppmloader.cpp178
-rw-r--r--src/libs/dimg/loaders/ppmloader.h59
-rw-r--r--src/libs/dimg/loaders/qimageloader.cpp126
-rw-r--r--src/libs/dimg/loaders/qimageloader.h57
-rw-r--r--src/libs/dimg/loaders/rawloader.cpp371
-rw-r--r--src/libs/dimg/loaders/rawloader.h86
-rw-r--r--src/libs/dimg/loaders/tiffloader.cpp806
-rw-r--r--src/libs/dimg/loaders/tiffloader.h76
-rw-r--r--src/libs/dimg/loaders/tiffsettings.cpp94
-rw-r--r--src/libs/dimg/loaders/tiffsettings.h60
29 files changed, 5902 insertions, 0 deletions
diff --git a/src/libs/dimg/loaders/Makefile.am b/src/libs/dimg/loaders/Makefile.am
new file mode 100644
index 00000000..62e96fe8
--- /dev/null
+++ b/src/libs/dimg/loaders/Makefile.am
@@ -0,0 +1,21 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdimgloaders.la
+
+libdimgloaders_la_SOURCES = dimgloader.cpp pngloader.cpp jpegloader.cpp tiffloader.cpp \
+ rawloader.cpp ppmloader.cpp qimageloader.cpp iccjpeg.c \
+ jp2kloader.cpp jpegsettings.cpp pngsettings.cpp \
+ tiffsettings.cpp jp2ksettings.cpp
+
+libdimgloaders_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) \
+ $(LIBJPEG) $(LIB_TIFF) $(LIB_PNG) $(LIB_JASPER)
+
+INCLUDES = $(all_includes) -I$(top_srcdir)/src/libs/dimg \
+ -I$(top_srcdir)/src/libs/dimg/filters \
+ -I$(top_srcdir)/src/libs/curves \
+ -I$(top_srcdir)/src/libs/levels \
+ -I$(top_srcdir)/src/libs/histogram \
+ -I$(top_srcdir)/src/libs/whitebalance \
+ -I$(top_srcdir)/src/libs/dmetadata \
+ -I$(top_srcdir)/src/digikam \
+ $(LIBKEXIV2_CFLAGS) $(LIBKDCRAW_CFLAGS)
diff --git a/src/libs/dimg/loaders/README b/src/libs/dimg/loaders/README
new file mode 100644
index 00000000..b77e9c4c
--- /dev/null
+++ b/src/libs/dimg/loaders/README
@@ -0,0 +1,42 @@
+---------------------------------------------------------------------------
+
+Native DIMG Loaders status (Gilles Caulier - 2006-11-29)
+
+Format Read Write ICC MetaData Thumb 8bits 16bits depency Remarks
+
+JPG Done Done Done Done TODO yes N.A libjpeg Metadata are EXIF/IPTC/XMP/ICC profil
+PNG Done Done Done Done N.A yes yes libpng Metadata are EXIF/IPTC/XMP/ICC profil
+TIF/EP Done Done Done Done TODO yes yes libtiff Metadata are EXIF/IPTC/XMP/ICC profil
+RAW Done N.A N.A Done Done yes yes dcraw Metadata are EXIF
+PPM Done TODO N.A N.A N.A yes yes none
+JPEG2K Done Done Done TODO N.A yes yes libjasper Metadata are EXIF/XMP/ICC profil
+
+Others file formats are supported only in 8 bits/color/pixel using TQImage/kimgio.
+QT3.x + KDE 3.4.x support these formats :
+
+Format Read Write Remarks
+
+PSD yes no Photoshop file format
+EXR yes no OpenEXR (libopenexr)
+XCF yes no Gimp file format
+PBM yes yes
+PGM yes yes
+PPM no yes
+TGA yes yes
+PCX yes yes
+BMP yes yes Win32 bitmap format
+RGB yes yes
+XBM yes yes
+XPM yes yes
+EPS yes yes
+DDS yes no
+ICO yes no Win32 icon format
+MNG yes no
+GIF yes no
+
+---------------------------------------------------------------------------
+
+TODO :
+
+Add PCD support using http://linux.bytesex.org/fbida/libpcd.html
+
diff --git a/src/libs/dimg/loaders/dimgloader.cpp b/src/libs/dimg/loaders/dimgloader.cpp
new file mode 100644
index 00000000..615b11e1
--- /dev/null
+++ b/src/libs/dimg/loaders/dimgloader.cpp
@@ -0,0 +1,200 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : DImg image loader interface
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// KDE includes.
+
+#include <kstandarddirs.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimgprivate.h"
+#include "dmetadata.h"
+#include "dimgloaderobserver.h"
+#include "dimgloader.h"
+
+namespace Digikam
+{
+
+DImgLoader::DImgLoader(DImg* image)
+ : m_image(image)
+{
+}
+
+int DImgLoader::granularity(DImgLoaderObserver *observer, int total, float progressSlice)
+{
+ // Splits expect total value into the chunks where checks shall occur
+ // and combines this with a possible correction factor from observer.
+ // Progress slice is the part of 100% concerned with the current granularity
+ // (E.g. in a loop only the values from 10% to 90% are used, then progressSlice is 0.8)
+ // Current default is 1/20, that is progress info every 5%
+ int granularity=0;
+
+ if (observer)
+ granularity = (int)(( total / (20 * progressSlice)) / observer->granularity());
+
+ return granularity ? granularity : 1;
+}
+
+unsigned char*& DImgLoader::imageData()
+{
+ return m_image->m_priv->data;
+}
+
+unsigned int& DImgLoader::imageWidth()
+{
+ return m_image->m_priv->width;
+}
+
+unsigned int& DImgLoader::imageHeight()
+{
+ return m_image->m_priv->height;
+}
+
+bool DImgLoader::imageHasAlpha()
+{
+ return m_image->hasAlpha();
+}
+
+bool DImgLoader::imageSixteenBit()
+{
+ return m_image->sixteenBit();
+}
+
+int DImgLoader::imageBitsDepth()
+{
+ return m_image->bitsDepth();
+}
+
+int DImgLoader::imageBytesDepth()
+{
+ return m_image->bytesDepth();
+}
+
+TQMap<int, TQByteArray>& DImgLoader::imageMetaData()
+{
+ return m_image->m_priv->metaData;
+}
+
+TQVariant DImgLoader::imageGetAttribute(const TQString& key)
+{
+ return m_image->attribute(key);
+}
+
+TQString DImgLoader::imageGetEmbbededText(const TQString& key)
+{
+ return m_image->embeddedText(key);
+}
+
+void DImgLoader::imageSetAttribute(const TQString& key, const TQVariant& value)
+{
+ m_image->setAttribute(key, value);
+}
+
+TQMap<TQString, TQString>& DImgLoader::imageEmbeddedText()
+{
+ return m_image->m_priv->embeddedText;
+}
+
+void DImgLoader::imageSetEmbbededText(const TQString& key, const TQString& text)
+{
+ m_image->setEmbeddedText(key, text);
+}
+
+bool DImgLoader::readMetadata(const TQString& filePath, DImg::FORMAT /*ff*/)
+{
+ TQMap<int, TQByteArray>& imageMetadata = imageMetaData();
+ imageMetadata.clear();
+
+ DMetadata metaDataFromFile(filePath);
+ if (!metaDataFromFile.load(filePath))
+ return false;
+
+ // Do not insert null data into metaData map:
+ // Even if byte array is null, if there is a key in the map, it will
+ // be interpreted as "There was data, so write it again to the file".
+ if (!metaDataFromFile.getComments().isNull())
+ imageMetadata.insert(DImg::COM, metaDataFromFile.getComments());
+ if (!metaDataFromFile.getExif().isNull())
+ imageMetadata.insert(DImg::EXIF, metaDataFromFile.getExif());
+ if (!metaDataFromFile.getIptc().isNull())
+ imageMetadata.insert(DImg::IPTC, metaDataFromFile.getIptc());
+
+ return true;
+}
+
+bool DImgLoader::saveMetadata(const TQString& filePath)
+{
+ DMetadata metaDataToFile(filePath);
+ metaDataToFile.setComments(m_image->getComments());
+ metaDataToFile.setExif(m_image->getExif());
+ metaDataToFile.setIptc(m_image->getIptc());
+ return metaDataToFile.applyChanges();
+}
+
+bool DImgLoader::checkExifWorkingColorSpace()
+{
+ DMetadata metaData;
+ metaData.setExif(m_image->getExif());
+
+ // Check if Exif data contains an ICC color profile.
+ TQByteArray profile = metaData.getExifTagData("Exif.Image.InterColorProfile");
+ if (!profile.isNull())
+ {
+ DDebug() << "Found an ICC profile in Exif metadata" << endl;
+ m_image->setICCProfil(profile);
+ return true;
+ }
+
+ // Else check the Exif color-space tag and use a default profiles available in digiKam.
+ TDEGlobal::dirs()->addResourceType("profiles", TDEGlobal::dirs()->kde_default("data") + "digikam/profiles");
+
+ switch(metaData.getImageColorWorkSpace())
+ {
+ case DMetadata::WORKSPACE_SRGB:
+ {
+ TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "srgb-d65.icm");
+ m_image->getICCProfilFromFile(directory + "srgb-d65.icm");
+ DDebug() << "Exif color-space tag is sRGB. Using default sRGB ICC profile." << endl;
+ return true;
+ break;
+ }
+
+ case DMetadata::WORKSPACE_ADOBERGB:
+ {
+ TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "adobergb.icm");
+ m_image->getICCProfilFromFile(directory + "adobergb.icm");
+ DDebug() << "Exif color-space tag is AdobeRGB. Using default AdobeRGB ICC profile." << endl;
+ return true;
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ return false;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/dimgloader.h b/src/libs/dimg/loaders/dimgloader.h
new file mode 100644
index 00000000..39025888
--- /dev/null
+++ b/src/libs/dimg/loaders/dimgloader.h
@@ -0,0 +1,97 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : DImg image loader interface
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef DIMGLOADER_H
+#define DIMGLOADER_H
+
+// TQt includes.
+
+#include <tqmap.h>
+#include <tqstring.h>
+#include <tqcstring.h>
+#include <tqvariant.h>
+
+// Local includes.
+
+#include "dimg.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DImgLoaderObserver;
+
+class DIGIKAM_EXPORT DImgLoader
+{
+public:
+
+ virtual ~DImgLoader() {};
+
+ virtual bool load(const TQString& filePath, DImgLoaderObserver *observer) = 0;
+ virtual bool save(const TQString& filePath, DImgLoaderObserver *observer) = 0;
+
+ virtual bool hasAlpha() const = 0;
+ virtual bool sixteenBit() const = 0;
+ virtual bool isReadOnly() const = 0;
+
+protected:
+
+ DImgLoader(DImg* image);
+
+ unsigned char*& imageData();
+ unsigned int& imageWidth();
+ unsigned int& imageHeight();
+
+ bool imageHasAlpha();
+ bool imageSixteenBit();
+
+ int imageBitsDepth();
+ int imageBytesDepth();
+
+ TQMap<int, TQByteArray>& imageMetaData();
+ TQVariant imageGetAttribute(const TQString& key);
+ void imageSetAttribute(const TQString& key, const TQVariant& value);
+
+ TQMap<TQString, TQString>& imageEmbeddedText();
+ TQString imageGetEmbbededText(const TQString& key);
+ void imageSetEmbbededText(const TQString& key, const TQString& text);
+
+ virtual bool readMetadata(const TQString& filePath, DImg::FORMAT ff);
+ virtual bool saveMetadata(const TQString& filePath);
+ virtual int granularity(DImgLoaderObserver *observer, int total, float progressSlice = 1.0);
+
+ bool checkExifWorkingColorSpace();
+
+protected:
+
+ DImg *m_image;
+
+private:
+
+ DImgLoader();
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMGLOADER_H */
diff --git a/src/libs/dimg/loaders/dimgloaderobserver.h b/src/libs/dimg/loaders/dimgloaderobserver.h
new file mode 100644
index 00000000..ea83bede
--- /dev/null
+++ b/src/libs/dimg/loaders/dimgloaderobserver.h
@@ -0,0 +1,67 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-03
+ * Description : DImgLoader observer interface
+ *
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <[email protected]>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef DIMGLOADEROBSERVER_H
+#define DIMGLOADEROBSERVER_H
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DImg;
+
+class DIGIKAM_EXPORT DImgLoaderObserver
+{
+
+public:
+ // posts progress information about image IO
+ virtual void progressInfo(const DImg *, float /*progress*/)
+ {};
+
+ // queries whether the image IO operation shall be continued
+ virtual bool continueQuery(const DImg *)
+ { return true; };
+
+ // Return a relative value which determines the granularity, the frequency
+ // with which the DImgLoaderObserver is checked and progress is posted.
+ // Standard is 1.0. Values < 1 mean less granularity (fewer checks),
+ // values > 1 mean higher granularity (more checks).
+ virtual float granularity()
+ { return 1.0; };
+
+ // This is a hack needed to prevent hanging when a TDEProcess-based loader (raw loader)
+ // is waiting for the process to finish, but the main thread is waiting
+ // for the thread to finish and no TDEProcess events are delivered.
+ // Remove when porting to TQt4.
+ virtual bool isShuttingDown()
+ { return false; }
+
+ virtual ~DImgLoaderObserver(){};
+};
+
+} // namespace Digikam
+
+#endif // DIMGLOADEROBSERVER_H
diff --git a/src/libs/dimg/loaders/iccjpeg.c b/src/libs/dimg/loaders/iccjpeg.c
new file mode 100644
index 00000000..fefa9509
--- /dev/null
+++ b/src/libs/dimg/loaders/iccjpeg.c
@@ -0,0 +1,270 @@
+/*
+ * Little cms
+ * Copyright (C) 1998-2004 Marti Maria
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * iccprofile.c
+ *
+ * This file provides code to read and write International Color Consortium
+ * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has
+ * defined a standard format for including such data in JPEG "APP2" markers.
+ * The code given here does not know anything about the internal structure
+ * of the ICC profile data; it just knows how to put the profile data into
+ * a JPEG file being written, or get it back out when reading.
+ *
+ * This code depends on new features added to the IJG JPEG library as of
+ * IJG release 6b; it will not compile or work with older IJG versions.
+ *
+ * NOTE: this code would need surgery to work on 16-bit-int machines
+ * with ICC profiles exceeding 64K bytes in size. If you need to do that,
+ * change all the "unsigned int" variables to "INT32". You'll also need
+ * to find a malloc() replacement that can allocate more than 64K.
+ */
+
+#include "iccjpeg.h"
+#include <stdlib.h> /* define malloc() */
+
+
+/*
+ * Since an ICC profile can be larger than the maximum size of a JPEG marker
+ * (64K), we need provisions to split it into multiple markers. The format
+ * defined by the ICC specifies one or more APP2 markers containing the
+ * following data:
+ * Identifying string ASCII "ICC_PROFILE\0" (12 bytes)
+ * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte)
+ * Number of markers Total number of APP2's used (1 byte)
+ * Profile data (remainder of APP2 data)
+ * Decoders should use the marker sequence numbers to reassemble the profile,
+ * rather than assuming that the APP2 markers appear in the correct sequence.
+ */
+
+#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */
+#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */
+#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */
+#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN)
+
+
+/*
+ * This routine writes the given ICC profile data into a JPEG file.
+ * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
+ * the first call to jpeg_write_scanlines().
+ * (This ordering ensures that the APP2 marker(s) will appear after the
+ * SOI and JFIF or Adobe markers, but before all else.)
+ */
+
+void
+write_icc_profile (j_compress_ptr cinfo,
+ const JOCTET *icc_data_ptr,
+ unsigned int icc_data_len)
+{
+ unsigned int num_markers; /* total number of markers we'll write */
+ int cur_marker = 1; /* per spec, counting starts at 1 */
+ unsigned int length; /* number of bytes to write in this marker */
+
+ /* Calculate the number of markers we'll need, rounding up of course */
+ num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER;
+ if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len)
+ num_markers++;
+
+ while (icc_data_len > 0) {
+ /* length of profile to put in this marker */
+ length = icc_data_len;
+ if (length > MAX_DATA_BYTES_IN_MARKER)
+ length = MAX_DATA_BYTES_IN_MARKER;
+ icc_data_len -= length;
+
+ /* Write the JPEG marker header (APP2 code and marker length) */
+ jpeg_write_m_header(cinfo, ICC_MARKER,
+ (unsigned int) (length + ICC_OVERHEAD_LEN));
+
+ /* Write the marker identifying string "ICC_PROFILE" (null-terminated).
+ * We code it in this less-than-transparent way so that the code works
+ * even if the local character set is not ASCII.
+ */
+ jpeg_write_m_byte(cinfo, 0x49);
+ jpeg_write_m_byte(cinfo, 0x43);
+ jpeg_write_m_byte(cinfo, 0x43);
+ jpeg_write_m_byte(cinfo, 0x5F);
+ jpeg_write_m_byte(cinfo, 0x50);
+ jpeg_write_m_byte(cinfo, 0x52);
+ jpeg_write_m_byte(cinfo, 0x4F);
+ jpeg_write_m_byte(cinfo, 0x46);
+ jpeg_write_m_byte(cinfo, 0x49);
+ jpeg_write_m_byte(cinfo, 0x4C);
+ jpeg_write_m_byte(cinfo, 0x45);
+ jpeg_write_m_byte(cinfo, 0x0);
+
+ /* Add the sequencing info */
+ jpeg_write_m_byte(cinfo, cur_marker);
+ jpeg_write_m_byte(cinfo, (int) num_markers);
+
+ /* Add the profile data */
+ while (length--) {
+ jpeg_write_m_byte(cinfo, *icc_data_ptr);
+ icc_data_ptr++;
+ }
+ cur_marker++;
+ }
+}
+
+
+/*
+ * Prepare for reading an ICC profile
+ */
+
+void
+setup_read_icc_profile (j_decompress_ptr cinfo)
+{
+ /* Tell the library to keep any APP2 data it may find */
+ jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF);
+}
+
+
+/*
+ * Handy subroutine to test whether a saved marker is an ICC profile marker.
+ */
+
+static boolean
+marker_is_icc (jpeg_saved_marker_ptr marker)
+{
+ return
+ marker->marker == ICC_MARKER &&
+ marker->data_length >= ICC_OVERHEAD_LEN &&
+ /* verify the identifying string */
+ GETJOCTET(marker->data[0]) == 0x49 &&
+ GETJOCTET(marker->data[1]) == 0x43 &&
+ GETJOCTET(marker->data[2]) == 0x43 &&
+ GETJOCTET(marker->data[3]) == 0x5F &&
+ GETJOCTET(marker->data[4]) == 0x50 &&
+ GETJOCTET(marker->data[5]) == 0x52 &&
+ GETJOCTET(marker->data[6]) == 0x4F &&
+ GETJOCTET(marker->data[7]) == 0x46 &&
+ GETJOCTET(marker->data[8]) == 0x49 &&
+ GETJOCTET(marker->data[9]) == 0x4C &&
+ GETJOCTET(marker->data[10]) == 0x45 &&
+ GETJOCTET(marker->data[11]) == 0x0;
+}
+
+
+/*
+ * See if there was an ICC profile in the JPEG file being read;
+ * if so, reassemble and return the profile data.
+ *
+ * TRUE is returned if an ICC profile was found, FALSE if not.
+ * If TRUE is returned, *icc_data_ptr is set to point to the
+ * returned data, and *icc_data_len is set to its length.
+ *
+ * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
+ * and must be freed by the caller with free() when the caller no longer
+ * needs it. (Alternatively, we could write this routine to use the
+ * IJG library's memory allocator, so that the data would be freed implicitly
+ * at jpeg_finish_decompress() time. But it seems likely that many apps
+ * will prefer to have the data stick around after decompression finishes.)
+ *
+ * NOTE: if the file contains invalid ICC APP2 markers, we just silently
+ * return FALSE. You might want to issue an error message instead.
+ */
+
+boolean
+read_icc_profile (j_decompress_ptr cinfo,
+ JOCTET **icc_data_ptr,
+ unsigned int *icc_data_len)
+{
+ jpeg_saved_marker_ptr marker;
+ int num_markers = 0;
+ int seq_no;
+ JOCTET *icc_data;
+ unsigned int total_length;
+#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */
+ char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */
+ unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */
+ unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */
+
+ *icc_data_ptr = NULL; /* avoid confusion if FALSE return */
+ *icc_data_len = 0;
+
+ /* This first pass over the saved markers discovers whether there are
+ * any ICC markers and verifies the consistency of the marker numbering.
+ */
+
+ for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++)
+ marker_present[seq_no] = 0;
+
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ if (num_markers == 0)
+ num_markers = GETJOCTET(marker->data[13]);
+ else if (num_markers != GETJOCTET(marker->data[13]))
+ return FALSE; /* inconsistent num_markers fields */
+ seq_no = GETJOCTET(marker->data[12]);
+ if (seq_no <= 0 || seq_no > num_markers)
+ return FALSE; /* bogus sequence number */
+ if (marker_present[seq_no])
+ return FALSE; /* duplicate sequence numbers */
+ marker_present[seq_no] = 1;
+ data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN;
+ }
+ }
+
+ if (num_markers == 0)
+ return FALSE;
+
+ /* Check for missing markers, count total space needed,
+ * compute offset of each marker's part of the data.
+ */
+
+ total_length = 0;
+ for (seq_no = 1; seq_no <= num_markers; seq_no++) {
+ if (marker_present[seq_no] == 0)
+ return FALSE; /* missing sequence number */
+ data_offset[seq_no] = total_length;
+ total_length += data_length[seq_no];
+ }
+
+ if (total_length <= 0)
+ return FALSE; /* found only empty markers? */
+
+ /* Allocate space for assembled data */
+ icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET));
+ if (icc_data == NULL)
+ return FALSE; /* oops, out of memory */
+
+ /* and fill it in */
+ for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (marker_is_icc(marker)) {
+ JOCTET FAR *src_ptr;
+ JOCTET *dst_ptr;
+ unsigned int length;
+ seq_no = GETJOCTET(marker->data[12]);
+ dst_ptr = icc_data + data_offset[seq_no];
+ src_ptr = marker->data + ICC_OVERHEAD_LEN;
+ length = data_length[seq_no];
+ while (length--) {
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ }
+
+ *icc_data_ptr = icc_data;
+ *icc_data_len = total_length;
+
+ return TRUE;
+}
diff --git a/src/libs/dimg/loaders/iccjpeg.h b/src/libs/dimg/loaders/iccjpeg.h
new file mode 100644
index 00000000..2aab4196
--- /dev/null
+++ b/src/libs/dimg/loaders/iccjpeg.h
@@ -0,0 +1,101 @@
+/*
+ * Little cms
+ * Copyright (C) 1998-2004 Marti Maria
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+ * KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+ * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ * iccprofile.h
+ *
+ * This file provides code to read and write International Color Consortium
+ * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has
+ * defined a standard format for including such data in JPEG "APP2" markers.
+ * The code given here does not know anything about the internal structure
+ * of the ICC profile data; it just knows how to put the profile data into
+ * a JPEG file being written, or get it back out when reading.
+ *
+ * This code depends on new features added to the IJG JPEG library as of
+ * IJG release 6b; it will not compile or work with older IJG versions.
+ *
+ * NOTE: this code would need surgery to work on 16-bit-int machines
+ * with ICC profiles exceeding 64K bytes in size. See iccprofile.c
+ * for details.
+ */
+
+#ifndef ICCJPEG_H
+#define ICCJPEG_H
+
+#include <stdio.h> /* needed to define "FILE", "NULL" */
+#include <jpeglib.h>
+
+
+/**
+ * This routine writes the given ICC profile data into a JPEG file.
+ * It *must* be called AFTER calling jpeg_start_compress() and BEFORE
+ * the first call to jpeg_write_scanlines().
+ * (This ordering ensures that the APP2 marker(s) will appear after the
+ * SOI and JFIF or Adobe markers, but before all else.)
+ */
+
+extern void write_icc_profile JPP((j_compress_ptr cinfo,
+ const JOCTET *icc_data_ptr,
+ unsigned int icc_data_len));
+
+
+/**
+ * Reading a JPEG file that may contain an ICC profile requires two steps:
+ *
+ * 1. After jpeg_create_decompress() but before jpeg_read_header(),
+ * call setup_read_icc_profile(). This routine tells the IJG library
+ * to save in memory any APP2 markers it may find in the file.
+ *
+ * 2. After jpeg_read_header(), call read_icc_profile() to find out
+ * whether there was a profile and obtain it if so.
+ */
+
+
+/**
+ * Prepare for reading an ICC profile
+ */
+
+extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo));
+
+
+/**
+ * See if there was an ICC profile in the JPEG file being read;
+ * if so, reassemble and return the profile data.
+ *
+ * TRUE is returned if an ICC profile was found, FALSE if not.
+ * If TRUE is returned, *icc_data_ptr is set to point to the
+ * returned data, and *icc_data_len is set to its length.
+ *
+ * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc()
+ * and must be freed by the caller with free() when the caller no longer
+ * needs it. (Alternatively, we could write this routine to use the
+ * IJG library's memory allocator, so that the data would be freed implicitly
+ * at jpeg_finish_decompress() time. But it seems likely that many apps
+ * will prefer to have the data stick around after decompression finishes.)
+ */
+
+extern boolean read_icc_profile JPP((j_decompress_ptr cinfo,
+ JOCTET **icc_data_ptr,
+ unsigned int *icc_data_len));
+
+#endif /* ICCJPEG_H */
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
diff --git a/src/libs/dimg/loaders/jp2kloader.h b/src/libs/dimg/loaders/jp2kloader.h
new file mode 100644
index 00000000..04ec214e
--- /dev/null
+++ b/src/libs/dimg/loaders/jp2kloader.h
@@ -0,0 +1,69 @@
+/* ============================================================
+ *
+ * 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 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.
+ *
+ * ============================================================ */
+
+#ifndef JP2KLOADER_H
+#define JP2KLOADER_H
+
+// C ansi includes.
+
+extern "C"
+{
+#include <jasper/jasper.h>
+}
+
+// TQt includes.
+
+#include <tqstring.h>
+
+// Local includes.
+
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+class DImg;
+
+class DIGIKAM_EXPORT JP2KLoader : public DImgLoader
+{
+
+public:
+
+ JP2KLoader(DImg* image);
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer);
+ bool save(const TQString& filePath, DImgLoaderObserver *observer);
+
+ virtual bool hasAlpha() const;
+ virtual bool sixteenBit() const;
+ virtual bool isReadOnly() const { return false; };
+
+private:
+
+ bool m_sixteenBit;
+ bool m_hasAlpha;
+};
+
+} // NameSpace Digikam
+
+#endif /* JP2KLOADER_H */
diff --git a/src/libs/dimg/loaders/jp2ksettings.cpp b/src/libs/dimg/loaders/jp2ksettings.cpp
new file mode 100644
index 00000000..af0737c1
--- /dev/null
+++ b/src/libs/dimg/loaders/jp2ksettings.cpp
@@ -0,0 +1,139 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save JPEG 2000 image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqstring.h>
+#include <tqlabel.h>
+#include <tqcheckbox.h>
+#include <tqlayout.h>
+#include <tqwhatsthis.h>
+
+// KDE includes.
+
+#include <tdelocale.h>
+#include <kdialog.h>
+#include <knuminput.h>
+
+// Local includes.
+
+#include "jp2ksettings.h"
+#include "jp2ksettings.moc"
+
+namespace Digikam
+{
+
+class JP2KSettingsPriv
+{
+
+public:
+
+ JP2KSettingsPriv()
+ {
+ JPEG2000Grid = 0;
+ labelJPEG2000compression = 0;
+ JPEG2000compression = 0;
+ JPEG2000LossLess = 0;
+ }
+
+ TQGridLayout *JPEG2000Grid;
+
+ TQLabel *labelJPEG2000compression;
+
+ TQCheckBox *JPEG2000LossLess;
+
+ KIntNumInput *JPEG2000compression;
+};
+
+JP2KSettings::JP2KSettings(TQWidget *parent)
+ : TQWidget(parent, 0, TQt::WDestructiveClose)
+{
+ d = new JP2KSettingsPriv;
+
+ d->JPEG2000Grid = new TQGridLayout(this, 1, 1, KDialog::spacingHint());
+ d->JPEG2000LossLess = new TQCheckBox(i18n("Lossless JPEG 2000 files"), this);
+
+ TQWhatsThis::add(d->JPEG2000LossLess, i18n("<p>Toggle lossless compression for JPEG 2000 images.<p>"
+ "If you enable this option, you will use a lossless method "
+ "to compress JPEG 2000 pictures.<p>"));
+
+ d->JPEG2000compression = new KIntNumInput(75, this);
+ d->JPEG2000compression->setRange(1, 100, 1, true );
+ d->labelJPEG2000compression = new TQLabel(i18n("JPEG 2000 quality:"), this);
+
+ TQWhatsThis::add( d->JPEG2000compression, i18n("<p>The quality value for JPEG 2000 images:<p>"
+ "<b>1</b>: low quality (high compression and small "
+ "file size)<p>"
+ "<b>50</b>: medium quality<p>"
+ "<b>75</b>: good quality (default)<p>"
+ "<b>100</b>: high quality (no compression and "
+ "large file size)<p>"
+ "<b>Note: JPEG 2000 is not a lossless image "
+ "compression format when you use this setting.</b>"));
+
+ d->JPEG2000Grid->addMultiCellWidget(d->JPEG2000LossLess, 0, 0, 0, 1);
+ d->JPEG2000Grid->addMultiCellWidget(d->labelJPEG2000compression, 1, 1, 0, 0);
+ d->JPEG2000Grid->addMultiCellWidget(d->JPEG2000compression, 1, 1, 1, 1);
+ d->JPEG2000Grid->setColStretch(1, 10);
+
+ connect(d->JPEG2000LossLess, TQ_SIGNAL(toggled(bool)),
+ this, TQ_SLOT(slotToggleJPEG2000LossLess(bool)));
+
+ connect(d->JPEG2000LossLess, TQ_SIGNAL(toggled(bool)),
+ this, TQ_SLOT(slotToggleJPEG2000LossLess(bool)));
+}
+
+JP2KSettings::~JP2KSettings()
+{
+ delete d;
+}
+
+void JP2KSettings::setCompressionValue(int val)
+{
+ d->JPEG2000compression->setValue(val);
+}
+
+int JP2KSettings::getCompressionValue()
+{
+ return d->JPEG2000compression->value();
+}
+
+void JP2KSettings::setLossLessCompression(bool b)
+{
+ d->JPEG2000LossLess->setChecked(b);
+ slotToggleJPEG2000LossLess(d->JPEG2000LossLess->isChecked());
+}
+
+bool JP2KSettings::getLossLessCompression()
+{
+ return d->JPEG2000LossLess->isChecked();
+}
+
+void JP2KSettings::slotToggleJPEG2000LossLess(bool b)
+{
+ d->JPEG2000compression->setEnabled(!b);
+ d->labelJPEG2000compression->setEnabled(!b);
+}
+
+} // namespace Digikam
+
diff --git a/src/libs/dimg/loaders/jp2ksettings.h b/src/libs/dimg/loaders/jp2ksettings.h
new file mode 100644
index 00000000..951cb2fc
--- /dev/null
+++ b/src/libs/dimg/loaders/jp2ksettings.h
@@ -0,0 +1,67 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save JPEG 2000 image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef JP2KSETTINGS_H
+#define JP2KSETTINGS_H
+
+// KDE includes.
+
+#include <tqwidget.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class JP2KSettingsPriv;
+
+class DIGIKAM_EXPORT JP2KSettings : public TQWidget
+{
+TQ_OBJECT
+
+
+public:
+
+ JP2KSettings(TQWidget *parent=0);
+ ~JP2KSettings();
+
+ void setCompressionValue(int val);
+ int getCompressionValue();
+
+ void setLossLessCompression(bool b);
+ bool getLossLessCompression();
+
+private slots:
+
+ void slotToggleJPEG2000LossLess(bool);
+
+private:
+
+ JP2KSettingsPriv* d;
+};
+
+} // namespace Digikam
+
+#endif /* JP2KSETTINGS_H */
diff --git a/src/libs/dimg/loaders/jpegloader.cpp b/src/libs/dimg/loaders/jpegloader.cpp
new file mode 100644
index 00000000..58f6e20a
--- /dev/null
+++ b/src/libs/dimg/loaders/jpegloader.cpp
@@ -0,0 +1,676 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : A JPEG IO file for DImg framework
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#define XMD_H
+
+// 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"
+{
+#include "iccjpeg.h"
+}
+
+// C+ includes.
+
+#include <cstdio>
+#include <cstdlib>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqcstring.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "jpegloader.h"
+
+namespace Digikam
+{
+
+// To manage Errors/Warnings handling provide by libjpeg
+
+void JPEGLoader::dimg_jpeg_error_exit(j_common_ptr cinfo)
+{
+ dimg_jpeg_error_mgr* myerr = (dimg_jpeg_error_mgr*) cinfo->err;
+
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << buffer << endl;
+#endif
+
+ longjmp(myerr->setjmp_buffer, 1);
+}
+
+void JPEGLoader::dimg_jpeg_emit_message(j_common_ptr cinfo, int msg_level)
+{
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << buffer << " (" << msg_level << ")" << endl;
+#else
+ Q_UNUSED(msg_level);
+#endif
+}
+
+void JPEGLoader::dimg_jpeg_output_message(j_common_ptr cinfo)
+{
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message)(cinfo, buffer);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << buffer << endl;
+#endif
+}
+
+JPEGLoader::JPEGLoader(DImg* image)
+ : DImgLoader(image)
+{
+}
+
+bool JPEGLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ readMetadata(filePath, DImg::JPEG);
+
+ FILE *file = fopen(TQFile::encodeName(filePath), "rb");
+ if (!file)
+ return false;
+
+ unsigned char header[2];
+
+ if (fread(&header, 2, 1, file) != 1)
+ {
+ fclose(file);
+ return false;
+ }
+
+ unsigned char jpegID[] = { 0xFF, 0xD8 };
+
+ if (memcmp(header, jpegID, 2) != 0)
+ {
+ // not a jpeg file
+ fclose(file);
+ return false;
+ }
+
+ fseek(file, 0L, SEEK_SET);
+
+ struct jpeg_decompress_struct cinfo;
+ struct dimg_jpeg_error_mgr jerr;
+
+ // -------------------------------------------------------------------
+ // JPEG error handling.
+
+ cinfo.err = jpeg_std_error(&jerr);
+ cinfo.err->error_exit = dimg_jpeg_error_exit;
+ cinfo.err->emit_message = dimg_jpeg_emit_message;
+ cinfo.err->output_message = dimg_jpeg_output_message;
+
+ // If an error occurs during reading, libjpeg will jump here
+
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Find out if we do the fast-track loading with reduced size. Jpeg specific.
+ int scaledLoadingSize = 0;
+ TQVariant attribute = imageGetAttribute("jpegScaledLoadingSize");
+ if (attribute.isValid())
+ scaledLoadingSize = attribute.toInt();
+
+ // -------------------------------------------------------------------
+ // Set JPEG decompressor instance
+
+ jpeg_create_decompress(&cinfo);
+ jpeg_stdio_src(&cinfo, file);
+
+ // Recording ICC profile marker (from iccjpeg.c)
+ setup_read_icc_profile(&cinfo);
+
+ // read image information
+ jpeg_read_header(&cinfo, true);
+
+ // set decompression parameters
+ cinfo.do_fancy_upsampling = false;
+ cinfo.do_block_smoothing = false;
+
+ if (scaledLoadingSize)
+ {
+ int imgSize = TQMAX(cinfo.image_width, cinfo.image_height);
+
+ // libjpeg supports 1/1, 1/2, 1/4, 1/8
+ int scale=1;
+ while(scaledLoadingSize*scale*2<=imgSize)
+ {
+ scale*=2;
+ }
+ if(scale>8) scale=8;
+
+ cinfo.scale_num=1;
+ cinfo.scale_denom=scale;
+ }
+
+ // Libjpeg handles the following conversions:
+ // YCbCr => GRAYSCALE, YCbCr => RGB, GRAYSCALE => RGB, YCCK => CMYK
+ // So we cannot get RGB from CMYK or YCCK, CMYK conversion is handled below
+ switch (cinfo.jpeg_color_space)
+ {
+ case JCS_UNKNOWN:
+ // perhaps jpeg_read_header did some guessing, leave value unchanged
+ break;
+ case JCS_GRAYSCALE:
+ case JCS_RGB:
+ case JCS_YCbCr:
+ cinfo.out_color_space = JCS_RGB;
+ break;
+ case JCS_CMYK:
+ case JCS_YCCK:
+ cinfo.out_color_space = JCS_CMYK;
+ break;
+ }
+
+ // initialize decompression
+ jpeg_start_decompress(&cinfo);
+
+ // some pseudo-progress
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // -------------------------------------------------------------------
+ // Get image data.
+
+ int w = cinfo.output_width;
+ int h = cinfo.output_height;
+ uchar *dest = 0;
+
+ uchar *ptr, *line[16], *data=0;
+ uchar *ptr2=0;
+ int x, y, l, i, scans, count, prevy;
+
+ if (cinfo.rec_outbuf_height > 16)
+ {
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ DDebug() << k_funcinfo << "Height of JPEG scanline buffer out of range!" << endl;
+ return false;
+ }
+
+ // We only take RGB with 1 or 3 components, or CMYK with 4 components
+ if (!(
+ (cinfo.out_color_space == JCS_RGB && (cinfo.output_components == 3 || cinfo.output_components == 1))
+ || (cinfo.out_color_space == JCS_CMYK && cinfo.output_components == 4)
+ ))
+ {
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ DDebug() << k_funcinfo
+ << "JPEG colorspace ("
+ << cinfo.out_color_space
+ << ") or Number of JPEG color components ("
+ << cinfo.output_components
+ << ") unsupported!" << endl;
+ return false;
+ }
+
+ data = new uchar[w * 16 * cinfo.output_components];
+
+ if (!data)
+ {
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ DDebug() << k_funcinfo << "Cannot allocate memory!" << endl;
+ return false;
+ }
+
+ dest = new uchar[w * h * 4];
+
+ if (!dest)
+ {
+ delete [] data;
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ DDebug() << k_funcinfo << "Cannot allocate memory!" << endl;
+ return false;
+ }
+
+ ptr2 = dest;
+ count = 0;
+ prevy = 0;
+
+ if (cinfo.output_components == 3)
+ {
+ for (i = 0; i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w * 3);
+
+ int checkPoint = 0;
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ // use 0-10% and 90-100% for pseudo-progress
+ if (observer && l >= checkPoint)
+ {
+ checkPoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ delete [] dest;
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)l)/((float)h) )));
+ }
+
+ jpeg_read_scanlines(&cinfo, &line[0], cinfo.rec_outbuf_height);
+ scans = cinfo.rec_outbuf_height;
+
+ if ((h - l) < scans)
+ scans = h - l;
+
+ ptr = data;
+
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ ptr2[3] = 0xFF;
+ ptr2[2] = ptr[0];
+ ptr2[1] = ptr[1];
+ ptr2[0] = ptr[2];
+
+ ptr += 3;
+ ptr2 += 4;
+ }
+ }
+ }
+ }
+ else if (cinfo.output_components == 1)
+ {
+ for (i = 0; i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w);
+
+ int checkPoint = 0;
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ if (observer && l >= checkPoint)
+ {
+ checkPoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ delete [] dest;
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)l)/((float)h) )));
+ }
+
+ jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
+ scans = cinfo.rec_outbuf_height;
+
+ if ((h - l) < scans)
+ scans = h - l;
+
+ ptr = data;
+
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ ptr2[3] = 0xFF;
+ ptr2[2] = ptr[0];
+ ptr2[1] = ptr[0];
+ ptr2[0] = ptr[0];
+
+ ptr ++;
+ ptr2 += 4;
+ }
+ }
+ }
+ }
+ else // CMYK
+ {
+ for (i = 0; i < cinfo.rec_outbuf_height; i++)
+ line[i] = data + (i * w * 4);
+
+ int checkPoint = 0;
+ for (l = 0; l < h; l += cinfo.rec_outbuf_height)
+ {
+ // use 0-10% and 90-100% for pseudo-progress
+ if (observer && l >= checkPoint)
+ {
+ checkPoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ delete [] dest;
+ jpeg_destroy_decompress(&cinfo);
+ fclose(file);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)l)/((float)h) )));
+ }
+
+ jpeg_read_scanlines(&cinfo, &line[0], cinfo.rec_outbuf_height);
+ scans = cinfo.rec_outbuf_height;
+
+ if ((h - l) < scans)
+ scans = h - l;
+
+ ptr = data;
+
+ for (y = 0; y < scans; y++)
+ {
+ for (x = 0; x < w; x++)
+ {
+ // Inspired by TQt's JPEG loader
+
+ int k = ptr[3];
+
+ ptr2[3] = 0xFF;
+ ptr2[2] = k * ptr[0] / 255;
+ ptr2[1] = k * ptr[1] / 255;
+ ptr2[0] = k * ptr[2] / 255;
+
+ ptr += 4;
+ ptr2 += 4;
+ }
+ }
+ }
+ }
+
+ delete [] data;
+
+ // -------------------------------------------------------------------
+ // Read image ICC profile
+
+ TQMap<int, TQByteArray>& metaData = imageMetaData();
+
+ JOCTET *profile_data=NULL;
+ uint profile_size;
+
+ read_icc_profile (&cinfo, &profile_data, &profile_size);
+
+ if (profile_data != NULL)
+ {
+ TQByteArray profile_rawdata(profile_size);
+ memcpy(profile_rawdata.data(), profile_data, profile_size);
+ metaData.insert(DImg::ICC, profile_rawdata);
+ free (profile_data);
+ }
+ else
+ {
+ // If ICC profile is null, check Exif metadata.
+ checkExifWorkingColorSpace();
+ }
+
+ // -------------------------------------------------------------------
+
+ jpeg_finish_decompress(&cinfo);
+ jpeg_destroy_decompress(&cinfo);
+
+ // -------------------------------------------------------------------
+
+ fclose(file);
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageWidth() = w;
+ imageHeight() = h;
+ imageData() = dest;
+ imageSetAttribute("format", "JPEG");
+
+ return true;
+}
+
+bool JPEGLoader::save(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ FILE *file = fopen(TQFile::encodeName(filePath), "wb");
+ if (!file)
+ return false;
+
+ struct jpeg_compress_struct cinfo;
+ struct dimg_jpeg_error_mgr jerr;
+
+ // -------------------------------------------------------------------
+ // JPEG error handling.
+
+ cinfo.err = jpeg_std_error(&jerr);
+ cinfo.err->error_exit = dimg_jpeg_error_exit;
+ cinfo.err->emit_message = dimg_jpeg_emit_message;
+ cinfo.err->output_message = dimg_jpeg_output_message;
+
+ // If an error occurs during writing, libjpeg will jump here
+
+ if (setjmp(jerr.setjmp_buffer))
+ {
+ jpeg_destroy_compress(&cinfo);
+ fclose(file);
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Set JPEG compressor instance
+
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, file);
+
+ uint& w = imageWidth();
+ uint& h = imageHeight();
+ unsigned char*& data = imageData();
+
+ // Size of image.
+ cinfo.image_width = w;
+ cinfo.image_height = h;
+
+ // Color components of image in RGB.
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+
+ TQVariant qualityAttr = imageGetAttribute("quality");
+ int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
+
+ if (quality < 0)
+ quality = 90;
+ if (quality > 100)
+ quality = 100;
+
+ TQVariant subSamplingAttr = imageGetAttribute("subsampling");
+ int subsampling = subSamplingAttr.isValid() ? subSamplingAttr.toInt() : 1; // Medium
+
+ jpeg_set_defaults(&cinfo);
+
+ // B.K.O #149578: set horizontal and vertical chroma subsampling factor to encoder.
+ // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling
+
+ switch (subsampling)
+ {
+ case 1: // 2x1, 1x1, 1x1 (4:2:2) : Medium
+ {
+ DDebug() << "Using LibJPEG medium chroma-subsampling (4:2:2)" << endl;
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+ }
+ case 2: // 2x2, 1x1, 1x1 (4:1:1) : High
+ {
+ DDebug() << "Using LibJPEG high chroma-subsampling (4:1:1)" << endl;
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+ }
+ default: // 1x1 1x1 1x1 (4:4:4) : None
+ {
+ DDebug() << "Using LibJPEG none chroma-subsampling (4:4:4)" << endl;
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[2].h_samp_factor = 1;
+ cinfo.comp_info[2].v_samp_factor = 1;
+ break;
+ }
+ }
+
+ jpeg_set_quality(&cinfo, quality, true);
+ jpeg_start_compress(&cinfo, true);
+
+ DDebug() << "Using LibJPEG quality compression value: " << quality << endl;
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // -------------------------------------------------------------------
+ // Write ICC profil.
+
+ TQByteArray profile_rawdata = m_image->getICCProfil();
+
+ if (!profile_rawdata.isEmpty())
+ {
+ write_icc_profile (&cinfo, (JOCTET *)profile_rawdata.data(), profile_rawdata.size());
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.2);
+
+ // -------------------------------------------------------------------
+ // Write Image data.
+
+ uchar* line = new uchar[w*3];
+ uchar* dstPtr = 0;
+ uint checkPoint = 0;
+
+ if (!imageSixteenBit()) // 8 bits image.
+ {
+
+ uchar* srcPtr = data;
+
+ for (uint j=0; j<h; j++)
+ {
+
+ if (observer && j == checkPoint)
+ {
+ checkPoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] line;
+ jpeg_destroy_compress(&cinfo);
+ fclose(file);
+ return false;
+ }
+ // use 0-20% for pseudo-progress, now fill 20-100%
+ observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)j)/((float)h) )));
+ }
+
+ dstPtr = line;
+
+ for (uint i = 0; i < w; i++)
+ {
+ dstPtr[2] = srcPtr[0];
+ dstPtr[1] = srcPtr[1];
+ dstPtr[0] = srcPtr[2];
+
+ srcPtr += 4;
+ dstPtr += 3;
+ }
+
+ jpeg_write_scanlines(&cinfo, &line, 1);
+ }
+ }
+ else
+ {
+ unsigned short* srcPtr = (unsigned short*)data;
+
+ for (uint j=0; j<h; j++)
+ {
+
+ if (observer && j == checkPoint)
+ {
+ checkPoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] line;
+ jpeg_destroy_compress(&cinfo);
+ fclose(file);
+ return false;
+ }
+ // use 0-20% for pseudo-progress, now fill 20-100%
+ observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)j)/((float)h) )));
+ }
+
+ dstPtr = line;
+
+ for (uint i = 0; i < w; i++)
+ {
+ dstPtr[2] = (srcPtr[0] * 255UL)/65535UL;
+ dstPtr[1] = (srcPtr[1] * 255UL)/65535UL;
+ dstPtr[0] = (srcPtr[2] * 255UL)/65535UL;
+
+ srcPtr += 4;
+ dstPtr += 3;
+ }
+
+ jpeg_write_scanlines(&cinfo, &line, 1);
+ }
+ }
+
+ delete [] line;
+
+ // -------------------------------------------------------------------
+
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ fclose(file);
+
+ imageSetAttribute("savedformat", "JPEG");
+
+ saveMetadata(filePath);
+
+ return true;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/jpegloader.h b/src/libs/dimg/loaders/jpegloader.h
new file mode 100644
index 00000000..b5d64f18
--- /dev/null
+++ b/src/libs/dimg/loaders/jpegloader.h
@@ -0,0 +1,80 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : A JPEG IO file for DImg framework
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>, Gilles Caulier
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef JPEGLOADER_H
+#define JPEGLOADER_H
+
+// C ansi includes.
+
+extern "C"
+{
+#include <setjmp.h>
+#include <jpeglib.h>
+}
+
+// TQt includes.
+
+#include <tqstring.h>
+
+// Local includes.
+
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+class DImg;
+
+class DIGIKAM_EXPORT JPEGLoader : public DImgLoader
+{
+
+public:
+
+ JPEGLoader(DImg* image);
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer);
+ bool save(const TQString& filePath, DImgLoaderObserver *observer);
+
+ virtual bool hasAlpha() const { return false; }
+ virtual bool sixteenBit() const { return false; }
+ virtual bool isReadOnly() const { return false; };
+
+private:
+
+ // To manage Errors/Warnings handling provide by libjpeg
+
+ struct dimg_jpeg_error_mgr : public jpeg_error_mgr
+ {
+ jmp_buf setjmp_buffer;
+ };
+
+ static void dimg_jpeg_error_exit(j_common_ptr cinfo);
+ static void dimg_jpeg_emit_message(j_common_ptr cinfo, int msg_level);
+ static void dimg_jpeg_output_message(j_common_ptr cinfo);
+
+};
+
+} // NameSpace Digikam
+
+#endif /* JPEGLOADER_H */
diff --git a/src/libs/dimg/loaders/jpegsettings.cpp b/src/libs/dimg/loaders/jpegsettings.cpp
new file mode 100644
index 00000000..62bd6365
--- /dev/null
+++ b/src/libs/dimg/loaders/jpegsettings.cpp
@@ -0,0 +1,154 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save JPEG image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqstring.h>
+#include <tqlabel.h>
+#include <tqlayout.h>
+#include <tqwhatsthis.h>
+#include <tqcombobox.h>
+
+// KDE includes.
+
+#include <tdelocale.h>
+#include <kdialog.h>
+#include <knuminput.h>
+#include <kactivelabel.h>
+
+// Local includes.
+
+#include "jpegsettings.h"
+#include "jpegsettings.moc"
+
+namespace Digikam
+{
+
+class JPEGSettingsPriv
+{
+
+public:
+
+ JPEGSettingsPriv()
+ {
+ JPEGGrid = 0;
+ labelJPEGcompression = 0;
+ JPEGcompression = 0;
+ labelWarning = 0;
+ labelSubSampling = 0;
+ subSamplingCB = 0;
+ }
+
+ TQGridLayout *JPEGGrid;
+
+ TQLabel *labelJPEGcompression;
+ TQLabel *labelSubSampling;
+
+ TQComboBox *subSamplingCB;
+
+ KActiveLabel *labelWarning;
+
+ KIntNumInput *JPEGcompression;
+};
+
+JPEGSettings::JPEGSettings(TQWidget *parent)
+ : TQWidget(parent, 0, TQt::WDestructiveClose)
+{
+ d = new JPEGSettingsPriv;
+
+ d->JPEGGrid = new TQGridLayout(this, 1, 2, KDialog::spacingHint());
+ d->JPEGcompression = new KIntNumInput(75, this);
+ d->JPEGcompression->setRange(1, 100, 1, true );
+ d->labelJPEGcompression = new TQLabel(i18n("JPEG quality:"), this);
+
+ TQWhatsThis::add(d->JPEGcompression, i18n("<p>The JPEG image quality:<p>"
+ "<b>1</b>: low quality (high compression and small "
+ "file size)<p>"
+ "<b>50</b>: medium quality<p>"
+ "<b>75</b>: good quality (default)<p>"
+ "<b>100</b>: high quality (no compression and "
+ "large file size)<p>"
+ "<b>Note: JPEG always uses lossy compression.</b>"));
+
+ d->labelWarning = new KActiveLabel(i18n("<qt><font size=-1 color=\"red\"><i>"
+ "Warning: <a href='http://en.wikipedia.org/wiki/JPEG'>JPEG</a> is a<br>"
+ "lossy compression<br>"
+ "image format!</p>"
+ "</i></qt>"), this);
+
+ d->labelWarning->setFrameStyle(TQFrame::Box | TQFrame::Plain);
+ d->labelWarning->setLineWidth(1);
+ d->labelWarning->setFrameShape(TQFrame::Box);
+
+ d->labelSubSampling = new TQLabel(i18n("Chroma subsampling:"), this);
+
+ d->subSamplingCB = new TQComboBox(false, this);
+ d->subSamplingCB->insertItem(i18n("None")); // 1x1, 1x1, 1x1 (4:4:4)
+ d->subSamplingCB->insertItem(i18n("Medium")); // 2x1, 1x1, 1x1 (4:2:2)
+ d->subSamplingCB->insertItem(i18n("High")); // 2x2, 1x1, 1x1 (4:1:1)
+ TQWhatsThis::add(d->subSamplingCB, i18n("<p>JPEG Chroma subsampling level \n(color is saved with less resolution " "than luminance):<p>"
+ "<b>None</b>=best: uses 4:4:4 ratio. Does not employ chroma "
+ "subsampling at all. This preserves edges and contrasting "
+ "colors, whilst adding no additional compression<p>"
+ "<b>Medium</b>: uses 4:2:2 ratio. Medium compression: reduces "
+ "the color resolution by one-third with little to "
+ "no visual difference<p>"
+ "<b>High</b>: use 4:1:1 ratio. High compression: suits "
+ "images with soft edges but tends to alter colors<p>"
+ "<b>Note: JPEG always uses lossy compression.</b>"));
+
+ d->JPEGGrid->addMultiCellWidget(d->labelJPEGcompression, 0, 0, 0, 0);
+ d->JPEGGrid->addMultiCellWidget(d->JPEGcompression, 0, 0, 1, 1);
+ d->JPEGGrid->addMultiCellWidget(d->labelSubSampling, 1, 1, 0, 0);
+ d->JPEGGrid->addMultiCellWidget(d->subSamplingCB, 1, 1, 1, 1);
+ d->JPEGGrid->addMultiCellWidget(d->labelWarning, 0, 1, 2, 2);
+ d->JPEGGrid->setColStretch(1, 10);
+ d->JPEGGrid->setRowStretch(2, 10);
+}
+
+JPEGSettings::~JPEGSettings()
+{
+ delete d;
+}
+
+void JPEGSettings::setCompressionValue(int val)
+{
+ d->JPEGcompression->setValue(val);
+}
+
+int JPEGSettings::getCompressionValue()
+{
+ return d->JPEGcompression->value();
+}
+
+void JPEGSettings::setSubSamplingValue(int val)
+{
+ d->subSamplingCB->setCurrentItem(val);
+}
+
+int JPEGSettings::getSubSamplingValue()
+{
+ return d->subSamplingCB->currentItem();
+}
+
+} // namespace Digikam
diff --git a/src/libs/dimg/loaders/jpegsettings.h b/src/libs/dimg/loaders/jpegsettings.h
new file mode 100644
index 00000000..70e20d42
--- /dev/null
+++ b/src/libs/dimg/loaders/jpegsettings.h
@@ -0,0 +1,63 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save JPEG image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef JPEGSETTINGS_H
+#define JPEGSETTINGS_H
+
+// KDE includes.
+
+#include <tqwidget.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class JPEGSettingsPriv;
+
+class DIGIKAM_EXPORT JPEGSettings : public TQWidget
+{
+TQ_OBJECT
+
+
+public:
+
+ JPEGSettings(TQWidget *parent=0);
+ ~JPEGSettings();
+
+ void setCompressionValue(int val);
+ int getCompressionValue();
+
+ void setSubSamplingValue(int val);
+ int getSubSamplingValue();
+
+private:
+
+ JPEGSettingsPriv* d;
+};
+
+} // namespace Digikam
+
+#endif /* JPEGSETTINGS_H */
diff --git a/src/libs/dimg/loaders/pngloader.cpp b/src/libs/dimg/loaders/pngloader.cpp
new file mode 100644
index 00000000..402cf944
--- /dev/null
+++ b/src/libs/dimg/loaders/pngloader.cpp
@@ -0,0 +1,993 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-01
+ * Description : a PNG image loader for DImg framework.
+ *
+ * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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
+
+#define PNG_BYTES_TO_CHECK 4
+
+// C Ansi includes.
+
+extern "C"
+{
+#include <unistd.h>
+#include <stdarg.h>
+}
+
+// C++ includes.
+
+#include <cstdlib>
+#include <cstdio>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqcstring.h>
+
+// Local includes.
+
+#include "daboutdata.h"
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "pngloader.h"
+
+namespace Digikam
+{
+
+#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 )
+ typedef png_bytep iCCP_data;
+#else
+ typedef png_charp iCCP_data;
+#endif
+
+PNGLoader::PNGLoader(DImg* image)
+ : DImgLoader(image)
+{
+ m_hasAlpha = false;
+ m_sixteenBit = false;
+}
+
+bool PNGLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ png_uint_32 w32, h32;
+ int width, height;
+ FILE *f;
+ int bit_depth, color_type, interlace_type;
+ png_structp png_ptr = NULL;
+ png_infop info_ptr = NULL;
+
+ readMetadata(filePath, DImg::PNG);
+
+ // -------------------------------------------------------------------
+ // Open the file
+
+ f = fopen(TQFile::encodeName(filePath), "rb");
+ if ( !f )
+ {
+ DDebug() << k_funcinfo << "Cannot open image file." << endl;
+ return false;
+ }
+
+ unsigned char buf[PNG_BYTES_TO_CHECK];
+
+ fread(buf, 1, PNG_BYTES_TO_CHECK, f);
+ if (png_sig_cmp(buf, 0, PNG_BYTES_TO_CHECK))
+ {
+ DDebug() << k_funcinfo << "Not a PNG image file." << endl;
+ fclose(f);
+ return false;
+ }
+ rewind(f);
+
+ // -------------------------------------------------------------------
+ // Initialize the internal structures
+
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ DDebug() << k_funcinfo << "Invalid PNG image file structure." << endl;
+ fclose(f);
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (!info_ptr)
+ {
+ DDebug() << k_funcinfo << "Cannot reading PNG image file structure." << endl;
+ png_destroy_read_struct(&png_ptr, NULL, NULL);
+ fclose(f);
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // PNG error handling. If an error occurs during reading, libpng
+ // will jump here
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ DDebug() << k_funcinfo << "Internal libPNG error during reading file. Process aborted!" << endl;
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ fclose(f);
+ return false;
+ }
+
+ png_init_io(png_ptr, f);
+
+ // -------------------------------------------------------------------
+ // Read all PNG info up to image data
+
+ png_read_info(png_ptr, info_ptr);
+
+ png_get_IHDR(png_ptr, info_ptr, (png_uint_32 *) (&w32),
+ (png_uint_32 *) (&h32), &bit_depth, &color_type,
+ &interlace_type, NULL, NULL);
+
+ width = (int)w32;
+ height = (int)h32;
+
+ // TODO: Endianness:
+ // You may notice that the code for little and big endian
+ // below is now identical. This was found to work by PPC users.
+ // If this proves right, all the conditional clauses can be removed.
+
+ if (bit_depth == 16)
+ {
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in 16 bits/color/pixel." << endl;
+#endif
+ m_sixteenBit = true;
+
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_RGB : // RGB
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB" << endl;
+#endif
+ m_hasAlpha = false;
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA : // RGBA
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA" << endl;
+#endif
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY : // Grayscale
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY" << endl;
+#endif
+ png_set_gray_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA : // Grayscale + Alpha
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA" << endl;
+#endif
+ png_set_gray_to_rgb(png_ptr);
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE : // Indexed
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_PALETTE" << endl;
+#endif
+ png_set_palette_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFFFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ default:
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << "PNG color type unknown." << endl;
+#endif
+ return false;
+ }
+ }
+ else
+ {
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << "PNG in >=8 bits/color/pixel." << endl;
+#endif
+ m_sixteenBit = false;
+ png_set_packing(png_ptr);
+
+ switch (color_type)
+ {
+ case PNG_COLOR_TYPE_RGB : // RGB
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB" << endl;
+#endif
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA : // RGBA
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_RGB_ALPHA" << endl;
+#endif
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY : // Grayscale
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY" << endl;
+#endif
+ png_set_expand_gray_1_2_4_to_8(png_ptr);
+ png_set_gray_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = false;
+ break;
+
+ case PNG_COLOR_TYPE_GRAY_ALPHA : // Grayscale + alpha
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_GRAY_ALPHA" << endl;
+#endif
+ png_set_gray_to_rgb(png_ptr);
+ m_hasAlpha = true;
+ break;
+
+ case PNG_COLOR_TYPE_PALETTE : // Indexed
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "PNG in PNG_COLOR_TYPE_PALETTE" << endl;
+#endif
+ png_set_packing(png_ptr);
+ png_set_palette_to_rgb(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+ else // PPC
+ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER);
+
+ m_hasAlpha = true;
+ break;
+
+ default:
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << k_funcinfo << "PNG color type unknown." << endl;
+#endif
+ return false;
+ }
+ }
+
+ if(png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(png_ptr);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_bgr(png_ptr);
+ else // PPC
+ png_set_bgr(png_ptr);
+ //png_set_swap_alpha(png_ptr);
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // -------------------------------------------------------------------
+ // Get image data.
+
+ // Call before png_read_update_info and png_start_read_image()
+ // For non-interlaced images number_passes will be 1
+ int number_passes = png_set_interlace_handling(png_ptr);
+
+ png_read_update_info(png_ptr, info_ptr);
+
+ uchar *data = 0;
+
+ if (m_sixteenBit)
+ data = new uchar[width*height*8]; // 16 bits/color/pixel
+ else
+ data = new uchar[width*height*4]; // 8 bits/color/pixel
+
+ uchar **lines = 0;
+ lines = (uchar **)malloc(height * sizeof(uchar *));
+ if (!lines)
+ {
+ DDebug() << k_funcinfo << "Cannot allocate memory to load PNG image data." << endl;
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+ delete [] data;
+ return false;
+ }
+
+ for (int i = 0; i < height; i++)
+ {
+ if (m_sixteenBit)
+ lines[i] = data + (i * width * 8);
+ else
+ lines[i] = data + (i * width * 4);
+ }
+
+ // The easy way to read the whole image
+ // png_read_image(png_ptr, lines);
+ // The other way to read images is row by row. Necessary for observer.
+ // Now we need to deal with interlacing.
+
+ for (int pass = 0; pass < number_passes; pass++)
+ {
+ int y;
+ int checkPoint = 0;
+ for (y = 0; y < height; y++)
+ {
+ if (observer && y == checkPoint)
+ {
+ checkPoint += granularity(observer, height, 0.7);
+ if (!observer->continueQuery(m_image))
+ {
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+ delete [] data;
+ free(lines);
+ return false;
+ }
+ // use 10% - 80% for progress while reading rows
+ observer->progressInfo(m_image, 0.1 + (0.7 * ( ((float)y)/((float)height) )) );
+ }
+
+ png_read_rows(png_ptr, lines+y, NULL, 1);
+ }
+ }
+
+ free(lines);
+
+ // Swap bytes in 16 bits/color/pixel for DImg
+
+ if (m_sixteenBit)
+ {
+ uchar ptr[8]; // One pixel to swap
+
+ for (int p = 0; p < width*height*8; p+=8)
+ {
+ memcpy (&ptr[0], &data[p], 8); // Current pixel
+
+ data[ p ] = ptr[1]; // Blue
+ data[p+1] = ptr[0];
+ data[p+2] = ptr[3]; // Green
+ data[p+3] = ptr[2];
+ data[p+4] = ptr[5]; // Red
+ data[p+5] = ptr[4];
+ data[p+6] = ptr[7]; // Alpha
+ data[p+7] = ptr[6];
+ }
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.9);
+
+ // -------------------------------------------------------------------
+ // Read image ICC profile
+
+ TQMap<int, TQByteArray>& metaData = imageMetaData();
+
+#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 )
+ png_charp profile_name;
+ iCCP_data profile_data=NULL;
+#else
+ png_charp profile_name, profile_data=NULL;
+#endif
+ png_uint_32 profile_size;
+ int compression_type;
+
+ png_get_iCCP(png_ptr, info_ptr, &profile_name, &compression_type, &profile_data, &profile_size);
+
+ if (profile_data != NULL)
+ {
+ TQByteArray profile_rawdata(profile_size);
+ memcpy(profile_rawdata.data(), profile_data, profile_size);
+ metaData.insert(DImg::ICC, profile_rawdata);
+ }
+ else
+ {
+ // If ICC profile is null, check Exif metadata.
+ checkExifWorkingColorSpace();
+ }
+
+ // -------------------------------------------------------------------
+ // Get embbeded text data.
+
+ png_text* text_ptr;
+ int num_comments = png_get_text(png_ptr, info_ptr, &text_ptr, NULL);
+
+ /*
+ Standard Embedded text includes in PNG :
+
+ Title Short (one line) title or caption for image
+ Author Name of image's creator
+ Description Description of image (possibly long)
+ Copyright Copyright notice
+ Creation Time Time of original image creation
+ Software Software used to create the image
+ Disclaimer Legal disclaimer
+ Warning Warning of nature of content
+ Source Device used to create the image
+ Comment Miscellaneous comment; conversion from GIF comment
+
+ Extra Raw profiles tag are used by ImageMAgick and defines at this URL :
+ http://search.cpan.org/src/EXIFTOOL/Image-ExifTool-5.87/html/TagNames/PNG.html#TextualData
+ */
+
+ for (int i = 0; i < num_comments; i++)
+ {
+ // Check if we have a Raw profile embedded using ImageMagick technic.
+
+ if (memcmp(text_ptr[i].key, "Raw profile type exif", 21) != 0 ||
+ memcmp(text_ptr[i].key, "Raw profile type APP1", 21) != 0 ||
+ memcmp(text_ptr[i].key, "Raw profile type iptc", 21) != 0)
+ {
+ imageSetEmbbededText(text_ptr[i].key, text_ptr[i].text);
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "Reading PNG Embedded text: key=" << text_ptr[i].key
+ << " text=" << text_ptr[i].text << endl;
+#endif
+ }
+ }
+
+ // -------------------------------------------------------------------
+
+ png_read_end(png_ptr, info_ptr);
+ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
+ fclose(f);
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageWidth() = width;
+ imageHeight() = height;
+ imageData() = data;
+ imageSetAttribute("format", "PNG");
+
+ return true;
+}
+
+bool PNGLoader::save(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ FILE *f;
+ png_structp png_ptr;
+ png_infop info_ptr;
+ uchar *ptr, *data = 0;
+ uint x, y, j;
+ png_bytep row_ptr;
+ png_color_8 sig_bit;
+ int quality = 75;
+ int compression = 3;
+
+ // -------------------------------------------------------------------
+ // Open the file
+
+ f = fopen(TQFile::encodeName(filePath), "wb");
+ if ( !f )
+ {
+ DDebug() << k_funcinfo << "Cannot open target image file." << endl;
+ return false;
+ }
+
+
+ // -------------------------------------------------------------------
+ // Initialize the internal structures
+
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ if (!png_ptr)
+ {
+ DDebug() << k_funcinfo << "Invalid target PNG image file structure." << endl;
+ fclose(f);
+ return false;
+ }
+
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL)
+ {
+ DDebug() << k_funcinfo << "Cannot create PNG image file structure." << endl;
+ png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
+ fclose(f);
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // PNG error handling. If an error occurs during writing, libpng
+ // will jump here
+
+ if (setjmp(png_jmpbuf(png_ptr)))
+ {
+ DDebug() << k_funcinfo << "Internal libPNG error during writing file. Process aborted!" << endl;
+ fclose(f);
+ png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
+ png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
+ return false;
+ }
+
+ png_init_io(png_ptr, f);
+
+ if (TQImage::systemByteOrder() == TQImage::LittleEndian) // Intel
+ png_set_bgr(png_ptr);
+ else // PPC
+ png_set_swap_alpha(png_ptr);
+
+ if (imageHasAlpha())
+ {
+ png_set_IHDR(png_ptr, info_ptr, imageWidth(), imageHeight(), imageBitsDepth(),
+ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (imageSixteenBit())
+ data = new uchar[imageWidth() * 8 * sizeof(uchar)];
+ else
+ data = new uchar[imageWidth() * 4 * sizeof(uchar)];
+ }
+ else
+ {
+ png_set_IHDR(png_ptr, info_ptr, imageWidth(), imageHeight(), imageBitsDepth(),
+ PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
+ PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
+
+ if (imageSixteenBit())
+ data = new uchar[imageWidth() * 6 * sizeof(uchar)];
+ else
+ data = new uchar[imageWidth() * 3 * sizeof(uchar)];
+ }
+
+ sig_bit.red = imageBitsDepth();
+ sig_bit.green = imageBitsDepth();
+ sig_bit.blue = imageBitsDepth();
+ sig_bit.alpha = imageBitsDepth();
+ png_set_sBIT(png_ptr, info_ptr, &sig_bit);
+
+ // -------------------------------------------------------------------
+ // Quality to convert to compression
+
+ TQVariant qualityAttr = imageGetAttribute("quality");
+ quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
+
+ if (quality < 1)
+ quality = 1;
+ if (quality > 99)
+ quality = 99;
+
+ quality = quality / 10;
+ compression = 9 - quality;
+
+ if (compression < 0)
+ compression = 0;
+ if (compression > 9)
+ compression = 9;
+
+ png_set_compression_level(png_ptr, compression);
+
+ // -------------------------------------------------------------------
+ // Write ICC profil.
+
+ TQByteArray profile_rawdata = m_image->getICCProfil();
+
+ if (!profile_rawdata.isEmpty())
+ {
+#if PNG_LIBPNG_VER_MAJOR > 1 || ( PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR >= 5 )
+ png_set_iCCP(png_ptr, info_ptr, (png_charp)("icc"), PNG_COMPRESSION_TYPE_BASE, reinterpret_cast<iCCP_data>(profile_rawdata.data()), profile_rawdata.size());
+#else
+ png_set_iCCP(png_ptr, info_ptr, (png_charp)"icc", PNG_COMPRESSION_TYPE_BASE, profile_rawdata.data(), profile_rawdata.size());
+#endif
+ }
+
+ // -------------------------------------------------------------------
+ // Write embbeded Text
+
+ typedef TQMap<TQString, TQString> EmbeddedTextMap;
+ EmbeddedTextMap map = imageEmbeddedText();
+
+ for (EmbeddedTextMap::iterator it = map.begin(); it != map.end(); ++it)
+ {
+ if (it.key() != TQString("Software") && it.key() != TQString("Comment"))
+ {
+ png_text text;
+ text.key = (char*)it.key().ascii();
+ text.text = (char*)it.data().ascii();
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "Writing PNG Embedded text: key=" << text.key << " text=" << text.text << endl;
+#endif
+ text.compression = PNG_TEXT_COMPRESSION_zTXt;
+ png_set_text(png_ptr, info_ptr, &(text), 1);
+ }
+ }
+
+ // Update 'Software' text tag.
+ TQString software("digiKam ");
+ software.append(digikam_version);
+ TQString libpngver(PNG_HEADER_VERSION_STRING);
+ libpngver.replace('\n', ' ');
+ software.append(TQString(" (%1)").arg(libpngver));
+ png_text text;
+ text.key = (png_charp)("Software");
+ text.text = (char *)software.ascii();
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "Writing PNG Embedded text: key=" << text.key << " text=" << text.text << endl;
+#endif
+ text.compression = PNG_TEXT_COMPRESSION_zTXt;
+ png_set_text(png_ptr, info_ptr, &(text), 1);
+
+ // Write embedded Raw profiles metadata (Exif/Iptc) in text tag using ImageMagick technic.
+ // Write digiKam comment like an iTXt chunk using UTF8 encoding.
+ // NOTE: iTXt will be enable by default with libpng >= 1.3.0.
+
+ typedef TQMap<int, TQByteArray> MetaDataMap;
+ MetaDataMap metaDataMap = imageMetaData();
+
+ for (MetaDataMap::iterator it = metaDataMap.begin(); it != metaDataMap.end(); ++it)
+ {
+ TQByteArray ba = it.data();
+
+ switch (it.key())
+ {
+
+#ifdef PNG_iTXt_SUPPORTED
+
+ // TODO : this code is not yet tested. It require libpng 1.3.0.
+
+ case(DImg::COM):
+ {
+ png_text comment;
+ comment.key = "Comment";
+ comment.text = ba.data();
+ comment.itxt_length = ba.size();
+ comment.compression = PNG_ITXT_COMPRESSION_zTXt;
+ png_set_text(png_ptr, info_ptr, &(comment), 1);
+
+ DDebug() << "Writing digiKam comment into iTXt PNG chunk : " << ba << endl;
+ break;
+ }
+#endif
+
+ case(DImg::EXIF):
+ {
+ const uchar ExifHeader[] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00};
+ TQByteArray profile;
+
+ // If bytes array do not start with ImageMagick header, Exif metadata have been created from
+ // scratch using Exiv2. In this case, we need to add Exif header from start.
+ if (memcmp(ba.data(), "exif", 4) != 0 &&
+ memcmp(ba.data(), "iptc", 4) != 0 &&
+ memcmp(ba.data(), "profile", 7) != 0)
+ {
+ profile = TQByteArray(ba.size() + sizeof(ExifHeader));
+ memcpy(profile.data(), ExifHeader, sizeof(ExifHeader));
+ memcpy(profile.data()+sizeof(ExifHeader), ba.data(), ba.size());
+ }
+ else
+ {
+ profile = ba;
+ }
+
+ writeRawProfile(png_ptr, info_ptr, (png_charp)("exif"), profile.data(), (png_uint_32) profile.size());
+ break;
+ }
+ case(DImg::IPTC):
+ {
+ writeRawProfile(png_ptr, info_ptr, (png_charp)("iptc"), ba.data(), (png_uint_32) ba.size());
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.2);
+
+ // -------------------------------------------------------------------
+ // Write image data
+
+ png_write_info(png_ptr, info_ptr);
+ png_set_shift(png_ptr, &sig_bit);
+ png_set_packing(png_ptr);
+ ptr = imageData();
+
+ uint checkPoint = 0;
+ for (y = 0; y < imageHeight(); y++)
+ {
+
+ if (observer && y == checkPoint)
+ {
+ checkPoint += granularity(observer, imageHeight(), 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ fclose(f);
+ png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
+ png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.2 + (0.8 * ( ((float)y)/((float)imageHeight()) )));
+ }
+
+ j = 0;
+
+ for (x = 0; x < imageWidth()*imageBytesDepth(); x+=imageBytesDepth())
+ {
+ if (imageSixteenBit())
+ {
+ if (imageHasAlpha())
+ {
+ data[j++] = ptr[x+1]; // Blue
+ data[j++] = ptr[ x ];
+ data[j++] = ptr[x+3]; // Green
+ data[j++] = ptr[x+2];
+ data[j++] = ptr[x+5]; // Red
+ data[j++] = ptr[x+4];
+ data[j++] = ptr[x+7]; // Alpha
+ data[j++] = ptr[x+6];
+ }
+ else
+ {
+ data[j++] = ptr[x+1]; // Blue
+ data[j++] = ptr[ x ];
+ data[j++] = ptr[x+3]; // Green
+ data[j++] = ptr[x+2];
+ data[j++] = ptr[x+5]; // Red
+ data[j++] = ptr[x+4];
+ }
+ }
+ else
+ {
+ if (imageHasAlpha())
+ {
+ data[j++] = ptr[ x ]; // Blue
+ data[j++] = ptr[x+1]; // Green
+ data[j++] = ptr[x+2]; // Red
+ data[j++] = ptr[x+3]; // Alpha
+ }
+ else
+ {
+ data[j++] = ptr[ x ]; // Blue
+ data[j++] = ptr[x+1]; // Green
+ data[j++] = ptr[x+2]; // Red
+ }
+ }
+ }
+
+ row_ptr = (png_bytep) data;
+
+ png_write_rows(png_ptr, &row_ptr, 1);
+ ptr += (imageWidth() * imageBytesDepth());
+ }
+
+ delete [] data;
+
+ // -------------------------------------------------------------------
+
+ png_write_end(png_ptr, info_ptr);
+ png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr);
+ png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr);
+
+ fclose(f);
+
+ imageSetAttribute("savedformat", "PNG");
+
+ saveMetadata(filePath);
+
+ return true;
+}
+
+bool PNGLoader::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+bool PNGLoader::sixteenBit() const
+{
+ return m_sixteenBit;
+}
+
+void PNGLoader::writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type,
+ char *profile_data, png_uint_32 length)
+{
+ png_textp text;
+
+ long i;
+
+ uchar *sp;
+
+ png_charp dp;
+
+ png_uint_32 allocated_length, description_length;
+
+ const uchar hex[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
+
+ DDebug() << "Writing Raw profile: type=" << profile_type << ", length=" << length << endl;
+
+ text = (png_textp) png_malloc(ping, (png_uint_32) sizeof(png_text));
+ description_length = strlen((const char *) profile_type);
+ allocated_length = (png_uint_32) (length*2 + (length >> 5) + 20 + description_length);
+
+ text[0].text = (png_charp) png_malloc(ping, allocated_length);
+ text[0].key = (png_charp) png_malloc(ping, (png_uint_32) 80);
+ text[0].key[0] = '\0';
+
+ concatenateString(text[0].key, "Raw profile type ", 4096);
+ concatenateString(text[0].key, (const char *) profile_type, 62);
+
+ sp = (uchar*)profile_data;
+ dp = text[0].text;
+ *dp++='\n';
+
+ copyString(dp, (const char *) profile_type, allocated_length);
+
+ dp += description_length;
+ *dp++='\n';
+
+ formatString(dp, allocated_length-strlen(text[0].text), "%8lu ", length);
+
+ dp += 8;
+
+ for (i=0; i < (long) length; i++)
+ {
+ if (i%36 == 0)
+ *dp++='\n';
+
+ *(dp++)=(char) hex[((*sp >> 4) & 0x0f)];
+ *(dp++)=(char) hex[((*sp++ ) & 0x0f)];
+ }
+
+ *dp++='\n';
+ *dp='\0';
+ text[0].text_length = (png_size_t) (dp-text[0].text);
+ text[0].compression = -1;
+
+ if (text[0].text_length <= allocated_length)
+ png_set_text(ping, ping_info,text, 1);
+
+ png_free(ping, text[0].text);
+ png_free(ping, text[0].key);
+ png_free(ping, text);
+}
+
+size_t PNGLoader::concatenateString(char *destination, const char *source, const size_t length)
+{
+ char *q;
+
+ const char *p;
+
+ size_t i;
+
+ size_t count;
+
+ if ( !destination || !source || length == 0 )
+ return 0;
+
+ p = source;
+ q = destination;
+ i = length;
+
+ while ((i-- != 0) && (*q != '\0'))
+ q++;
+
+ count = (size_t) (q-destination);
+ i = length-count;
+
+ if (i == 0)
+ return(count+strlen(p));
+
+ while (*p != '\0')
+ {
+ if (i != 1)
+ {
+ *q++=(*p);
+ i--;
+ }
+ p++;
+ }
+
+ *q='\0';
+
+ return(count+(p-source));
+}
+
+size_t PNGLoader::copyString(char *destination, const char *source, const size_t length)
+{
+ char *q;
+
+ const char *p;
+
+ size_t i;
+
+ if ( !destination || !source || length == 0 )
+ return 0;
+
+ p = source;
+ q = destination;
+ i = length;
+
+ if ((i != 0) && (--i != 0))
+ {
+ do
+ {
+ if ((*q++=(*p++)) == '\0')
+ break;
+ }
+ while (--i != 0);
+ }
+
+ if (i == 0)
+ {
+ if (length != 0)
+ *q='\0';
+
+ do
+ {
+ }
+ while (*p++ != '\0');
+ }
+
+ return((size_t) (p-source-1));
+}
+
+long PNGLoader::formatString(char *string, const size_t length, const char *format,...)
+{
+ long n;
+
+ va_list operands;
+
+ va_start(operands,format);
+ n = (long) formatStringList(string, length, format, operands);
+ va_end(operands);
+ return(n);
+}
+
+long PNGLoader::formatStringList(char *string, const size_t length, const char *format, va_list operands)
+{
+ int n = vsnprintf(string, length, format, operands);
+
+ if (n < 0)
+ string[length-1] = '\0';
+
+ return((long) n);
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/pngloader.h b/src/libs/dimg/loaders/pngloader.h
new file mode 100644
index 00000000..202a278c
--- /dev/null
+++ b/src/libs/dimg/loaders/pngloader.h
@@ -0,0 +1,73 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-01
+ * Description : a PNG image loader for DImg framework.
+ *
+ * Copyright (C) 2005-2009 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef PNGLOADER_H
+#define PNGLOADER_H
+
+extern "C"
+{
+#include <stdarg.h>
+#include <png.h>
+}
+
+// Local includes.
+
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+class DImg;
+
+class DIGIKAM_EXPORT PNGLoader : public DImgLoader
+{
+public:
+
+ PNGLoader(DImg* image);
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer);
+ bool save(const TQString& filePath, DImgLoaderObserver *observer);
+
+ virtual bool hasAlpha() const;
+ virtual bool sixteenBit() const;
+ virtual bool isReadOnly() const { return false; };
+
+private:
+
+ void writeRawProfile(png_struct *ping, png_info *ping_info, char *profile_type,
+ char *profile_data, png_uint_32 length);
+
+ size_t concatenateString(char *destination, const char *source, const size_t length);
+ size_t copyString(char *destination, const char *source, const size_t length);
+ long formatString(char *string, const size_t length, const char *format,...);
+ long formatStringList(char *string, const size_t length, const char *format, va_list operands);
+
+private:
+
+ bool m_sixteenBit;
+ bool m_hasAlpha;
+};
+
+} // NameSpace Digikam
+
+#endif /* PNGLOADER_H */
diff --git a/src/libs/dimg/loaders/pngsettings.cpp b/src/libs/dimg/loaders/pngsettings.cpp
new file mode 100644
index 00000000..ce39219b
--- /dev/null
+++ b/src/libs/dimg/loaders/pngsettings.cpp
@@ -0,0 +1,102 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save PNG image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqstring.h>
+#include <tqlabel.h>
+#include <tqlayout.h>
+#include <tqwhatsthis.h>
+
+// KDE includes.
+
+#include <tdelocale.h>
+#include <kdialog.h>
+#include <knuminput.h>
+
+// Local includes.
+
+#include "pngsettings.h"
+#include "pngsettings.moc"
+
+namespace Digikam
+{
+
+class PNGSettingsPriv
+{
+
+public:
+
+ PNGSettingsPriv()
+ {
+ PNGGrid = 0;
+ labelPNGcompression = 0;
+ PNGcompression = 0;
+ }
+
+ TQGridLayout *PNGGrid;
+
+ TQLabel *labelPNGcompression;
+
+ KIntNumInput *PNGcompression;
+};
+
+PNGSettings::PNGSettings(TQWidget *parent)
+ : TQWidget(parent, 0, TQt::WDestructiveClose)
+{
+ d = new PNGSettingsPriv;
+
+ d->PNGGrid = new TQGridLayout(this, 1, 1, KDialog::spacingHint());
+ d->PNGcompression = new KIntNumInput(9, this);
+ d->PNGcompression->setRange(1, 9, 1, true );
+ d->labelPNGcompression = new TQLabel(i18n("PNG compression:"), this);
+
+ TQWhatsThis::add(d->PNGcompression, i18n("<p>The compression value for PNG images:<p>"
+ "<b>1</b>: low compression (large file size but "
+ "short compression duration - default)<p>"
+ "<b>5</b>: medium compression<p>"
+ "<b>9</b>: high compression (small file size but "
+ "long compression duration)<p>"
+ "<b>Note: PNG is always a lossless image "
+ "compression format.</b>"));
+ d->PNGGrid->addMultiCellWidget(d->labelPNGcompression, 0, 0, 0, 0);
+ d->PNGGrid->addMultiCellWidget(d->PNGcompression, 0, 0, 1, 1);
+ d->PNGGrid->setColStretch(1, 10);
+}
+
+PNGSettings::~PNGSettings()
+{
+ delete d;
+}
+
+void PNGSettings::setCompressionValue(int val)
+{
+ d->PNGcompression->setValue(val);
+}
+
+int PNGSettings::getCompressionValue()
+{
+ return d->PNGcompression->value();
+}
+
+} // namespace Digikam
diff --git a/src/libs/dimg/loaders/pngsettings.h b/src/libs/dimg/loaders/pngsettings.h
new file mode 100644
index 00000000..aecca935
--- /dev/null
+++ b/src/libs/dimg/loaders/pngsettings.h
@@ -0,0 +1,60 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save PNG image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef PNGSETTINGS_H
+#define PNGSETTINGS_H
+
+// KDE includes.
+
+#include <tqwidget.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class PNGSettingsPriv;
+
+class DIGIKAM_EXPORT PNGSettings : public TQWidget
+{
+TQ_OBJECT
+
+
+public:
+
+ PNGSettings(TQWidget *parent=0);
+ ~PNGSettings();
+
+ void setCompressionValue(int val);
+ int getCompressionValue();
+
+private:
+
+ PNGSettingsPriv* d;
+};
+
+} // namespace Digikam
+
+#endif /* PNGSETTINGS_H */
diff --git a/src/libs/dimg/loaders/ppmloader.cpp b/src/libs/dimg/loaders/ppmloader.cpp
new file mode 100644
index 00000000..15c19423
--- /dev/null
+++ b/src/libs/dimg/loaders/ppmloader.cpp
@@ -0,0 +1,178 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-21-11
+ * Description : A 16 bits/color/pixel PPM IO file for
+ * DImg framework
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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"
+{
+#include <unistd.h>
+}
+
+// C++ includes.
+
+#include <cstdio>
+#include <cmath>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqimage.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "ppmloader.h"
+
+namespace Digikam
+{
+
+PPMLoader::PPMLoader(DImg* image)
+ : DImgLoader(image)
+{
+}
+
+bool PPMLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ //TODO: progress information
+ int width, height, rgbmax;
+ char nl;
+
+ FILE *file = fopen(TQFile::encodeName(filePath), "rb");
+ if (!file)
+ {
+ DDebug() << k_funcinfo << "Cannot open image file." << endl;
+ return false;
+ }
+
+ ushort header;
+
+ if (fread(&header, 2, 1, file) != 1)
+ {
+ DDebug() << k_funcinfo << "Cannot read header of file." << endl;
+ fclose(file);
+ return false;
+ }
+
+ uchar* c = (uchar*) &header;
+ if (*c != 'P')
+ {
+ DDebug() << k_funcinfo << "Not a PPM file." << endl;
+ fclose(file);
+ return false;
+ }
+
+ c++;
+ if (*c != '6')
+ {
+ DDebug() << k_funcinfo << "Not a PPM file." << endl;
+ fclose(file);
+ return false;
+ }
+
+ rewind(file);
+
+ if (fscanf (file, "P6 %d %d %d%c", &width, &height, &rgbmax, &nl) != 4)
+ {
+ DDebug() << "Corrupted PPM file." << endl;
+ pclose (file);
+ return false;
+ }
+
+ if (rgbmax <= 255)
+ {
+ DDebug() << k_funcinfo << "Not a 16 bits per color per pixel PPM file." << endl;
+ pclose (file);
+ return false;
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ unsigned short *data;
+
+ data = new unsigned short[width*height*4];
+ unsigned short *dst = data;
+ uchar src[6];
+ float fac = 65535.0 / rgbmax;
+ int checkpoint = 0;
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ DDebug() << "rgbmax=" << rgbmax << " fac=" << fac << endl;
+#endif
+
+ for (int h = 0; h < height; h++)
+ {
+
+ if (observer && h == checkpoint)
+ {
+ checkpoint += granularity(observer, height, 0.9);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ pclose( file );
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.9 * ( ((float)h)/((float)height) )));
+ }
+
+ for (int w = 0; w < width; w++)
+ {
+
+ fread (src, 6 *sizeof(unsigned char), 1, file);
+
+ dst[0] = (unsigned short)((src[4]*256 + src[5]) * fac); // Blue
+ dst[1] = (unsigned short)((src[2]*256 + src[3]) * fac); // Green
+ dst[2] = (unsigned short)((src[0]*256 + src[1]) * fac); // Red
+ dst[3] = 0xFFFF;
+
+ dst += 4;
+ }
+ }
+
+ fclose( file );
+
+ //----------------------------------------------------------
+
+ imageWidth() = width;
+ imageHeight() = height;
+ imageData() = (uchar*)data;
+ imageSetAttribute("format", "PPM");
+
+ return true;
+}
+
+bool PPMLoader::save(const TQString& /*filePath*/, DImgLoaderObserver */*observer*/)
+{
+ return false;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/ppmloader.h b/src/libs/dimg/loaders/ppmloader.h
new file mode 100644
index 00000000..283fdd26
--- /dev/null
+++ b/src/libs/dimg/loaders/ppmloader.h
@@ -0,0 +1,59 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-21-11
+ * Description : A 16 bits/color/pixel PPM IO file for
+ * DImg framework
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2006 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef PPMLOADER_H
+#define PPMLOADER_H
+
+// Local includes.
+
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+class DImg;
+
+class DIGIKAM_EXPORT PPMLoader : public DImgLoader
+{
+public:
+
+ PPMLoader(DImg* image);
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer);
+ bool save(const TQString& filePath, DImgLoaderObserver *observer);
+
+ virtual bool hasAlpha() const { return false; };
+ virtual bool sixteenBit() const { return true; };
+ virtual bool isReadOnly() const { return true; };
+
+private:
+
+ bool m_alpha;
+ bool m_sixteenBit;
+};
+
+} // NameSpace Digikam
+
+#endif /* PPMLOADER_H */
diff --git a/src/libs/dimg/loaders/qimageloader.cpp b/src/libs/dimg/loaders/qimageloader.cpp
new file mode 100644
index 00000000..f35335cf
--- /dev/null
+++ b/src/libs/dimg/loaders/qimageloader.cpp
@@ -0,0 +1,126 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : A TQImage loader for DImg framework.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2006-2007 by Caulier Gilles <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqimage.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "qimageloader.h"
+
+namespace Digikam
+{
+
+TQImageLoader::TQImageLoader(DImg* image)
+ : DImgLoader(image)
+{
+}
+
+bool TQImageLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ // Loading is opaque to us. No support for stopping from observer,
+ // progress info are only pseudo values
+ TQImage image(filePath);
+
+ if (observer)
+ observer->progressInfo(m_image, 0.9);
+
+ if (image.isNull())
+ {
+ DDebug() << "Cannot loading \"" << filePath << "\" using DImg::TQImageLoader!" << endl;
+ return false;
+ }
+
+ m_hasAlpha = image.hasAlphaBuffer();
+ TQImage target = image.convertDepth(32);
+
+ uint w = target.width();
+ uint h = target.height();
+ uchar* data = new uchar[w*h*4];
+ uint* sptr = (uint*)target.bits();
+ uchar* dptr = data;
+
+ for (uint i = 0 ; i < w*h ; i++)
+ {
+ dptr[0] = tqBlue(*sptr);
+ dptr[1] = tqGreen(*sptr);
+ dptr[2] = tqRed(*sptr);
+ dptr[3] = tqAlpha(*sptr);
+
+ dptr += 4;
+ sptr++;
+ }
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageWidth() = w;
+ imageHeight() = h;
+ imageData() = data;
+
+ // We considering that PNG is the most representative format of an image loaded by TQt
+ imageSetAttribute("format", "PNG");
+
+ return true;
+}
+
+bool TQImageLoader::save(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ TQVariant qualityAttr = imageGetAttribute("quality");
+ int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90;
+
+ if (quality < 0)
+ quality = 90;
+ if (quality > 100)
+ quality = 100;
+
+ TQVariant formatAttr = imageGetAttribute("format");
+ TQCString format = formatAttr.toCString();
+
+ TQImage image = m_image->copyTQImage();
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ // Saving is opaque to us. No support for stopping from observer,
+ // progress info are only pseudo values
+ bool success = image.save(filePath, format.upper(), quality);
+ if (observer && success)
+ observer->progressInfo(m_image, 1.0);
+
+ imageSetAttribute("format", format.upper());
+
+ return success;
+}
+
+bool TQImageLoader::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/qimageloader.h b/src/libs/dimg/loaders/qimageloader.h
new file mode 100644
index 00000000..f81cf7ef
--- /dev/null
+++ b/src/libs/dimg/loaders/qimageloader.h
@@ -0,0 +1,57 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : A TQImage loader for DImg framework.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2006-2007 by Caulier Gilles <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef TQIMAGELOADER_H
+#define TQIMAGELOADER_H
+
+// Local includes.
+
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+class DImg;
+
+class DIGIKAM_EXPORT TQImageLoader : public DImgLoader
+{
+public:
+
+ TQImageLoader(DImg* image);
+
+ virtual bool load(const TQString& filePath, DImgLoaderObserver *observer);
+ virtual bool save(const TQString& filePath, DImgLoaderObserver *observer);
+
+ virtual bool hasAlpha() const;
+ virtual bool sixteenBit() const { return false; };
+ virtual bool isReadOnly() const { return false; };
+
+private:
+
+ bool m_hasAlpha;
+};
+
+} // NameSpace Digikam
+
+#endif /* TQIMAGELOADER_H */
diff --git a/src/libs/dimg/loaders/rawloader.cpp b/src/libs/dimg/loaders/rawloader.cpp
new file mode 100644
index 00000000..8ecaa1f3
--- /dev/null
+++ b/src/libs/dimg/loaders/rawloader.cpp
@@ -0,0 +1,371 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-01
+ * Description : A digital camera RAW files loader for DImg
+ * framework using an external dcraw instance.
+ *
+ * Copyright (C) 2005-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2005-2008 by Marcel Wiesweg <[email protected]>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// C++ includes.
+
+#include <cmath>
+
+// TQt includes.
+
+#include <tqcstring.h>
+
+// KDE includes.
+
+#include <kstandarddirs.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "imagehistogram.h"
+#include "imagecurves.h"
+#include "imagelevels.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "bcgmodifier.h"
+#include "whitebalance.h"
+#include "rawloader.h"
+#include "rawloader.moc"
+
+namespace Digikam
+{
+
+RAWLoader::RAWLoader(DImg* image, DRawDecoding rawDecodingSettings)
+ : DImgLoader(image)
+{
+ m_rawDecodingSettings = rawDecodingSettings;
+ m_customRawSettings = rawDecodingSettings;
+ m_observer = 0;
+}
+
+bool RAWLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ m_observer = observer;
+
+ // We are using TDEProcess here, and make two assumptions:
+ // - there is an event loop (not for ioslaves)
+ // - we are not called from the event loop thread
+ // These assumptions are currently true for all use cases in digikam,
+ // except the thumbnails iosalve, which will set this attribute.
+ // I hope when porting to TQt4, all the event loop stuff (and this problem) can be removed.
+ if (imageGetAttribute("noeventloop").isValid())
+ return false;
+
+ readMetadata(filePath, DImg::RAW);
+
+ // NOTE: Here, we don't check a possible embedded work-space color profile using
+ // the method checkExifWorkingColorSpace() like with JPEG, PNG, and TIFF loaders,
+ // because RAW file are always in linear mode.
+
+ int width, height, rgbmax;
+ TQByteArray data;
+ if (!KDcrawIface::KDcraw::decodeRAWImage(filePath, m_rawDecodingSettings,
+ data, width, height, rgbmax))
+ return false;
+
+ return (loadedFromDcraw(data, width, height, rgbmax, observer));
+}
+
+bool RAWLoader::checkToCancelWaitingData()
+{
+ return (m_observer ? !m_observer->continueQuery(m_image) : false);
+}
+
+void RAWLoader::setWaitingDataProgress(double value)
+{
+ if (m_observer)
+ m_observer->progressInfo(m_image, value);
+}
+
+#if KDCRAW_VERSION < 0x000106
+bool RAWLoader::checkToCancelRecievingData()
+{
+ return (m_observer ? m_observer->isShuttingDown() : false);
+}
+
+void RAWLoader::setRecievingDataProgress(double value)
+{
+ if (m_observer)
+ m_observer->progressInfo(m_image, value);
+}
+#endif
+
+bool RAWLoader::loadedFromDcraw(TQByteArray data, int width, int height, int rgbmax,
+ DImgLoaderObserver *observer)
+{
+ int checkpoint = 0;
+
+ if (m_rawDecodingSettings.sixteenBitsImage) // 16 bits image
+ {
+ uchar *image = new uchar[width*height*8];
+
+ unsigned short *dst = (unsigned short *)image;
+ uchar *src = (uchar*)data.data();
+ float fac = 65535.0 / rgbmax;
+ checkpoint = 0;
+
+ for (int h = 0; h < height; h++)
+ {
+ if (observer && h == checkpoint)
+ {
+ checkpoint += granularity(observer, height, 1.0);
+ if (!observer->continueQuery(m_image))
+ {
+ return false;
+ }
+ observer->progressInfo(m_image, 0.7 + 0.2*(((float)h)/((float)height)) );
+ }
+
+ for (int w = 0; w < width; w++)
+ {
+#if KDCRAW_VERSION < 0x000106
+ dst[0] = (unsigned short)((src[4]*256 + src[5]) * fac); // Blue
+ dst[1] = (unsigned short)((src[2]*256 + src[3]) * fac); // Green
+ dst[2] = (unsigned short)((src[0]*256 + src[1]) * fac); // Red
+#else
+ dst[0] = (unsigned short)((src[5]*256 + src[4]) * fac); // Blue
+ dst[1] = (unsigned short)((src[3]*256 + src[2]) * fac); // Green
+ dst[2] = (unsigned short)((src[1]*256 + src[0]) * fac); // Red
+#endif
+ dst[3] = 0xFFFF;
+
+ dst += 4;
+ src += 6;
+ }
+ }
+
+
+#if KDCRAW_VERSION < 0x000106
+ // ----------------------------------------------------------
+
+ // Special case : if Color Management is not used here, output color space is in sRGB* color space
+ // RAW decoded image is a linear-histogram image with 16 bits color depth.
+ // No auto white balance and no gamma adjustemnts are performed. Image is a black hole.
+ // We need to reproduce all dcraw 8 bits color depth adjustements here.
+
+ if (m_rawDecodingSettings.outputColorSpace != DRawDecoding::RAWCOLOR)
+ {
+ ImageHistogram histogram(image, width, height, true);
+
+ int perc, val, total;
+ float white=0.0, r, gamma=2.222222;
+ unsigned short lut[65536];
+
+ // Search 99th percentile white level.
+
+ perc = (int)(width * height * 0.01);
+ DDebug() << "White Level: " << perc << endl;
+ for (int c = 1 ; c < 4 ; c++)
+ {
+ total = 0;
+ for (val = 65535 ; val > 256 ; --val)
+ if ((total += (int)histogram.getValue(c, val)) > perc)
+ break;
+
+ if (white < val) white = (float)val;
+ }
+
+ white *= 1.0 / m_rawDecodingSettings.brightness;
+
+ DDebug() << "White Point: " << white << endl;
+
+ // Compute the Gamma lut accordingly.
+
+ for (int i=0; i < 65536; i++)
+ {
+ r = i / white;
+ val = (int)(65536.0 * (r <= 0.018 ? r*4.5 : pow(r, 1.0/gamma) * 1.099-0.099));
+ if (val > 65535) val = 65535;
+ lut[i] = val;
+ }
+
+ // Apply Gamma lut to the whole image.
+
+ unsigned short *im = (unsigned short *)image;
+ for (int i = 0; i < width*height; i++)
+ {
+ im[0] = lut[im[0]]; // Blue
+ im[1] = lut[im[1]]; // Green
+ im[2] = lut[im[2]]; // Red
+ im += 4;
+ }
+ }
+#endif
+
+ // ----------------------------------------------------------
+
+ imageData() = (uchar *)image;
+ }
+ else // 8 bits image
+ {
+ uchar *image = new uchar[width*height*4];
+ uchar *dst = image;
+ uchar *src = (uchar*)data.data();
+ checkpoint = 0;
+
+ for (int h = 0; h < height; h++)
+ {
+
+ if (observer && h == checkpoint)
+ {
+ checkpoint += granularity(observer, height, 1.0);
+ if (!observer->continueQuery(m_image))
+ {
+ return false;
+ }
+ observer->progressInfo(m_image, 0.7 + 0.2*(((float)h)/((float)height)) );
+ }
+
+ for (int w = 0; w < width; w++)
+ {
+ // No need to adapt RGB components accordinly with rgbmax value because dcraw
+ // always return rgbmax to 255 in 8 bits/color/pixels.
+
+ dst[0] = src[2]; // Blue
+ dst[1] = src[1]; // Green
+ dst[2] = src[0]; // Red
+ dst[3] = 0xFF; // Alpha
+
+ dst += 4;
+ src += 3;
+ }
+ }
+
+ // NOTE: if Color Management is not used here, output color space is in sRGB* color space.
+ // Gamma and White balance are previously adjusted by dcraw in 8 bits color depth.
+
+ imageData() = image;
+ }
+
+ //----------------------------------------------------------
+ // Assign the right color-space profile.
+
+ TDEGlobal::dirs()->addResourceType("profiles", TDEGlobal::dirs()->kde_default("data") + "digikam/profiles");
+ switch(m_rawDecodingSettings.outputColorSpace)
+ {
+ case DRawDecoding::SRGB:
+ {
+ TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "srgb.icm");
+ m_image->getICCProfilFromFile(directory + "srgb.icm");
+ break;
+ }
+ case DRawDecoding::ADOBERGB:
+ {
+ TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "adobergb.icm");
+ m_image->getICCProfilFromFile(directory + "adobergb.icm");
+ break;
+ }
+ case DRawDecoding::WIDEGAMMUT:
+ {
+ TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "widegamut.icm");
+ m_image->getICCProfilFromFile(directory + "widegamut.icm");
+ break;
+ }
+ case DRawDecoding::PROPHOTO:
+ {
+ TQString directory = TDEGlobal::dirs()->findResourceDir("profiles", "prophoto.icm");
+ m_image->getICCProfilFromFile(directory + "prophoto.icm");
+ break;
+ }
+ default:
+ // No icc color-space profile to assign in RAW color mode.
+ break;
+ }
+
+ //----------------------------------------------------------
+
+
+ imageWidth() = width;
+ imageHeight() = height;
+ imageSetAttribute("format", "RAW");
+
+ postProcessing(observer);
+
+ return true;
+}
+
+void RAWLoader::postProcessing(DImgLoaderObserver *observer)
+{
+ if (!m_customRawSettings.postProcessingSettingsIsDirty())
+ return;
+
+ if (m_customRawSettings.exposureComp != 0.0 || m_customRawSettings.saturation != 1.0)
+ {
+ WhiteBalance wb(m_rawDecodingSettings.sixteenBitsImage);
+ wb.whiteBalance(imageData(), imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage,
+ 0.0, // black
+ m_customRawSettings.exposureComp, // exposure
+ 6500.0, // temperature (neutral)
+ 1.0, // green
+ 0.5, // dark
+ 1.0, // gamma
+ m_customRawSettings.saturation); // saturation
+ }
+ if (observer) observer->progressInfo(m_image, 0.92);
+
+ if (m_customRawSettings.lightness != 0.0 ||
+ m_customRawSettings.contrast != 1.0 ||
+ m_customRawSettings.gamma != 1.0)
+ {
+ BCGModifier bcg;
+ bcg.setBrightness(m_customRawSettings.lightness);
+ bcg.setContrast(m_customRawSettings.contrast);
+ bcg.setGamma(m_customRawSettings.gamma);
+ bcg.applyBCG(imageData(), imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage);
+ }
+ if (observer) observer->progressInfo(m_image, 0.94);
+
+ if (!m_customRawSettings.curveAdjust.isEmpty())
+ {
+ DImg tmp(imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage);
+ ImageCurves curves(m_rawDecodingSettings.sixteenBitsImage);
+ curves.setCurvePoints(ImageHistogram::ValueChannel, m_customRawSettings.curveAdjust);
+ curves.curvesCalculateCurve(ImageHistogram::ValueChannel);
+ curves.curvesLutSetup(ImageHistogram::AlphaChannel);
+ curves.curvesLutProcess(imageData(), tmp.bits(), imageWidth(), imageHeight());
+ memcpy(imageData(), tmp.bits(), tmp.numBytes());
+ }
+ if (observer) observer->progressInfo(m_image, 0.96);
+
+ if (!m_customRawSettings.levelsAdjust.isEmpty())
+ {
+ DImg tmp(imageWidth(), imageHeight(), m_rawDecodingSettings.sixteenBitsImage);
+ ImageLevels levels(m_rawDecodingSettings.sixteenBitsImage);
+ int j=0;
+ for (int i = 0 ; i < 4; i++)
+ {
+ levels.setLevelLowInputValue(i, m_customRawSettings.levelsAdjust[j++]);
+ levels.setLevelHighInputValue(i, m_customRawSettings.levelsAdjust[j++]);
+ levels.setLevelLowOutputValue(i, m_customRawSettings.levelsAdjust[j++]);
+ levels.setLevelHighOutputValue(i, m_customRawSettings.levelsAdjust[j++]);
+ }
+
+ levels.levelsLutSetup(ImageHistogram::AlphaChannel);
+ levels.levelsLutProcess(imageData(), tmp.bits(), imageWidth(), imageHeight());
+ memcpy(imageData(), tmp.bits(), tmp.numBytes());
+ }
+ if (observer) observer->progressInfo(m_image, 0.98);
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/rawloader.h b/src/libs/dimg/loaders/rawloader.h
new file mode 100644
index 00000000..22171a20
--- /dev/null
+++ b/src/libs/dimg/loaders/rawloader.h
@@ -0,0 +1,86 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-01
+ * Description : A digital camera RAW files loader for DImg
+ * framework using an external dcraw instance.
+ *
+ * Copyright (C) 2005-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2005-2008 by Marcel Wiesweg <[email protected]>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef RAWLOADER_H
+#define RAWLOADER_H
+
+// LibKDcraw includes.
+
+#include <libkdcraw/version.h>
+#include <libkdcraw/kdcraw.h>
+
+// Local includes.
+
+#include "drawdecoding.h"
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+class DImg;
+
+class DIGIKAM_EXPORT RAWLoader : public KDcrawIface::KDcraw, public DImgLoader
+{
+ TQ_OBJECT
+
+
+public:
+
+ RAWLoader(DImg* image, DRawDecoding rawDecodingSettings=DRawDecoding());
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer=0);
+
+ // NOTE: RAW files are always Read only.
+ bool save(const TQString& /*filePath*/, DImgLoaderObserver */*observer=0*/) { return false; };
+
+ bool hasAlpha() const { return false; };
+ bool isReadOnly() const { return true; };
+ bool sixteenBit() const { return m_rawDecodingSettings.sixteenBitsImage; };
+
+private:
+
+ // Methods to load RAW image using external dcraw instance.
+
+ bool loadedFromDcraw(TQByteArray data, int width, int height, int rgbmax,
+ DImgLoaderObserver *observer);
+
+ bool checkToCancelWaitingData();
+ void setWaitingDataProgress(double value);
+ void postProcessing(DImgLoaderObserver *observer);
+
+#if KDCRAW_VERSION < 0x000106
+ bool checkToCancelRecievingData();
+ void setRecievingDataProgress(double value);
+#endif
+
+private:
+
+ DImgLoaderObserver *m_observer;
+ DRawDecoding m_customRawSettings;
+};
+
+} // NameSpace Digikam
+
+#endif /* RAWLOADER_H */
diff --git a/src/libs/dimg/loaders/tiffloader.cpp b/src/libs/dimg/loaders/tiffloader.cpp
new file mode 100644
index 00000000..2e554143
--- /dev/null
+++ b/src/libs/dimg/loaders/tiffloader.cpp
@@ -0,0 +1,806 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-17
+ * Description : A TIFF IO file for DImg framework
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Specifications & references:
+ * - TIFF 6.0 : http://partners.adobe.com/public/developer/en/tiff/TIFF6.pdf
+ * - TIFF/EP : http://www.map.tu.chiba-u.ac.jp/IEC/100/TA2/recdoc/N4378.pdf
+ * - TIFF/Tags : http://www.awaresystems.be/imaging/tiff/tifftags.html
+ * - DNG : http://www.adobe.com/products/dng/pdfs/dng_spec.pdf
+ *
+ * Others Linux Tiff Loader implementation using libtiff:
+ * - http://websvn.kde.org/trunk/koffice/filters/krita/tiff/kis_tiff_converter.cc
+ * - http://artis.inrialpes.fr/Software/TiffIO/
+ * - http://cvs.graphicsmagick.org/cgi-bin/cvsweb.cgi/GraphicsMagick/coders/tiff.c
+ * - http://freeimage.cvs.sourceforge.net/freeimage/FreeImage/Source/FreeImage/PluginTIFF.cpp
+ * - http://freeimage.cvs.sourceforge.net/freeimage/FreeImage/Source/Metadata/XTIFF.cpp
+ * - https://subversion.imagemagick.org/subversion/ImageMagick/trunk/coders/tiff.c
+ *
+ * Test images repository:
+ * - http://www.remotesensing.org/libtiff/images.html
+ *
+ * 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"
+{
+#include <tiffvers.h>
+}
+
+// C++ includes.
+
+#include <cstdio>
+
+// TQt includes.
+
+#include <tqfile.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimgloaderobserver.h"
+#include "dmetadata.h"
+#include "tiffloader.h"
+
+namespace Digikam
+{
+
+// To manage Errors/Warnings handling provide by libtiff
+
+void TIFFLoader::dimg_tiff_warning(const char* module, const char* format, va_list warnings)
+{
+#ifdef ENABLE_DEBUG_MESSAGES
+ char message[4096];
+ vsnprintf(message, 4096, format, warnings);
+ DDebug() << module << "::" << message << endl;
+#else
+ Q_UNUSED(module);
+ Q_UNUSED(format);
+ Q_UNUSED(warnings);
+#endif
+}
+
+void TIFFLoader::dimg_tiff_error(const char* module, const char* format, va_list errors)
+{
+#ifdef ENABLE_DEBUG_MESSAGES
+ char message[4096];
+ vsnprintf(message, 4096, format, errors);
+ DDebug() << module << "::" << message << endl;
+#else
+ Q_UNUSED(module);
+ Q_UNUSED(format);
+ Q_UNUSED(errors);
+#endif
+}
+
+TIFFLoader::TIFFLoader(DImg* image)
+ : DImgLoader(image)
+{
+ m_hasAlpha = false;
+ m_sixteenBit = false;
+}
+
+bool TIFFLoader::load(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ readMetadata(filePath, DImg::TIFF);
+
+ // -------------------------------------------------------------------
+ // TIFF error handling. If an errors/warnings occurs during reading,
+ // libtiff will call these methods
+
+ TIFFSetWarningHandler(dimg_tiff_warning);
+ TIFFSetErrorHandler(dimg_tiff_error);
+
+ // -------------------------------------------------------------------
+ // Open the file
+
+ TIFF* tif = TIFFOpen(TQFile::encodeName(filePath), "r");
+ if (!tif)
+ {
+ DDebug() << k_funcinfo << "Cannot open image file." << endl;
+ return false;
+ }
+
+#ifdef ENABLE_DEBUG_MESSAGES
+ TIFFPrintDirectory(tif, stdout, 0);
+#endif
+
+ // -------------------------------------------------------------------
+ // Get image information.
+
+ uint32 w, h;
+ uint16 bits_per_sample;
+ uint16 samples_per_pixel;
+ uint16 photometric;
+ uint32 rows_per_strip;
+ tsize_t strip_size;
+ tstrip_t num_of_strips;
+
+ TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGEWIDTH, &w);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_IMAGELENGTH, &h);
+
+ TIFFGetFieldDefaulted(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample);
+ TIFFGetFieldDefaulted(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel);
+
+ if (TIFFGetFieldDefaulted(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip) == 0 ||
+ rows_per_strip == 0 || rows_per_strip == (unsigned int)-1)
+ {
+ DWarning() << "TIFF loader: Cannot handle non-stripped images. Loading file " << filePath << endl;
+ TIFFClose(tif);
+ return false;
+ }
+
+ if (bits_per_sample == 0 || samples_per_pixel == 0 ||
+ rows_per_strip == 0 || rows_per_strip > h)
+ {
+ DWarning() << "TIFF loader: Encountered invalid value 0 in image."
+ << " bits_per_sample " << bits_per_sample
+ << " samples_per_pixel " << samples_per_pixel
+ << " rows_per_strip " << rows_per_strip
+ << " Loading file " << filePath << endl;
+ TIFFClose(tif);
+ return false;
+ }
+
+ // TODO: check others TIFF color-spaces here. Actually, only RGB and MINISBLACK
+ // have been tested.
+ // Complete description of TIFFTAG_PHOTOMETRIC tag can be found at this url:
+ // http://www.awaresystems.be/imaging/tiff/tifftags/photometricinterpretation.html
+
+ TIFFGetFieldDefaulted(tif, TIFFTAG_PHOTOMETRIC, &photometric);
+ if (photometric != PHOTOMETRIC_RGB &&
+ photometric != PHOTOMETRIC_MINISBLACK)
+ {
+ DWarning() << "Can't handle image without RGB color-space: "
+ << photometric << endl;
+ TIFFClose(tif);
+ return false;
+ }
+
+ if (samples_per_pixel == 4)
+ m_hasAlpha = true;
+ else
+ m_hasAlpha = false;
+
+ if (bits_per_sample == 16)
+ m_sixteenBit = true;
+ else
+ m_sixteenBit = false;
+
+ // -------------------------------------------------------------------
+ // Read image ICC profile
+
+ TQMap<int, TQByteArray>& metaData = imageMetaData();
+
+ uchar *profile_data=0;
+ uint32 profile_size;
+
+ if (TIFFGetField (tif, TIFFTAG_ICCPROFILE, &profile_size, &profile_data))
+ {
+ TQByteArray profile_rawdata(profile_size);
+ memcpy(profile_rawdata.data(), profile_data, profile_size);
+ metaData.insert(DImg::ICC, profile_rawdata);
+ }
+ else
+ {
+ // If ICC profile is null, check Exif metadata.
+ checkExifWorkingColorSpace();
+ }
+
+ // -------------------------------------------------------------------
+ // Get image data.
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ uchar* data = 0;
+
+ strip_size = TIFFStripSize(tif);
+ num_of_strips = TIFFNumberOfStrips(tif);
+
+ if (bits_per_sample == 16) // 16 bits image.
+ {
+ data = new uchar[w*h*8];
+ uchar* strip = new uchar[strip_size];
+ long offset = 0;
+ long bytesRead = 0;
+
+ uint checkpoint = 0;
+
+ for (tstrip_t st=0; st < num_of_strips; st++)
+ {
+ if (observer && st == checkpoint)
+ {
+ checkpoint += granularity(observer, num_of_strips, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ delete [] strip;
+ TIFFClose(tif);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)st)/((float)num_of_strips) )));
+ }
+
+ bytesRead = TIFFReadEncodedStrip(tif, st, strip, strip_size);
+
+ if (bytesRead == -1)
+ {
+ DDebug() << k_funcinfo << "Failed to read strip" << endl;
+ delete [] data;
+ TIFFClose(tif);
+ return false;
+ }
+
+ ushort *stripPtr = (ushort*)(strip);
+ ushort *dataPtr = (ushort*)(data + offset);
+ ushort *p;
+
+ // tiff data is read as BGR or ABGR or Greyscale
+
+ if (samples_per_pixel == 3)
+ {
+ for (int i=0; i < bytesRead/6; i++)
+ {
+ p = dataPtr;
+
+ // See B.K.O #148037 : take a care about byte order with Motorola computers.
+ if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC
+ {
+ p[3] = *stripPtr++;
+ p[0] = *stripPtr++;
+ p[1] = *stripPtr++;
+ p[2] = 0xFFFF;
+ }
+ else
+ {
+ p[2] = *stripPtr++;
+ p[1] = *stripPtr++;
+ p[0] = *stripPtr++;
+ p[3] = 0xFFFF;
+ }
+
+ dataPtr += 4;
+ }
+
+ offset += bytesRead/6 * 8;
+ }
+ else if (samples_per_pixel == 1) // See B.K.O #148400: Greyscale pictures only have _one_ sample per pixel
+ {
+ for (int i=0; i < bytesRead/2; i++)
+ {
+ // We have to read two bytes for one pixel
+ p = dataPtr;
+
+ // See B.K.O #148037 : take a care about byte order with Motorola computers.
+ if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC
+ {
+ p[3] = 0xFFFF;
+ p[0] = *stripPtr;
+ p[1] = *stripPtr;
+ p[2] = *stripPtr++;
+ }
+ else
+ {
+ p[0] = *stripPtr; // RGB have to be set to the _same_ value
+ p[1] = *stripPtr;
+ p[2] = *stripPtr++;
+ p[3] = 0xFFFF; // set alpha to 100%
+ }
+ dataPtr += 4;
+ }
+
+ offset += bytesRead*4; // The _byte_offset in the data array is, of course, four times bytesRead
+ }
+ else // ABGR
+ {
+ for (int i=0; i < bytesRead/8; i++)
+ {
+ p = dataPtr;
+
+ // See B.K.O #148037 : take a care about byte order with Motorola computers.
+ if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC
+ {
+ p[3] = *stripPtr++;
+ p[0] = *stripPtr++;
+ p[1] = *stripPtr++;
+ p[2] = *stripPtr++;
+ }
+ else
+ {
+ p[2] = *stripPtr++;
+ p[1] = *stripPtr++;
+ p[0] = *stripPtr++;
+ p[3] = *stripPtr++;
+ }
+
+ dataPtr += 4;
+ }
+
+ offset += bytesRead;
+ }
+ }
+
+ delete [] strip;
+ }
+ else // Non 16 bits images ==> get it on BGRA 8 bits.
+ {
+ data = new uchar[w*h*4];
+ uchar* strip = new uchar[w*rows_per_strip*4];
+ long offset = 0;
+ long pixelsRead = 0;
+
+ // this is inspired by TIFFReadRGBAStrip, tif_getimage.c
+ char emsg[1024] = "";
+ TIFFRGBAImage img;
+ uint32 rows_to_read;
+
+ uint checkpoint = 0;
+
+ // test whether libtiff can read format and initiate reading
+
+ if (!TIFFRGBAImageOK(tif, emsg) || !TIFFRGBAImageBegin(&img, tif, 0, emsg))
+ {
+ DDebug() << k_funcinfo << "Failed to set up RGBA reading of image, filename "
+ << TIFFFileName(tif) << " error message from Libtiff: " << emsg << endl;
+ delete [] data;
+ delete [] strip;
+ TIFFClose(tif);
+ return false;
+ }
+
+ img.req_orientation = ORIENTATION_TOPLEFT;
+
+ // read strips from image: read rows_per_strip, so always start at beginning of a strip
+ for (uint row = 0; row < h; row += rows_per_strip)
+ {
+ if (observer && row >= checkpoint)
+ {
+ checkpoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ delete [] data;
+ delete [] strip;
+ TIFFClose(tif);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)row)/((float)h) )));
+ }
+
+ img.row_offset = row;
+ img.col_offset = 0;
+
+ if( row + rows_per_strip > img.height )
+ rows_to_read = img.height - row;
+ else
+ rows_to_read = rows_per_strip;
+
+ // Read data
+
+ if (TIFFRGBAImageGet(&img, (uint32*)strip, img.width, rows_to_read ) == -1)
+ {
+ DDebug() << k_funcinfo << "Failed to read image data" << endl;
+ delete [] data;
+ delete [] strip;
+ TIFFClose(tif);
+ return false;
+ }
+
+ pixelsRead = rows_to_read * img.width;
+
+ uchar *stripPtr = (uchar*)(strip);
+ uchar *dataPtr = (uchar*)(data + offset);
+ uchar *p;
+
+ // Reverse red and blue
+
+ for (int i=0; i < pixelsRead; i++)
+ {
+ p = dataPtr;
+
+ // See B.K.O #148037 : take a care about byte order with Motorola computers.
+ if (TQImage::systemByteOrder() == TQImage::BigEndian) // PPC
+ {
+ p[3] = *stripPtr++;
+ p[0] = *stripPtr++;
+ p[1] = *stripPtr++;
+ p[2] = *stripPtr++;
+ }
+ else
+ {
+ p[2] = *stripPtr++;
+ p[1] = *stripPtr++;
+ p[0] = *stripPtr++;
+ p[3] = *stripPtr++;
+ }
+
+ dataPtr += 4;
+ }
+
+ offset += pixelsRead * 4;
+ }
+
+ TIFFRGBAImageEnd(&img);
+ delete [] strip;
+ }
+
+ // -------------------------------------------------------------------
+
+ TIFFClose(tif);
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageWidth() = w;
+ imageHeight() = h;
+ imageData() = data;
+ imageSetAttribute("format", "TIFF");
+
+ return true;
+}
+
+bool TIFFLoader::save(const TQString& filePath, DImgLoaderObserver *observer)
+{
+ TIFF *tif;
+ uchar *data;
+ uint32 w, h;
+
+ w = imageWidth();
+ h = imageHeight();
+ data = imageData();
+
+ // -------------------------------------------------------------------
+ // TIFF error handling. If an errors/warnings occurs during reading,
+ // libtiff will call these methods
+
+ TIFFSetWarningHandler(dimg_tiff_warning);
+ TIFFSetErrorHandler(dimg_tiff_error);
+
+ // -------------------------------------------------------------------
+ // Open the file
+
+ tif = TIFFOpen(TQFile::encodeName(filePath), "w");
+
+ if (!tif)
+ {
+ DDebug() << k_funcinfo << "Cannot open target image file." << endl;
+ return false;
+ }
+
+ // -------------------------------------------------------------------
+ // Set image properties
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w);
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h);
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
+
+ // Image must be compressed using deflate algorithm ?
+ TQVariant compressAttr = imageGetAttribute("compress");
+ bool compress = compressAttr.isValid() ? compressAttr.toBool() : false;
+
+ if (compress)
+ {
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE);
+ TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9);
+ // NOTE : this tag values aren't defined in libtiff 3.6.1. '2' is PREDICTOR_HORIZONTAL.
+ // Use horizontal differencing for images which are
+ // likely to be continuous tone. The TIFF spec says that this
+ // usually leads to better compression.
+ // See this url for more details:
+ // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html
+ TIFFSetField(tif, TIFFTAG_PREDICTOR, 2);
+ }
+ else
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+
+ // Image has an alpha channel ?
+ if (imageHasAlpha())
+ {
+ uint16 sampleinfo[1] = { EXTRASAMPLE_UNASSALPHA };
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4);
+ TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, EXTRASAMPLE_ASSOCALPHA, sampleinfo);
+ }
+ else
+ {
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ }
+
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16)imageBitsDepth());
+ TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));
+
+ // -------------------------------------------------------------------
+ // Write meta-data Tags contents.
+
+ DMetadata metaData;
+ metaData.setExif(m_image->getExif());
+ metaData.setIptc(m_image->getIptc());
+
+ // Standard IPTC tag (available with libtiff 3.6.1)
+
+ TQByteArray ba = metaData.getIptc(true);
+ if (!ba.isEmpty())
+ {
+#if defined(TIFFTAG_PHOTOSHOP)
+ TIFFSetField (tif, TIFFTAG_PHOTOSHOP, (uint32)ba.size(), (uchar *)ba.data());
+#endif
+ }
+
+ // Standard XMP tag (available with libtiff 3.6.1)
+
+#if defined(TIFFTAG_XMLPACKET)
+ tiffSetExifDataTag(tif, TIFFTAG_XMLPACKET, &metaData, "Exif.Image.XMLPacket");
+#endif
+
+ // Standard Exif Ascii tags (available with libtiff 3.6.1)
+
+ tiffSetExifAsciiTag(tif, TIFFTAG_DOCUMENTNAME, &metaData, "Exif.Image.DocumentName");
+ tiffSetExifAsciiTag(tif, TIFFTAG_IMAGEDESCRIPTION, &metaData, "Exif.Image.ImageDescription");
+ tiffSetExifAsciiTag(tif, TIFFTAG_MAKE, &metaData, "Exif.Image.Make");
+ tiffSetExifAsciiTag(tif, TIFFTAG_MODEL, &metaData, "Exif.Image.Model");
+ tiffSetExifAsciiTag(tif, TIFFTAG_DATETIME, &metaData, "Exif.Image.DateTime");
+ tiffSetExifAsciiTag(tif, TIFFTAG_ARTIST, &metaData, "Exif.Image.Artist");
+ tiffSetExifAsciiTag(tif, TIFFTAG_COPYRIGHT, &metaData, "Exif.Image.Copyright");
+
+ TQString soft = metaData.getExifTagString("Exif.Image.Software");
+ TQString libtiffver(TIFFLIB_VERSION_STR);
+ libtiffver.replace('\n', ' ');
+ soft.append(TQString(" ( %1 )").arg(libtiffver));
+ TIFFSetField(tif, TIFFTAG_SOFTWARE, (const char*)soft.ascii());
+
+ // NOTE: All others Exif tags will be written by Exiv2 (<= 0.18)
+
+ // -------------------------------------------------------------------
+ // Write ICC profil.
+
+ TQByteArray profile_rawdata = m_image->getICCProfil();
+
+ if (!profile_rawdata.isEmpty())
+ {
+#if defined(TIFFTAG_ICCPROFILE)
+ TIFFSetField(tif, TIFFTAG_ICCPROFILE, (uint32)profile_rawdata.size(), (uchar *)profile_rawdata.data());
+#endif
+ }
+
+ // -------------------------------------------------------------------
+ // Write full image data in tiff directory IFD0
+
+ if (observer)
+ observer->progressInfo(m_image, 0.1);
+
+ uint8 *buf=0;
+ uchar *pixel;
+ double alpha_factor;
+ uint32 x, y;
+ uint8 r8, g8, b8, a8=0;
+ uint16 r16, g16, b16, a16=0;
+ int i=0;
+
+ buf = (uint8 *) _TIFFmalloc(TIFFScanlineSize(tif));
+
+ if (!buf)
+ {
+ DDebug() << k_funcinfo << "Cannot allocate memory buffer for main image." << endl;
+ TIFFClose(tif);
+ return false;
+ }
+
+ uint checkpoint = 0;
+
+ for (y = 0; y < h; y++)
+ {
+
+ if (observer && y == checkpoint)
+ {
+ checkpoint += granularity(observer, h, 0.8);
+ if (!observer->continueQuery(m_image))
+ {
+ _TIFFfree(buf);
+ TIFFClose(tif);
+ return false;
+ }
+ observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)h) )));
+ }
+
+ i = 0;
+
+ for (x = 0; x < w; x++)
+ {
+ pixel = &data[((y * w) + x) * imageBytesDepth()];
+
+ if ( imageSixteenBit() ) // 16 bits image.
+ {
+ b16 = (uint16)(pixel[0]+256*pixel[1]);
+ g16 = (uint16)(pixel[2]+256*pixel[3]);
+ r16 = (uint16)(pixel[4]+256*pixel[5]);
+
+ if (imageHasAlpha())
+ {
+ // TIFF makes you pre-mutiply the rgb components by alpha
+
+ a16 = (uint16)(pixel[6]+256*pixel[7]);
+ alpha_factor = ((double)a16 / 65535.0);
+ r16 = (uint16)(r16*alpha_factor);
+ g16 = (uint16)(g16*alpha_factor);
+ b16 = (uint16)(b16*alpha_factor);
+ }
+
+ // This might be endian dependent
+
+ buf[i++] = (uint8)(r16);
+ buf[i++] = (uint8)(r16 >> 8);
+ buf[i++] = (uint8)(g16);
+ buf[i++] = (uint8)(g16 >> 8);
+ buf[i++] = (uint8)(b16);
+ buf[i++] = (uint8)(b16 >> 8);
+
+ if (imageHasAlpha())
+ {
+ buf[i++] = (uint8)(a16) ;
+ buf[i++] = (uint8)(a16 >> 8) ;
+ }
+ }
+ else // 8 bits image.
+ {
+ b8 = (uint8)pixel[0];
+ g8 = (uint8)pixel[1];
+ r8 = (uint8)pixel[2];
+
+ if (imageHasAlpha())
+ {
+ // TIFF makes you pre-mutiply the rgb components by alpha
+
+ a8 = (uint8)(pixel[3]);
+ alpha_factor = ((double)a8 / 255.0);
+ r8 = (uint8)(r8*alpha_factor);
+ g8 = (uint8)(g8*alpha_factor);
+ b8 = (uint8)(b8*alpha_factor);
+ }
+
+ // This might be endian dependent
+
+ buf[i++] = r8;
+ buf[i++] = g8;
+ buf[i++] = b8;
+
+ if (imageHasAlpha())
+ buf[i++] = a8;
+ }
+ }
+
+ if (!TIFFWriteScanline(tif, buf, y, 0))
+ {
+ DDebug() << k_funcinfo << "Cannot write main image to target file." << endl;
+ _TIFFfree(buf);
+ TIFFClose(tif);
+ return false;
+ }
+ }
+
+ _TIFFfree(buf);
+ TIFFWriteDirectory(tif);
+
+ // -------------------------------------------------------------------
+ // Write thumbnail in tiff directory IFD1
+
+ TQImage thumb = m_image->smoothScale(160, 120, TQSize::ScaleMin).copyTQImage();
+
+ TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, (uint32)thumb.width());
+ TIFFSetField(tif, TIFFTAG_IMAGELENGTH, (uint32)thumb.height());
+ TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+ TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+ TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
+ TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE);
+ TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+ TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3);
+ TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 8);
+ TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0));
+
+ uchar *pixelThumb;
+ uchar *dataThumb = thumb.bits();
+ uint8 *bufThumb = (uint8 *) _TIFFmalloc(TIFFScanlineSize(tif));
+
+ if (!bufThumb)
+ {
+ DDebug() << k_funcinfo << "Cannot allocate memory buffer for thumbnail." << endl;
+ TIFFClose(tif);
+ return false;
+ }
+
+ for (y = 0 ; y < uint32(thumb.height()) ; y++)
+ {
+ i = 0;
+
+ for (x = 0 ; x < uint32(thumb.width()) ; x++)
+ {
+ pixelThumb = &dataThumb[((y * thumb.width()) + x) * 4];
+
+ // This might be endian dependent
+ bufThumb[i++] = (uint8)pixelThumb[2];
+ bufThumb[i++] = (uint8)pixelThumb[1];
+ bufThumb[i++] = (uint8)pixelThumb[0];
+ }
+
+ if (!TIFFWriteScanline(tif, bufThumb, y, 0))
+ {
+ DDebug() << k_funcinfo << "Cannot write thumbnail to target file." << endl;
+ _TIFFfree(bufThumb);
+ TIFFClose(tif);
+ return false;
+ }
+ }
+
+ _TIFFfree(bufThumb);
+ TIFFClose(tif);
+
+ // -------------------------------------------------------------------
+
+ if (observer)
+ observer->progressInfo(m_image, 1.0);
+
+ imageSetAttribute("savedformat", "TIFF");
+
+ saveMetadata(filePath);
+
+ return true;
+}
+
+bool TIFFLoader::hasAlpha() const
+{
+ return m_hasAlpha;
+}
+
+bool TIFFLoader::sixteenBit() const
+{
+ return m_sixteenBit;
+}
+
+void TIFFLoader::tiffSetExifAsciiTag(TIFF* tif, ttag_t tiffTag,
+ const DMetadata *metaData, const char* exifTagName)
+{
+ TQByteArray tag = metaData->getExifTagData(exifTagName);
+ if (!tag.isEmpty())
+ {
+ TQCString str(tag.data(), tag.size());
+ TIFFSetField(tif, tiffTag, (const char*)str);
+ }
+}
+
+void TIFFLoader::tiffSetExifDataTag(TIFF* tif, ttag_t tiffTag,
+ const DMetadata *metaData, const char* exifTagName)
+{
+ TQByteArray tag = metaData->getExifTagData(exifTagName);
+ if (!tag.isEmpty())
+ {
+ TIFFSetField (tif, tiffTag, (uint32)tag.size(), (char *)tag.data());
+ }
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/loaders/tiffloader.h b/src/libs/dimg/loaders/tiffloader.h
new file mode 100644
index 00000000..484afd06
--- /dev/null
+++ b/src/libs/dimg/loaders/tiffloader.h
@@ -0,0 +1,76 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-17
+ * Description : A TIFF IO file for DImg framework
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef TIFFLOADER_H
+#define TIFFLOADER_H
+
+// C ansi includes.
+
+extern "C"
+{
+#include <tiffio.h>
+#include <tiff.h>
+}
+
+// Local includes.
+
+#include "dimgloader.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DImg;
+class DMetadata;
+
+class DIGIKAM_EXPORT TIFFLoader : public DImgLoader
+{
+public:
+
+ TIFFLoader(DImg* image);
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer);
+ bool save(const TQString& filePath, DImgLoaderObserver *observer);
+
+ virtual bool hasAlpha() const;
+ virtual bool sixteenBit() const;
+ virtual bool isReadOnly() const { return false; };
+
+private:
+
+ void tiffSetExifAsciiTag(TIFF* tif, ttag_t tiffTag, const DMetadata *metaData, const char* exifTagName);
+ void tiffSetExifDataTag(TIFF* tif, ttag_t tiffTag, const DMetadata *metaData, const char* exifTagName);
+
+ static void dimg_tiff_warning(const char* module, const char* format, va_list warnings);
+ static void dimg_tiff_error(const char* module, const char* format, va_list errors);
+
+private:
+
+ bool m_sixteenBit;
+ bool m_hasAlpha;
+};
+
+} // NameSpace Digikam
+
+#endif /* TIFFLOADER_H */
diff --git a/src/libs/dimg/loaders/tiffsettings.cpp b/src/libs/dimg/loaders/tiffsettings.cpp
new file mode 100644
index 00000000..3ea7e20c
--- /dev/null
+++ b/src/libs/dimg/loaders/tiffsettings.cpp
@@ -0,0 +1,94 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save TIFF image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqstring.h>
+#include <tqlabel.h>
+#include <tqcheckbox.h>
+#include <tqlayout.h>
+#include <tqwhatsthis.h>
+
+// KDE includes.
+
+#include <tdelocale.h>
+#include <kdialog.h>
+
+// Local includes.
+
+#include "tiffsettings.h"
+#include "tiffsettings.moc"
+
+namespace Digikam
+{
+
+class TIFFSettingsPriv
+{
+
+public:
+
+ TIFFSettingsPriv()
+ {
+ TIFFGrid = 0;
+ TIFFcompression = 0;
+ }
+
+ TQGridLayout *TIFFGrid;
+
+ TQCheckBox *TIFFcompression;
+};
+
+TIFFSettings::TIFFSettings(TQWidget *parent)
+ : TQWidget(parent, 0, TQt::WDestructiveClose)
+{
+ d = new TIFFSettingsPriv;
+
+ d->TIFFGrid = new TQGridLayout(this, 1, 1, KDialog::spacingHint());
+ d->TIFFcompression = new TQCheckBox(i18n("Compress TIFF files"), this);
+
+ TQWhatsThis::add( d->TIFFcompression, i18n("<p>Toggle compression for TIFF images.<p>"
+ "If you enable this option, you can reduce "
+ "the final file size of the TIFF image.</p>"
+ "<p>A lossless compression format (Deflate) "
+ "is used to save the file.<p>"));
+ d->TIFFGrid->addMultiCellWidget(d->TIFFcompression, 0, 0, 0, 1);
+ d->TIFFGrid->setColStretch(1, 10);
+}
+
+TIFFSettings::~TIFFSettings()
+{
+ delete d;
+}
+
+void TIFFSettings::setCompression(bool b)
+{
+ d->TIFFcompression->setChecked(b);
+}
+
+bool TIFFSettings::getCompression()
+{
+ return d->TIFFcompression->isChecked();
+}
+
+} // namespace Digikam
+
diff --git a/src/libs/dimg/loaders/tiffsettings.h b/src/libs/dimg/loaders/tiffsettings.h
new file mode 100644
index 00000000..e546ce0f
--- /dev/null
+++ b/src/libs/dimg/loaders/tiffsettings.h
@@ -0,0 +1,60 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-08-02
+ * Description : save TIFF image options.
+ *
+ * Copyright (C) 2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef TIFFSETTINGS_H
+#define TIFFSETTINGS_H
+
+// KDE includes.
+
+#include <tqwidget.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class TIFFSettingsPriv;
+
+class DIGIKAM_EXPORT TIFFSettings : public TQWidget
+{
+TQ_OBJECT
+
+
+public:
+
+ TIFFSettings(TQWidget *parent=0);
+ ~TIFFSettings();
+
+ void setCompression(bool b);
+ bool getCompression();
+
+private:
+
+ TIFFSettingsPriv* d;
+};
+
+} // namespace Digikam
+
+#endif /* TIFFSETTINGS_H */