summaryrefslogtreecommitdiffstats
path: root/src/libs/dimg
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/dimg')
-rw-r--r--src/libs/dimg/Makefile.am27
-rw-r--r--src/libs/dimg/README95
-rw-r--r--src/libs/dimg/dcolor.cpp281
-rw-r--r--src/libs/dimg/dcolor.h152
-rw-r--r--src/libs/dimg/dcolorblend.h179
-rw-r--r--src/libs/dimg/dcolorcomposer.cpp436
-rw-r--r--src/libs/dimg/dcolorcomposer.h133
-rw-r--r--src/libs/dimg/dcolorpixelaccess.h78
-rw-r--r--src/libs/dimg/ddebug.cpp92
-rw-r--r--src/libs/dimg/ddebug.h73
-rw-r--r--src/libs/dimg/dimg.cpp1700
-rw-r--r--src/libs/dimg/dimg.h353
-rw-r--r--src/libs/dimg/dimgprivate.h81
-rw-r--r--src/libs/dimg/dimgscale.cpp2127
-rw-r--r--src/libs/dimg/drawdecoding.h128
-rw-r--r--src/libs/dimg/exposurecontainer.h65
-rw-r--r--src/libs/dimg/filters/Makefile.am21
-rw-r--r--src/libs/dimg/filters/bcgmodifier.cpp208
-rw-r--r--src/libs/dimg/filters/bcgmodifier.h73
-rw-r--r--src/libs/dimg/filters/colormodifier.cpp287
-rw-r--r--src/libs/dimg/filters/colormodifier.h63
-rw-r--r--src/libs/dimg/filters/dimggaussianblur.cpp327
-rw-r--r--src/libs/dimg/filters/dimggaussianblur.h97
-rw-r--r--src/libs/dimg/filters/dimgimagefilters.cpp977
-rw-r--r--src/libs/dimg/filters/dimgimagefilters.h133
-rw-r--r--src/libs/dimg/filters/dimgsharpen.cpp243
-rw-r--r--src/libs/dimg/filters/dimgsharpen.h69
-rw-r--r--src/libs/dimg/filters/dimgthreadedfilter.cpp170
-rw-r--r--src/libs/dimg/filters/dimgthreadedfilter.h153
-rw-r--r--src/libs/dimg/filters/hslmodifier.cpp240
-rw-r--r--src/libs/dimg/filters/hslmodifier.h58
-rw-r--r--src/libs/dimg/filters/icctransform.cpp831
-rw-r--r--src/libs/dimg/filters/icctransform.h94
-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
62 files changed, 15946 insertions, 0 deletions
diff --git a/src/libs/dimg/Makefile.am b/src/libs/dimg/Makefile.am
new file mode 100644
index 00000000..23e746a2
--- /dev/null
+++ b/src/libs/dimg/Makefile.am
@@ -0,0 +1,27 @@
+SUBDIRS = loaders filters
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdimg.la
+
+libdimg_la_SOURCES = dimg.cpp dimgscale.cpp dcolor.cpp dcolorcomposer.cpp ddebug.cpp
+
+libdimg_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor
+
+libdimg_la_LIBADD = $(top_builddir)/src/libs/histogram/libhistogram.la \
+ $(top_builddir)/src/libs/levels/liblevels.la \
+ $(top_builddir)/src/libs/curves/libcurves.la \
+ $(top_builddir)/src/libs/whitebalance/libwhitebalance.la \
+ $(top_builddir)/src/libs/dimg/loaders/libdimgloaders.la \
+ $(top_builddir)/src/libs/dimg/filters/libdimgfilters.la \
+ $(top_builddir)/src/libs/dmetadata/libdmetadata.la \
+ $(LIBKDCRAW_LIBS) $(LCMS_LIBS)
+
+INCLUDES = $(all_includes) $(LIBKDCRAW_CFLAGS) \
+ -I$(top_srcdir)/src/libs/dimg/loaders \
+ -I$(top_srcdir)/src/libs/dimg/filters \
+ -I$(top_srcdir)/src/libs/dmetadata \
+ -I$(top_srcdir)/src/digikam
+
+digikaminclude_HEADERS = dimg.h dcolor.h dcolorpixelaccess.h dcolorcomposer.h \
+ dcolorblend.h ddebug.h
+digikamincludedir = $(includedir)/digikam
diff --git a/src/libs/dimg/README b/src/libs/dimg/README
new file mode 100644
index 00000000..4b13f3fe
--- /dev/null
+++ b/src/libs/dimg/README
@@ -0,0 +1,95 @@
+
+---------------------------------------------------------------------------
+WHAT'S DIMG FRAMEWORK ?
+
+
+Original post from Renchi Raju on digiKam mailing list :
+
+[Digikam-devel] Imaging Library
+Renchi Raju renchi at pooh.tam.uiuc.edu
+Sun Jun 19 23:15:20 CEST 2005
+
+as you all know, we have been using imlib2 library for the 0.7 series. we
+have had a fairly large number of bugreports because of imlib2 (not
+imlib2's fault, but because of tdelibs's handling of ltdl). In addition,
+some imlib2 bugreports I reported to upstream have gone unfixed for long
+time now.
+
+Also, we need to think about 16bit imaging support. this won't come from
+imlib2 and neither from tqt. with qt4 there was hope of 16bit image
+support, but trolltech has made it clear that imaging apps forms only 0.1%
+of their customer base and they are not interested in providing custom
+support for them. so the only solution I see (without depending on
+imagemagick) is to roll our own library.
+
+i have been working on a imaging library for digikam, its called DImg.
+it doesn't aim to be a complete imaging library; it uses TQImage for
+rendering and for loading files which are not supported natively by it.
+some of the working/planned features:
+
+* Native Image Loaders, for some imageformats which are of interest to
+us: JPEG (complete), TIFF (a rudimentary one currently), PNG (planned), PPM
+(planned). for the rest tqt's qimage is used.
+
+* Metadata preservation: when a file is loaded, its metadata like XMP,
+IPTC, EXIF, JFIF are read and held in memory. now when you save back the
+file to the original file or to a different file, the metadata is
+automatically written (How, we have been handling it currently with
+imlib2 is on saving a file: we save the file to a temporary file, reread
+the exif info from the original file and then write to a second temporary
+file.)
+
+* Explicitly Shared Container format (see tqt docs): this is necessary for
+performance reasons.
+
+* 8bit and 16bit support: if the file format is 16 bit, it will load up
+the image in 16bit format (currently only 16bit tiff support) and all
+operations are done in 16 bit format, except when the rendering to screen
+is done, when its converted on the fly to a temporary 8bit image and then
+rendered.
+
+* Basic image manipulation: rotate, flip, color modifications, crop,
+scale (this has been ported from Imlib2 - originally ported by Mosfet, I
+added 16 bit scaling support and support for scaling of only a section of
+the image)
+
+* Rendering to Pixmap: using TQImage/QPixmap. (see above for rendering of
+16bit images).
+
+* Pixel format: the pixel format is different from TQImage/Imlib2 pixel
+format. In TQImage/Imlib2 the pixel data is stored as unsigned ints and to
+access the individual colors you need to use bit-shifting to ensure
+endian correctness. in DImg, the pixel data is stored as unsigned char.
+the color layout is B,G,R,A (blue, green, red, alpha)
+
+for 8bit images: you can access individual color components like this:
+
+uchar* pixels = image.bits();
+for (int i=0; i<image.width()*image.height(); i++)
+{
+ pixel[0] // blue
+ pixel[1] // green
+ pixel[2] // red
+ pixel[3] // alpha
+
+ pixel += 4; // go to next pixel
+}
+
+and for 16bit images:
+
+ushort* pixels = (ushort*)image.bits();
+for (int i=0; i<image.width()*image.height(); i++)
+{
+ pixel[0] // blue
+ pixel[1] // green
+ pixel[2] // red
+ pixel[3] // alpha
+
+ pixel += 4; // go to next pixel
+}
+
+the above is true for both big and little endian platforms. What this also
+means is that the pixel format is different from that of TQImage for big
+endian machines. Functions are provided if you want to get a copy of the
+DImg as a TQImage.
+
diff --git a/src/libs/dimg/dcolor.cpp b/src/libs/dimg/dcolor.cpp
new file mode 100644
index 00000000..5a363c78
--- /dev/null
+++ b/src/libs/dimg/dcolor.cpp
@@ -0,0 +1,281 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-12-02
+ * Description : 8-16 bits color container.
+ *
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * RGB<->HLS transformation algorithms are inspired from methods
+ * describe at this url :
+ * http://www.paris-pc-gis.com/MI_Enviro/Colors/color_models.htm
+ *
+ * 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>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dcolor.h"
+
+namespace Digikam
+{
+
+/*
+DColor::DColor(const DColor& color)
+{
+ m_red = color.m_red;
+ m_green = color.m_green;
+ m_blue = color.m_blue;
+ m_alpha = color.m_alpha;
+ m_sixteenBit = color.m_sixteenBit;
+}
+*/
+
+DColor::DColor(const TQColor& color, bool sixteenBit)
+{
+ // initialize as eight bit
+ m_red = color.red();
+ m_green = color.green();
+ m_blue = color.blue();
+ m_alpha = 255;
+ m_sixteenBit = false;
+
+ // convert to sixteen bit if requested
+ if (sixteenBit)
+ convertToSixteenBit();
+}
+
+/*
+DColor& DColor::operator=(const DColor& color)
+{
+ m_red = color.m_red;
+ m_green = color.m_green;
+ m_blue = color.m_blue;
+ m_alpha = color.m_alpha;
+ m_sixteenBit = color.m_sixteenBit;
+ return *this;
+}
+*/
+
+TQColor DColor::getTQColor() const
+{
+ if (m_sixteenBit)
+ {
+ DColor eightBit(*this);
+ eightBit.convertToEightBit();
+ return eightBit.getTQColor();
+ }
+
+ return (TQColor(m_red, m_green, m_blue));
+}
+
+void DColor::convertToSixteenBit()
+{
+ if (m_sixteenBit)
+ return;
+
+ m_red = (m_red + 1) * 256 - 1;
+ m_green = (m_green + 1) * 256 - 1;
+ m_blue = (m_blue + 1) * 256 - 1;
+ m_alpha = (m_alpha + 1) * 256 - 1;
+ m_sixteenBit = true;
+}
+
+void DColor::convertToEightBit()
+{
+ if (!m_sixteenBit)
+ return;
+
+ m_red = (m_red + 1) / 256 - 1;
+ m_green = (m_green + 1) / 256 - 1;
+ m_blue = (m_blue + 1) / 256 - 1;
+ m_alpha = (m_alpha + 1) / 256 - 1;
+ m_sixteenBit = false;
+}
+
+
+void DColor::getHSL(int* h, int* s, int* l) const
+{
+ double min;
+ double max;
+ double red;
+ double green;
+ double blue;
+ double delta;
+ double sum;
+ double hue, sat, lig;
+
+ double range = m_sixteenBit ? 65535.0 : 255.0;
+
+ red = m_red / range;
+ green = m_green / range;
+ blue = m_blue / range;
+
+ if (red > green)
+ {
+ if (red > blue)
+ max = red;
+ else
+ max = blue;
+
+ if (green < blue)
+ min = green;
+ else
+ min = blue;
+ }
+ else
+ {
+ if (green > blue)
+ max = green;
+ else
+ max = blue;
+
+ if (red < blue)
+ min = red;
+ else
+ min = blue;
+ }
+
+ sum = max + min;
+
+ lig = sum / 2;
+ sat = 0;
+ hue = 0;
+
+ if (max != min)
+ {
+ delta = max - min;
+
+ if (lig <= 0.5)
+ sat = delta / sum;
+ else
+ sat = delta / (2 - sum);
+
+ if (red == max)
+ hue = (green - blue) / delta;
+ else if (green == max)
+ hue = 2 + (blue - red) / delta;
+ else if (blue == max)
+ hue = 4 + (red - green) / delta;
+
+ if (hue < 0)
+ hue += 6;
+ if (hue > 6)
+ hue -= 6;
+
+ hue *= 60;
+ }
+
+ *h = lround(hue * range / 360.0);
+ *s = lround(sat * range);
+ *l = lround(lig * range);
+}
+
+void DColor::setRGB(int h, int s, int l, bool sixteenBit)
+{
+ double hue;
+ double lightness;
+ double saturation;
+ double m1, m2;
+ double r, g, b;
+
+ double range = m_sixteenBit ? 65535.0 : 255.0;
+
+ if (s == 0)
+ {
+ m_red = l;
+ m_green = l;
+ m_blue = l;
+ }
+ else
+ {
+ hue = (double)(h * 360.0 / range);
+ lightness = (double)(l / range);
+ saturation = (double)(s / range);
+
+ if (lightness <= 0.5)
+ m2 = lightness * (1 + saturation);
+ else
+ m2 = lightness + saturation - lightness * saturation;
+
+ m1 = 2 * lightness - m2;
+
+ double mh;
+
+ mh = hue + 120;
+ while (mh > 360)
+ mh -= 360;
+ while (mh < 0)
+ mh += 360;
+
+ if (mh < 60)
+ r = m1 + (m2 - m1) * mh / 60;
+ else if (mh < 180)
+ r = m2;
+ else if (mh < 240)
+ r = m1 + (m2 - m1) * (240 - mh) / 60;
+ else
+ r = m1;
+
+ mh = hue;
+ while (mh > 360)
+ mh -= 360;
+ while (mh < 0)
+ mh += 360;
+
+ if (mh < 60)
+ g = m1 + (m2 - m1) * mh / 60;
+ else if (mh < 180)
+ g = m2;
+ else if (mh < 240)
+ g = m1 + (m2 - m1) * (240 - mh) / 60;
+ else
+ g = m1;
+
+ mh = hue - 120;
+ while (mh > 360)
+ mh -= 360;
+ while (mh < 0)
+ mh += 360;
+
+ if (mh < 60)
+ b = m1 + (m2 - m1) * mh / 60;
+ else if (mh < 180)
+ b = m2;
+ else if (mh < 240)
+ b = m1 + (m2 - m1) * (240 - mh) / 60;
+ else
+ b = m1;
+
+ m_red = lround(r * range);
+ m_green = lround(g * range);
+ m_blue = lround(b * range);
+ }
+
+ m_sixteenBit = sixteenBit;
+
+ // Fully opaque color.
+ if (m_sixteenBit)
+ m_alpha = 65535;
+ else
+ m_alpha = 255;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/dcolor.h b/src/libs/dimg/dcolor.h
new file mode 100644
index 00000000..16a34f55
--- /dev/null
+++ b/src/libs/dimg/dcolor.h
@@ -0,0 +1,152 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-12-02
+ * Description : 8-16 bits color container.
+ *
+ * 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 DCOLOR_H
+#define DCOLOR_H
+
+// TQt includes.
+
+#include <tqcolor.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DColor
+{
+public:
+
+ /** Initialize with default value, fully transparent eight bit black */
+ DColor()
+ : m_red(0), m_green(0), m_blue(0), m_alpha(0), m_sixteenBit(false)
+ {};
+
+ /** Read value from data. Equivalent to setColor() */
+ DColor(const uchar *data, bool sixteenBit = false)
+ { setColor(data, sixteenBit); }
+
+ /** Initialize with given RGBA values */
+ DColor(int red, int green, int blue, int alpha, bool sixteenBit)
+ : m_red(red), m_green(green), m_blue(blue), m_alpha(alpha), m_sixteenBit(sixteenBit)
+ {};
+
+ /** Read values from TQColor, convert to sixteenBit of sixteenBit is true */
+ DColor(const TQColor& color, bool sixteenBit=false);
+
+ // Use default copy constructor, assignment operator and destructor
+
+ /** Read color values as RGBA from the given memory location.
+ If sixteenBit is false, 4 bytes are read.
+ If sixteenBit is true, 8 bytes are read.
+ Inline method.
+ */
+ void setColor(const uchar *data, bool sixteenBit = false);
+
+ /** Write the values of this color to the given memory location.
+ If sixteenBit is false, 4 bytes are written.
+ If sixteenBit is true, 8 bytes are written.
+ Inline method.
+ */
+ void setPixel(uchar *data) const;
+
+ int red () const { return m_red; }
+ int green() const { return m_green; }
+ int blue () const { return m_blue; }
+ int alpha() const { return m_alpha; }
+ bool sixteenBit() const { return m_sixteenBit; }
+
+ void setRed (int red) { m_red = red; }
+ void setGreen(int green) { m_green = green; }
+ void setBlue (int blue) { m_blue = blue; }
+ void setAlpha(int alpha) { m_alpha = alpha; }
+ void setSixteenBit(bool sixteenBit) { m_sixteenBit = sixteenBit; }
+
+ TQColor getTQColor() const;
+
+ /** Convert the color values of this color to and from sixteen bit
+ and set the sixteenBit value accordingly
+ */
+ void convertToSixteenBit();
+ void convertToEightBit();
+
+ /** Premultiply and demultiply this color.
+ DImg stores the color non-premultiplied.
+ Inline methods.
+ */
+ void premultiply();
+ void demultiply();
+
+ /** Return the current RGB color values of this color
+ in the HSL color space.
+ Alpha is ignored for the conversion.
+ */
+ void getHSL(int* h, int* s, int* l) const;
+
+ /** Set the RGB color values of this color
+ to the given HSL values converted to RGB.
+ Alpha is set to be fully opaque.
+ sixteenBit determines both how the HSL values are interpreted
+ and the sixteenBit value of this color after this operation.
+ */
+ void setRGB(int h, int s, int l, bool sixteenBit);
+
+private:
+
+ int m_red;
+ int m_green;
+ int m_blue;
+ int m_alpha;
+
+ bool m_sixteenBit;
+
+public:
+
+ // Inline alpha blending helper functions.
+ // These functions are used by DColorComposer.
+ // Look at that code to learn how to use them for
+ // composition if you want to use them in optimized code.
+ void blendZero();
+ void blendAlpha8(int alpha);
+ void blendInvAlpha8(int alpha);
+ void blendAlpha16(int alpha);
+ void blendInvAlpha16(int alpha);
+ void premultiply16(int alpha);
+ void premultiply8(int alpha);
+ void demultiply16(int alpha);
+ void demultiply8(int alpha);
+ void blendAdd(const DColor &src);
+ void blendClamp8();
+ void blendClamp16();
+};
+
+} // NameSpace Digikam
+
+
+// Inline methods
+#include "dcolorpixelaccess.h"
+#include "dcolorblend.h"
+
+#endif /* DCOLOR_H */
diff --git a/src/libs/dimg/dcolorblend.h b/src/libs/dimg/dcolorblend.h
new file mode 100644
index 00000000..b68322ba
--- /dev/null
+++ b/src/libs/dimg/dcolorblend.h
@@ -0,0 +1,179 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-03-01
+ * Description : DColor methods for blending
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// Inspired by DirectFB, src/gfx/generic/generic.c:
+
+/*
+ (c) Copyright 2000-2002 convergence integrated media GmbH <[email protected]>
+ (c) Copyright 2002-2005 convergence GmbH.
+
+ All rights reserved.
+
+ Written by Denis Oliver Kropp <[email protected]>,
+ Andreas Hundt <[email protected]>,
+ Sven Neumann <[email protected]>,
+ Ville Syrj�l� <[email protected]> and
+ Claudio Ciccani <[email protected]>.
+
+*/
+
+#ifndef DCOLORBLEND_H
+#define DCOLORBLEND_H
+
+namespace Digikam
+{
+
+inline void DColor::premultiply()
+{
+ if (sixteenBit())
+ premultiply16(alpha());
+ else
+ premultiply8(alpha());
+}
+
+inline void DColor::demultiply()
+{
+ if (sixteenBit())
+ {
+ demultiply16(alpha());
+ blendClamp16();
+ }
+ else
+ {
+ demultiply8(alpha());
+ blendClamp8();
+ }
+}
+
+inline void DColor::blendZero()
+{
+ setAlpha(0);
+ setRed(0);
+ setGreen(0);
+ setBlue(0);
+}
+
+inline void DColor::blendAlpha16(int alphaValue)
+{
+ uint Sa = alphaValue + 1;
+
+ setRed ((Sa * (uint)red()) >> 16);
+ setGreen((Sa * (uint)green()) >> 16);
+ setBlue ((Sa * (uint)blue()) >> 16);
+ setAlpha((Sa * (uint)alpha()) >> 16);
+}
+
+inline void DColor::blendAlpha8(int alphaValue)
+{
+ uint Sa = alphaValue + 1;
+
+ setRed ((Sa * red()) >> 8);
+ setGreen((Sa * green()) >> 8);
+ setBlue ((Sa * blue()) >> 8);
+ setAlpha((Sa * alpha()) >> 8);
+}
+
+inline void DColor::blendInvAlpha16(int alphaValue)
+{
+ uint Sa = 65536 - alphaValue;
+
+ setRed ((Sa * (uint)red()) >> 16);
+ setGreen((Sa * (uint)green()) >> 16);
+ setBlue ((Sa * (uint)blue()) >> 16);
+ setAlpha((Sa * (uint)alpha()) >> 16);
+}
+
+inline void DColor::blendInvAlpha8(int alphaValue)
+{
+ uint Sa = 256 - alphaValue;
+
+ setRed ((Sa * red()) >> 8);
+ setGreen((Sa * green()) >> 8);
+ setBlue ((Sa * blue()) >> 8);
+ setAlpha((Sa * alpha()) >> 8);
+}
+
+inline void DColor::premultiply16(int alphaValue)
+{
+ uint Da = alphaValue + 1;
+
+ setRed ((Da * (uint)red()) >> 16);
+ setGreen((Da * (uint)green()) >> 16);
+ setBlue ((Da * (uint)blue()) >> 16);
+}
+
+inline void DColor::premultiply8(int alphaValue)
+{
+ uint Da = alphaValue + 1;
+
+ setRed ((Da * red()) >> 8);
+ setGreen((Da * green()) >> 8);
+ setBlue ((Da * blue()) >> 8);
+}
+
+inline void DColor::demultiply16(int alphaValue)
+{
+ uint Da = alphaValue + 1;
+
+ setRed (((uint)red() << 16) / Da);
+ setGreen(((uint)green() << 16) / Da);
+ setBlue (((uint)blue() << 16) / Da);
+}
+
+inline void DColor::demultiply8(int alphaValue)
+{
+ uint Da = alphaValue + 1;
+
+ setRed ((red() << 8) / Da);
+ setGreen((green() << 8) / Da);
+ setBlue ((blue() << 8) / Da);
+}
+
+inline void DColor::blendAdd(const DColor &src)
+{
+ setRed (red() + src.red());
+ setGreen(green() + src.green());
+ setBlue (blue() + src.blue());
+ setAlpha(alpha() + src.alpha());
+}
+
+inline void DColor::blendClamp16()
+{
+ if (0xFFFF0000 & red()) setRed(65535);
+ if (0xFFFF0000 & green()) setGreen(65535);
+ if (0xFFFF0000 & blue()) setBlue(65535);
+ if (0xFFFF0000 & alpha()) setAlpha(65535);
+}
+
+inline void DColor::blendClamp8()
+{
+ if (0xFF00 & red()) setRed(255);
+ if (0xFF00 & green()) setGreen(255);
+ if (0xFF00 & blue()) setBlue(255);
+ if (0xFF00 & alpha()) setAlpha(255);
+}
+
+} // namespace Digikam
+
+#endif // DCOLORBLEND_H
+
diff --git a/src/libs/dimg/dcolorcomposer.cpp b/src/libs/dimg/dcolorcomposer.cpp
new file mode 100644
index 00000000..653dbab3
--- /dev/null
+++ b/src/libs/dimg/dcolorcomposer.cpp
@@ -0,0 +1,436 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-03-02
+ * Description : DColor methods for composing
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// Integer arithmetic inspired by DirectFB,
+// src/gfx/generic/generic.c and src/display/idirectfbsurface.c:
+
+/*
+ (c) Copyright 2000-2002 convergence integrated media GmbH <[email protected]>
+ (c) Copyright 2002-2005 convergence GmbH.
+
+ All rights reserved.
+
+ Written by Denis Oliver Kropp <[email protected]>,
+ Andreas Hundt <[email protected]>,
+ Sven Neumann <[email protected]>,
+ Ville Syrj�l� <[email protected]> and
+ Claudio Ciccani <[email protected]>.
+
+*/
+
+// C++ includes.
+
+#include <cmath>
+
+// Local includes.
+
+#include "dcolorcomposer.h"
+
+namespace Digikam
+{
+
+class DColorComposerPorterDuffNone : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffClear : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+ virtual void compose(DColor &dest, DColor src, MultiplicationFlags multiplicationFlags);
+};
+
+class DColorComposerPorterDuffSrc : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+ virtual void compose(DColor &dest, DColor src, MultiplicationFlags multiplicationFlags);
+};
+
+class DColorComposerPorterDuffSrcOver : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffDstOver : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffSrcIn : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffDstIn : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffSrcOut : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffDstOut : public DColorComposer
+{
+public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffSrcAtop : public DColorComposer
+{
+ public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffDstAtop : public DColorComposer
+{
+ public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+class DColorComposerPorterDuffXor : public DColorComposer
+{
+ public:
+ virtual void compose(DColor &dest, DColor src);
+};
+
+// Porter-Duff None
+// component = (source * sa + destination * (1-sa))
+// Src blending function Src Alpha
+// Dst blending function Inv Src Alpha
+void DColorComposerPorterDuffNone::compose(DColor &dest, DColor src)
+{
+ // preserve src alpha value for dest blending,
+ // src.alpha() will be changed after blending src
+ int sa = src.alpha();
+ if (dest.sixteenBit())
+ {
+ src.blendAlpha16(sa);
+ dest.blendInvAlpha16(sa);
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendAlpha8(sa);
+ dest.blendInvAlpha8(sa);
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Clear
+// component = (source * 0 + destination * 0)
+// Src blending function Zero
+// Dst blending function Zero
+void DColorComposerPorterDuffClear::compose(DColor &dest, DColor src)
+{
+ src.blendZero();
+ dest.blendZero();
+ dest.blendAdd(src);
+}
+
+void DColorComposerPorterDuffClear::compose(DColor &dest, DColor src, MultiplicationFlags)
+{
+ // skip pre- and demultiplication
+ compose(dest, src);
+}
+
+// Porter-Duff Src
+// Normal Painter's algorithm
+// component = (source * 1 + destination * 0)
+// Src blending function One
+// Dst blending function Zero
+void DColorComposerPorterDuffSrc::compose(DColor &dest, DColor src)
+{
+ // src: no-op
+ dest.blendZero();
+ dest.blendAdd(src);
+}
+
+void DColorComposerPorterDuffSrc::compose(DColor &dest, DColor src, MultiplicationFlags)
+{
+ // skip pre- and demultiplication
+ compose(dest, src);
+}
+
+// Porter-Duff Src Over
+// component = (source * 1 + destination * (1-sa))
+// Src blending function One
+// Dst blending function Inv Src Alpha
+void DColorComposerPorterDuffSrcOver::compose(DColor &dest, DColor src)
+{
+ if (dest.sixteenBit())
+ {
+ // src: no-op
+ dest.blendInvAlpha16(src.alpha());
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ // src: no-op
+ dest.blendInvAlpha8(src.alpha());
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Dst over
+// component = (source * (1.0-da) + destination * 1)
+// Src blending function Inv Dst Alpha
+// Dst blending function One
+void DColorComposerPorterDuffDstOver::compose(DColor &dest, DColor src)
+{
+ if (dest.sixteenBit())
+ {
+ src.blendInvAlpha16(dest.alpha());
+ // dest: no-op
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendInvAlpha8(dest.alpha());
+ // dest: no-op
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Src In
+// component = (source * da + destination * 0)
+// Src blending function Dst Alpha
+// Dst blending function Zero
+void DColorComposerPorterDuffSrcIn::compose(DColor &dest, DColor src)
+{
+ if (dest.sixteenBit())
+ {
+ src.blendAlpha16(dest.alpha());
+ dest.blendZero();
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendAlpha8(dest.alpha());
+ dest.blendZero();
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Dst In
+// component = (source * 0 + destination * sa)
+// Src blending function Zero
+// Dst blending function Src Alpha
+void DColorComposerPorterDuffDstIn::compose(DColor &dest, DColor src)
+{
+ int sa = src.alpha();
+ if (dest.sixteenBit())
+ {
+ src.blendZero();
+ dest.blendAlpha16(sa);
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendZero();
+ dest.blendAlpha8(sa);
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Src Out
+// component = (source * (1-da) + destination * 0)
+// Src blending function Inv Dst Alpha
+// Dst blending function Zero
+void DColorComposerPorterDuffSrcOut::compose(DColor &dest, DColor src)
+{
+ if (dest.sixteenBit())
+ {
+ src.blendInvAlpha16(dest.alpha());
+ dest.blendZero();
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendInvAlpha8(dest.alpha());
+ dest.blendZero();
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Dst Out
+// component = (source * 0 + destination * (1-sa))
+// Src blending function Zero
+// Dst blending function Inv Src Alpha
+void DColorComposerPorterDuffDstOut::compose(DColor &dest, DColor src)
+{
+ int sa = src.alpha();
+ if (dest.sixteenBit())
+ {
+ src.blendZero();
+ dest.blendInvAlpha16(sa);
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendZero();
+ dest.blendInvAlpha8(sa);
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Src Atop
+// component = (source * da + destination * (1-sa))
+// Src blending function Dst Alpha
+// Dst blending function Inv Src Alpha
+void DColorComposerPorterDuffSrcAtop::compose(DColor &dest, DColor src)
+{
+ int sa = src.alpha();
+ if (dest.sixteenBit())
+ {
+ src.blendAlpha16(dest.alpha());
+ dest.blendInvAlpha16(sa);
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendAlpha8(dest.alpha());
+ dest.blendInvAlpha8(sa);
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Dst Atop
+// component = (source * (1-da) + destination * sa)
+// Src blending function Inv Dest Alpha
+// Dst blending function Src Alpha
+void DColorComposerPorterDuffDstAtop::compose(DColor &dest, DColor src)
+{
+ int sa = src.alpha();
+ if (dest.sixteenBit())
+ {
+ src.blendInvAlpha16(dest.alpha());
+ dest.blendAlpha16(sa);
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendInvAlpha8(dest.alpha());
+ dest.blendInvAlpha8(sa);
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// Porter-Duff Xor
+// component = (source * (1-da) + destination * (1-sa))
+// Src blending function Inv Dst Alpha
+// Dst blending function Inv Src Alpha
+void DColorComposerPorterDuffXor::compose(DColor &dest, DColor src)
+{
+ int sa = src.alpha();
+ if (dest.sixteenBit())
+ {
+ src.blendInvAlpha16(dest.alpha());
+ dest.blendInvAlpha16(sa);
+ dest.blendAdd(src);
+ dest.blendClamp16();
+ }
+ else
+ {
+ src.blendInvAlpha8(dest.alpha());
+ dest.blendInvAlpha8(sa);
+ dest.blendAdd(src);
+ dest.blendClamp8();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void DColorComposer::compose(DColor &dest, DColor src, DColorComposer::MultiplicationFlags multiplicationFlags)
+{
+ if (multiplicationFlags & PremultiplySrc)
+ src.premultiply();
+ if (multiplicationFlags & PremultiplyDst)
+ dest.premultiply();
+
+ compose(dest, src);
+
+ if (multiplicationFlags & DemultiplyDst)
+ dest.demultiply();
+}
+
+DColorComposer *DColorComposer::getComposer(DColorComposer::CompositingOperation rule)
+{
+ switch(rule)
+ {
+ case PorterDuffNone:
+ return new DColorComposerPorterDuffNone;
+ case PorterDuffClear:
+ return new DColorComposerPorterDuffClear;
+ case PorterDuffSrc:
+ return new DColorComposerPorterDuffSrc;
+ case PorterDuffSrcOver:
+ return new DColorComposerPorterDuffSrcOver;
+ case PorterDuffDstOver:
+ return new DColorComposerPorterDuffDstOver;
+ case PorterDuffSrcIn:
+ return new DColorComposerPorterDuffSrcIn;
+ case PorterDuffDstIn:
+ return new DColorComposerPorterDuffDstIn;
+ case PorterDuffSrcOut:
+ return new DColorComposerPorterDuffSrcOut;
+ case PorterDuffDstOut:
+ return new DColorComposerPorterDuffDstOut;
+ case PorterDuffSrcAtop:
+ return new DColorComposerPorterDuffDstOut;
+ case PorterDuffDstAtop:
+ return new DColorComposerPorterDuffDstOut;
+ case PorterDuffXor:
+ return new DColorComposerPorterDuffDstOut;
+ }
+ return 0;
+}
+
+} // namespace DigiKam
diff --git a/src/libs/dimg/dcolorcomposer.h b/src/libs/dimg/dcolorcomposer.h
new file mode 100644
index 00000000..3e885ca8
--- /dev/null
+++ b/src/libs/dimg/dcolorcomposer.h
@@ -0,0 +1,133 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-03-02
+ * Description : DColor methods for composing
+ *
+ * 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 DCOLORCOMPOSER_H
+#define DCOLORCOMPOSER_H
+
+// Local includes.
+
+#include "dcolor.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DColorComposer
+{
+public:
+ /** The available rules to combine src and destination color.
+
+ For the Porter-Duff rules, the formula is
+ component = (source * fs + destination * fd)
+ where
+ fs, fd according to the following table with
+ sa = source alpha,
+ da = destination alpha:
+
+ None fs: sa fd: 1.0-sa
+ Clear fs: 0.0 fd: 0.0
+ Src fs: 1.0 fd: 0.0
+ Src Over fs: 1.0 fd: 1.0-sa
+ Dst Over fs: 1.0-da fd: 1.0
+ Src In fs: da fd: 0.0
+ Dst In fs: 0.0 fd: sa
+ Src Out fs: 1.0-da fd: 0.0
+ Dst Out fs: 0.0 fd: 1.0-sa
+
+ Src Atop fs: da fd: 1.0-sa
+ Dst Atop fs: 1.0-da fd: sa
+ Xor fs: 1.0-da fd: 1.0-sa
+
+ None is the default, classical blending mode, a "Src over" simplification:
+ Blend non-premultiplied RGBA data "src over" a fully opaque background.
+ Src is the painter's algorithm.
+ All other operations require premultiplied colors.
+ The documentation of java.awt.AlphaComposite (Java 1.5)
+ provides a good introduction and documentation on Porter Duff.
+ */
+
+ enum CompositingOperation
+ {
+ PorterDuffNone,
+ PorterDuffClear,
+ PorterDuffSrc,
+ PorterDuffSrcOver,
+ PorterDuffDstOver,
+ PorterDuffSrcIn,
+ PorterDuffDstIn,
+ PorterDuffSrcOut,
+ PorterDuffDstOut,
+ PorterDuffSrcAtop,
+ PorterDuffDstAtop,
+ PorterDuffXor
+ };
+
+ enum MultiplicationFlags
+ {
+ NoMultiplication = 0x00,
+ PremultiplySrc = 0x01,
+ PremultiplyDst = 0x02,
+ DemultiplyDst = 0x04,
+
+ MultiplicationFlagsDImg = PremultiplySrc | PremultiplyDst | DemultiplyDst,
+ MultiplicationFlagsPremultipliedColorOnDImg = PremultiplyDst | DemultiplyDst
+ };
+
+ /**
+ Retrieve a DColorComposer object for one of the predefined rules.
+ The object needs to be deleted by the caller.
+ */
+ static DColorComposer *getComposer(CompositingOperation rule);
+
+ /**
+ Carry out the actual composition process.
+ Src and Dest are composed and the result is written to dest.
+ No pre-/demultiplication is done by this method, use the other overloaded
+ methods, which call this method, if you need pre- or demultiplication
+ (you need it if any of the colors are read from or written to a DImg).
+
+ If you just pass the object to a DImg method, you do not need to call this.
+ Call this function if you want to compose two colors.
+ Implement this function if you create a custom DColorComposer.
+
+ The bit depth of source and destination color must be identical.
+ */
+ virtual void compose(DColor &dest, DColor src) = 0;
+
+ /**
+ Compose the two colors by calling compose(dest, src).
+ Pre- and demultiplication operations are done as specified.
+ For PorterDuff operations except PorterDuffNone, you need
+
+ - PremultiplySrc if src is not premultiplied (read from a DImg)
+ - PremultiplyDst if dst is not premultiplied (read from a DImg)
+ - DemultiplyDst if dst will be written to non-premultiplied data (a DImg)
+ */
+ virtual void compose(DColor &dest, DColor src, MultiplicationFlags multiplicationFlags);
+
+ virtual ~DColorComposer(){};
+};
+
+} // namespace Digikam
+
+#endif // DCOLORCOMPOSER_H
diff --git a/src/libs/dimg/dcolorpixelaccess.h b/src/libs/dimg/dcolorpixelaccess.h
new file mode 100644
index 00000000..30695c3e
--- /dev/null
+++ b/src/libs/dimg/dcolorpixelaccess.h
@@ -0,0 +1,78 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-03-02
+ * Description : methods to access on pixels color
+ *
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
+ *
+ * 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 DCOLORPIXELACCESS_H
+#define DCOLORPIXELACCESS_H
+
+namespace Digikam
+{
+
+// These methods are used in quite a few image effects,
+// typically in loops iterating the data.
+// Providing them as inline methods allows the compiler to optimize better.
+
+inline void DColor::setColor(const uchar *data, bool sixteenBit)
+{
+ m_sixteenBit = sixteenBit;
+
+ if (!sixteenBit) // 8 bits image
+ {
+ setBlue (data[0]);
+ setGreen(data[1]);
+ setRed (data[2]);
+ setAlpha(data[3]);
+ }
+ else // 16 bits image
+ {
+ unsigned short* data16 = (unsigned short*)data;
+ setBlue (data16[0]);
+ setGreen(data16[1]);
+ setRed (data16[2]);
+ setAlpha(data16[3]);
+ }
+}
+
+inline void DColor::setPixel(uchar *data) const
+{
+ if (sixteenBit()) // 16 bits image.
+ {
+ unsigned short *data16 = (unsigned short *)data;
+ data16[0] = (unsigned short)blue();
+ data16[1] = (unsigned short)green();
+ data16[2] = (unsigned short)red();
+ data16[3] = (unsigned short)alpha();
+ }
+ else // 8 bits image.
+ {
+ data[0] = (uchar)blue();
+ data[1] = (uchar)green();
+ data[2] = (uchar)red();
+ data[3] = (uchar)alpha();
+ }
+}
+
+
+} // namespace Digikam
+
+#endif // DCOLORPIXELACCESS_H
diff --git a/src/libs/dimg/ddebug.cpp b/src/libs/dimg/ddebug.cpp
new file mode 100644
index 00000000..3f8f07c4
--- /dev/null
+++ b/src/libs/dimg/ddebug.cpp
@@ -0,0 +1,92 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-06-11
+ * Description : thread safe debugging.
+ *
+ * See B.K.O #133026: because kdDebug() is not thread-safe
+ * we need to use a dedicaced debug statements in threaded
+ * implementation to prevent crash.
+ *
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
+ *
+ * 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 <tqmutex.h>
+
+// Local includes.
+
+#include "ddebug.h"
+
+#undef DDebug
+#undef kdDebug
+
+namespace Digikam
+{
+
+//static KStaticDeleter<TQMutex> deleter;
+static TQMutex *_ddebug_mutex_ = 0;
+
+Ddbgstream::Ddbgstream(kdbgstream stream)
+ : kdbgstream(stream)
+{
+ // using a static variable here - we can safely assume that kdDebug
+ // is called at least once from the main thread before threads start.
+ if (!_ddebug_mutex_)
+ {
+ // leak the mutex object for simplicity
+ _ddebug_mutex_ = new TQMutex;
+ //deleter.setObject(mutex, new TQMutex);
+ //TDEGlobal::unregisterStaticDeleter(&deleter);
+ }
+ _ddebug_mutex_->lock();
+}
+
+Ddbgstream::~Ddbgstream()
+{
+ _ddebug_mutex_->unlock();
+}
+
+Dndbgstream::Dndbgstream(kndbgstream stream)
+ : kndbgstream(stream)
+{
+ // using a static variable here - we can safely assume that kdDebug
+ // is called at least once from the main thread before threads start.
+ if (!_ddebug_mutex_)
+ {
+ // leak the mutex object for simplicity
+ _ddebug_mutex_ = new TQMutex;
+ //deleter.setObject(mutex, new TQMutex);
+ //TDEGlobal::unregisterStaticDeleter(&deleter);
+ }
+ _ddebug_mutex_->lock();
+}
+
+Dndbgstream::~Dndbgstream()
+{
+ _ddebug_mutex_->unlock();
+}
+
+} // namespace Digikam
+
+Digikam::Ddbgstream DDebug(int area) { return Digikam::Ddbgstream(kdDebug(area)); }
+Digikam::Ddbgstream DError(int area) { return Digikam::Ddbgstream(kdError(area)); }
+Digikam::Ddbgstream DWarning(int area) { return Digikam::Ddbgstream(kdWarning(area)); }
+
+Digikam::Dndbgstream DnDebug(int area) { return Digikam::Dndbgstream(kndDebug(area)); }
+
diff --git a/src/libs/dimg/ddebug.h b/src/libs/dimg/ddebug.h
new file mode 100644
index 00000000..1ea337eb
--- /dev/null
+++ b/src/libs/dimg/ddebug.h
@@ -0,0 +1,73 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-06-11
+ * Description : thread safe debugging.
+ *
+ * See B.K.O #133026: because kdDebug() is not thread-safe
+ * we need to use a dedicaced debug statements in threaded
+ * implementation to prevent crash.
+ *
+ * Copyright (C) 2006 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
+ *
+ * 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 _DDEBUG_H_
+#define _DDEBUG_H_
+
+// KDE includes.
+
+#include <kdebug.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT Ddbgstream : public kdbgstream
+{
+
+public:
+
+ Ddbgstream(kdbgstream stream);
+ ~Ddbgstream();
+};
+
+class DIGIKAM_EXPORT Dndbgstream : public kndbgstream
+{
+
+public:
+
+ Dndbgstream(kndbgstream stream);
+ ~Dndbgstream();
+};
+
+} // namespace Digikam
+
+DIGIKAM_EXPORT Digikam::Ddbgstream DDebug(int area = 0);
+DIGIKAM_EXPORT Digikam::Ddbgstream DWarning(int area = 0);
+DIGIKAM_EXPORT Digikam::Ddbgstream DError(int area = 0);
+
+DIGIKAM_EXPORT Digikam::Dndbgstream DnDebug(int area = 0);
+
+#ifdef NDEBUG
+#define DDebug DnDebug
+#endif
+
+#endif // _DDEBUG_H_
+
diff --git a/src/libs/dimg/dimg.cpp b/src/libs/dimg/dimg.cpp
new file mode 100644
index 00000000..d61f2dd0
--- /dev/null
+++ b/src/libs/dimg/dimg.cpp
@@ -0,0 +1,1700 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : digiKam 8/16 bits image management API
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * 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.
+ *
+ * ============================================================ */
+
+// C ANSI includes.
+
+extern "C"
+{
+#if !defined(__STDC_LIMIT_MACROS)
+#define __STDC_LIMIT_MACROS
+#endif
+#include <stdint.h>
+}
+
+// C++ includes.
+
+#include <cstdio>
+
+// TQt includes.
+
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqmap.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/version.h>
+#include <libkdcraw/kdcraw.h>
+
+#if KDCRAW_VERSION < 0x000106
+#include <libkdcraw/dcrawbinary.h>
+#endif
+
+// Local includes.
+
+#include "pngloader.h"
+#include "jpegloader.h"
+#include "tiffloader.h"
+#include "ppmloader.h"
+#include "rawloader.h"
+#include "jp2kloader.h"
+#include "qimageloader.h"
+#include "icctransform.h"
+#include "exposurecontainer.h"
+#include "ddebug.h"
+#include "dimgprivate.h"
+#include "dimgloaderobserver.h"
+#include "dimg.h"
+
+typedef uint64_t ullong;
+typedef int64_t llong;
+
+namespace Digikam
+{
+
+DImg::DImg()
+ : m_priv(new DImgPrivate)
+{
+}
+
+DImg::DImg(const TQCString& filePath, DImgLoaderObserver *observer,
+ DRawDecoding rawDecodingSettings)
+ : m_priv(new DImgPrivate)
+{
+ load(filePath, observer, rawDecodingSettings);
+}
+
+DImg::DImg(const TQString& filePath, DImgLoaderObserver *observer,
+ DRawDecoding rawDecodingSettings)
+ : m_priv(new DImgPrivate)
+{
+ load(filePath, observer, rawDecodingSettings);
+}
+
+DImg::DImg(const DImg& image)
+{
+ m_priv = image.m_priv;
+ m_priv->ref();
+}
+
+DImg::DImg(uint width, uint height, bool sixteenBit, bool alpha, uchar* data, bool copyData)
+ : m_priv(new DImgPrivate)
+{
+ putImageData(width, height, sixteenBit, alpha, data, copyData);
+}
+
+DImg::DImg(const DImg &image, int w, int h)
+ : m_priv(new DImgPrivate)
+{
+ // This private constructor creates a copy of everything except the data.
+ // The image size is set to the given values and a buffer corresponding to these values is allocated.
+ // This is used by copy and scale.
+ copyImageData(image.m_priv);
+ copyMetaData(image.m_priv);
+ setImageDimension(w, h);
+ allocateData();
+}
+
+DImg::DImg(const TQImage& image)
+ : m_priv(new DImgPrivate)
+{
+ if (!image.isNull())
+ {
+ 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++;
+ }
+
+ putImageData(w, h, false, image.hasAlphaBuffer(), data, false);
+ }
+}
+
+DImg::~DImg()
+{
+ if (m_priv->deref())
+ delete m_priv;
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// data management
+
+
+DImg& DImg::operator=(const DImg& image)
+{
+ if (m_priv == image.m_priv)
+ return *this;
+
+ if (m_priv->deref())
+ {
+ delete m_priv;
+ m_priv = 0;
+ }
+
+ m_priv = image.m_priv;
+ m_priv->ref();
+
+ return *this;
+}
+
+bool DImg::operator==(const DImg& image) const
+{
+ return m_priv == image.m_priv;
+}
+
+void DImg::reset(void)
+{
+ if (m_priv->deref())
+ delete m_priv;
+
+ m_priv = new DImgPrivate;
+}
+
+void DImg::detach()
+{
+ // are we being shared?
+ if (m_priv->count <= 1)
+ {
+ return;
+ }
+
+ DImgPrivate* old = m_priv;
+
+ m_priv = new DImgPrivate;
+ copyImageData(old);
+ copyMetaData(old);
+
+ if (old->data)
+ {
+ int size = allocateData();
+ memcpy(m_priv->data, old->data, size);
+ }
+
+ old->deref();
+}
+
+void DImg::putImageData(uint width, uint height, bool sixteenBit, bool alpha, uchar *data, bool copyData)
+{
+ // set image data, metadata is untouched
+
+ bool null = (width == 0) || (height == 0);
+ // allocateData, or code below will set null to false
+ setImageData(true, width, height, sixteenBit, alpha);
+
+ // replace data
+ delete [] m_priv->data;
+ if (null)
+ {
+ // image is null - no data
+ m_priv->data = 0;
+ }
+ else if (copyData)
+ {
+ int size = allocateData();
+ if (data)
+ memcpy(m_priv->data, data, size);
+ }
+ else
+ {
+ if (data)
+ {
+ m_priv->data = data;
+ m_priv->null = false;
+ }
+ else
+ allocateData();
+ }
+}
+
+void DImg::putImageData(uchar *data, bool copyData)
+{
+ if (!data)
+ {
+ delete [] m_priv->data;
+ m_priv->data = 0;
+ m_priv->null = true;
+ }
+ else if (copyData)
+ {
+ memcpy(m_priv->data, data, numBytes());
+ }
+ else
+ {
+ m_priv->data = data;
+ }
+}
+
+void DImg::resetMetaData()
+{
+ m_priv->attributes.clear();
+ m_priv->embeddedText.clear();
+ m_priv->metaData.clear();
+}
+
+uchar *DImg::stripImageData()
+{
+ uchar *data = m_priv->data;
+ m_priv->data = 0;
+ m_priv->null = true;
+ return data;
+}
+
+void DImg::copyMetaData(const DImgPrivate *src)
+{
+ m_priv->isReadOnly = src->isReadOnly;
+ m_priv->attributes = src->attributes;
+ m_priv->embeddedText = src->embeddedText;
+
+ // since qbytearrays are explicitly shared, we need to make sure that they are
+ // detached from any shared references
+
+ for (TQMap<int, TQByteArray>::const_iterator it = src->metaData.begin();
+ it != src->metaData.end(); ++it)
+ {
+ m_priv->metaData.insert(it.key(), it.data().copy());
+ }
+}
+
+void DImg::copyImageData(const DImgPrivate *src)
+{
+ setImageData(src->null, src->width, src->height, src->sixteenBit, src->alpha);
+}
+
+int DImg::allocateData()
+{
+ int size = m_priv->width * m_priv->height * (m_priv->sixteenBit ? 8 : 4);
+ m_priv->data = new uchar[size];
+ m_priv->null = false;
+ return size;
+}
+
+void DImg::setImageDimension(uint width, uint height)
+{
+ m_priv->width = width;
+ m_priv->height = height;
+}
+
+void DImg::setImageData(bool null, uint width, uint height, bool sixteenBit, bool alpha)
+{
+ m_priv->null = null;
+ m_priv->width = width;
+ m_priv->height = height;
+ m_priv->alpha = alpha;
+ m_priv->sixteenBit = sixteenBit;
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// load and save
+
+
+bool DImg::load(const TQString& filePath, DImgLoaderObserver *observer,
+ DRawDecoding rawDecodingSettings)
+{
+ FORMAT format = fileFormat(filePath);
+
+ switch (format)
+ {
+ case(NONE):
+ {
+ DDebug() << filePath << " : Unknown image format !!!" << endl;
+ return false;
+ break;
+ }
+ case(JPEG):
+ {
+ DDebug() << filePath << " : JPEG file identified" << endl;
+ JPEGLoader loader(this);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ case(TIFF):
+ {
+ DDebug() << filePath << " : TIFF file identified" << endl;
+ TIFFLoader loader(this);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ case(PNG):
+ {
+ DDebug() << filePath << " : PNG file identified" << endl;
+ PNGLoader loader(this);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ case(PPM):
+ {
+ DDebug() << filePath << " : PPM file identified" << endl;
+ PPMLoader loader(this);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ case(RAW):
+ {
+ DDebug() << filePath << " : RAW file identified" << endl;
+ RAWLoader loader(this, rawDecodingSettings);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ case(JP2K):
+ {
+ DDebug() << filePath << " : JPEG2000 file identified" << endl;
+ JP2KLoader loader(this);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ default:
+ {
+ DDebug() << filePath << " : TQIMAGE file identified" << endl;
+ TQImageLoader loader(this);
+ if (loader.load(filePath, observer))
+ {
+ m_priv->null = false;
+ m_priv->alpha = loader.hasAlpha();
+ m_priv->sixteenBit = loader.sixteenBit();
+ m_priv->isReadOnly = loader.isReadOnly();
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+bool DImg::save(const TQString& filePath, const TQString& format, DImgLoaderObserver *observer)
+{
+ if (isNull())
+ return false;
+
+ if (format.isEmpty())
+ return false;
+
+ TQString frm = format.upper();
+
+ if (frm == "JPEG" || frm == "JPG" || frm == "JPE")
+ {
+ JPEGLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+ else if (frm == "PNG")
+ {
+ PNGLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+ else if (frm == "TIFF" || frm == "TIF")
+ {
+ TIFFLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+ else if (frm == "PPM")
+ {
+ PPMLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+ if (frm == "JP2" || frm == "JPX" || frm == "JPC" || frm == "PGX")
+ {
+ JP2KLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+ else
+ {
+ setAttribute("format", format);
+ TQImageLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+
+ return false;
+}
+
+DImg::FORMAT DImg::fileFormat(const TQString& filePath)
+{
+ if ( filePath.isNull() )
+ return NONE;
+
+ // In first we trying to check the file extension. This is mandatory because
+ // some tiff files are detected like RAW files by dcraw::identify method.
+
+ TQFileInfo fileInfo(filePath);
+ if (!fileInfo.exists())
+ {
+ DDebug() << k_funcinfo << "File \"" << filePath << "\" does not exist" << endl;
+ return NONE;
+ }
+
+#if KDCRAW_VERSION < 0x000106
+ TQString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles());
+#else
+ TQString rawFilesExt(KDcrawIface::KDcraw::rawFiles());
+#endif
+ TQString ext = fileInfo.extension(false).upper();
+
+ if (!ext.isEmpty())
+ {
+ if (ext == TQString("JPEG") || ext == TQString("JPG") || ext == TQString("JPE"))
+ return JPEG;
+ else if (ext == TQString("PNG"))
+ return PNG;
+ else if (ext == TQString("TIFF") || ext == TQString("TIF"))
+ return TIFF;
+ else if (rawFilesExt.upper().contains(ext))
+ return RAW;
+ if (ext == TQString("JP2") || ext == TQString("JPX") || // JPEG2000 file format
+ ext == TQString("JPC") || // JPEG2000 code stream
+ ext == TQString("PGX")) // JPEG2000 WM format
+ return JP2K;
+ }
+
+ // In second, we trying to parse file header.
+
+ FILE* f = fopen(TQFile::encodeName(filePath), "rb");
+
+ if (!f)
+ {
+ DDebug() << k_funcinfo << "Failed to open file \"" << filePath << "\"" << endl;
+ return NONE;
+ }
+
+ const int headerLen = 9;
+ unsigned char header[headerLen];
+
+ if (fread(&header, headerLen, 1, f) != 1)
+ {
+ DDebug() << k_funcinfo << "Failed to read header of file \"" << filePath << "\"" << endl;
+ fclose(f);
+ return NONE;
+ }
+
+ fclose(f);
+
+ KDcrawIface::DcrawInfoContainer dcrawIdentify;
+ KDcrawIface::KDcraw::rawFileIdentify(dcrawIdentify, filePath);
+ uchar jpegID[2] = { 0xFF, 0xD8 };
+ uchar tiffBigID[2] = { 0x4D, 0x4D };
+ uchar tiffLilID[2] = { 0x49, 0x49 };
+ uchar pngID[8] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
+ uchar jp2ID[5] = { 0x6A, 0x50, 0x20, 0x20, 0x0D, };
+ uchar jpcID[2] = { 0xFF, 0x4F };
+
+ if (memcmp(&header, &jpegID, 2) == 0) // JPEG file ?
+ {
+ return JPEG;
+ }
+ else if (memcmp(&header, &pngID, 8) == 0) // PNG file ?
+ {
+ return PNG;
+ }
+ else if (memcmp(&header[0], "P", 1) == 0 &&
+ memcmp(&header[2], "\n", 1) == 0) // PPM 16 bits file ?
+ {
+ int width, height, rgbmax;
+ char nl;
+ FILE *file = fopen(TQFile::encodeName(filePath), "rb");
+
+ if (fscanf (file, "P6 %d %d %d%c", &width, &height, &rgbmax, &nl) == 4)
+ {
+ if (rgbmax > 255)
+ {
+ pclose (file);
+ return PPM;
+ }
+ }
+
+ pclose (file);
+ }
+ else if (dcrawIdentify.isDecodable)
+ {
+ // RAW File test using dcraw::identify method.
+ // Need to test it before TIFF because any RAW file
+ // formats using TIFF header.
+ return RAW;
+ }
+ else if (memcmp(&header, &tiffBigID, 2) == 0 || // TIFF file ?
+ memcmp(&header, &tiffLilID, 2) == 0)
+ {
+ return TIFF;
+ }
+ else if (memcmp(&header[4], &jp2ID, 5) == 0 || // JPEG2000 file ?
+ memcmp(&header, &jpcID, 2) == 0)
+ {
+ return JP2K;
+ }
+
+ // In others cases, TQImage will be used to try to open file.
+ return TQIMAGE;
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// accessing properties
+
+
+bool DImg::isNull() const
+{
+ return m_priv->null;
+}
+
+uint DImg::width() const
+{
+ return m_priv->width;
+}
+
+uint DImg::height() const
+{
+ return m_priv->height;
+}
+
+TQSize DImg::size() const
+{
+ return TQSize(m_priv->width, m_priv->height);
+}
+
+uchar* DImg::bits() const
+{
+ return m_priv->data;
+}
+
+uchar* DImg::scanLine(uint i) const
+{
+ if ( i >= height() )
+ return 0;
+
+ uchar *data = bits() + (width() * bytesDepth() * i);
+ return data;
+}
+
+bool DImg::hasAlpha() const
+{
+ return m_priv->alpha;
+}
+
+bool DImg::sixteenBit() const
+{
+ return m_priv->sixteenBit;
+}
+
+bool DImg::isReadOnly() const
+{
+ return m_priv->isReadOnly;
+}
+
+bool DImg::getICCProfilFromFile(const TQString& filePath)
+{
+ TQFile file(filePath);
+ if ( !file.open(IO_ReadOnly) )
+ return false;
+
+ TQByteArray data(file.size());
+ TQDataStream stream( &file );
+ stream.readRawBytes(data.data(), data.size());
+ setICCProfil(data);
+ file.close();
+ return true;
+}
+
+bool DImg::setICCProfilToFile(const TQString& filePath)
+{
+ TQFile file(filePath);
+ if ( !file.open(IO_WriteOnly) )
+ return false;
+
+ TQByteArray data(getICCProfil());
+ TQDataStream stream( &file );
+ stream.writeRawBytes(data.data(), data.size());
+ file.close();
+ return true;
+}
+
+TQByteArray DImg::getComments() const
+{
+ return metadata(COM);
+}
+
+TQByteArray DImg::getExif() const
+{
+ return metadata(EXIF);
+}
+
+TQByteArray DImg::getIptc() const
+{
+ return metadata(IPTC);
+}
+
+TQByteArray DImg::getICCProfil() const
+{
+ return metadata(ICC);
+}
+
+void DImg::setComments(const TQByteArray& commentsData)
+{
+ m_priv->metaData.replace(COM, commentsData);
+}
+
+void DImg::setExif(const TQByteArray& exifData)
+{
+ m_priv->metaData.replace(EXIF, exifData);
+}
+
+void DImg::setIptc(const TQByteArray& iptcData)
+{
+ m_priv->metaData.replace(IPTC, iptcData);
+}
+
+void DImg::setICCProfil(const TQByteArray& profile)
+{
+ m_priv->metaData.replace(ICC, profile);
+}
+
+TQByteArray DImg::metadata(DImg::METADATA key) const
+{
+ typedef TQMap<int, TQByteArray> MetaDataMap;
+
+ for (MetaDataMap::iterator it = m_priv->metaData.begin(); it != m_priv->metaData.end(); ++it)
+ {
+ if (it.key() == key)
+ return it.data();
+ }
+
+ return TQByteArray();
+}
+
+uint DImg::numBytes() const
+{
+ return (width() * height() * bytesDepth());
+}
+
+uint DImg::numPixels() const
+{
+ return (width() * height());
+}
+
+int DImg::bytesDepth() const
+{
+ if (sixteenBit())
+ return 8;
+
+ return 4;
+}
+
+int DImg::bitsDepth() const
+{
+ if (sixteenBit())
+ return 16;
+
+ return 8;
+}
+
+void DImg::setAttribute(const TQString& key, const TQVariant& value)
+{
+ m_priv->attributes.insert(key, value);
+}
+
+TQVariant DImg::attribute(const TQString& key) const
+{
+ if (m_priv->attributes.contains(key))
+ return m_priv->attributes[key];
+
+ return TQVariant();
+}
+
+void DImg::setEmbeddedText(const TQString& key, const TQString& text)
+{
+ m_priv->embeddedText.insert(key, text);
+}
+
+TQString DImg::embeddedText(const TQString& key) const
+{
+ if (m_priv->embeddedText.contains(key))
+ return m_priv->embeddedText[key];
+
+ return TQString();
+}
+
+DColor DImg::getPixelColor(uint x, uint y) const
+{
+ if (isNull() || x > width() || y > height())
+ {
+ DDebug() << k_funcinfo << " : wrong pixel position!" << endl;
+ return DColor();
+ }
+
+ uchar *data = bits() + x*bytesDepth() + (width()*y*bytesDepth());
+
+ return( DColor(data, sixteenBit()) );
+}
+
+void DImg::setPixelColor(uint x, uint y, DColor color)
+{
+ if (isNull() || x > width() || y > height())
+ {
+ DDebug() << k_funcinfo << " : wrong pixel position!" << endl;
+ return;
+ }
+
+ if (color.sixteenBit() != sixteenBit())
+ {
+ DDebug() << k_funcinfo << " : wrong color depth!" << endl;
+ return;
+ }
+
+ uchar *data = bits() + x*bytesDepth() + (width()*y*bytesDepth());
+ color.setPixel(data);
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// copying operations
+
+
+DImg DImg::copy()
+{
+ DImg img(*this);
+ img.detach();
+ return img;
+}
+
+DImg DImg::copyImageData()
+{
+ DImg img(width(), height(), sixteenBit(), hasAlpha(), bits(), true);
+ return img;
+}
+
+DImg DImg::copyMetaData()
+{
+ DImg img;
+ // copy width, height, alpha, sixteenBit, null
+ img.copyImageData(m_priv);
+ // deeply copy metadata
+ img.copyMetaData(m_priv);
+ // set image to null
+ img.m_priv->null = true;
+ return img;
+}
+
+DImg DImg::copy(TQRect rect)
+{
+ return copy(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+DImg DImg::copy(int x, int y, int w, int h)
+{
+ if ( isNull() || w <= 0 || h <= 0)
+ {
+ DDebug() << k_funcinfo << " : return null image!" << endl;
+ return DImg();
+ }
+
+ DImg image(*this, w, h);
+ image.bitBltImage(this, x, y, w, h, 0, 0);
+
+ return image;
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// bitwise operations
+
+
+void DImg::bitBltImage(const DImg* src, int dx, int dy)
+{
+ bitBltImage(src, 0, 0, src->width(), src->height(), dx, dy);
+}
+
+void DImg::bitBltImage(const DImg* src, int sx, int sy, int dx, int dy)
+{
+ bitBltImage(src, sx, sy, src->width() - sx, src->height() - sy, dx, dy);
+}
+
+void DImg::bitBltImage(const DImg* src, int sx, int sy, int w, int h, int dx, int dy)
+{
+ if (isNull())
+ return;
+
+ if (src->sixteenBit() != sixteenBit())
+ {
+ DWarning() << "Blitting from 8-bit to 16-bit or vice versa is not supported" << endl;
+ return;
+ }
+
+ if (w == -1 && h == -1)
+ {
+ w = src->width();
+ h = src->height();
+ }
+
+ bitBlt(src->bits(), bits(), sx, sy, w, h, dx, dy,
+ src->width(), src->height(), width(), height(), sixteenBit(), src->bytesDepth(), bytesDepth());
+}
+
+void DImg::bitBltImage(const uchar* src, int sx, int sy, int w, int h, int dx, int dy,
+ uint swidth, uint sheight, int sdepth)
+{
+ if (isNull())
+ return;
+
+ if (bytesDepth() != sdepth)
+ {
+ DWarning() << "Blitting from 8-bit to 16-bit or vice versa is not supported" << endl;
+ return;
+ }
+
+ if (w == -1 && h == -1)
+ {
+ w = swidth;
+ h = sheight;
+ }
+
+ bitBlt(src, bits(), sx, sy, w, h, dx, dy, swidth, sheight, width(), height(), sixteenBit(), sdepth, bytesDepth());
+}
+
+bool DImg::normalizeRegionArguments(int &sx, int &sy, int &w, int &h, int &dx, int &dy,
+ uint swidth, uint sheight, uint dwidth, uint dheight)
+{
+ if (sx < 0)
+ {
+ // sx is negative, so + is - and - is +
+ dx -= sx;
+ w += sx;
+ sx = 0;
+ }
+
+ if (sy < 0)
+ {
+ dy -= sy;
+ h += sy;
+ sy = 0;
+ }
+
+ if (dx < 0)
+ {
+ sx -= dx;
+ w += dx;
+ dx = 0;
+ }
+
+ if (dy < 0)
+ {
+ sy -= dy;
+ h += dy;
+ dy = 0;
+ }
+
+ if (sx + w > (int)swidth)
+ {
+ w = swidth - sx;
+ }
+
+ if (sy + h > (int)sheight)
+ {
+ h = sheight - sy;
+ }
+
+ if (dx + w > (int)dwidth)
+ {
+ w = dwidth - dx;
+ }
+
+ if (dy + h > (int)dheight)
+ {
+ h = dheight - dy;
+ }
+
+ // Nothing left to copy
+ if (w <= 0 || h <= 0)
+ return false;
+
+ return true;
+}
+
+void DImg::bitBlt (const uchar *src, uchar *dest,
+ int sx, int sy, int w, int h, int dx, int dy,
+ uint swidth, uint sheight, uint dwidth, uint dheight,
+ bool /*sixteenBit*/, int sdepth, int ddepth)
+{
+ // Normalize
+ if (!normalizeRegionArguments(sx, sy, w, h, dx, dy, swidth, sheight, dwidth, dheight))
+ return;
+
+ // Same pixels
+ if (src == dest && dx==sx && dy==sy)
+ return;
+
+ const uchar *sptr;
+ uchar *dptr;
+ uint slinelength = swidth * sdepth;
+ uint dlinelength = dwidth * ddepth;
+
+ int scurY = sy;
+ int dcurY = dy;
+ for (int j = 0 ; j < h ; j++, scurY++, dcurY++)
+ {
+ sptr = &src [ scurY * slinelength ] + sx * sdepth;
+ dptr = &dest[ dcurY * dlinelength ] + dx * ddepth;
+
+ // plain and simple bitBlt
+ for (int i = 0; i < w * sdepth ; i++, sptr++, dptr++)
+ {
+ *dptr = *sptr;
+ }
+ }
+}
+
+void DImg::bitBlendImage(DColorComposer *composer, const DImg* src,
+ int sx, int sy, int w, int h, int dx, int dy,
+ DColorComposer::MultiplicationFlags multiplicationFlags)
+{
+ if (isNull())
+ return;
+
+ if (src->sixteenBit() != sixteenBit())
+ {
+ DWarning() << "Blending from 8-bit to 16-bit or vice versa is not supported" << endl;
+ return;
+ }
+
+ bitBlend(composer, src->bits(), bits(), sx, sy, w, h, dx, dy,
+ src->width(), src->height(), width(), height(), sixteenBit(),
+ src->bytesDepth(), bytesDepth(), multiplicationFlags);
+}
+
+void DImg::bitBlend (DColorComposer *composer, const uchar *src, uchar *dest,
+ int sx, int sy, int w, int h, int dx, int dy,
+ uint swidth, uint sheight, uint dwidth, uint dheight,
+ bool sixteenBit, int sdepth, int ddepth,
+ DColorComposer::MultiplicationFlags multiplicationFlags)
+{
+ // Normalize
+ if (!normalizeRegionArguments(sx, sy, w, h, dx, dy, swidth, sheight, dwidth, dheight))
+ return;
+
+ const uchar *sptr;
+ uchar *dptr;
+ uint slinelength = swidth * sdepth;
+ uint dlinelength = dwidth * ddepth;
+
+ int scurY = sy;
+ int dcurY = dy;
+ for (int j = 0 ; j < h ; j++, scurY++, dcurY++)
+ {
+ sptr = &src [ scurY * slinelength ] + sx * sdepth;
+ dptr = &dest[ dcurY * dlinelength ] + dx * ddepth;
+
+ // blend src and destination
+ for (int i = 0 ; i < w ; i++, sptr+=sdepth, dptr+=ddepth)
+ {
+ DColor src(sptr, sixteenBit);
+ DColor dst(dptr, sixteenBit);
+
+ // blend colors
+ composer->compose(dst, src, multiplicationFlags);
+
+ dst.setPixel(dptr);
+ }
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// TQImage / TQPixmap access
+
+
+TQImage DImg::copyTQImage()
+{
+ if (isNull())
+ return TQImage();
+
+ if (sixteenBit())
+ {
+ DImg img(*this);
+ img.detach();
+ img.convertDepth(32);
+ return img.copyTQImage();
+ }
+
+ TQImage img(width(), height(), 32);
+
+ uchar* sptr = bits();
+ uint* dptr = (uint*)img.bits();
+
+ for (uint i=0; i < width()*height(); i++)
+ {
+ *dptr++ = tqRgba(sptr[2], sptr[1], sptr[0], sptr[3]);
+ sptr += 4;
+ }
+
+ if (hasAlpha())
+ {
+ img.setAlphaBuffer(true);
+ }
+
+ return img;
+}
+
+TQImage DImg::copyTQImage(TQRect rect)
+{
+ return (copyTQImage(rect.x(), rect.y(), rect.width(), rect.height()));
+}
+
+TQImage DImg::copyTQImage(int x, int y, int w, int h)
+{
+ if (isNull())
+ return TQImage();
+
+ DImg img = copy(x, y, w, h);
+
+ if (img.sixteenBit())
+ img.convertDepth(32);
+
+ return img.copyTQImage();
+}
+
+TQPixmap DImg::convertToPixmap()
+{
+ if (isNull())
+ return TQPixmap();
+
+ if (sixteenBit())
+ {
+ // make fastaaaa..
+ return TQPixmap(copyTQImage(0, 0, width(), height()));
+ }
+
+ if (TQImage::systemByteOrder() == TQImage::BigEndian)
+ {
+ TQImage img(width(), height(), 32);
+
+ uchar* sptr = bits();
+ uint* dptr = (uint*)img.bits();
+
+ for (uint i=0; i<width()*height(); i++)
+ {
+ *dptr++ = tqRgba(sptr[2], sptr[1], sptr[0], sptr[3]);
+ sptr += 4;
+ }
+
+ if (hasAlpha())
+ {
+ img.setAlphaBuffer(true);
+ }
+
+ return TQPixmap(img);
+ }
+ else
+ {
+ TQImage img(bits(), width(), height(), 32, 0, 0, TQImage::IgnoreEndian);
+
+ if (hasAlpha())
+ {
+ img.setAlphaBuffer(true);
+ }
+
+ return TQPixmap(img);
+ }
+}
+
+TQPixmap DImg::convertToPixmap(IccTransform *monitorICCtrans)
+{
+ if (isNull())
+ return TQPixmap();
+
+ if (!monitorICCtrans->hasOutputProfile())
+ {
+ DDebug() << k_funcinfo << " : no monitor ICC profile available!" << endl;
+ return convertToPixmap();
+ }
+
+ DImg img = copy();
+
+ // Without embedded profile
+ if (img.getICCProfil().isNull())
+ {
+ TQByteArray fakeProfile;
+ monitorICCtrans->apply(img, fakeProfile, monitorICCtrans->getRenderingIntent(),
+ monitorICCtrans->getUseBPC(), false,
+ monitorICCtrans->inputProfile().isNull());
+ }
+ // With embedded profile.
+ else
+ {
+ monitorICCtrans->getEmbeddedProfile( img );
+ monitorICCtrans->apply( img );
+ }
+
+ return (img.convertToPixmap());
+}
+
+TQImage DImg::pureColorMask(ExposureSettingsContainer *expoSettings)
+{
+ if (isNull() || (!expoSettings->underExposureIndicator && !expoSettings->overExposureIndicator))
+ return TQImage();
+
+ TQImage img(size(), 32);
+ img.fill(0x00000000); // Full transparent.
+ img.setAlphaBuffer(true);
+
+ uchar *bits = img.bits();
+ int max = sixteenBit() ? 65535 : 255;
+ int index;
+ DColor pix;
+
+ for (uint x=0 ; x < width() ; x++)
+ {
+ for (uint y=0 ; y<height() ; y++)
+ {
+ pix = getPixelColor(x, y);
+ index = y*img.bytesPerLine() + x*4;
+
+ if (expoSettings->underExposureIndicator &&
+ pix.red() == 0 && pix.green() == 0 && pix.blue() == 0)
+ {
+ bits[index ] = expoSettings->underExposureColor.blue();
+ bits[index + 1] = expoSettings->underExposureColor.green();
+ bits[index + 2] = expoSettings->underExposureColor.red();
+ bits[index + 3] = 0xFF;
+ }
+
+ if (expoSettings->overExposureIndicator &&
+ pix.red() == max && pix.green() == max && pix.blue() == max)
+ {
+ bits[index ] = expoSettings->overExposureColor.blue();
+ bits[index + 1] = expoSettings->overExposureColor.green();
+ bits[index + 2] = expoSettings->overExposureColor.red();
+ bits[index + 3] = 0xFF;
+ }
+ }
+ }
+
+ return img;
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// basic imaging operations
+
+
+void DImg::crop(TQRect rect)
+{
+ crop(rect.x(), rect.y(), rect.width(), rect.height());
+}
+
+void DImg::crop(int x, int y, int w, int h)
+{
+ if ( isNull() || w <= 0 || h <= 0)
+ return;
+
+ uint oldw = width();
+ uint oldh = height();
+ uchar *old = stripImageData();
+
+ // set new image data, bits(), width(), height() change
+ setImageDimension(w, h);
+ allocateData();
+
+ // copy image region (x|y), wxh, from old data to point (0|0) of new data
+ bitBlt(old, bits(), x, y, w, h, 0, 0, oldw, oldh, width(), height(), sixteenBit(), bytesDepth(), bytesDepth());
+ delete [] old;
+}
+
+void DImg::resize(int w, int h)
+{
+ if ( w <= 0 || h <= 0)
+ return;
+
+ DImg image = smoothScale(w, h);
+
+ delete [] m_priv->data;
+ m_priv->data = image.stripImageData();
+ setImageDimension(w, h);
+}
+
+void DImg::rotate(ANGLE angle)
+{
+ if (isNull())
+ return;
+
+ switch (angle)
+ {
+ case(ROT90):
+ {
+ uint w = height();
+ uint h = width();
+
+ if (sixteenBit())
+ {
+ ullong* newData = new ullong[w*h];
+
+ ullong *from = (ullong*) m_priv->data;
+ ullong *to;
+
+ for (int y = w-1; y >=0; y--)
+ {
+ to = newData + y;
+
+ for (uint x=0; x < h; x++)
+ {
+ *to = *from++;
+ to += w;
+ }
+ }
+
+ setImageDimension(w, h);
+
+ delete [] m_priv->data;
+ m_priv->data = (uchar*)newData;
+ }
+ else
+ {
+ uint* newData = new uint[w*h];
+
+ uint *from = (uint*) m_priv->data;
+ uint *to;
+
+ for (int y = w-1; y >=0; y--)
+ {
+ to = newData + y;
+
+ for (uint x=0; x < h; x++)
+ {
+ *to = *from++;
+ to += w;
+ }
+ }
+
+ setImageDimension(w, h);
+
+ delete [] m_priv->data;
+ m_priv->data = (uchar*)newData;
+ }
+
+ break;
+ }
+ case(ROT180):
+ {
+ uint w = width();
+ uint h = height();
+
+ int middle_line = -1;
+ if (h % 2)
+ middle_line = h / 2;
+
+ if (sixteenBit())
+ {
+ ullong *line1;
+ ullong *line2;
+
+ ullong* data = (ullong*) bits();
+ ullong tmp;
+
+ // can be done inplace
+ for (uint y = 0; y < (h+1)/2; y++)
+ {
+ line1 = data + y * w;
+ line2 = data + (h-y) * w;
+ for (uint x=0; x < w; x++)
+ {
+ tmp = *line1;
+ *line1 = *line2;
+ *line2 = tmp;
+
+ line1++;
+ line2--;
+ if ((int)y == middle_line && x * 2 >= w)
+ break;
+ }
+ }
+ }
+ else
+ {
+ uint *line1;
+ uint *line2;
+
+ uint* data = (uint*) bits();
+ uint tmp;
+
+ // can be done inplace
+ for (uint y = 0; y < (h+1)/2; y++)
+ {
+ line1 = data + y * w;
+ line2 = data + (h-y) * w;
+
+ for (uint x=0; x < w; x++)
+ {
+ tmp = *line1;
+ *line1 = *line2;
+ *line2 = tmp;
+
+ line1++;
+ line2--;
+ if ((int)y == middle_line && x * 2 >= w)
+ break;
+ }
+ }
+ }
+
+ break;
+ }
+ case(ROT270):
+ {
+ uint w = height();
+ uint h = width();
+
+ if (sixteenBit())
+ {
+ ullong* newData = new ullong[w*h];
+
+ ullong *from = (ullong*) m_priv->data;
+ ullong *to;
+
+ for (uint y = 0; y < w; y++)
+ {
+ to = newData + y + w*(h-1);
+
+ for (uint x=0; x < h; x++)
+ {
+ *to = *from++;
+ to -= w;
+ }
+ }
+
+ setImageDimension(w, h);
+
+ delete [] m_priv->data;
+ m_priv->data = (uchar*)newData;
+ }
+ else
+ {
+ uint* newData = new uint[w*h];
+
+ uint *from = (uint*) m_priv->data;
+ uint *to;
+
+ for (uint y = 0; y < w; y++)
+ {
+ to = newData + y + w*(h-1);
+
+ for (uint x=0; x < h; x++)
+ {
+ *to = *from++;
+ to -= w;
+ }
+ }
+
+ setImageDimension(w, h);
+
+ delete [] m_priv->data;
+ m_priv->data = (uchar*)newData;
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+// 15-11-2005: This method have been tested indeep with valgrind by Gilles.
+
+void DImg::flip(FLIP direction)
+{
+ if (isNull())
+ return;
+
+ switch (direction)
+ {
+ case(HORIZONTAL):
+ {
+ uint w = width();
+ uint h = height();
+
+ if (sixteenBit())
+ {
+ unsigned short tmp[4];
+ unsigned short *beg;
+ unsigned short *end;
+
+ unsigned short * data = (unsigned short *)bits();
+
+ // can be done inplace
+ for (uint y = 0 ; y < h ; y++)
+ {
+ beg = data + y * w * 4;
+ end = beg + (w-1) * 4;
+
+ for (uint x=0 ; x < (w/2) ; x++)
+ {
+ memcpy(&tmp, beg, 8);
+ memcpy(beg, end, 8);
+ memcpy(end, &tmp, 8);
+
+ beg+=4;
+ end-=4;
+ }
+ }
+ }
+ else
+ {
+ uchar tmp[4];
+ uchar *beg;
+ uchar *end;
+
+ uchar* data = bits();
+
+ // can be done inplace
+ for (uint y = 0 ; y < h ; y++)
+ {
+ beg = data + y * w * 4;
+ end = beg + (w-1) * 4;
+
+ for (uint x=0 ; x < (w/2) ; x++)
+ {
+ memcpy(&tmp, beg, 4);
+ memcpy(beg, end, 4);
+ memcpy(end, &tmp, 4);
+
+ beg+=4;
+ end-=4;
+ }
+ }
+ }
+
+ break;
+ }
+ case(VERTICAL):
+ {
+ uint w = width();
+ uint h = height();
+
+ if (sixteenBit())
+ {
+ unsigned short tmp[4];
+ unsigned short *line1;
+ unsigned short *line2;
+
+ unsigned short* data = (unsigned short*) bits();
+
+ // can be done inplace
+ for (uint y = 0 ; y < (h/2) ; y++)
+ {
+ line1 = data + y * w * 4;
+ line2 = data + (h-y-1) * w * 4;
+
+ for (uint x=0 ; x < w ; x++)
+ {
+ memcpy(&tmp, line1, 8);
+ memcpy(line1, line2, 8);
+ memcpy(line2, &tmp, 8);
+
+ line1+=4;
+ line2+=4;
+ }
+ }
+ }
+ else
+ {
+ uchar tmp[4];
+ uchar *line1;
+ uchar *line2;
+
+ uchar* data = bits();
+
+ // can be done inplace
+ for (uint y = 0 ; y < (h/2) ; y++)
+ {
+ line1 = data + y * w * 4;
+ line2 = data + (h-y-1) * w * 4;
+
+ for (uint x=0 ; x < w ; x++)
+ {
+ memcpy(&tmp, line1, 4);
+ memcpy(line1, line2, 4);
+ memcpy(line2, &tmp, 4);
+
+ line1+=4;
+ line2+=4;
+ }
+ }
+ }
+
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+void DImg::convertToSixteenBit()
+{
+ convertDepth(64);
+}
+
+void DImg::convertToEightBit()
+{
+ convertDepth(32);
+}
+
+void DImg::convertToDepthOfImage(const DImg *otherImage)
+{
+ if (otherImage->sixteenBit())
+ convertToSixteenBit();
+ else
+ convertToEightBit();
+}
+
+void DImg::convertDepth(int depth)
+{
+ if (isNull())
+ return;
+
+ if (depth != 32 && depth != 64)
+ {
+ DDebug() << k_funcinfo << " : wrong color depth!" << endl;
+ return;
+ }
+
+ if (((depth == 32) && !sixteenBit()) ||
+ ((depth == 64) && sixteenBit()))
+ return;
+
+ if (depth == 32)
+ {
+ // downgrading from 16 bit to 8 bit
+
+ uchar* data = new uchar[width()*height()*4];
+ uchar* dptr = data;
+ ushort* sptr = (ushort*)bits();
+
+ for (uint i=0; i<width()*height()*4; i++)
+ {
+ *dptr++ = (*sptr++ * 255UL)/65535UL;
+ }
+
+ delete [] m_priv->data;
+ m_priv->data = data;
+ m_priv->sixteenBit = false;
+ }
+ else if (depth == 64)
+ {
+ // upgrading from 8 bit to 16 bit
+
+ uchar* data = new uchar[width()*height()*8];
+ ushort* dptr = (ushort*)data;
+ uchar* sptr = bits();
+
+ for (uint i=0; i<width()*height()*4; i++)
+ {
+ *dptr++ = (*sptr++ * 65535ULL)/255ULL;
+ }
+
+ delete [] m_priv->data;
+ m_priv->data = data;
+ m_priv->sixteenBit = true;
+ }
+}
+
+void DImg::fill(DColor color)
+{
+ if (sixteenBit())
+ {
+ unsigned short *imgData16 = (unsigned short *)m_priv->data;
+
+ for (uint i = 0 ; i < width()*height()*4 ; i+=4)
+ {
+ imgData16[ i ] = (unsigned short)color.blue();
+ imgData16[i+1] = (unsigned short)color.green();
+ imgData16[i+2] = (unsigned short)color.red();
+ imgData16[i+3] = (unsigned short)color.alpha();
+ }
+ }
+ else
+ {
+ uchar *imgData = m_priv->data;
+
+ for (uint i = 0 ; i < width()*height()*4 ; i+=4)
+ {
+ imgData[ i ] = (uchar)color.blue();
+ imgData[i+1] = (uchar)color.green();
+ imgData[i+2] = (uchar)color.red();
+ imgData[i+3] = (uchar)color.alpha();
+ }
+ }
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/dimg.h b/src/libs/dimg/dimg.h
new file mode 100644
index 00000000..48973e47
--- /dev/null
+++ b/src/libs/dimg/dimg.h
@@ -0,0 +1,353 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : digiKam 8/16 bits image management API
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * 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 DIMG_H
+#define DIMG_H
+
+// TQt includes.
+
+#include <tqcstring.h>
+#include <tqsize.h>
+#include <tqrect.h>
+#include <tqimage.h>
+#include <tqpixmap.h>
+#include <tqvariant.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+#include "drawdecoding.h"
+#include "dcolor.h"
+#include "dcolorcomposer.h"
+
+class TQString;
+
+namespace Digikam
+{
+
+class ExposureSettingsContainer;
+class DImgPrivate;
+class IccTransform;
+class DImgLoaderObserver;
+
+class DIGIKAM_EXPORT DImg
+{
+public:
+
+ enum FORMAT
+ {
+ NONE = 0,
+ JPEG,
+ PNG,
+ TIFF,
+ RAW,
+ PPM,
+ JP2K,
+ TQIMAGE
+ };
+
+ enum METADATA
+ {
+ COM, // JFIF comments section data.
+ EXIF, // EXIF meta-data.
+ IPTC, // IPTC meta-data.
+ ICC // ICC color profile.
+ };
+
+ enum ANGLE
+ {
+ ROT90,
+ ROT180,
+ ROT270
+ };
+
+ enum FLIP
+ {
+ HORIZONTAL,
+ VERTICAL
+ };
+
+ /** Identify file format */
+ static FORMAT fileFormat(const TQString& filePath);
+
+ /** Create null image */
+ DImg();
+
+ /** Load image using TQCString as file path */
+ DImg(const TQCString& filePath, DImgLoaderObserver *observer = 0,
+ DRawDecoding rawDecodingSettings=DRawDecoding());
+
+ /** Load image using TQString as file path */
+ DImg(const TQString& filePath, DImgLoaderObserver *observer = 0,
+ DRawDecoding rawDecodingSettings=DRawDecoding());
+
+ /** Copy image: Creates a shallow copy that refers to the same shared data.
+ The two images will be equal. Call detach() or copy() to create deep copies.
+ */
+ DImg(const DImg& image);
+
+ /** Copy image: Creates a copy of a TQImage object. If the TQImage is null, a
+ null DImg will be created.
+ */
+ DImg(const TQImage& image);
+
+ /** Create image from data.
+ If data is 0, a new buffer will be allocated, otherwise the given data will be used:
+ If copydata is true, the data will be copied to a newly allocated buffer.
+ If copyData is false, this DImg object will take ownership of the data pointer.
+
+ If there is an alpha channel, the data shall be in non-premultiplied form (unassociated alpha).
+ */
+ DImg(uint width, uint height, bool sixteenBit, bool alpha=false, uchar* data = 0, bool copyData = true);
+
+ ~DImg();
+
+ /** Equivalent to the copy constructor */
+ DImg& operator=(const DImg& image);
+
+ /** Detaches from shared data and makes sure that this image
+ is the only one referring to the data.
+ If multiple images share common data, this image makes a copy
+ of the data and detaches itself from the sharing mechanism.
+ Nothing is done if there is just a single reference.
+ */
+ void detach();
+
+ /** Returns whether two images are equal.
+ Two images are equal if and only if they refer to the same shared data.
+ (Thus, DImg() == DImg() is not true, both instances refer two their
+ own shared data. image == DImg(image) is true.)
+ If two or more images refer to the same data, they have the same
+ image data, bits() returns the same data, they have the same metadata,
+ and a change to one image also affects the others.
+ Call detach() to split one image from the group of equal images.
+ */
+ bool operator==(const DImg& image) const;
+
+
+ /** Replaces image data of this object. Metadata is unchanged. Parameters like constructor above. */
+ void putImageData(uint width, uint height, bool sixteenBit, bool alpha, uchar *data, bool copyData = true);
+
+ /** Overloaded function, provided for convenience, behaves essentially
+ like the function above if data is not 0.
+ Uses current width, height, sixteenBit, and alpha values.
+ If data is 0, the current data is deleted and the image is set to null
+ (But metadata unchanged).
+ */
+ void putImageData(uchar *data, bool copyData = true);
+
+ /** Reset metadata and image data to null image */
+ void reset(void);
+
+ /** Reset metadata, but do not change image data */
+ void resetMetaData(void);
+
+ /** Returns the data of this image.
+ Ownership of the buffer is passed to the caller, this image will be null afterwards.
+ */
+ uchar* stripImageData();
+
+
+
+ bool load(const TQString& filePath, DImgLoaderObserver *observer = 0,
+ DRawDecoding rawDecodingSettings=DRawDecoding());
+
+ bool save(const TQString& filePath, const TQString& format, DImgLoaderObserver *observer = 0);
+
+ bool isNull() const;
+ uint width() const;
+ uint height() const;
+ TQSize size() const;
+ uchar* bits() const;
+ uchar* scanLine(uint i) const;
+ bool hasAlpha() const;
+ bool sixteenBit() const;
+ uint numBytes() const;
+ uint numPixels() const;
+
+ /** Return the number of bytes depth of one pixel : 4 (non sixteenBit) or 8 (sixteen) */
+ int bytesDepth() const;
+
+ /** Return the number of bits depth of one color component for one pixel : 8 (non sixteenBit) or 16 (sixteen) */
+ int bitsDepth() const;
+
+ /** Access a single pixel of the image.
+ These functions add some safety checks and then use the methods from DColor.
+ In optimized code working directly on the data,
+ better use the inline methods from DColor.
+ */
+ DColor getPixelColor(uint x, uint y) const;
+ void setPixelColor(uint x, uint y, DColor color);
+
+ /**
+ Return true if the original image file format cannot be saved.
+ This is depending of DImgLoader::save() implementation. For example
+ RAW file formats are supported by DImg uing dcraw than cannot support
+ writing operations.
+ */
+ bool isReadOnly() const;
+
+ /** Metadata manipulation methods */
+ TQByteArray getComments() const;
+ TQByteArray getExif() const;
+ TQByteArray getIptc() const;
+ TQByteArray getICCProfil() const;
+ void setComments(const TQByteArray& commentsData);
+ void setExif(const TQByteArray& exifData);
+ void setIptc(const TQByteArray& iptcData);
+ void setICCProfil(const TQByteArray& profile);
+
+ TQByteArray metadata(METADATA key) const;
+
+ bool getICCProfilFromFile(const TQString& filePath);
+ bool setICCProfilToFile(const TQString& filePath);
+
+ void setAttribute(const TQString& key, const TQVariant& value);
+ TQVariant attribute(const TQString& key) const;
+
+ void setEmbeddedText(const TQString& key, const TQString& text);
+ TQString embeddedText(const TQString& key) const;
+
+
+ /** Return a deep copy of full image */
+ DImg copy();
+
+ /** Return a deep copy of the image, but do not include metadata. */
+ DImg copyImageData();
+
+ /** Return an image that containes a deep copy of
+ this image's metadata and the information associated
+ with the image data (width, height, hasAlpha, sixteenBit),
+ but no image data, i.e. isNull() is true.
+ */
+ DImg copyMetaData();
+
+ /** Return a region of image */
+ DImg copy(TQRect rect);
+ DImg copy(int x, int y, int w, int h);
+
+ /** Copy a region of pixels from a source image to this image.
+ Parameters:
+ sx|sy Coordinates in the source image of the rectangle to be copied
+ w h Width and height of the rectangle (Default, or when both are -1: whole source image)
+ dx|dy Coordinates in this image of the rectangle in which the region will be copied
+ (Default: 0|0)
+ The bit depth of source and destination must be identical.
+ */
+ void bitBltImage(const DImg* src, int dx, int dy);
+ void bitBltImage(const DImg* src, int sx, int sy, int dx, int dy);
+ void bitBltImage(const DImg* src, int sx, int sy, int w, int h, int dx, int dy);
+ void bitBltImage(const uchar* src, int sx, int sy, int w, int h, int dx, int dy,
+ uint swidth, uint sheight, int sdepth);
+
+ /** Blend src image on this image (this is dest) with the specified composer
+ and multiplication flags. See documentation of DColorComposer for more info.
+ For the other arguments, see documentation of bitBltImage above.
+ */
+ void bitBlendImage(DColorComposer *composer, const DImg* src,
+ int sx, int sy, int w, int h, int dx, int dy,
+ DColorComposer::MultiplicationFlags multiplicationFlags =
+ DColorComposer::NoMultiplication);
+
+ /** TQImage wrapper methods */
+ TQImage copyTQImage();
+ TQImage copyTQImage(TQRect rect);
+ TQImage copyTQImage(int x, int y, int w, int h);
+
+ /** Crop image to the specified region */
+ void crop(TQRect rect);
+ void crop(int x, int y, int w, int h);
+
+ /** Set width and height of this image, smoothScale it to the given size */
+ void resize(int w, int h);
+
+ /** Return a version of this image scaled to the specified size with the specified mode.
+ See TQSize documentation for information on available modes
+ */
+ DImg smoothScale(int width, int height, TQSize::ScaleMode scaleMode=TQSize::ScaleFree);
+
+ /** Take the region specified by the rectangle sx|sy, width and height sw * sh,
+ and scale it to an image with size dw * dh
+ */
+ DImg smoothScaleSection(int sx, int sy, int sw, int sh,
+ int dw, int dh);
+
+ void rotate(ANGLE angle);
+ void flip(FLIP direction);
+
+ TQPixmap convertToPixmap();
+ TQPixmap convertToPixmap(IccTransform* monitorICCtrans);
+
+ /** Return a mask image where pure white and pure black pixels are over-colored.
+ This way is used to identify over and under exposed pixels.
+ */
+ TQImage pureColorMask(ExposureSettingsContainer *expoSettings);
+
+ /** Convert depth of image. Depth is bytesDepth * bitsDepth.
+ If depth is 32, converts to 8 bits,
+ if depth is 64, converts to 16 bits.
+ */
+ void convertDepth(int depth);
+
+ /** Wrapper methods for convertDepth */
+ void convertToSixteenBit();
+ void convertToEightBit();
+ void convertToDepthOfImage(const DImg *otherImage);
+
+ /** Fill whole image with specified color.
+ The bit depth of the color must be identical to the depth of this image.
+ */
+ void fill(DColor color);
+
+private:
+
+ DImgPrivate *m_priv;
+
+private:
+
+ void copyMetaData(const DImgPrivate *src);
+ void copyImageData(const DImgPrivate *src);
+ void setImageData(bool null, uint width, uint height, bool sixteenBit, bool alpha);
+ void setImageDimension(uint width, uint height);
+ int allocateData();
+ DImg(const DImg &image, int w, int h);
+ static void bitBlt(const uchar *src, uchar *dest,
+ int sx, int sy, int w, int h, int dx, int dy,
+ uint swidth, uint sheight, uint dwidth, uint dheight,
+ bool sixteenBit, int sdepth, int ddepth);
+ static void bitBlend(DColorComposer *composer, const uchar *src, uchar *dest,
+ int sx, int sy, int w, int h, int dx, int dy,
+ uint swidth, uint sheight, uint dwidth, uint dheight,
+ bool sixteenBit, int sdepth, int ddepth,
+ DColorComposer::MultiplicationFlags multiplicationFlags);
+ static bool normalizeRegionArguments(int &sx, int &sy, int &w, int &h, int &dx, int &dy,
+ uint swidth, uint sheight, uint dwidth, uint dheight);
+
+ friend class DImgLoader;
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMG_H */
diff --git a/src/libs/dimg/dimgprivate.h b/src/libs/dimg/dimgprivate.h
new file mode 100644
index 00000000..021208d3
--- /dev/null
+++ b/src/libs/dimg/dimgprivate.h
@@ -0,0 +1,81 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-15
+ * Description : DImg private data members
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#ifndef DIMGPRIVATE_H
+#define DIMGPRIVATE_H
+
+// TQt includes.
+
+#include <tqshared.h>
+#include <tqstring.h>
+#include <tqcstring.h>
+#include <tqvariant.h>
+#include <tqmap.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DImgPrivate : public TQShared
+{
+public:
+
+ DImgPrivate()
+ {
+ null = true;
+ width = 0;
+ height = 0;
+ data = 0;
+ alpha = false;
+ sixteenBit = false;
+ isReadOnly = false;
+ }
+
+ ~DImgPrivate()
+ {
+ delete [] data;
+ }
+
+ bool null;
+ bool alpha;
+ bool sixteenBit;
+ bool isReadOnly;
+
+ unsigned int width;
+ unsigned int height;
+
+ unsigned char *data;
+
+ TQMap<int, TQByteArray> metaData;
+ TQStringVariantMap attributes;
+ TQMap<TQString, TQString> embeddedText;
+
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMGPRIVATE_H */
diff --git a/src/libs/dimg/dimgscale.cpp b/src/libs/dimg/dimgscale.cpp
new file mode 100644
index 00000000..850e36de
--- /dev/null
+++ b/src/libs/dimg/dimgscale.cpp
@@ -0,0 +1,2127 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-06-14
+ * Description : This is the normal smoothscale method,
+ * based on Imlib2's smoothscale. Added
+ * smoothScaleSection - Scaling only of a
+ * section of a image. Added 16bit image support
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2006-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2006-2008 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
+ *
+ * Ported to C++/TQImage by Daniel M. Duley
+ * Following modification are (C) Daniel M. Duley
+ * Changes include formatting, namespaces and other C++'ings, removal of old
+ * #ifdef'ed code, and removal of unneeded border calculation code.
+ *
+ * Imlib2 is (C) Carsten Haitzler and various contributors. The MMX code
+ * is by Willem Monsuwe <[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 ansi includes.
+
+extern "C"
+{
+#include <stdint.h>
+}
+
+// C++ includes.
+
+#include <cstring>
+#include <cstdlib>
+#include <cstdio>
+
+// Local includes.
+
+#include "dimgprivate.h"
+#include "dimg.h"
+
+typedef uint64_t ullong;
+typedef int64_t llong;
+
+namespace Digikam
+{
+
+namespace DImgScale
+{
+ typedef struct __dimg_scale_info
+ {
+ int *xpoints;
+ uint **ypoints;
+ ullong **ypoints16;
+ int *xapoints;
+ int *yapoints;
+ int xup_yup;
+ } DImgScaleInfo;
+
+ uint** dimgCalcYPoints(uint *src, int sw, int sh, int dh);
+ ullong** dimgCalcYPoints16(ullong *src, int sw, int sh, int dh);
+ int* dimgCalcXPoints(int sw, int dw);
+ int* dimgCalcApoints(int s, int d, int up);
+
+ DImgScaleInfo* dimgFreeScaleInfo(DImgScaleInfo *isi);
+ DImgScaleInfo *dimgCalcScaleInfo(const DImg &img,
+ int sw, int sh,
+ int dw, int dh,
+ bool sixteenBit,
+ bool aa);
+
+ void dimgSampleRGBA(DImgScaleInfo *isi, unsigned int *dest, int dxx,
+ int dyy, int dx, int dy, int dw, int dh, int dow);
+ void dimgScaleAARGBA(DImgScaleInfo *isi, unsigned int *dest, int dxx,
+ int dyy, int dx, int dy, int dw, int dh, int dow,
+ int sow);
+ void dimgScaleAARGB(DImgScaleInfo *isi, unsigned int *dest, int dxx,
+ int dyy, int dx, int dy, int dw, int dh, int dow, int
+ sow);
+
+ void dimgScaleAARGBA16(DImgScaleInfo *isi, ullong *dest,
+ int dxx, int dyy, int dw, int dh,
+ int dow, int sow);
+ void dimgScaleAARGB16(DImgScaleInfo *isi, ullong *dest,
+ int dxx, int dyy, int dw, int dh,
+ int dow, int sow);
+};
+
+using namespace DImgScale;
+
+DImg DImg::smoothScale(int dw, int dh, TQSize::ScaleMode scaleMode)
+{
+ if (dw < 0 || dh < 0 || isNull())
+ return DImg();
+
+ uint w = width();
+ uint h = height();
+
+ if (w <= 0 || h <= 0)
+ return DImg();
+
+ TQSize newSize(w, h);
+ newSize.scale( TQSize(dw, dh), scaleMode );
+ if (!newSize.isValid())
+ return DImg();
+
+ dw = newSize.width();
+ dh = newSize.height();
+
+ // do we actually need to scale?
+ if ((w == (uint)dw) && (h == (uint)dh))
+ {
+ return copy();
+ }
+
+ DImgScale::DImgScaleInfo *scaleinfo = dimgCalcScaleInfo(*this, w, h, dw, dh, sixteenBit(), true);
+ if (!scaleinfo)
+ return *this;
+
+ DImg buffer(*this, dw, dh);
+
+ if (sixteenBit())
+ {
+ if (hasAlpha())
+ {
+ dimgScaleAARGBA16(scaleinfo, (ullong*) buffer.bits(),
+ 0, 0, dw, dh, dw, w);
+ }
+ else
+ {
+ dimgScaleAARGB16(scaleinfo, (ullong*) buffer.bits(),
+ 0, 0, dw, dh, dw, w);
+ }
+ }
+ else
+ {
+ if (hasAlpha())
+ {
+ dimgScaleAARGBA(scaleinfo, (unsigned int *)buffer.bits(),
+ 0, 0, 0, 0, dw, dh, dw, w);
+ }
+ else
+ {
+ dimgScaleAARGB(scaleinfo, (unsigned int *)buffer.bits(),
+ 0, 0, 0, 0, dw, dh, dw, w);
+ }
+ }
+
+ dimgFreeScaleInfo(scaleinfo);
+
+ return buffer;
+}
+
+#define CLIP(x, y, w, h, xx, yy, ww, hh) \
+if (x < (xx)) {w += (x - (xx)); x = (xx);} \
+if (y < (yy)) {h += (y - (yy)); y = (yy);} \
+if ((x + w) > ((xx) + (ww))) {w = (ww) - (x - xx);} \
+if ((y + h) > ((yy) + (hh))) {h = (hh) - (y - yy);}
+
+DImg DImg::smoothScaleSection(int sx, int sy,
+ int sw, int sh,
+ int dw, int dh)
+{
+ uint w = width();
+ uint h = height();
+
+ // sanity checks
+ if ((dw <= 0) || (dh <= 0))
+ return DImg();
+
+ if ((sw <= 0) || (sh <= 0))
+ return DImg();
+
+ // clip the source rect to be within the actual image
+ int psx, psy, psw, psh;
+ psx = sx;
+ psy = sy;
+ psw = sw;
+ psh = sh;
+ CLIP(sx, sy, sw, sh, 0, 0, (int)w, (int)h);
+
+ // clip output coords to clipped input coords
+ if (psw != sw)
+ dw = (dw * sw) / psw;
+ if (psh != sh)
+ dh = (dh * sh) / psh;
+
+ // do a second check to see if we now have invalid coords
+ // do not do anything if we have a 0 widht or height image to render
+ if ((dw <= 0) || (dh <= 0))
+ return DImg();
+
+ // if the input rect size < 0 do not render either
+ if ((sw <= 0) || (sh <= 0))
+ return DImg();
+
+ // do we actually need to scale?
+ if ((sw == dw) && (sh == dh))
+ {
+ return copy(sx, sy, sw, sh);
+ }
+
+ // calculate scaleinfo
+ DImgScaleInfo *scaleinfo = dimgCalcScaleInfo(*this, sw, sh, dw, dh, sixteenBit(), true);
+ if (!scaleinfo)
+ return DImg();
+
+ DImg buffer(*this, dw, dh);
+
+ if (sixteenBit())
+ {
+ if (hasAlpha())
+ {
+ dimgScaleAARGBA16(scaleinfo, (ullong*) buffer.bits(),
+ ((sx * dw) / sw),
+ ((sy * dh) / sh),
+ dw, dh,
+ dw, w);
+ }
+ else
+ {
+ dimgScaleAARGB16(scaleinfo, (ullong*) buffer.bits(),
+ ((sx * dw) / sw),
+ ((sy * dh) / sh),
+ dw, dh,
+ dw, w);
+ }
+ }
+ else
+ {
+ if (hasAlpha())
+ {
+ dimgScaleAARGBA(scaleinfo,
+ (uint *)buffer.bits(),
+ ((sx * dw) / sw),
+ ((sy * dh) / sh),
+ 0, 0,
+ dw, dh,
+ dw, w);
+ }
+ else
+ {
+ dimgScaleAARGB(scaleinfo,
+ (uint *)buffer.bits(),
+ ((sx * dw) / sw),
+ ((sy * dh) / sh),
+ 0, 0,
+ dw, dh,
+ dw, w);
+ }
+ }
+
+ dimgFreeScaleInfo(scaleinfo);
+
+ return buffer;
+}
+
+
+//
+// Code ported from Imlib2...
+//
+
+// FIXME: replace with mRed, etc... These work on pointers to pixels, not
+// pixel values
+#define A_VAL(p) ((unsigned char *)(p))[3]
+#define R_VAL(p) ((unsigned char *)(p))[2]
+#define G_VAL(p) ((unsigned char *)(p))[1]
+#define B_VAL(p) ((unsigned char *)(p))[0]
+
+#define INV_XAP (256 - xapoints[x])
+#define XAP (xapoints[x])
+#define INV_YAP (256 - yapoints[dyy + y])
+#define YAP (yapoints[dyy + y])
+
+unsigned int** DImgScale::dimgCalcYPoints(unsigned int *src, int sw, int sh, int dh)
+{
+ unsigned int **p;
+ int i, j = 0;
+ int val, inc;
+
+ p = new unsigned int* [dh+1];
+
+ val = 0;
+ inc = (sh << 16) / dh;
+ for(i = 0; i < dh; i++)
+ {
+ p[j++] = src + ((val >> 16) * sw);
+ val += inc;
+ }
+
+ return(p);
+}
+
+ullong** DImgScale::dimgCalcYPoints16(ullong* src, int sw, int sh, int dh)
+{
+ ullong** p;
+ int i, j = 0;
+ int val, inc;
+
+ p = new ullong*[(dh+1)];
+
+ val = 0;
+ inc = (sh << 16) / dh;
+ for(i = 0; i < dh; i++)
+ {
+ p[j++] = src + ((val >> 16) * sw);
+ val += inc;
+ }
+
+ return p;
+}
+
+int* DImgScale::dimgCalcXPoints(int sw, int dw)
+{
+ int *p, i, j = 0;
+ int val, inc;
+
+ p = new int[dw+1];
+
+ val = 0;
+ inc = (sw << 16) / dw;
+ for(i = 0; i < dw; i++)
+ {
+ p[j++] = (val >> 16);
+ val += inc;
+ }
+
+ return(p);
+}
+
+int* DImgScale::dimgCalcApoints(int s, int d, int up)
+{
+ int *p, i, j = 0;
+
+ p = new int[d];
+
+ /* scaling up */
+ if(up)
+ {
+ int val, inc;
+
+ val = 0;
+ inc = (s << 16) / d;
+ for(i = 0; i < d; i++)
+ {
+ p[j++] = (val >> 8) - ((val >> 8) & 0xffffff00);
+ if((val >> 16) >= (s - 1))
+ p[j - 1] = 0;
+ val += inc;
+ }
+ }
+ /* scaling down */
+ else
+ {
+ int val, inc, ap, Cp;
+ val = 0;
+ inc = (s << 16) / d;
+ Cp = ((d << 14) / s) + 1;
+
+ for(i = 0; i < d; i++)
+ {
+ ap = ((0x100 - ((val >> 8) & 0xff)) * Cp) >> 8;
+ p[j] = ap | (Cp << 16);
+ j++;
+ val += inc;
+ }
+ }
+
+ return(p);
+}
+
+DImgScaleInfo* DImgScale::dimgCalcScaleInfo(const DImg &img,
+ int sw, int sh,
+ int dw, int dh,
+ bool /*sixteenBit*/,
+ bool aa)
+{
+ DImgScaleInfo *isi;
+ int scw, sch;
+
+ scw = dw * img.width() / sw;
+ sch = dh * img.height() / sh;
+
+ isi = new DImgScaleInfo;
+ if(!isi)
+ return(NULL);
+
+ memset(isi, 0, sizeof(DImgScaleInfo));
+
+ isi->xup_yup = (abs(dw) >= sw) + ((abs(dh) >= sh) << 1);
+
+ isi->xpoints = dimgCalcXPoints(img.width(), scw);
+ if(!isi->xpoints)
+ return(dimgFreeScaleInfo(isi));
+
+ if (img.sixteenBit())
+ {
+ isi->ypoints = 0;
+ isi->ypoints16 = dimgCalcYPoints16((ullong*)img.bits(), img.width(), img.height(), sch);
+ if (!isi->ypoints16) return(dimgFreeScaleInfo(isi));
+ }
+ else
+ {
+ isi->ypoints16 = 0;
+ isi->ypoints = dimgCalcYPoints((uint*)img.bits(), img.width(), img.height(), sch);
+ if (!isi->ypoints) return(dimgFreeScaleInfo(isi));
+ }
+
+ if (aa)
+ {
+ isi->xapoints = dimgCalcApoints(img.width(), scw, isi->xup_yup & 1);
+ if(!isi->xapoints) return(dimgFreeScaleInfo(isi));
+
+ isi->yapoints = dimgCalcApoints(img.height(), sch, isi->xup_yup & 2);
+ if(!isi->yapoints) return(dimgFreeScaleInfo(isi));
+ }
+/* It doesn't work...
+ else
+ {
+ isi->xapoints = new int[scw];
+ if(!isi->xapoints) return(dimgFreeScaleInfo(isi));
+ for(int i = 0; i < scw; i++) isi->xapoints[i] = 0;
+
+ isi->yapoints = new int[sch];
+ if(!isi->yapoints) return(dimgFreeScaleInfo(isi));
+ for(int i = 0; i < sch; i++) isi->yapoints[i] = 0;
+ }*/
+
+ return(isi);
+}
+
+DImgScaleInfo* DImgScale::dimgFreeScaleInfo(DImgScaleInfo *isi)
+{
+ if(isi)
+ {
+ delete [] isi->xpoints;
+ delete [] isi->ypoints;
+ delete [] isi->ypoints16;
+ delete [] isi->xapoints;
+ delete [] isi->yapoints;
+ delete isi;
+ }
+
+ return 0;
+}
+
+/** scale by pixel sampling only */
+void DImgScale::dimgSampleRGBA(DImgScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+
+ /* whats the last pixel ont he line so we stop there */
+ end = dxx + dw;
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ /* get the pointer to the start of the destination scanline */
+ dptr = dest + dx + ((y + dy) * dow);
+ /* calculate the source line we'll scan from */
+ sptr = ypoints[dyy + y];
+ /* go thru the scanline and copy across */
+ for(x = dxx; x < end; x++)
+ *dptr++ = sptr[xpoints[x]];
+ }
+}
+
+/* FIXME: NEED to optimise ScaleAARGBA - currently its "ok" but needs work*/
+
+/** scale by area sampling */
+void DImgScale::dimgScaleAARGBA(DImgScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3)
+ {
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0)
+ {
+ for(x = dxx; x < end; x++)
+ {
+ int r, g, b, a;
+ int rr, gg, bb, aa;
+ unsigned int *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ aa = A_VAL(pix) * XAP;
+ pix--;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ aa += A_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ a = ((aa * YAP) + (a * INV_YAP)) >> 16;
+
+ A_VAL(dptr) = a;
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+
+ dptr++;
+ }
+ else
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ a = A_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ a += A_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+
+ A_VAL(dptr) = a;
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+
+ dptr++;
+ }
+ }
+ }
+ else
+ {
+ for(x = dxx; x < end; x++)
+ {
+ int r, g, b, a;
+ unsigned int *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ a = A_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ a += A_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+
+ A_VAL(dptr) = a;
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+
+ dptr++;
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1)
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * yap) >> 10;
+ g = (G_VAL(pix) * yap) >> 10;
+ b = (B_VAL(pix) * yap) >> 10;
+ a = (A_VAL(pix) * yap) >> 10;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix += sow;
+ r += (R_VAL(pix) * Cy) >> 10;
+ g += (G_VAL(pix) * Cy) >> 10;
+ b += (B_VAL(pix) * Cy) >> 10;
+ a += (A_VAL(pix) * Cy) >> 10;
+ }
+ if(j > 0)
+ {
+ pix += sow;
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ a += (A_VAL(pix) * j) >> 10;
+ }
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = (R_VAL(pix) * yap) >> 10;
+ gg = (G_VAL(pix) * yap) >> 10;
+ bb = (B_VAL(pix) * yap) >> 10;
+ aa = (A_VAL(pix) * yap) >> 10;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix += sow;
+ rr += (R_VAL(pix) * Cy) >> 10;
+ gg += (G_VAL(pix) * Cy) >> 10;
+ bb += (B_VAL(pix) * Cy) >> 10;
+ aa += (A_VAL(pix) * Cy) >> 10;
+ }
+ if(j > 0)
+ {
+ pix += sow;
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ aa += (A_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ a = a * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ a = (a + ((aa * XAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+
+ A_VAL(dptr) = a;
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2)
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, a, rr, gg, bb, aa;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * xap) >> 10;
+ g = (G_VAL(pix) * xap) >> 10;
+ b = (B_VAL(pix) * xap) >> 10;
+ a = (A_VAL(pix) * xap) >> 10;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ pix++;
+ r += (R_VAL(pix) * Cx) >> 10;
+ g += (G_VAL(pix) * Cx) >> 10;
+ b += (B_VAL(pix) * Cx) >> 10;
+ a += (A_VAL(pix) * Cx) >> 10;
+ }
+ if(j > 0)
+ {
+ pix++;
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ a += (A_VAL(pix) * j) >> 10;
+ }
+ if(YAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = (R_VAL(pix) * xap) >> 10;
+ gg = (G_VAL(pix) * xap) >> 10;
+ bb = (B_VAL(pix) * xap) >> 10;
+ aa = (A_VAL(pix) * xap) >> 10;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ pix++;
+ rr += (R_VAL(pix) * Cx) >> 10;
+ gg += (G_VAL(pix) * Cx) >> 10;
+ bb += (B_VAL(pix) * Cx) >> 10;
+ aa += (A_VAL(pix) * Cx) >> 10;
+ }
+ if(j > 0)
+ {
+ pix++;
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ aa += (A_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ a = a * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ a = (a + ((aa * YAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+
+ A_VAL(dptr) = a;
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally & vertically */
+ else
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification:
+ |*| The operation 'b = (b * c) >> 16' translates to pmulhw,
+ |*| so the operation 'b = (b * c) >> d' would translate to
+ |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb
+ \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int a, r, g, b, ax, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ ax = (A_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ ax += (A_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ ax += (A_VAL(pix) * i) >> 9;
+ }
+
+ r = (rx * yap) >> 14;
+ g = (gx * yap) >> 14;
+ b = (bx * yap) >> 14;
+ a = (ax * yap) >> 14;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ ax = (A_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ ax += (A_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ ax += (A_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * Cy) >> 14;
+ g += (gx * Cy) >> 14;
+ b += (bx * Cy) >> 14;
+ a += (ax * Cy) >> 14;
+ }
+ if(j > 0)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ ax = (A_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ ax += (A_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ ax += (A_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * j) >> 14;
+ g += (gx * j) >> 14;
+ b += (bx * j) >> 14;
+ a += (ax * j) >> 14;
+ }
+
+ R_VAL(dptr) = r >> 5;
+ G_VAL(dptr) = g >> 5;
+ B_VAL(dptr) = b >> 5;
+ A_VAL(dptr) = a >> 5;
+ dptr++;
+ }
+ }
+ }
+}
+
+/** scale by area sampling - IGNORE the ALPHA byte */
+void DImgScale::dimgScaleAARGB(DImgScaleInfo *isi, unsigned int *dest,
+ int dxx, int dyy, int dx, int dy, int dw,
+ int dh, int dow, int sow)
+{
+ unsigned int *sptr, *dptr;
+ int x, y, end;
+ unsigned int **ypoints = isi->ypoints;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3)
+ {
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ /* calculate the source line we'll scan from */
+ dptr = dest + dx + ((y + dy) * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0)
+ {
+ for(x = dxx; x < end; x++)
+ {
+ int r = 0, g = 0, b = 0;
+ int rr = 0, gg = 0, bb = 0;
+ unsigned int *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ pix += sow;
+ rr = R_VAL(pix) * XAP;
+ gg = G_VAL(pix) * XAP;
+ bb = B_VAL(pix) * XAP;
+ pix --;
+ rr += R_VAL(pix) * INV_XAP;
+ gg += G_VAL(pix) * INV_XAP;
+ bb += B_VAL(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+ A_VAL(dptr) = 0xFF;
+
+ dptr++;
+ }
+ else
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_YAP;
+ g = G_VAL(pix) * INV_YAP;
+ b = B_VAL(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL(pix) * YAP;
+ g += G_VAL(pix) * YAP;
+ b += B_VAL(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+ A_VAL(dptr) = 0xFF;
+
+ dptr++;
+ }
+ }
+ }
+ else
+ {
+ for(x = dxx; x < end; x++)
+ {
+ int r = 0, g = 0, b = 0;
+ unsigned int *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL(pix) * INV_XAP;
+ g = G_VAL(pix) * INV_XAP;
+ b = B_VAL(pix) * INV_XAP;
+ pix++;
+ r += R_VAL(pix) * XAP;
+ g += G_VAL(pix) * XAP;
+ b += B_VAL(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+ A_VAL(dptr) = 0xFF;
+
+ dptr++;
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1)
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * yap) >> 10;
+ g = (G_VAL(pix) * yap) >> 10;
+ b = (B_VAL(pix) * yap) >> 10;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ r += (R_VAL(pix) * Cy) >> 10;
+ g += (G_VAL(pix) * Cy) >> 10;
+ b += (B_VAL(pix) * Cy) >> 10;
+ pix += sow;
+ }
+ if(j > 0)
+ {
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ }
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = (R_VAL(pix) * yap) >> 10;
+ gg = (G_VAL(pix) * yap) >> 10;
+ bb = (B_VAL(pix) * yap) >> 10;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ rr += (R_VAL(pix) * Cy) >> 10;
+ gg += (G_VAL(pix) * Cy) >> 10;
+ bb += (B_VAL(pix) * Cy) >> 10;
+ pix += sow;
+ }
+ if(j > 0)
+ {
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+ A_VAL(dptr) = 0xFF;
+
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2)
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ unsigned int *pix;
+ int r, g, b, rr, gg, bb;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL(pix) * xap) >> 10;
+ g = (G_VAL(pix) * xap) >> 10;
+ b = (B_VAL(pix) * xap) >> 10;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ r += (R_VAL(pix) * Cx) >> 10;
+ g += (G_VAL(pix) * Cx) >> 10;
+ b += (B_VAL(pix) * Cx) >> 10;
+ pix++;
+ }
+ if(j > 0)
+ {
+ r += (R_VAL(pix) * j) >> 10;
+ g += (G_VAL(pix) * j) >> 10;
+ b += (B_VAL(pix) * j) >> 10;
+ }
+ if(YAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = (R_VAL(pix) * xap) >> 10;
+ gg = (G_VAL(pix) * xap) >> 10;
+ bb = (B_VAL(pix) * xap) >> 10;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ rr += (R_VAL(pix) * Cx) >> 10;
+ gg += (G_VAL(pix) * Cx) >> 10;
+ bb += (B_VAL(pix) * Cx) >> 10;
+ pix++;
+ }
+ if(j > 0)
+ {
+ rr += (R_VAL(pix) * j) >> 10;
+ gg += (G_VAL(pix) * j) >> 10;
+ bb += (B_VAL(pix) * j) >> 10;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+
+ R_VAL(dptr) = r;
+ G_VAL(dptr) = g;
+ B_VAL(dptr) = b;
+ A_VAL(dptr) = 0xFF;
+
+ dptr++;
+ }
+ }
+ }
+ /* fully optimized (i think) - onyl change of algorithm can help */
+ /* if we're scaling down horizontally & vertically */
+ else
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, Cy, i, j;
+ unsigned int *pix;
+ int r, g, b, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + dx + ((y + dy) * dow);
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ }
+
+ r = (rx * yap) >> 14;
+ g = (gx * yap) >> 14;
+ b = (bx * yap) >> 14;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * Cy) >> 14;
+ g += (gx * Cy) >> 14;
+ b += (bx * Cy) >> 14;
+ }
+ if(j > 0)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL(pix) * xap) >> 9;
+ gx = (G_VAL(pix) * xap) >> 9;
+ bx = (B_VAL(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL(pix) * Cx) >> 9;
+ gx += (G_VAL(pix) * Cx) >> 9;
+ bx += (B_VAL(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL(pix) * i) >> 9;
+ gx += (G_VAL(pix) * i) >> 9;
+ bx += (B_VAL(pix) * i) >> 9;
+ }
+
+ r += (rx * j) >> 14;
+ g += (gx * j) >> 14;
+ b += (bx * j) >> 14;
+ }
+
+ R_VAL(dptr) = r >> 5;
+ G_VAL(dptr) = g >> 5;
+ B_VAL(dptr) = b >> 5;
+ A_VAL(dptr) = 0xFF;
+ dptr++;
+ }
+ }
+ }
+}
+
+#define A_VAL16(p) ((ushort *)(p))[3]
+#define R_VAL16(p) ((ushort *)(p))[2]
+#define G_VAL16(p) ((ushort *)(p))[1]
+#define B_VAL16(p) ((ushort *)(p))[0]
+
+/** scale by area sampling - IGNORE the ALPHA byte*/
+void DImgScale::dimgScaleAARGB16(DImgScaleInfo *isi, ullong *dest,
+ int dxx, int dyy, int dw, int dh,
+ int dow, int sow)
+{
+ ullong *sptr, *dptr;
+ int x, y, end;
+ ullong **ypoints = isi->ypoints16;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+
+ // scaling up both ways
+ if(isi->xup_yup == 3)
+ {
+ // go through every scanline in the output buffer
+ for(y = 0; y < dh; y++)
+ {
+ // calculate the source line we'll scan from
+ dptr = dest + (y * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0)
+ {
+ for(x = dxx; x < end; x++)
+ {
+ llong r = 0, g = 0, b = 0;
+ llong rr = 0, gg = 0, bb = 0;
+ ullong *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL16(pix) * INV_XAP;
+ g = G_VAL16(pix) * INV_XAP;
+ b = B_VAL16(pix) * INV_XAP;
+ pix++;
+ r += R_VAL16(pix) * XAP;
+ g += G_VAL16(pix) * XAP;
+ b += B_VAL16(pix) * XAP;
+ pix += sow;
+ rr = R_VAL16(pix) * XAP;
+ gg = G_VAL16(pix) * XAP;
+ bb = B_VAL16(pix) * XAP;
+ pix --;
+ rr += R_VAL16(pix) * INV_XAP;
+ gg += G_VAL16(pix) * INV_XAP;
+ bb += B_VAL16(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = 0xFFFF;
+
+ dptr++;
+ }
+ else
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL16(pix) * INV_YAP;
+ g = G_VAL16(pix) * INV_YAP;
+ b = B_VAL16(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL16(pix) * YAP;
+ g += G_VAL16(pix) * YAP;
+ b += B_VAL16(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = 0xFFFF;
+
+ dptr++;
+ }
+ }
+ }
+ else
+ {
+ for(x = dxx; x < end; x++)
+ {
+ llong r = 0, g = 0, b = 0;
+ ullong *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL16(pix) * INV_XAP;
+ g = G_VAL16(pix) * INV_XAP;
+ b = B_VAL16(pix) * INV_XAP;
+ pix++;
+ r += R_VAL16(pix) * XAP;
+ g += G_VAL16(pix) * XAP;
+ b += B_VAL16(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = 0xFFFF;
+
+ dptr++;
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ // if we're scaling down vertically
+ else if(isi->xup_yup == 1)
+ {
+ // 'Correct' version, with math units prepared for MMXification
+ int Cy, j;
+ ullong *pix;
+ llong r, g, b, rr, gg, bb;
+ int yap;
+
+ // go through every scanline in the output buffer
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + y * dow;
+ for(x = dxx; x < end; x++)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL16(pix) * yap) >> 10;
+ g = (G_VAL16(pix) * yap) >> 10;
+ b = (B_VAL16(pix) * yap) >> 10;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ r += (R_VAL16(pix) * Cy) >> 10;
+ g += (G_VAL16(pix) * Cy) >> 10;
+ b += (B_VAL16(pix) * Cy) >> 10;
+ pix += sow;
+ }
+ if(j > 0)
+ {
+ r += (R_VAL16(pix) * j) >> 10;
+ g += (G_VAL16(pix) * j) >> 10;
+ b += (B_VAL16(pix) * j) >> 10;
+ }
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = (R_VAL16(pix) * yap) >> 10;
+ gg = (G_VAL16(pix) * yap) >> 10;
+ bb = (B_VAL16(pix) * yap) >> 10;
+ pix += sow;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ rr += (R_VAL16(pix) * Cy) >> 10;
+ gg += (G_VAL16(pix) * Cy) >> 10;
+ bb += (B_VAL16(pix) * Cy) >> 10;
+ pix += sow;
+ }
+ if(j > 0)
+ {
+ rr += (R_VAL16(pix) * j) >> 10;
+ gg += (G_VAL16(pix) * j) >> 10;
+ bb += (B_VAL16(pix) * j) >> 10;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = 0xFFFF;
+ dptr++;
+ }
+ }
+ }
+ // if we're scaling down horizontally
+ else if(isi->xup_yup == 2)
+ {
+ // 'Correct' version, with math units prepared for MMXification
+ int Cx, j;
+ ullong *pix;
+ llong r, g, b, rr, gg, bb;
+ int xap;
+
+ // go through every scanline in the output buffer
+ for(y = 0; y < dh; y++)
+ {
+ dptr = dest + y * dow;
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL16(pix) * xap) >> 10;
+ g = (G_VAL16(pix) * xap) >> 10;
+ b = (B_VAL16(pix) * xap) >> 10;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ r += (R_VAL16(pix) * Cx) >> 10;
+ g += (G_VAL16(pix) * Cx) >> 10;
+ b += (B_VAL16(pix) * Cx) >> 10;
+ pix++;
+ }
+ if(j > 0)
+ {
+ r += (R_VAL16(pix) * j) >> 10;
+ g += (G_VAL16(pix) * j) >> 10;
+ b += (B_VAL16(pix) * j) >> 10;
+ }
+ if(YAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = (R_VAL16(pix) * xap) >> 10;
+ gg = (G_VAL16(pix) * xap) >> 10;
+ bb = (B_VAL16(pix) * xap) >> 10;
+ pix++;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ rr += (R_VAL16(pix) * Cx) >> 10;
+ gg += (G_VAL16(pix) * Cx) >> 10;
+ bb += (B_VAL16(pix) * Cx) >> 10;
+ pix++;
+ }
+ if(j > 0)
+ {
+ rr += (R_VAL16(pix) * j) >> 10;
+ gg += (G_VAL16(pix) * j) >> 10;
+ bb += (B_VAL16(pix) * j) >> 10;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ }
+ else{
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ }
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = 0xFFFF;
+ dptr++;
+ }
+ }
+ }
+ // fully optimized (i think) - onyl change of algorithm can help
+ // if we're scaling down horizontally & vertically
+ else
+ {
+ // 'Correct' version, with math units prepared for MMXification
+ int Cx, Cy, i, j;
+ ullong *pix;
+ llong r, g, b, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+ dptr = dest + y * dow;
+
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+
+ rx = (R_VAL16(pix) * xap) >> 9;
+ gx = (G_VAL16(pix) * xap) >> 9;
+ bx = (B_VAL16(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL16(pix) * Cx) >> 9;
+ gx += (G_VAL16(pix) * Cx) >> 9;
+ bx += (B_VAL16(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL16(pix) * i) >> 9;
+ gx += (G_VAL16(pix) * i) >> 9;
+ bx += (B_VAL16(pix) * i) >> 9;
+ }
+
+ r = (rx * yap) >> 14;
+ g = (gx * yap) >> 14;
+ b = (bx * yap) >> 14;
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL16(pix) * xap) >> 9;
+ gx = (G_VAL16(pix) * xap) >> 9;
+ bx = (B_VAL16(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL16(pix) * Cx) >> 9;
+ gx += (G_VAL16(pix) * Cx) >> 9;
+ bx += (B_VAL16(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL16(pix) * i) >> 9;
+ gx += (G_VAL16(pix) * i) >> 9;
+ bx += (B_VAL16(pix) * i) >> 9;
+ }
+
+ r += (rx * Cy) >> 14;
+ g += (gx * Cy) >> 14;
+ b += (bx * Cy) >> 14;
+ }
+ if(j > 0)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL16(pix) * xap) >> 9;
+ gx = (G_VAL16(pix) * xap) >> 9;
+ bx = (B_VAL16(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL16(pix) * Cx) >> 9;
+ gx += (G_VAL16(pix) * Cx) >> 9;
+ bx += (B_VAL16(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL16(pix) * i) >> 9;
+ gx += (G_VAL16(pix) * i) >> 9;
+ bx += (B_VAL16(pix) * i) >> 9;
+ }
+
+ r += (rx * j) >> 14;
+ g += (gx * j) >> 14;
+ b += (bx * j) >> 14;
+ }
+
+ R_VAL16(dptr) = r >> 5;
+ G_VAL16(dptr) = g >> 5;
+ B_VAL16(dptr) = b >> 5;
+ A_VAL16(dptr) = 0xFFFF;
+ dptr++;
+ }
+ }
+ }
+}
+
+/* scale by area sampling */
+void DImgScale::dimgScaleAARGBA16(DImgScaleInfo *isi, ullong *dest,
+ int dxx, int dyy,
+ int dw, int dh,
+ int dow, int sow)
+{
+ ullong *sptr, *dptr;
+ int x, y, end;
+ ullong **ypoints = isi->ypoints16;
+ int *xpoints = isi->xpoints;
+ int *xapoints = isi->xapoints;
+ int *yapoints = isi->yapoints;
+
+ end = dxx + dw;
+ /* scaling up both ways */
+ if(isi->xup_yup == 3)
+ {
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ /* calculate the source line we'll scan from */
+ dptr = dest + (y * dow);
+ sptr = ypoints[dyy + y];
+ if(YAP > 0)
+ {
+ for(x = dxx; x < end; x++)
+ {
+ llong r, g, b, a;
+ llong rr, gg, bb, aa;
+ ullong *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL16(pix) * INV_XAP;
+ g = G_VAL16(pix) * INV_XAP;
+ b = B_VAL16(pix) * INV_XAP;
+ a = A_VAL16(pix) * INV_XAP;
+ pix++;
+ r += R_VAL16(pix) * XAP;
+ g += G_VAL16(pix) * XAP;
+ b += B_VAL16(pix) * XAP;
+ a += A_VAL16(pix) * XAP;
+ pix += sow;
+ rr = R_VAL16(pix) * XAP;
+ gg = G_VAL16(pix) * XAP;
+ bb = B_VAL16(pix) * XAP;
+ aa = A_VAL16(pix) * XAP;
+ pix--;
+ rr += R_VAL16(pix) * INV_XAP;
+ gg += G_VAL16(pix) * INV_XAP;
+ bb += B_VAL16(pix) * INV_XAP;
+ aa += A_VAL16(pix) * INV_XAP;
+ r = ((rr * YAP) + (r * INV_YAP)) >> 16;
+ g = ((gg * YAP) + (g * INV_YAP)) >> 16;
+ b = ((bb * YAP) + (b * INV_YAP)) >> 16;
+ a = ((aa * YAP) + (a * INV_YAP)) >> 16;
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = a;
+
+ dptr++;
+ }
+ else
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL16(pix) * INV_YAP;
+ g = G_VAL16(pix) * INV_YAP;
+ b = B_VAL16(pix) * INV_YAP;
+ a = A_VAL16(pix) * INV_YAP;
+ pix += sow;
+ r += R_VAL16(pix) * YAP;
+ g += G_VAL16(pix) * YAP;
+ b += B_VAL16(pix) * YAP;
+ a += A_VAL16(pix) * YAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = a;
+
+ dptr++;
+ }
+ }
+ }
+ else
+ {
+ for(x = dxx; x < end; x++)
+ {
+ llong r, g, b, a;
+ ullong *pix;
+
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = R_VAL16(pix) * INV_XAP;
+ g = G_VAL16(pix) * INV_XAP;
+ b = B_VAL16(pix) * INV_XAP;
+ a = A_VAL16(pix) * INV_XAP;
+ pix++;
+ r += R_VAL16(pix) * XAP;
+ g += G_VAL16(pix) * XAP;
+ b += B_VAL16(pix) * XAP;
+ a += A_VAL16(pix) * XAP;
+ r >>= 8;
+ g >>= 8;
+ b >>= 8;
+ a >>= 8;
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = a;
+
+ dptr++;
+ }
+ else
+ *dptr++ = sptr[xpoints[x] ];
+ }
+ }
+ }
+ }
+ /* if we're scaling down vertically */
+ else if(isi->xup_yup == 1)
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cy, j;
+ ullong *pix;
+ llong r, g, b, a, rr, gg, bb, aa;
+ int yap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + (y * dow);
+ for(x = dxx; x < end; x++)
+ {
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL16(pix) * yap) >> 10;
+ g = (G_VAL16(pix) * yap) >> 10;
+ b = (B_VAL16(pix) * yap) >> 10;
+ a = (A_VAL16(pix) * yap) >> 10;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix += sow;
+ r += (R_VAL16(pix) * Cy) >> 10;
+ g += (G_VAL16(pix) * Cy) >> 10;
+ b += (B_VAL16(pix) * Cy) >> 10;
+ a += (A_VAL16(pix) * Cy) >> 10;
+ }
+ if(j > 0)
+ {
+ pix += sow;
+ r += (R_VAL16(pix) * j) >> 10;
+ g += (G_VAL16(pix) * j) >> 10;
+ b += (B_VAL16(pix) * j) >> 10;
+ a += (A_VAL16(pix) * j) >> 10;
+ }
+ if(XAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + 1;
+ rr = (R_VAL16(pix) * yap) >> 10;
+ gg = (G_VAL16(pix) * yap) >> 10;
+ bb = (B_VAL16(pix) * yap) >> 10;
+ aa = (A_VAL16(pix) * yap) >> 10;
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix += sow;
+ rr += (R_VAL16(pix) * Cy) >> 10;
+ gg += (G_VAL16(pix) * Cy) >> 10;
+ bb += (B_VAL16(pix) * Cy) >> 10;
+ aa += (A_VAL16(pix) * Cy) >> 10;
+ }
+ if(j > 0)
+ {
+ pix += sow;
+ rr += (R_VAL16(pix) * j) >> 10;
+ gg += (G_VAL16(pix) * j) >> 10;
+ bb += (B_VAL16(pix) * j) >> 10;
+ aa += (A_VAL16(pix) * j) >> 10;
+ }
+ r = r * INV_XAP;
+ g = g * INV_XAP;
+ b = b * INV_XAP;
+ a = a * INV_XAP;
+ r = (r + ((rr * XAP))) >> 12;
+ g = (g + ((gg * XAP))) >> 12;
+ b = (b + ((bb * XAP))) >> 12;
+ a = (a + ((aa * XAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = a;
+
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally */
+ else if(isi->xup_yup == 2)
+ {
+ /*\ 'Correct' version, with math units prepared for MMXification \*/
+ int Cx, j;
+ ullong *pix;
+ llong r, g, b, a, rr, gg, bb, aa;
+ int xap;
+
+ /* go through every scanline in the output buffer */
+ for(y = 0; y < dh; y++)
+ {
+ dptr = dest + y * dow;
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ pix = ypoints[dyy + y] + xpoints[x];
+ r = (R_VAL16(pix) * xap) >> 10;
+ g = (G_VAL16(pix) * xap) >> 10;
+ b = (B_VAL16(pix) * xap) >> 10;
+ a = (A_VAL16(pix) * xap) >> 10;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ pix++;
+ r += (R_VAL16(pix) * Cx) >> 10;
+ g += (G_VAL16(pix) * Cx) >> 10;
+ b += (B_VAL16(pix) * Cx) >> 10;
+ a += (A_VAL16(pix) * Cx) >> 10;
+ }
+ if(j > 0)
+ {
+ pix++;
+ r += (R_VAL16(pix) * j) >> 10;
+ g += (G_VAL16(pix) * j) >> 10;
+ b += (B_VAL16(pix) * j) >> 10;
+ a += (A_VAL16(pix) * j) >> 10;
+ }
+ if(YAP > 0)
+ {
+ pix = ypoints[dyy + y] + xpoints[x] + sow;
+ rr = (R_VAL16(pix) * xap) >> 10;
+ gg = (G_VAL16(pix) * xap) >> 10;
+ bb = (B_VAL16(pix) * xap) >> 10;
+ aa = (A_VAL16(pix) * xap) >> 10;
+ for(j = (1 << 14) - xap; j > Cx; j -= Cx)
+ {
+ pix++;
+ rr += (R_VAL16(pix) * Cx) >> 10;
+ gg += (G_VAL16(pix) * Cx) >> 10;
+ bb += (B_VAL16(pix) * Cx) >> 10;
+ aa += (A_VAL16(pix) * Cx) >> 10;
+ }
+ if(j > 0)
+ {
+ pix++;
+ rr += (R_VAL16(pix) * j) >> 10;
+ gg += (G_VAL16(pix) * j) >> 10;
+ bb += (B_VAL16(pix) * j) >> 10;
+ aa += (A_VAL16(pix) * j) >> 10;
+ }
+ r = r * INV_YAP;
+ g = g * INV_YAP;
+ b = b * INV_YAP;
+ a = a * INV_YAP;
+ r = (r + ((rr * YAP))) >> 12;
+ g = (g + ((gg * YAP))) >> 12;
+ b = (b + ((bb * YAP))) >> 12;
+ a = (a + ((aa * YAP))) >> 12;
+ }
+ else
+ {
+ r >>= 4;
+ g >>= 4;
+ b >>= 4;
+ a >>= 4;
+ }
+
+ R_VAL16(dptr) = r;
+ G_VAL16(dptr) = g;
+ B_VAL16(dptr) = b;
+ A_VAL16(dptr) = a;
+
+ dptr++;
+ }
+ }
+ }
+ /* if we're scaling down horizontally & vertically */
+ else{
+ /*\ 'Correct' version, with math units prepared for MMXification:
+ |*| The operation 'b = (b * c) >> 16' translates to pmulhw,
+ |*| so the operation 'b = (b * c) >> d' would translate to
+ |*| psllw (16 - d), %mmb; pmulh %mmc, %mmb
+ \*/
+ int Cx, Cy, i, j;
+ ullong *pix;
+ llong a, r, g, b, ax, rx, gx, bx;
+ int xap, yap;
+
+ for(y = 0; y < dh; y++)
+ {
+ Cy = YAP >> 16;
+ yap = YAP & 0xffff;
+
+ dptr = dest + y * dow;
+ for(x = dxx; x < end; x++)
+ {
+ Cx = XAP >> 16;
+ xap = XAP & 0xffff;
+
+ sptr = ypoints[dyy + y] + xpoints[x];
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL16(pix) * xap) >> 9;
+ gx = (G_VAL16(pix) * xap) >> 9;
+ bx = (B_VAL16(pix) * xap) >> 9;
+ ax = (A_VAL16(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL16(pix) * Cx) >> 9;
+ gx += (G_VAL16(pix) * Cx) >> 9;
+ bx += (B_VAL16(pix) * Cx) >> 9;
+ ax += (A_VAL16(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL16(pix) * i) >> 9;
+ gx += (G_VAL16(pix) * i) >> 9;
+ bx += (B_VAL16(pix) * i) >> 9;
+ ax += (A_VAL16(pix) * i) >> 9;
+ }
+
+ r = (rx * yap) >> 14;
+ g = (gx * yap) >> 14;
+ b = (bx * yap) >> 14;
+ a = (ax * yap) >> 14;
+
+
+ for(j = (1 << 14) - yap; j > Cy; j -= Cy)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL16(pix) * xap) >> 9;
+ gx = (G_VAL16(pix) * xap) >> 9;
+ bx = (B_VAL16(pix) * xap) >> 9;
+ ax = (A_VAL16(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL16(pix) * Cx) >> 9;
+ gx += (G_VAL16(pix) * Cx) >> 9;
+ bx += (B_VAL16(pix) * Cx) >> 9;
+ ax += (A_VAL16(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL16(pix) * i) >> 9;
+ gx += (G_VAL16(pix) * i) >> 9;
+ bx += (B_VAL16(pix) * i) >> 9;
+ ax += (A_VAL16(pix) * i) >> 9;
+ }
+
+ r += (rx * Cy) >> 14;
+ g += (gx * Cy) >> 14;
+ b += (bx * Cy) >> 14;
+ a += (ax * Cy) >> 14;
+ }
+ if(j > 0)
+ {
+ pix = sptr;
+ sptr += sow;
+ rx = (R_VAL16(pix) * xap) >> 9;
+ gx = (G_VAL16(pix) * xap) >> 9;
+ bx = (B_VAL16(pix) * xap) >> 9;
+ ax = (A_VAL16(pix) * xap) >> 9;
+ pix++;
+ for(i = (1 << 14) - xap; i > Cx; i -= Cx)
+ {
+ rx += (R_VAL16(pix) * Cx) >> 9;
+ gx += (G_VAL16(pix) * Cx) >> 9;
+ bx += (B_VAL16(pix) * Cx) >> 9;
+ ax += (A_VAL16(pix) * Cx) >> 9;
+ pix++;
+ }
+ if(i > 0)
+ {
+ rx += (R_VAL16(pix) * i) >> 9;
+ gx += (G_VAL16(pix) * i) >> 9;
+ bx += (B_VAL16(pix) * i) >> 9;
+ ax += (A_VAL16(pix) * i) >> 9;
+ }
+
+ r += (rx * j) >> 14;
+ g += (gx * j) >> 14;
+ b += (bx * j) >> 14;
+ a += (ax * j) >> 14;
+ }
+
+ R_VAL16(dptr) = r >> 5;
+ G_VAL16(dptr) = g >> 5;
+ B_VAL16(dptr) = b >> 5;
+ A_VAL16(dptr) = a >> 5;
+ dptr++;
+ }
+ }
+ }
+}
+
+/**
+//Documentation of the cryptic dimgScaleAARGBA
+dimgScaleAARGBA(
+DImgScaleInfo *isi, // scaleinfo
+unsigned int *dest, // destination img data
+int dxx, // destination x location corresponding to start x of src section
+int dyy, // destination y location corresponding to start y of src section
+int dx, // destination x start location
+int dy, // destination y start location
+int dw, // destination width
+int dh, // destination height
+int dow, // destination scanline width
+int sow); // src scanline width
+*/
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/drawdecoding.h b/src/libs/dimg/drawdecoding.h
new file mode 100644
index 00000000..e2d82fdc
--- /dev/null
+++ b/src/libs/dimg/drawdecoding.h
@@ -0,0 +1,128 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2008-08-06
+ * Description : Raw decoding settings for digiKam:
+ * standard libkdcraw parameters plus
+ * few customized for post processing.
+ *
+ * Copyright (C) 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 DRAW_DECODING_H
+#define DRAW_DECODING_H
+
+// TQt includes.
+
+#include <tqvaluelist.h>
+#include <tqpointarray.h>
+
+// LibKDcraw includes.
+
+#include <libkdcraw/rawdecodingsettings.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DRawDecoding : public KDcrawIface::RawDecodingSettings
+{
+
+public:
+
+ /** Standard constructor with default settings
+ */
+ DRawDecoding()
+ {
+ resetPostProcessingSettings();
+ };
+
+ /** Standard destructor
+ */
+ virtual ~DRawDecoding(){};
+
+ /** Method to use a settings to optimize time loading, for exemple to compute image histogram
+ */
+ void optimizeTimeLoading()
+ {
+ KDcrawIface::RawDecodingSettings::optimizeTimeLoading();
+ resetPostProcessingSettings();
+ };
+
+ /** Method to reset to default values all Raw processing settings.
+ */
+ void resetPostProcessingSettings()
+ {
+ lightness = 0.0;
+ contrast = 1.0;
+ gamma = 1.0;
+ saturation = 1.0;
+ exposureComp = 0.0;
+ curveAdjust = TQPointArray();
+ levelsAdjust = TQValueList<int>();
+ };
+
+ /** Method to check is a post-processing setting have been changed
+ */
+ bool postProcessingSettingsIsDirty() const
+ {
+ return (lightness != 0.0 ||
+ contrast != 1.0 ||
+ gamma != 1.0 ||
+ saturation != 1.0 ||
+ exposureComp != 0.0 ||
+ !curveAdjust.isEmpty() ||
+ !levelsAdjust.isEmpty());
+ }
+
+public:
+
+ /** Lightness correction value.
+ */
+ double lightness;
+
+ /** Contrast correction value.
+ */
+ double contrast;
+
+ /** Gamma correction value.
+ */
+ double gamma;
+
+ /** Color saturation correction value.
+ */
+ double saturation;
+
+ /** Exposure compensation value.
+ */
+ double exposureComp;
+
+ /** Luminosity curve adjustements.
+ */
+ TQPointArray curveAdjust;
+
+ /** Levels adjustements: 4 channels (L, R, G, B * 2 values).
+ */
+ TQValueList<int> levelsAdjust;
+};
+
+} // namespace Digikam
+
+#endif /* DRAW_DECODING_H */
diff --git a/src/libs/dimg/exposurecontainer.h b/src/libs/dimg/exposurecontainer.h
new file mode 100644
index 00000000..62469258
--- /dev/null
+++ b/src/libs/dimg/exposurecontainer.h
@@ -0,0 +1,65 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2007-01-12
+ * Description : exposure indicator settings container.
+ *
+ * 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 EXPOSURESETTINGSCONTAINER_H
+#define EXPOSURESETTINGSCONTAINER_H
+
+// TQt includes.
+
+#include <tqcolor.h>
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT ExposureSettingsContainer
+{
+
+public:
+
+ ExposureSettingsContainer()
+ {
+ underExposureIndicator = false;
+ overExposureIndicator = false;
+
+ underExposureColor = TQt::white;
+ overExposureColor = TQt::black;
+ };
+
+ ~ExposureSettingsContainer(){};
+
+public:
+
+ bool underExposureIndicator;
+ bool overExposureIndicator;
+
+ TQColor underExposureColor;
+ TQColor overExposureColor;
+};
+
+} // namespace Digikam
+
+#endif // EXPOSURESETTINGSCONTAINER_H
diff --git a/src/libs/dimg/filters/Makefile.am b/src/libs/dimg/filters/Makefile.am
new file mode 100644
index 00000000..25c4ee54
--- /dev/null
+++ b/src/libs/dimg/filters/Makefile.am
@@ -0,0 +1,21 @@
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdimgfilters.la
+
+libdimgfilters_la_SOURCES = bcgmodifier.cpp hslmodifier.cpp icctransform.cpp \
+ dimgimagefilters.cpp dimgthreadedfilter.cpp \
+ dimggaussianblur.cpp dimgsharpen.cpp colormodifier.cpp
+
+libdimgfilters_la_LDFLAGS = $(all_libraries) $(KDE_RPATH) $(LIB_TQT) -lDCOP $(LIB_TDECORE) $(LIB_TDEUI) -ltdefx $(LIB_TDEIO) -ltdetexteditor
+
+INCLUDES = -I$(top_srcdir)/src/libs/dimg \
+ -I$(top_srcdir)/src/libs/levels \
+ -I$(top_srcdir)/src/libs/histogram \
+ -I$(top_srcdir)/src/digikam \
+ $(LIBKDCRAW_CFLAGS) \
+ $(all_includes)
+
+
+digikaminclude_HEADERS = bcgmodifier.h hslmodifier.h dimgthreadedfilter.h dimgimagefilters.h \
+ icctransform.h colormodifier.h dimgsharpen.h dimggaussianblur.h
+digikamincludedir = $(includedir)/digikam
diff --git a/src/libs/dimg/filters/bcgmodifier.cpp b/src/libs/dimg/filters/bcgmodifier.cpp
new file mode 100644
index 00000000..b3899c20
--- /dev/null
+++ b/src/libs/dimg/filters/bcgmodifier.cpp
@@ -0,0 +1,208 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-03-06
+ * Description : a Brightness/Contrast/Gamma image filter.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-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.
+ *
+ * ============================================================ */
+
+#define CLAMP_0_255(x) TQMAX(TQMIN(x, 255), 0)
+#define CLAMP_0_65535(x) TQMAX(TQMIN(x, 65535), 0)
+
+// C++ includes.
+
+#include <cstdio>
+#include <cmath>
+
+// Local includes.
+
+#include "dimg.h"
+#include "bcgmodifier.h"
+
+namespace Digikam
+{
+
+class BCGModifierPriv
+{
+public:
+
+ BCGModifierPriv()
+ {
+ channel = BCGModifier::CHANNEL_ALL;
+ modified = false;
+ }
+
+ bool modified;
+
+ int channel;
+ int map16[65536];
+ int map[256];
+};
+
+BCGModifier::BCGModifier()
+{
+ d = new BCGModifierPriv;
+ reset();
+}
+
+BCGModifier::~BCGModifier()
+{
+ delete d;
+}
+
+bool BCGModifier::modified() const
+{
+ return d->modified;
+}
+
+void BCGModifier::reset()
+{
+ // initialize to linear mapping
+
+ for (int i=0; i<65536; i++)
+ d->map16[i] = i;
+
+ for (int i=0; i<256; i++)
+ d->map[i] = i;
+
+ d->modified = false;
+}
+
+void BCGModifier::applyBCG(DImg& image)
+{
+ if (!d->modified || image.isNull())
+ return;
+
+ applyBCG(image.bits(), image.width(), image.height(), image.sixteenBit());
+}
+
+void BCGModifier::applyBCG(uchar *bits, uint width, uint height, bool sixteenBits)
+{
+ if (!d->modified || !bits)
+ return;
+
+ uint size = width*height;
+
+ if (!sixteenBits) // 8 bits image.
+ {
+ uchar* data = bits;
+
+ for (uint i=0; i<size; i++)
+ {
+ switch (d->channel)
+ {
+ case CHANNEL_BLUE:
+ data[0] = CLAMP_0_255(d->map[data[0]]);
+ break;
+
+ case CHANNEL_GREEN:
+ data[1] = CLAMP_0_255(d->map[data[1]]);
+ break;
+
+ case CHANNEL_RED:
+ data[2] = CLAMP_0_255(d->map[data[2]]);
+ break;
+
+ default: // CHANNEL_ALL
+ data[0] = CLAMP_0_255(d->map[data[0]]);
+ data[1] = CLAMP_0_255(d->map[data[1]]);
+ data[2] = CLAMP_0_255(d->map[data[2]]);
+ break;
+ }
+
+ data += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ ushort* data = (ushort*)bits;
+
+ for (uint i=0; i<size; i++)
+ {
+ switch (d->channel)
+ {
+ case CHANNEL_BLUE:
+ data[0] = CLAMP_0_65535(d->map16[data[0]]);
+ break;
+
+ case CHANNEL_GREEN:
+ data[1] = CLAMP_0_65535(d->map16[data[1]]);
+ break;
+
+ case CHANNEL_RED:
+ data[2] = CLAMP_0_65535(d->map16[data[2]]);
+ break;
+
+ default: // CHANNEL_ALL
+ data[0] = CLAMP_0_65535(d->map16[data[0]]);
+ data[1] = CLAMP_0_65535(d->map16[data[1]]);
+ data[2] = CLAMP_0_65535(d->map16[data[2]]);
+ break;
+ }
+
+ data += 4;
+ }
+ }
+}
+
+void BCGModifier::setChannel(int channel)
+{
+ d->channel = channel;
+}
+
+void BCGModifier::setGamma(double val)
+{
+ val = (val < 0.01) ? 0.01 : val;
+
+ for (int i=0; i<65536; i++)
+ d->map16[i] = lround(pow(((double)d->map16[i] / 65535.0), (1.0 / val)) * 65535.0);
+
+ for (int i=0; i<256; i++)
+ d->map[i] = lround(pow(((double)d->map[i] / 255.0), (1.0 / val)) * 255.0);
+
+ d->modified = true;
+}
+
+void BCGModifier::setBrightness(double val)
+{
+ int val1 = lround(val * 65535);
+
+ for (int i = 0; i < 65536; i++)
+ d->map16[i] = d->map16[i] + val1;
+
+ val1 = lround(val * 255);
+
+ for (int i = 0; i < 256; i++)
+ d->map[i] = d->map[i] + val1;
+
+ d->modified = true;
+}
+
+void BCGModifier::setContrast(double val)
+{
+ for (int i = 0; i < 65536; i++)
+ d->map16[i] = lround((d->map16[i] - 32767) * val) + 32767;
+
+ for (int i = 0; i < 256; i++)
+ d->map[i] = lround((d->map[i] - 127) * val) + 127;
+
+ d->modified = true;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/bcgmodifier.h b/src/libs/dimg/filters/bcgmodifier.h
new file mode 100644
index 00000000..b0c915d6
--- /dev/null
+++ b/src/libs/dimg/filters/bcgmodifier.h
@@ -0,0 +1,73 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-03-06
+ * Description : a Brightness/Contrast/Gamma image filter.
+ *
+ * Copyright (C) 2005 by Renchi Raju <[email protected]>
+ * Copyright (C) 2005-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 BCGMODIFIER_H
+#define BCGMODIFIER_H
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DImg;
+class BCGModifierPriv;
+
+class DIGIKAM_EXPORT BCGModifier
+{
+
+public:
+
+ enum CHANNEL
+ {
+ CHANNEL_ALL=0,
+ CHANNEL_RED,
+ CHANNEL_GREEN,
+ CHANNEL_BLUE
+ };
+
+public:
+
+ BCGModifier();
+ ~BCGModifier();
+
+ void reset();
+ bool modified() const;
+
+ void setChannel(int channel);
+ void setGamma(double val);
+ void setBrightness(double val);
+ void setContrast(double val);
+ void applyBCG(DImg& image);
+ void applyBCG(uchar *bits, uint width, uint height, bool sixteenBits);
+
+private:
+
+ BCGModifierPriv* d;
+};
+
+} // NameSpace Digikam
+
+#endif /* BCGMODIFIER_H */
diff --git a/src/libs/dimg/filters/colormodifier.cpp b/src/libs/dimg/filters/colormodifier.cpp
new file mode 100644
index 00000000..74ddf241
--- /dev/null
+++ b/src/libs/dimg/filters/colormodifier.cpp
@@ -0,0 +1,287 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-18
+ * Description : color modifier methods 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.
+ *
+ * ============================================================ */
+
+#define CLAMP_0_255(x) TQMAX(TQMIN(x, 255), 0)
+#define CLAMP_0_65535(x) TQMAX(TQMIN(x, 65535), 0)
+
+// C++ includes.
+
+#include <cstdio>
+#include <cmath>
+
+// Local includes.
+
+#include "dimg.h"
+#include "colormodifier.h"
+
+namespace Digikam
+{
+
+class ColorModifierPriv
+{
+public:
+
+ ColorModifierPriv()
+ {
+ modified = false;
+ }
+
+ bool modified;
+
+ int redMap[256];
+ int greenMap[256];
+ int blueMap[256];
+ int alphaMap[256];
+
+ int redMap16[65536];
+ int greenMap16[65536];
+ int blueMap16[65536];
+ int alphaMap16[65536];
+};
+
+ColorModifier::ColorModifier()
+{
+ d = new ColorModifierPriv;
+ reset();
+}
+
+ColorModifier::~ColorModifier()
+{
+ delete d;
+}
+
+bool ColorModifier::modified() const
+{
+ return d->modified;
+}
+
+void ColorModifier::reset()
+{
+ // initialize to linear mapping
+
+ for (int i=0; i<65536; i++)
+ {
+ d->redMap16[i] = i;
+ d->greenMap16[i] = i;
+ d->blueMap16[i] = i;
+ d->alphaMap16[i] = i;
+ }
+
+ for (int i=0; i<256; i++)
+ {
+ d->redMap[i] = i;
+ d->greenMap[i] = i;
+ d->blueMap[i] = i;
+ d->alphaMap[i] = i;
+ }
+
+ d->modified = false;
+}
+
+void ColorModifier::setTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit)
+{
+ if (!sixteenBit)
+ {
+ for (int i = 0; i < 256; i++)
+ {
+ if (redMap)
+ d->redMap[i] = redMap[i];
+ if (greenMap)
+ d->greenMap[i] = greenMap[i];
+ if (blueMap)
+ d->blueMap[i] = blueMap[i];
+ if (alphaMap)
+ d->alphaMap[i] = alphaMap[i];
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 65536; i++)
+ {
+ if (redMap)
+ d->redMap16[i] = redMap[i];
+ if (greenMap)
+ d->greenMap16[i] = greenMap[i];
+ if (blueMap)
+ d->blueMap16[i] = blueMap[i];
+ if (alphaMap)
+ d->alphaMap16[i] = alphaMap[i];
+ }
+ }
+
+ d->modified = true;
+}
+
+void ColorModifier::getTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit)
+{
+ if (!sixteenBit)
+ {
+ if (redMap)
+ memcpy(redMap, d->redMap, (256 * sizeof(int)));
+ if (greenMap)
+ memcpy(greenMap, d->greenMap, (256 * sizeof(int)));
+ if (blueMap)
+ memcpy(blueMap, d->blueMap, (256 * sizeof(int)));
+ if (alphaMap)
+ memcpy(alphaMap, d->alphaMap, (256 * sizeof(int)));
+ }
+ else
+ {
+ if (redMap)
+ memcpy(redMap, d->redMap16, (65536 * sizeof(int)));
+ if (greenMap)
+ memcpy(greenMap, d->greenMap16, (65536 * sizeof(int)));
+ if (blueMap)
+ memcpy(blueMap, d->blueMap16, (65536 * sizeof(int)));
+ if (alphaMap)
+ memcpy(alphaMap, d->alphaMap16, (65536 * sizeof(int)));
+ }
+}
+
+void ColorModifier::applyColorModifier(DImg& image, double r, double g, double b, double a)
+{
+ if (image.isNull())
+ return;
+
+ adjustRGB(r, g, b, a, image.sixteenBit());
+
+ if (!image.sixteenBit()) // 8 bits image.
+ {
+ uchar* data = (uchar*) image.bits();
+
+ for (uint i=0; i<image.width()*image.height(); i++)
+ {
+ data[0] = d->blueMap[data[0]];
+ data[1] = d->greenMap[data[1]];
+ data[2] = d->redMap[data[2]];
+ data[3] = d->alphaMap[data[3]];
+
+ data += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ ushort* data = (ushort*) image.bits();
+
+ for (uint i=0; i<image.width()*image.height(); i++)
+ {
+ data[0] = d->blueMap16[data[0]];
+ data[1] = d->greenMap16[data[1]];
+ data[2] = d->redMap16[data[2]];
+ data[3] = d->alphaMap16[data[3]];
+
+ data += 4;
+ }
+ }
+}
+
+void ColorModifier::setGamma(double val)
+{
+ val = (val < 0.01) ? 0.01 : val;
+ int val2;
+
+ for (int i=0; i<65536; i++)
+ {
+ val2 = (int)(pow(((double)d->redMap16[i] / 65535), (1 / val)) * 65535);
+ d->redMap16[i] = CLAMP_0_65535(val2);
+
+ val2 = (int)(pow(((double)d->greenMap16[i] / 65535), (1 / val)) * 65535);
+ d->greenMap16[i] = CLAMP_0_65535(val2);
+
+ val2 = (int)(pow(((double)d->blueMap16[i] / 65535), (1 / val)) * 65535);
+ d->blueMap16[i] = CLAMP_0_65535(val2);
+
+ val2 = (int)(pow(((double)d->alphaMap16[i] / 65535), (1 / val)) * 65535);
+ d->alphaMap16[i] = CLAMP_0_65535(val2);
+ }
+
+ for (int i=0; i<256; i++)
+ {
+ val2 = (int)(pow(((double)d->redMap[i] / 255), (1 / val)) * 255);
+ d->redMap[i] = CLAMP_0_255(val2);
+
+ val2 = (int)(pow(((double)d->greenMap[i] / 255), (1 / val)) * 255);
+ d->greenMap[i] = CLAMP_0_255(val2);
+
+ val2 = (int)(pow(((double)d->blueMap[i] / 255), (1 / val)) * 255);
+ d->blueMap[i] = CLAMP_0_255(val2);
+
+ val2 = (int)(pow(((double)d->alphaMap[i] / 255), (1 / val)) * 255);
+ d->alphaMap[i] = CLAMP_0_255(val2);
+ }
+
+ d->modified = true;
+}
+
+void ColorModifier::adjustRGB(double r, double g, double b, double a, bool sixteenBit)
+{
+ int r_table[65536];
+ int g_table[65536];
+ int b_table[65536];
+ int a_table[65536];
+ int dummy_table[65536];
+
+ if (r == 1.0 && g == 1.0 && b == 1.0 && a == 1.0)
+ return ;
+
+ if (r == g && r == b && r == a)
+ {
+ setGamma(r);
+ }
+ else
+ {
+ getTables(r_table, g_table, b_table, a_table, sixteenBit);
+
+ if(r != 1.0)
+ {
+ setGamma(r);
+ getTables(r_table, dummy_table, dummy_table, dummy_table, sixteenBit);
+ reset();
+ }
+
+ if(g != 1.0)
+ {
+ setGamma(g);
+ getTables(dummy_table, g_table, dummy_table, dummy_table, sixteenBit);
+ reset();
+ }
+
+ if(b != 1.0)
+ {
+ setGamma(b);
+ getTables(dummy_table, dummy_table, b_table, dummy_table, sixteenBit);
+ reset();
+ }
+
+ if(a != 1.0)
+ {
+ setGamma(a);
+ getTables(dummy_table, dummy_table, dummy_table, a_table, sixteenBit);
+ reset();
+ }
+
+ setTables(r_table, g_table, b_table, a_table, sixteenBit);
+ }
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/colormodifier.h b/src/libs/dimg/filters/colormodifier.h
new file mode 100644
index 00000000..9473b273
--- /dev/null
+++ b/src/libs/dimg/filters/colormodifier.h
@@ -0,0 +1,63 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2006-01-18
+ * Description : color modifier methods 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 COLORMODIFIER_H
+#define COLORMODIFIER_H
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DImg;
+class ColorModifierPriv;
+
+class DIGIKAM_EXPORT ColorModifier
+{
+public:
+
+ ColorModifier();
+ ~ColorModifier();
+
+ void reset();
+ bool modified() const;
+ void applyColorModifier(DImg& image, double r, double g, double b, double a);
+
+private:
+
+ void setTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit);
+ void getTables(int *redMap, int *greenMap, int *blueMap, int *alphaMap, bool sixteenBit);
+ void setGamma(double val);
+ void adjustRGB(double r, double g, double b, double a, bool sixteenBit);
+
+private:
+
+ ColorModifierPriv* d;
+
+};
+
+} // NameSpace Digikam
+
+#endif /* COLORMODIFIER_H */
diff --git a/src/libs/dimg/filters/dimggaussianblur.cpp b/src/libs/dimg/filters/dimggaussianblur.cpp
new file mode 100644
index 00000000..63e19909
--- /dev/null
+++ b/src/libs/dimg/filters/dimggaussianblur.cpp
@@ -0,0 +1,327 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-17-07
+ * Description : A Gaussian Blur threaded image filter.
+ *
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Original Gaussian Blur algorithm copyrighted 2004 by
+ * Pieter Z. Voloshyn <pieter_voloshyn at ame dot com dot br>.
+ *
+ * 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>
+#include <cstdlib>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimgimagefilters.h"
+#include "dimggaussianblur.h"
+
+namespace Digikam
+{
+
+DImgGaussianBlur::DImgGaussianBlur(DImg *orgImage, TQObject *parent, int radius)
+ : DImgThreadedFilter(orgImage, parent, "GaussianBlur")
+{
+ m_radius = radius;
+ initFilter();
+}
+
+DImgGaussianBlur::DImgGaussianBlur(DImgThreadedFilter *parentFilter,
+ const DImg &orgImage, const DImg &destImage,
+ int progressBegin, int progressEnd, int radius)
+ : DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd,
+ parentFilter->filterName() + ": GaussianBlur")
+{
+ m_radius = radius;
+ filterImage();
+}
+
+
+void DImgGaussianBlur::filterImage(void)
+{
+ gaussianBlurImage(m_orgImage.bits(), m_orgImage.width(), m_orgImage.height(),
+ m_orgImage.sixteenBit(), m_radius);
+}
+
+/** Function to apply the Gaussian Blur on an image*/
+
+void DImgGaussianBlur::gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius)
+{
+ if (!data || !width || !height)
+ {
+ DWarning() << ("DImgGaussianBlur::gaussianBlurImage: no image data available!")
+ << endl;
+ return;
+ }
+
+ if (radius > 100) radius = 100;
+ if (radius <= 0)
+ {
+ m_destImage = m_orgImage;
+ return;
+ }
+
+ // Gaussian kernel computation using the Radius parameter.
+
+ int nKSize, nCenter;
+ double x, sd, factor, lnsd, lnfactor;
+ int i, j, n, h, w;
+
+ nKSize = 2 * radius + 1;
+ nCenter = nKSize / 2;
+ int *Kernel = new int[nKSize];
+
+ lnfactor = (4.2485 - 2.7081) / 10 * nKSize + 2.7081;
+ lnsd = (0.5878 + 0.5447) / 10 * nKSize - 0.5447;
+ factor = exp (lnfactor);
+ sd = exp (lnsd);
+
+ for (i = 0; !m_cancel && (i < nKSize); i++)
+ {
+ x = sqrt ((i - nCenter) * (i - nCenter));
+ Kernel[i] = (int)(factor * exp (-0.5 * pow ((x / sd), 2)) / (sd * sqrt (2.0 * M_PI)));
+ }
+
+ // Now, we need to convolve the image descriptor.
+ // I've worked hard here, but I think this is a very smart
+ // way to convolve an array, its very hard to explain how I reach
+ // this, but the trick here its to store the sum used by the
+ // previous pixel, so we sum with the other pixels that wasn't get.
+
+ int nSumA, nSumR, nSumG, nSumB, nCount, progress;
+ int nKernelWidth = radius * 2 + 1;
+
+ // We need to alloc a 2d array to help us to store the values
+
+ int** arrMult = Alloc2DArray (nKernelWidth, sixteenBit ? 65536 : 256);
+
+ for (i = 0; !m_cancel && (i < nKernelWidth); i++)
+ for (j = 0; !m_cancel && (j < (sixteenBit ? 65536 : 256)); j++)
+ arrMult[i][j] = j * Kernel[i];
+
+ // We need to copy our bits to blur bits
+
+ uchar* pOutBits = m_destImage.bits();
+ uchar* pBlur = new uchar[m_destImage.numBytes()];
+
+ memcpy (pBlur, data, m_destImage.numBytes());
+
+ // We need to initialize all the loop and iterator variables
+
+ nSumA = nSumR = nSumG = nSumB = nCount = i = j = 0;
+ unsigned short* data16 = (unsigned short*)data;
+ unsigned short* pBlur16 = (unsigned short*)pBlur;
+ unsigned short* pOutBits16 = (unsigned short*)pOutBits;
+
+ // Now, we enter in the main loop
+
+ for (h = 0; !m_cancel && (h < height); h++)
+ {
+ for (w = 0; !m_cancel && (w < width); w++, i+=4)
+ {
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar *org, *dst;
+
+ // first of all, we need to blur the horizontal lines
+
+ for (n = -radius; !m_cancel && (n <= radius); n++)
+ {
+ // if is inside...
+ if (IsInside (width, height, w + n, h))
+ {
+ // we points to the pixel
+ j = i + 4*n;
+
+ // finally, we sum the pixels using a method similar to assigntables
+
+ org = &data[j];
+ nSumA += arrMult[n + radius][org[3]];
+ nSumR += arrMult[n + radius][org[2]];
+ nSumG += arrMult[n + radius][org[1]];
+ nSumB += arrMult[n + radius][org[0]];
+
+ // we need to add to the counter, the kernel value
+ nCount += Kernel[n + radius];
+ }
+ }
+
+ if (nCount == 0) nCount = 1;
+
+ // now, we return to blur bits the horizontal blur values
+ dst = &pBlur[i];
+ dst[3] = (uchar)CLAMP (nSumA / nCount, 0, 255);
+ dst[2] = (uchar)CLAMP (nSumR / nCount, 0, 255);
+ dst[1] = (uchar)CLAMP (nSumG / nCount, 0, 255);
+ dst[0] = (uchar)CLAMP (nSumB / nCount, 0, 255);
+
+ // ok, now we reinitialize the variables
+ nSumA = nSumR = nSumG = nSumB = nCount = 0;
+ }
+ else // 16 bits image.
+ {
+ unsigned short *org, *dst;
+
+ // first of all, we need to blur the horizontal lines
+
+ for (n = -radius; !m_cancel && (n <= radius); n++)
+ {
+ // if is inside...
+ if (IsInside (width, height, w + n, h))
+ {
+ // we points to the pixel
+ j = i + 4*n;
+
+ // finally, we sum the pixels using a method similar to assigntables
+
+ org = &data16[j];
+ nSumA += arrMult[n + radius][org[3]];
+ nSumR += arrMult[n + radius][org[2]];
+ nSumG += arrMult[n + radius][org[1]];
+ nSumB += arrMult[n + radius][org[0]];
+
+ // we need to add to the counter, the kernel value
+ nCount += Kernel[n + radius];
+ }
+ }
+
+ if (nCount == 0) nCount = 1;
+
+ // now, we return to blur bits the horizontal blur values
+ dst = &pBlur16[i];
+ dst[3] = (unsigned short)CLAMP (nSumA / nCount, 0, 65535);
+ dst[2] = (unsigned short)CLAMP (nSumR / nCount, 0, 65535);
+ dst[1] = (unsigned short)CLAMP (nSumG / nCount, 0, 65535);
+ dst[0] = (unsigned short)CLAMP (nSumB / nCount, 0, 65535);
+
+ // ok, now we reinitialize the variables
+ nSumA = nSumR = nSumG = nSumB = nCount = 0;
+ }
+ }
+
+ progress = (int) (((double)h * 50.0) / height);
+ if ( progress%5 == 0 )
+ postProgress( progress );
+ }
+
+ // getting the blur bits, we initialize position variables
+ i = j = 0;
+
+ // We enter in the second main loop
+ for (w = 0; !m_cancel && (w < width); w++, i = w*4)
+ {
+ for (h = 0; !m_cancel && (h < height); h++, i += width*4)
+ {
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar *org, *dst;
+
+ // first of all, we need to blur the vertical lines
+ for (n = -radius; !m_cancel && (n <= radius); n++)
+ {
+ // if is inside...
+ if (IsInside(width, height, w, h + n))
+ {
+ // we points to the pixel
+ j = i + n * 4 * width;
+
+ // finally, we sum the pixels using a method similar to assigntables
+ org = &pBlur[j];
+ nSumA += arrMult[n + radius][org[3]];
+ nSumR += arrMult[n + radius][org[2]];
+ nSumG += arrMult[n + radius][org[1]];
+ nSumB += arrMult[n + radius][org[0]];
+
+ // we need to add to the counter, the kernel value
+ nCount += Kernel[n + radius];
+ }
+ }
+
+ if (nCount == 0) nCount = 1;
+
+ // To preserve Alpha channel.
+ memcpy (&pOutBits[i], &data[i], 4);
+
+ // now, we return to bits the vertical blur values
+ dst = &pOutBits[i];
+ dst[3] = (uchar)CLAMP (nSumA / nCount, 0, 255);
+ dst[2] = (uchar)CLAMP (nSumR / nCount, 0, 255);
+ dst[1] = (uchar)CLAMP (nSumG / nCount, 0, 255);
+ dst[0] = (uchar)CLAMP (nSumB / nCount, 0, 255);
+
+ // ok, now we reinitialize the variables
+ nSumA = nSumR = nSumG = nSumB = nCount = 0;
+ }
+ else // 16 bits image.
+ {
+ unsigned short *org, *dst;
+
+ // first of all, we need to blur the vertical lines
+ for (n = -radius; !m_cancel && (n <= radius); n++)
+ {
+ // if is inside...
+ if (IsInside(width, height, w, h + n))
+ {
+ // we points to the pixel
+ j = i + n * 4 * width;
+
+ // finally, we sum the pixels using a method similar to assigntables
+ org = &pBlur16[j];
+ nSumA += arrMult[n + radius][org[3]];
+ nSumR += arrMult[n + radius][org[2]];
+ nSumG += arrMult[n + radius][org[1]];
+ nSumB += arrMult[n + radius][org[0]];
+
+ // we need to add to the counter, the kernel value
+ nCount += Kernel[n + radius];
+ }
+ }
+
+ if (nCount == 0) nCount = 1;
+
+ // To preserve Alpha channel.
+ memcpy (&pOutBits16[i], &data16[i], 8);
+
+ // now, we return to bits the vertical blur values
+ dst = &pOutBits16[i];
+ dst[3] = (unsigned short)CLAMP (nSumA / nCount, 0, 65535);
+ dst[2] = (unsigned short)CLAMP (nSumR / nCount, 0, 65535);
+ dst[1] = (unsigned short)CLAMP (nSumG / nCount, 0, 65535);
+ dst[0] = (unsigned short)CLAMP (nSumB / nCount, 0, 65535);
+
+ // ok, now we reinitialize the variables
+ nSumA = nSumR = nSumG = nSumB = nCount = 0;
+ }
+ }
+
+ progress = (int) (50.0 + ((double)w * 50.0) / width);
+ if ( progress%5 == 0 )
+ postProgress( progress );
+ }
+
+ // now, we must free memory
+ Free2DArray (arrMult, nKernelWidth);
+ delete [] pBlur;
+ delete [] Kernel;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/dimggaussianblur.h b/src/libs/dimg/filters/dimggaussianblur.h
new file mode 100644
index 00000000..e88944bc
--- /dev/null
+++ b/src/libs/dimg/filters/dimggaussianblur.h
@@ -0,0 +1,97 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-17-07
+ * Description : A Gaussian Blur threaded image filter.
+ *
+ * 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 DIMGGAUSSIAN_BLUR_H
+#define DIMGGAUSSIAN_BLUR_H
+
+// Digikam includes.
+
+#include "digikam_export.h"
+
+// Local includes.
+
+#include "dimgthreadedfilter.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DImgGaussianBlur : public DImgThreadedFilter
+{
+
+public:
+
+ DImgGaussianBlur(DImg *orgImage, TQObject *parent=0, int radius=3);
+
+ // Constructor for slave mode: execute immediately in current thread with specified master filter
+ DImgGaussianBlur(DImgThreadedFilter *parentFilter, const DImg &orgImage, const DImg &destImage,
+ int progressBegin=0, int progressEnd=100, int radius=3);
+
+ ~DImgGaussianBlur(){};
+
+private: // Gaussian blur filter data.
+
+ int m_radius;
+
+private: // Gaussian blur filter methods.
+
+ virtual void filterImage(void);
+
+ void gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius);
+
+ // function to allocate a 2d array
+ int** Alloc2DArray (int Columns, int Rows)
+ {
+ // First, we declare our future 2d array to be returned
+ int** lpcArray = 0L;
+
+ // Now, we alloc the main pointer with Columns
+ lpcArray = new int*[Columns];
+
+ for (int i = 0; i < Columns; i++)
+ lpcArray[i] = new int[Rows];
+
+ return (lpcArray);
+ };
+
+ // Function to deallocates the 2d array previously created
+ void Free2DArray (int** lpcArray, int Columns)
+ {
+ // loop to dealocate the columns
+ for (int i = 0; i < Columns; i++)
+ delete [] lpcArray[i];
+
+ // now, we delete the main pointer
+ delete [] lpcArray;
+ };
+
+ inline bool IsInside (int Width, int Height, int X, int Y)
+ {
+ bool bIsWOk = ((X < 0) ? false : (X >= Width ) ? false : true);
+ bool bIsHOk = ((Y < 0) ? false : (Y >= Height) ? false : true);
+ return (bIsWOk && bIsHOk);
+ };
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMGGAUSSIAN_BLUR_H */
diff --git a/src/libs/dimg/filters/dimgimagefilters.cpp b/src/libs/dimg/filters/dimgimagefilters.cpp
new file mode 100644
index 00000000..b964ed4d
--- /dev/null
+++ b/src/libs/dimg/filters/dimgimagefilters.cpp
@@ -0,0 +1,977 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-24-01
+ * Description : misc image filters
+ *
+ * Copyright (C) 2004-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Original Equalise and StretchContrast Algorithms copyright 2002
+ * by Daniel M. Duley <[email protected]> from KImageEffect API.
+ *
+ * Original Normalize Image algorithm copyrighted 1997 by
+ * Adam D. Moss <[email protected]> from Gimp 2.0 implementation.
+ *
+ * Original channel mixer algorithm copyrighted 2002 by
+ * Martin Guldahl <mguldahl at xmission dot com> from Gimp 2.2
+ *
+ * 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 <cstring>
+#include <cstdlib>
+
+// Local includes.
+
+#include "imagehistogram.h"
+#include "imagelevels.h"
+#include "dcolor.h"
+#include "ddebug.h"
+#include "dimggaussianblur.h"
+#include "dimgsharpen.h"
+#include "dimgimagefilters.h"
+
+namespace Digikam
+{
+
+/** Performs an histogram equalisation of the image.
+ this method adjusts the brightness of colors across the
+ active image so that the histogram for the value channel
+ is as nearly as possible flat, that is, so that each possible
+ brightness value appears at about the same number of pixels
+ as each other value. Sometimes Equalize works wonderfully at
+ enhancing the contrasts in an image. Other times it gives
+ garbage. It is a very powerful operation, which can either work
+ miracles on an image or destroy it.*/
+void DImgImageFilters::equalizeImage(uchar *data, int w, int h, bool sixteenBit)
+{
+ if (!data || !w || !h)
+ {
+ DWarning() << ("DImgImageFilters::equalizeImage: no image data available!") << endl;
+ return;
+ }
+
+ struct double_packet high, low, intensity;
+ struct double_packet *map;
+ struct int_packet *equalize_map;
+ long i;
+
+ // Create an histogram of the current image.
+ ImageHistogram *histogram = new ImageHistogram(data, w, h, sixteenBit);
+
+ // Memory allocation.
+ map = new double_packet[histogram->getHistogramSegment()];
+ equalize_map = new int_packet[histogram->getHistogramSegment()];
+
+ if( !histogram || !map || !equalize_map )
+ {
+ if(histogram)
+ delete histogram;
+
+ if(map)
+ delete [] map;
+
+ if(equalize_map)
+ delete [] equalize_map;
+
+ DWarning() << ("DImgImageFilters::equalizeImage: Unable to allocate memory!") << endl;
+ return;
+ }
+
+ // Integrate the histogram to get the equalization map.
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+ memset(&high, 0, sizeof(struct double_packet));
+ memset(&low, 0, sizeof(struct double_packet));
+
+ for(i = 0 ; i < histogram->getHistogramSegment() ; i++)
+ {
+ intensity.red += histogram->getValue(ImageHistogram::RedChannel, i);
+ intensity.green += histogram->getValue(ImageHistogram::GreenChannel, i);
+ intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, i);
+ intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, i);
+ map[i] = intensity;
+ }
+
+ // Stretch the histogram.
+
+ low = map[0];
+ high = map[histogram->getHistogramSegment()-1];
+ memset(equalize_map, 0, histogram->getHistogramSegment()*sizeof(int_packet));
+
+ for(i = 0 ; i < histogram->getHistogramSegment() ; i++)
+ {
+ if(high.red != low.red)
+ equalize_map[i].red = (uint)(((256*histogram->getHistogramSegment() -1) *
+ (map[i].red-low.red))/(high.red-low.red));
+
+ if(high.green != low.green)
+ equalize_map[i].green = (uint)(((256*histogram->getHistogramSegment() -1) *
+ (map[i].green-low.green))/(high.green-low.green));
+
+ if(high.blue != low.blue)
+ equalize_map[i].blue = (uint)(((256*histogram->getHistogramSegment() -1) *
+ (map[i].blue-low.blue))/(high.blue-low.blue));
+
+ if(high.alpha != low.alpha)
+ equalize_map[i].alpha = (uint)(((256*histogram->getHistogramSegment() -1) *
+ (map[i].alpha-low.alpha))/(high.alpha-low.alpha));
+ }
+
+ delete histogram;
+ delete [] map;
+
+ // Apply results to image.
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar red, green, blue, alpha;
+ uchar *ptr = data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if(low.red != high.red)
+ red = (equalize_map[red].red)/257;
+
+ if(low.green != high.green)
+ green = (equalize_map[green].green)/257;
+
+ if(low.blue != high.blue)
+ blue = (equalize_map[blue].blue)/257;
+
+ if(low.alpha != high.alpha)
+ alpha = (equalize_map[alpha].alpha)/257;
+
+ ptr[0] = blue;
+ ptr[1] = green;
+ ptr[2] = red;
+ ptr[3] = alpha;
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short red, green, blue, alpha;
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if(low.red != high.red)
+ red = (equalize_map[red].red)/257;
+
+ if(low.green != high.green)
+ green = (equalize_map[green].green)/257;
+
+ if(low.blue != high.blue)
+ blue = (equalize_map[blue].blue)/257;
+
+ if(low.alpha != high.alpha)
+ alpha = (equalize_map[alpha].alpha)/257;
+
+ ptr[0] = blue;
+ ptr[1] = green;
+ ptr[2] = red;
+ ptr[3] = alpha;
+ ptr += 4;
+ }
+ }
+
+ delete [] equalize_map;
+}
+
+/** Performs histogram normalization of the image. The algorithm normalizes
+ the pixel values from an image for to span the full range
+ of color values. This is a contrast enhancement technique.*/
+void DImgImageFilters::stretchContrastImage(uchar *data, int w, int h, bool sixteenBit)
+{
+ if (!data || !w || !h)
+ {
+ DWarning() << ("DImgImageFilters::stretchContrastImage: no image data available!") << endl;
+ return;
+ }
+
+ struct double_packet high, low, intensity;
+ struct int_packet *normalize_map;
+ long long number_pixels;
+ long i;
+ unsigned long threshold_intensity;
+
+ // Create an histogram of the current image.
+ ImageHistogram *histogram = new ImageHistogram(data, w, h, sixteenBit);
+
+ // Memory allocation.
+ normalize_map = new int_packet[histogram->getHistogramSegment()];
+
+ if( !histogram || !normalize_map )
+ {
+ if(histogram)
+ delete histogram;
+
+ if(normalize_map)
+ delete [] normalize_map;
+
+ DWarning() << ("DImgImageFilters::stretchContrastImage: Unable to allocate memory!") << endl;
+ return;
+ }
+
+ // Find the histogram boundaries by locating the 0.1 percent levels.
+
+ number_pixels = (long long)(w*h);
+ threshold_intensity = number_pixels / 1000;
+
+ memset(&high, 0, sizeof(struct double_packet));
+ memset(&low, 0, sizeof(struct double_packet));
+
+ // Red.
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.red = histogram->getHistogramSegment()-1 ; high.red != 0 ; high.red--)
+ {
+ intensity.red += histogram->getValue(ImageHistogram::RedChannel, (int)high.red);
+
+ if( intensity.red > threshold_intensity )
+ break;
+ }
+
+ if( low.red == high.red )
+ {
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(low.red = 0 ; low.red < histogram->getHistogramSegment()-1 ; low.red++)
+ {
+ intensity.red += histogram->getValue(ImageHistogram::RedChannel, (int)low.red);
+
+ if( intensity.red > threshold_intensity )
+ break;
+ }
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.red = histogram->getHistogramSegment()-1 ; high.red != 0 ; high.red--)
+ {
+ intensity.red += histogram->getValue(ImageHistogram::RedChannel, (int)high.red);
+
+ if( intensity.red > threshold_intensity )
+ break;
+ }
+ }
+
+ // Green.
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.green = histogram->getHistogramSegment()-1 ; high.green != 0 ; high.green--)
+ {
+ intensity.green += histogram->getValue(ImageHistogram::GreenChannel, (int)high.green);
+
+ if( intensity.green > threshold_intensity )
+ break;
+ }
+
+ if( low.green == high.green )
+ {
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(low.green = 0 ; low.green < histogram->getHistogramSegment()-1 ; low.green++)
+ {
+ intensity.green += histogram->getValue(ImageHistogram::GreenChannel, (int)low.green);
+
+ if( intensity.green > threshold_intensity )
+ break;
+ }
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.green = histogram->getHistogramSegment()-1 ; high.green != 0 ; high.green--)
+ {
+ intensity.green += histogram->getValue(ImageHistogram::GreenChannel, (int)high.green);
+
+ if( intensity.green > threshold_intensity )
+ break;
+ }
+ }
+
+ // Blue.
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.blue = histogram->getHistogramSegment()-1 ; high.blue != 0 ; high.blue--)
+ {
+ intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, (int)high.blue);
+
+ if( intensity.blue > threshold_intensity )
+ break;
+ }
+
+ if( low.blue == high.blue )
+ {
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(low.blue = 0 ; low.blue < histogram->getHistogramSegment()-1 ; low.blue++)
+ {
+ intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, (int)low.blue);
+
+ if( intensity.blue > threshold_intensity )
+ break;
+ }
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.blue = histogram->getHistogramSegment()-1 ; high.blue != 0 ; high.blue--)
+ {
+ intensity.blue += histogram->getValue(ImageHistogram::BlueChannel, (int)high.blue);
+
+ if( intensity.blue > threshold_intensity )
+ break;
+ }
+ }
+
+ // Alpha.
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.alpha = histogram->getHistogramSegment()-1 ; high.alpha != 0 ; high.alpha--)
+ {
+ intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, (int)high.alpha);
+
+ if( intensity.alpha > threshold_intensity )
+ break;
+ }
+
+ if( low.alpha == high.alpha )
+ {
+ threshold_intensity = 0;
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(low.alpha = 0 ; low.alpha < histogram->getHistogramSegment()-1 ; low.alpha++)
+ {
+ intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, (int)low.alpha);
+
+ if( intensity.alpha > threshold_intensity )
+ break;
+ }
+
+ memset(&intensity, 0, sizeof(struct double_packet));
+
+ for(high.alpha = histogram->getHistogramSegment()-1 ; high.alpha != 0 ; high.alpha--)
+ {
+ intensity.alpha += histogram->getValue(ImageHistogram::AlphaChannel, (int)high.alpha);
+
+ if( intensity.alpha > threshold_intensity )
+ break;
+ }
+ }
+
+ delete histogram;
+
+ // Stretch the histogram to create the normalized image mapping.
+
+ memset(normalize_map, 0, histogram->getHistogramSegment()*sizeof(struct int_packet));
+
+ for(i = 0 ; i <= (long)histogram->getHistogramSegment()-1 ; i++)
+ {
+ if(i < (long) low.red)
+ normalize_map[i].red = 0;
+ else if (i > (long) high.red)
+ normalize_map[i].red = (256*histogram->getHistogramSegment() -1);
+ else if (low.red != high.red)
+ normalize_map[i].red = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.red))/(high.red-low.red));
+
+ if(i < (long) low.green)
+ normalize_map[i].green = 0;
+ else if (i > (long) high.green)
+ normalize_map[i].green = (256*histogram->getHistogramSegment() -1);
+ else if (low.green != high.green)
+ normalize_map[i].green = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.green))/(high.green-low.green));
+
+ if(i < (long) low.blue)
+ normalize_map[i].blue = 0;
+ else if (i > (long) high.blue)
+ normalize_map[i].blue = (256*histogram->getHistogramSegment() -1);
+ else if (low.blue != high.blue)
+ normalize_map[i].blue = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.blue))/(high.blue-low.blue));
+
+ if(i < (long) low.alpha)
+ normalize_map[i].alpha = 0;
+ else if (i > (long) high.alpha)
+ normalize_map[i].alpha = (256*histogram->getHistogramSegment() -1);
+ else if (low.alpha != high.alpha)
+ normalize_map[i].alpha = (int)(((256*histogram->getHistogramSegment() -1)*(i-low.alpha))/(high.alpha-low.alpha));
+ }
+
+ // Apply result to image.
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar red, green, blue, alpha;
+ uchar *ptr = data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if(low.red != high.red)
+ red = (normalize_map[red].red)/257;
+
+ if(low.green != high.green)
+ green = (normalize_map[green].green)/257;
+
+ if(low.blue != high.blue)
+ blue = (normalize_map[blue].blue)/257;
+
+ if(low.alpha != high.alpha)
+ alpha = (normalize_map[alpha].alpha)/257;
+
+ ptr[0] = blue;
+ ptr[1] = green;
+ ptr[2] = red;
+ ptr[3] = alpha;
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short red, green, blue, alpha;
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if(low.red != high.red)
+ red = (normalize_map[red].red)/257;
+
+ if(low.green != high.green)
+ green = (normalize_map[green].green)/257;
+
+ if(low.blue != high.blue)
+ blue = (normalize_map[blue].blue)/257;
+
+ if(low.alpha != high.alpha)
+ alpha = (normalize_map[alpha].alpha)/257;
+
+ ptr[0] = blue;
+ ptr[1] = green;
+ ptr[2] = red;
+ ptr[3] = alpha;
+ ptr += 4;
+ }
+ }
+
+ delete [] normalize_map;
+}
+
+/** This method scales brightness values across the active
+ image so that the darkest point becomes black, and the
+ brightest point becomes as bright as possible without
+ altering its hue. This is often a magic fix for
+ images that are dim or washed out.*/
+void DImgImageFilters::normalizeImage(uchar *data, int w, int h, bool sixteenBit)
+{
+ NormalizeParam param;
+ int x, i;
+ unsigned short range;
+
+ int segments = sixteenBit ? 65536 : 256;
+
+ // Memory allocation.
+
+ param.lut = new unsigned short[segments];
+
+ // Find min. and max. values.
+
+ param.min = segments-1;
+ param.max = 0;
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar red, green, blue;
+ uchar *ptr = data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+
+ if (red < param.min) param.min = red;
+ if (red > param.max) param.max = red;
+
+ if (green < param.min) param.min = green;
+ if (green > param.max) param.max = green;
+
+ if (blue < param.min) param.min = blue;
+ if (blue > param.max) param.max = blue;
+
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short red, green, blue;
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+
+ if (red < param.min) param.min = red;
+ if (red > param.max) param.max = red;
+
+ if (green < param.min) param.min = green;
+ if (green > param.max) param.max = green;
+
+ if (blue < param.min) param.min = blue;
+ if (blue > param.max) param.max = blue;
+
+ ptr += 4;
+ }
+ }
+
+ // Calculate LUT.
+
+ range = (unsigned short)(param.max - param.min);
+
+ if (range != 0)
+ {
+ for (x = (int)param.min ; x <= (int)param.max ; x++)
+ param.lut[x] = (unsigned short)((segments-1) * (x - param.min) / range);
+ }
+ else
+ param.lut[(int)param.min] = (unsigned short)param.min;
+
+ // Apply LUT to image.
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar red, green, blue;
+ uchar *ptr = data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+
+ ptr[0] = param.lut[blue];
+ ptr[1] = param.lut[green];
+ ptr[2] = param.lut[red];
+
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short red, green, blue;
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+
+ ptr[0] = param.lut[blue];
+ ptr[1] = param.lut[green];
+ ptr[2] = param.lut[red];
+
+ ptr += 4;
+ }
+ }
+
+ delete [] param.lut;
+}
+
+/** Performs histogram auto correction of levels.
+ This method maximizes the tonal range in the Red,
+ Green, and Blue channels. It search the image shadow and highlight
+ limit values and adjust the Red, Green, and Blue channels
+ to a full histogram range.*/
+void DImgImageFilters::autoLevelsCorrectionImage(uchar *data, int w, int h, bool sixteenBit)
+{
+ if (!data || !w || !h)
+ {
+ DWarning() << ("DImgImageFilters::autoLevelsCorrectionImage: no image data available!")
+ << endl;
+ return;
+ }
+ uchar* desData;
+
+ // Create the new empty destination image data space.
+ if (sixteenBit)
+ desData = new uchar[w*h*8];
+ else
+ desData = new uchar[w*h*4];
+
+ // Create an histogram of the current image.
+ ImageHistogram *histogram = new ImageHistogram(data, w, h, sixteenBit);
+
+ // Create an empty instance of levels to use.
+ ImageLevels *levels = new ImageLevels(sixteenBit);
+
+ // Initialize an auto levels correction of the histogram.
+ levels->levelsAuto(histogram);
+
+ // Calculate the LUT to apply on the image.
+ levels->levelsLutSetup(ImageHistogram::AlphaChannel);
+
+ // Apply the lut to the image.
+ levels->levelsLutProcess(data, desData, w, h);
+
+ if (sixteenBit)
+ memcpy (data, desData, w*h*8);
+ else
+ memcpy (data, desData, w*h*4);
+
+ delete [] desData;
+ delete histogram;
+ delete levels;
+}
+
+/** Performs image colors inversion. This tool is used for negate image
+ resulting of a positive film scanned.*/
+void DImgImageFilters::invertImage(uchar *data, int w, int h, bool sixteenBit)
+{
+ if (!data || !w || !h)
+ {
+ DWarning() << ("DImgImageFilters::invertImage: no image data available!")
+ << endl;
+ return;
+ }
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar *ptr = data;
+
+ for (int i = 0 ; i < w*h ; i++)
+ {
+ ptr[0] = 255 - ptr[0];
+ ptr[1] = 255 - ptr[1];
+ ptr[2] = 255 - ptr[2];
+ ptr[3] = 255 - ptr[3];
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (int i = 0 ; i < w*h ; i++)
+ {
+ ptr[0] = 65535 - ptr[0];
+ ptr[1] = 65535 - ptr[1];
+ ptr[2] = 65535 - ptr[2];
+ ptr[3] = 65535 - ptr[3];
+ ptr += 4;
+ }
+ }
+}
+
+/** Mix RGB channel color from image*/
+void DImgImageFilters::channelMixerImage(uchar *data, int Width, int Height, bool sixteenBit,
+ bool bPreserveLum, bool bMonochrome,
+ float rrGain, float rgGain, float rbGain,
+ float grGain, float ggGain, float gbGain,
+ float brGain, float bgGain, float bbGain)
+{
+ if (!data || !Width || !Height)
+ {
+ DWarning() << ("DImgImageFilters::channelMixerImage: no image data available!")
+ << endl;
+ return;
+ }
+
+ int i;
+
+ double rnorm = CalculateNorm (rrGain, rgGain, rbGain, bPreserveLum);
+ double gnorm = CalculateNorm (grGain, ggGain, gbGain, bPreserveLum);
+ double bnorm = CalculateNorm (brGain, bgGain, bbGain, bPreserveLum);
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar nGray, red, green, blue;
+ uchar *ptr = data;
+
+ for (i = 0 ; i < Width*Height ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+
+ if (bMonochrome)
+ {
+ nGray = MixPixel (rrGain, rgGain, rbGain,
+ (unsigned short)red, (unsigned short)green, (unsigned short)blue,
+ sixteenBit, rnorm);
+ ptr[0] = ptr[1] = ptr[2] = nGray;
+ }
+ else
+ {
+ ptr[0] = (uchar)MixPixel (brGain, bgGain, bbGain,
+ (unsigned short)red, (unsigned short)green, (unsigned short)blue,
+ sixteenBit, bnorm);
+ ptr[1] = (uchar)MixPixel (grGain, ggGain, gbGain,
+ (unsigned short)red, (unsigned short)green, (unsigned short)blue,
+ sixteenBit, gnorm);
+ ptr[2] = (uchar)MixPixel (rrGain, rgGain, rbGain,
+ (unsigned short)red, (unsigned short)green, (unsigned short)blue,
+ sixteenBit, rnorm);
+ }
+
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short nGray, red, green, blue;
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (i = 0 ; i < Width*Height ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+
+ if (bMonochrome)
+ {
+ nGray = MixPixel (rrGain, rgGain, rbGain, red, green, blue, sixteenBit, rnorm);
+ ptr[0] = ptr[1] = ptr[2] = nGray;
+ }
+ else
+ {
+ ptr[0] = MixPixel (brGain, bgGain, bbGain, red, green, blue, sixteenBit, bnorm);
+ ptr[1] = MixPixel (grGain, ggGain, gbGain, red, green, blue, sixteenBit, gnorm);
+ ptr[2] = MixPixel (rrGain, rgGain, rbGain, red, green, blue, sixteenBit, rnorm);
+ }
+
+ ptr += 4;
+ }
+ }
+}
+
+/** Change color tonality of an image to appling a RGB color mask.*/
+void DImgImageFilters::changeTonality(uchar *data, int width, int height, bool sixteenBit,
+ int redMask, int greenMask, int blueMask)
+{
+ if (!data || !width || !height)
+ {
+ DWarning() << ("DImgImageFilters::changeTonality: no image data available!")
+ << endl;
+ return;
+ }
+
+ int hue, sat, lig;
+
+ DColor mask(redMask, greenMask, blueMask, 0, sixteenBit);
+ mask.getHSL(&hue, &sat, &lig);
+
+ if (!sixteenBit) // 8 bits image.
+ {
+ uchar *ptr = data;
+
+ for (int i = 0 ; i < width*height ; i++)
+ {
+ // Convert to grayscale using tonal mask
+
+ lig = ROUND (0.3 * ptr[2] + 0.59 * ptr[1] + 0.11 * ptr[0]);
+
+ mask.setRGB(hue, sat, lig, sixteenBit);
+
+ ptr[0] = (uchar)mask.blue();
+ ptr[1] = (uchar)mask.green();
+ ptr[2] = (uchar)mask.red();
+ ptr += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short *ptr = (unsigned short *)data;
+
+ for (int i = 0 ; i < width*height ; i++)
+ {
+ // Convert to grayscale using tonal mask
+
+ lig = ROUND (0.3 * ptr[2] + 0.59 * ptr[1] + 0.11 * ptr[0]);
+
+ mask.setRGB(hue, sat, lig, sixteenBit);
+
+ ptr[0] = (unsigned short)mask.blue();
+ ptr[1] = (unsigned short)mask.green();
+ ptr[2] = (unsigned short)mask.red();
+ ptr += 4;
+ }
+ }
+}
+
+/** Function to apply the GaussianBlur on an image. This method do not use a
+ dedicaced thread.*/
+void DImgImageFilters::gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius)
+{
+ if (!data || !width || !height)
+ {
+ DWarning() << ("DImgImageFilters::gaussianBlurImage: no image data available!")
+ << endl;
+ return;
+ }
+
+ if (radius > 100) radius = 100;
+ if (radius <= 0) return;
+
+ DImg orgImage(width, height, sixteenBit, true, data);
+ DImgGaussianBlur *filter = new DImgGaussianBlur(&orgImage, 0L, radius);
+ DImg imDest = filter->getTargetImage();
+ memcpy( data, imDest.bits(), imDest.numBytes() );
+ delete filter;
+}
+
+/** Function to apply the sharpen filter on an image. This method do not use a
+ dedicaced thread.*/
+void DImgImageFilters::sharpenImage(uchar *data, int width, int height, bool sixteenBit, int radius)
+{
+ if (!data || !width || !height)
+ {
+ DWarning() << ("DImgImageFilters::sharpenImage: no image data available!")
+ << endl;
+ return;
+ }
+
+ if (radius > 100) radius = 100;
+ if (radius <= 0) return;
+
+ DImg orgImage(width, height, sixteenBit, true, data);
+ DImgSharpen *filter = new DImgSharpen(&orgImage, 0L, radius);
+ DImg imDest = filter->getTargetImage();
+ memcpy( data, imDest.bits(), imDest.numBytes() );
+ delete filter;
+}
+
+/** Function to perform pixel antialiasing with 8 bits/color/pixel images. This method is used to smooth target
+ image in transformation method like free rotation or shear tool. */
+void DImgImageFilters::pixelAntiAliasing(uchar *data, int Width, int Height, double X, double Y,
+ uchar *A, uchar *R, uchar *G, uchar *B)
+{
+ int nX, nY, j;
+ double lfWeightX[2], lfWeightY[2], lfWeight;
+ double lfTotalR = 0.0, lfTotalG = 0.0, lfTotalB = 0.0, lfTotalA = 0.0;
+
+ nX = (int)X;
+ nY = (int)Y;
+
+ if (Y >= 0.0)
+ lfWeightY[0] = 1.0 - (lfWeightY[1] = Y - (double)nY);
+ else
+ lfWeightY[1] = 1.0 - (lfWeightY[0] = -(Y - (double)nY));
+
+ if (X >= 0.0)
+ lfWeightX[0] = 1.0 - (lfWeightX[1] = X - (double)nX);
+ else
+ lfWeightX[1] = 1.0 - (lfWeightX[0] = -(X - (double)nX));
+
+ for (int loopx = 0; loopx <= 1; loopx++)
+ {
+ for (int loopy = 0; loopy <= 1; loopy++)
+ {
+ lfWeight = lfWeightX[loopx] * lfWeightY[loopy];
+ j = setPositionAdjusted (Width, Height, nX + loopx, nY + loopy);
+
+ lfTotalB += ((double)data[j] * lfWeight);
+ j++;
+ lfTotalG += ((double)data[j] * lfWeight);
+ j++;
+ lfTotalR += ((double)data[j] * lfWeight);
+ j++;
+ lfTotalA += ((double)data[j] * lfWeight);
+ j++;
+ }
+ }
+
+ *B = CLAMP0255((int)lfTotalB);
+ *G = CLAMP0255((int)lfTotalG);
+ *R = CLAMP0255((int)lfTotalR);
+ *A = CLAMP0255((int)lfTotalA);
+}
+
+/** Function to perform pixel antialiasing with 16 bits/color/pixel images. This method is used to smooth target
+ image in transformation method like free rotation or shear tool. */
+void DImgImageFilters::pixelAntiAliasing16(unsigned short *data, int Width, int Height, double X, double Y,
+ unsigned short *A, unsigned short *R, unsigned short *G,
+ unsigned short *B)
+{
+ int nX, nY, j;
+ double lfWeightX[2], lfWeightY[2], lfWeight;
+ double lfTotalR = 0.0, lfTotalG = 0.0, lfTotalB = 0.0, lfTotalA = 0.0;
+
+ nX = (int)X;
+ nY = (int)Y;
+
+ if (Y >= 0.0)
+ lfWeightY[0] = 1.0 - (lfWeightY[1] = Y - (double)nY);
+ else
+ lfWeightY[1] = 1.0 - (lfWeightY[0] = -(Y - (double)nY));
+
+ if (X >= 0.0)
+ lfWeightX[0] = 1.0 - (lfWeightX[1] = X - (double)nX);
+ else
+ lfWeightX[1] = 1.0 - (lfWeightX[0] = -(X - (double)nX));
+
+ for (int loopx = 0; loopx <= 1; loopx++)
+ {
+ for (int loopy = 0; loopy <= 1; loopy++)
+ {
+ lfWeight = lfWeightX[loopx] * lfWeightY[loopy];
+ j = setPositionAdjusted (Width, Height, nX + loopx, nY + loopy);
+
+ lfTotalB += ((double)data[j] * lfWeight);
+ j++;
+ lfTotalG += ((double)data[j] * lfWeight);
+ j++;
+ lfTotalR += ((double)data[j] * lfWeight);
+ j++;
+ lfTotalA += ((double)data[j] * lfWeight);
+ j++;
+ }
+ }
+
+ *B = CLAMP065535((int)lfTotalB);
+ *G = CLAMP065535((int)lfTotalG);
+ *R = CLAMP065535((int)lfTotalR);
+ *A = CLAMP065535((int)lfTotalA);
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/dimgimagefilters.h b/src/libs/dimg/filters/dimgimagefilters.h
new file mode 100644
index 00000000..009c3efb
--- /dev/null
+++ b/src/libs/dimg/filters/dimgimagefilters.h
@@ -0,0 +1,133 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-24-01
+ * Description : misc image filters
+ *
+ * Copyright (C) 2004-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 DIMGIMAGE_FILTERS_H
+#define DIMGIMAGE_FILTERS_H
+
+#define CLAMP0255(a) TQMIN(TQMAX(a,0), 255)
+#define CLAMP065535(a) TQMIN(TQMAX(a,0), 65535)
+#define CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
+#define ROUND(x) ((int) ((x) + 0.5))
+
+// C++ includes.
+
+#include <cmath>
+
+// Digikam includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DImgImageFilters
+{
+public:
+
+ DImgImageFilters(){};
+ ~DImgImageFilters(){};
+
+private: // Private structures used internally.
+
+ struct double_packet
+ {
+ double red;
+ double green;
+ double blue;
+ double alpha;
+ };
+
+ struct int_packet
+ {
+ unsigned int red;
+ unsigned int green;
+ unsigned int blue;
+ unsigned int alpha;
+ };
+
+ struct NormalizeParam
+ {
+ unsigned short *lut;
+ double min;
+ double max;
+ };
+
+private: // Private methods used internally.
+
+ // Methods for Channel Mixer.
+
+ inline double CalculateNorm(float RedGain, float GreenGain, float BlueGain, bool bPreserveLum)
+ {
+ double lfSum = RedGain + GreenGain + BlueGain;
+
+ if ((lfSum == 0.0) || (bPreserveLum == false))
+ return (1.0);
+
+ return( fabs (1.0 / lfSum) );
+ };
+
+ inline unsigned short MixPixel(float RedGain, float GreenGain, float BlueGain,
+ unsigned short R, unsigned short G, unsigned short B, bool sixteenBit,
+ double Norm)
+ {
+ double lfMix = RedGain * (double)R + GreenGain * (double)G + BlueGain * (double)B;
+ lfMix *= Norm;
+ int segment = sixteenBit ? 65535 : 255;
+
+ return( (unsigned short)CLAMP (lfMix, 0, segment) );
+ };
+
+ inline int setPositionAdjusted (int Width, int Height, int X, int Y)
+ {
+ X = (X < 0) ? 0 : (X >= Width ) ? Width - 1 : X;
+ Y = (Y < 0) ? 0 : (Y >= Height) ? Height - 1 : Y;
+ return (Y*Width*4 + 4*X);
+ };
+
+public: // Public methods.
+
+ void equalizeImage(uchar *data, int w, int h, bool sixteenBit);
+ void stretchContrastImage(uchar *data, int w, int h, bool sixteenBit);
+ void normalizeImage(uchar *data, int w, int h, bool sixteenBit);
+ void autoLevelsCorrectionImage(uchar *data, int w, int h, bool sixteenBit);
+ void invertImage(uchar *data, int w, int h, bool sixteenBit);
+ void channelMixerImage(uchar *data, int Width, int Height, bool sixteenBit,
+ bool bPreserveLum, bool bMonochrome,
+ float rrGain, float rgGain, float rbGain,
+ float grGain, float ggGain, float gbGain,
+ float brGain, float bgGain, float bbGain);
+ void changeTonality(uchar *data, int width, int height, bool sixteenBit,
+ int redMask, int greenMask, int blueMask);
+ void gaussianBlurImage(uchar *data, int width, int height, bool sixteenBit, int radius);
+ void sharpenImage(uchar *data, int width, int height, bool sixteenBit, int radius);
+
+ void pixelAntiAliasing(uchar *data, int Width, int Height, double X, double Y,
+ uchar *A, uchar *R, uchar *G, uchar *B);
+
+ void pixelAntiAliasing16(unsigned short *data, int Width, int Height, double X, double Y,
+ unsigned short *A, unsigned short *R, unsigned short *G, unsigned short *B);
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMGIMAGE_FILTERS_H */
diff --git a/src/libs/dimg/filters/dimgsharpen.cpp b/src/libs/dimg/filters/dimgsharpen.cpp
new file mode 100644
index 00000000..a27d5b10
--- /dev/null
+++ b/src/libs/dimg/filters/dimgsharpen.cpp
@@ -0,0 +1,243 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-17-07
+ * Description : A Sharpen threaded image filter.
+ *
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Original Sharpen algorithm copyright 2002
+ * by Daniel M. Duley <[email protected]> from KImageEffect API.
+ *
+ * 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 SQ2PI 2.50662827463100024161235523934010416269302368164062
+#define Epsilon 1.0e-12
+
+// C++ includes.
+
+#include <cmath>
+#include <cstdlib>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimgimagefilters.h"
+#include "dimgsharpen.h"
+
+namespace Digikam
+{
+
+DImgSharpen::DImgSharpen(DImg *orgImage, TQObject *parent, double radius, double sigma)
+ : DImgThreadedFilter(orgImage, parent, "Sharpen")
+{
+ m_radius = radius;
+ m_sigma = sigma;
+ initFilter();
+}
+
+DImgSharpen::DImgSharpen(DImgThreadedFilter *parentFilter,
+ const DImg &orgImage, const DImg &destImage,
+ int progressBegin, int progressEnd, double radius, double sigma)
+ : DImgThreadedFilter(parentFilter, orgImage, destImage, progressBegin, progressEnd,
+ parentFilter->filterName() + ": Sharpen")
+{
+ m_radius = radius;
+ m_sigma = sigma;
+ // We need to provide support for orgImage == destImage.
+ // The algorithm does not support this out of the box, so use a temporary.
+ if (orgImage.bits() == destImage.bits())
+ m_destImage = DImg(destImage.width(), destImage.height(), destImage.sixteenBit());
+ filterImage();
+ if (orgImage.bits() == destImage.bits())
+ memcpy(destImage.bits(), m_destImage.bits(), m_destImage.numBytes());
+}
+
+void DImgSharpen::filterImage(void)
+{
+ sharpenImage(m_radius, m_sigma);
+}
+
+/** Function to apply the sharpen filter on an image*/
+
+void DImgSharpen::sharpenImage(double radius, double sigma)
+{
+ if (m_orgImage.isNull())
+ {
+ DWarning() << k_funcinfo << "No image data available!"
+ << endl;
+ return;
+ }
+
+ if (radius <= 0.0)
+ {
+ m_destImage = m_orgImage;
+ return;
+ }
+
+ double alpha, normalize=0.0;
+ long i=0, u, v;
+
+ int kernelWidth = getOptimalKernelWidth(radius, sigma);
+
+ if((int)m_orgImage.width() < kernelWidth)
+ {
+ DWarning() << k_funcinfo << "Image is smaller than radius!"
+ << endl;
+ return;
+ }
+
+ double *kernel = new double[kernelWidth*kernelWidth];
+
+ if(!kernel)
+ {
+ DWarning() << k_funcinfo << "Unable to allocate memory!"
+ << endl;
+ return;
+ }
+
+ for(v=(-kernelWidth/2) ; v <= (kernelWidth/2) ; v++)
+ {
+ for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++)
+ {
+ alpha = exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
+ kernel[i] = alpha/(2.0*M_PI*sigma*sigma);
+ normalize += kernel[i];
+ i++;
+ }
+ }
+
+ kernel[i/2] = (-2.0)*normalize;
+ convolveImage(kernelWidth, kernel);
+
+ delete [] kernel;
+}
+
+bool DImgSharpen::convolveImage(const unsigned int order, const double *kernel)
+{
+ uint x, y;
+ int mx, my, sx, sy, mcx, mcy, progress;
+ long kernelWidth, i;
+ double red, green, blue, alpha, normalize=0.0;
+ double *k=0;
+ DColor color;
+
+ kernelWidth = order;
+
+ if((kernelWidth % 2) == 0)
+ {
+ DWarning() << k_funcinfo << "Kernel width must be an odd number!"
+ << endl;
+ return(false);
+ }
+
+ double *normal_kernel = new double[kernelWidth*kernelWidth];
+
+ if(!normal_kernel)
+ {
+ DWarning() << k_funcinfo << "Unable to allocate memory!"
+ << endl;
+ return(false);
+ }
+
+ for(i=0 ; i < (kernelWidth*kernelWidth) ; i++)
+ normalize += kernel[i];
+
+ if(fabs(normalize) <= Epsilon)
+ normalize=1.0;
+
+ normalize = 1.0/normalize;
+
+ for(i=0 ; i < (kernelWidth*kernelWidth) ; i++)
+ normal_kernel[i] = normalize*kernel[i];
+
+ double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0;
+
+ for(y=0 ; !m_cancel && (y < m_destImage.height()) ; y++)
+ {
+ sy = y-(kernelWidth/2);
+
+ for(x=0 ; !m_cancel && (x < m_destImage.width()) ; x++)
+ {
+ k = normal_kernel;
+ red = green = blue = alpha = 0;
+ sy = y-(kernelWidth/2);
+
+ for(mcy=0 ; !m_cancel && (mcy < kernelWidth) ; mcy++, sy++)
+ {
+ my = sy < 0 ? 0 : sy > (int)m_destImage.height()-1 ? m_destImage.height()-1 : sy;
+ sx = x+(-kernelWidth/2);
+
+ for(mcx=0 ; !m_cancel && (mcx < kernelWidth) ; mcx++, sx++)
+ {
+ mx = sx < 0 ? 0 : sx > (int)m_destImage.width()-1 ? m_destImage.width()-1 : sx;
+ color = m_orgImage.getPixelColor(mx, my);
+ red += (*k)*(color.red() * 257.0);
+ green += (*k)*(color.green() * 257.0);
+ blue += (*k)*(color.blue() * 257.0);
+ alpha += (*k)*(color.alpha() * 257.0);
+ k++;
+ }
+ }
+
+ red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red+0.5;
+ green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green+0.5;
+ blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue+0.5;
+ alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha+0.5;
+
+ m_destImage.setPixelColor(x, y, DColor((int)(red / 257UL), (int)(green / 257UL),
+ (int)(blue / 257UL), (int)(alpha / 257UL),
+ m_destImage.sixteenBit()));
+ }
+
+ progress = (int)(((double)y * 100.0) / m_destImage.height());
+ if ( progress%5 == 0 )
+ postProgress( progress );
+ }
+
+ delete [] normal_kernel;
+ return(true);
+}
+
+int DImgSharpen::getOptimalKernelWidth(double radius, double sigma)
+{
+ double normalize, value;
+ long kernelWidth;
+ long u;
+
+ if(radius > 0.0)
+ return((int)(2.0*ceil(radius)+1.0));
+
+ for(kernelWidth=5; ;)
+ {
+ normalize=0.0;
+
+ for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++)
+ normalize += exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma);
+
+ u = kernelWidth/2;
+ value = exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma)/normalize;
+
+ if((long)(65535*value) <= 0)
+ break;
+
+ kernelWidth+=2;
+ }
+
+ return((int)kernelWidth-2);
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/dimgsharpen.h b/src/libs/dimg/filters/dimgsharpen.h
new file mode 100644
index 00000000..5802769a
--- /dev/null
+++ b/src/libs/dimg/filters/dimgsharpen.h
@@ -0,0 +1,69 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-17-07
+ * Description : A Sharpen threaded image filter.
+ *
+ * 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 DIMGSHARPEN_H
+#define DIMGSHARPEN_H
+
+// Digikam includes.
+
+#include "digikam_export.h"
+
+// Local includes.
+
+#include "dimgthreadedfilter.h"
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DImgSharpen : public DImgThreadedFilter
+{
+
+public:
+
+ DImgSharpen(DImg *orgImage, TQObject *parent=0, double radius=0.0, double sigma=1.0);
+
+ // Constructor for slave mode: execute immediately in current thread with specified master filter
+ DImgSharpen(DImgThreadedFilter *parentFilter, const DImg &orgImage, const DImg &destImage,
+ int progressBegin=0, int progressEnd=100, double radius=0.0, double sigma=1.0);
+
+ ~DImgSharpen(){};
+
+private: // DImgSharpen filter data.
+
+ double m_radius;
+ double m_sigma;
+
+private: // DImgSharpen filter methods.
+
+ virtual void filterImage(void);
+
+ void sharpenImage(double radius, double sigma);
+
+ bool convolveImage(const unsigned int order, const double *kernel);
+
+ int getOptimalKernelWidth(double radius, double sigma);
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMGSHARPEN_H */
diff --git a/src/libs/dimg/filters/dimgthreadedfilter.cpp b/src/libs/dimg/filters/dimgthreadedfilter.cpp
new file mode 100644
index 00000000..205405e8
--- /dev/null
+++ b/src/libs/dimg/filters/dimgthreadedfilter.cpp
@@ -0,0 +1,170 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-05-25
+ * Description : threaded image filter class.
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+// TQt includes.
+
+#include <tqobject.h>
+#include <tqevent.h>
+#include <tqdeepcopy.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimgthreadedfilter.h"
+
+namespace Digikam
+{
+
+DImgThreadedFilter::DImgThreadedFilter(DImg *orgImage, TQObject *parent,
+ const TQString& name)
+ : TQThread()
+{
+ // remove meta data
+ m_orgImage = orgImage->copyImageData();
+ m_parent = parent;
+ m_cancel = false;
+
+ // See B.K.O #133026: make a deep copy of Qstring to prevent crash
+ // on Hyperthreading computer.
+ m_name = TQDeepCopy<TQString>(name);
+
+ m_master = 0;
+ m_slave = 0;
+ m_progressBegin = 0;
+ m_progressSpan = 100;
+}
+
+DImgThreadedFilter::DImgThreadedFilter(DImgThreadedFilter *master, const DImg &orgImage,
+ const DImg &destImage, int progressBegin, int progressEnd,
+ const TQString& name)
+{
+ m_orgImage = orgImage;
+ m_destImage = destImage;
+ m_parent = 0;
+ m_cancel = false;
+
+ // See B.K.O #133026: make a deep copy of Qstring to prevent crash
+ // on Hyperthreading computer.
+ m_name = TQDeepCopy<TQString>(name);
+
+ m_master = master;
+ m_slave = 0;
+ m_progressBegin = progressBegin;
+ m_progressSpan = progressEnd - progressBegin;
+
+ m_master->setSlave(this);
+}
+
+DImgThreadedFilter::~DImgThreadedFilter()
+{
+ stopComputation();
+ if (m_master)
+ m_master->setSlave(0);
+}
+
+void DImgThreadedFilter::initFilter(void)
+{
+ m_destImage.reset();
+ m_destImage = DImg(m_orgImage.width(), m_orgImage.height(),
+ m_orgImage.sixteenBit(), m_orgImage.hasAlpha());
+
+ if (m_orgImage.width() && m_orgImage.height())
+ {
+ if (m_parent)
+ start(); // m_parent is valide, start thread ==> run()
+ else
+ startComputation(); // no parent : no using thread.
+ }
+ else // No image data
+ {
+ if (m_parent) // If parent then send event about a problem.
+ {
+ postProgress(0, false, false);
+ DDebug() << m_name << "::No valid image data !!! ..." << endl;
+ }
+ }
+}
+
+void DImgThreadedFilter::stopComputation(void)
+{
+ m_cancel = true;
+ if (m_slave)
+ {
+ m_slave->m_cancel = true;
+ // do not wait on slave, it is not running in its own separate thread!
+ //m_slave->cleanupFilter();
+ }
+ wait();
+ cleanupFilter();
+}
+
+void DImgThreadedFilter::postProgress(int progress, bool starting, bool success)
+{
+ if (m_master)
+ {
+ progress = modulateProgress(progress);
+ m_master->postProgress(progress, starting, success);
+ }
+ else if (m_parent)
+ {
+ EventData *eventData = new EventData();
+ eventData->progress = progress;
+ eventData->starting = starting;
+ eventData->success = success;
+ TQApplication::postEvent(m_parent, new TQCustomEvent(TQEvent::User, eventData));
+ }
+}
+
+void DImgThreadedFilter::startComputation()
+{
+ // See B.K.O #133026: do not use kdDebug() statements in threaded implementation
+ // to prevent crash under Hyperthreaded CPU.
+
+ if (m_parent)
+ postProgress(0, true, false);
+
+ filterImage();
+
+ if (!m_cancel)
+ {
+ if (m_parent)
+ postProgress(0, false, true);
+ }
+ else
+ {
+ if (m_parent)
+ postProgress(0, false, false);
+ }
+}
+
+void DImgThreadedFilter::setSlave(DImgThreadedFilter *slave)
+{
+ m_slave = slave;
+}
+
+int DImgThreadedFilter::modulateProgress(int progress)
+{
+ return m_progressBegin + (int)((double)progress * (double)m_progressSpan / 100.0);
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/dimgthreadedfilter.h b/src/libs/dimg/filters/dimgthreadedfilter.h
new file mode 100644
index 00000000..1d4a97b8
--- /dev/null
+++ b/src/libs/dimg/filters/dimgthreadedfilter.h
@@ -0,0 +1,153 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-05-25
+ * Description : threaded image filter class.
+ *
+ * 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 DIMGTHREADEDFILTER_H
+#define DIMGTHREADEDFILTER_H
+
+// TQt includes.
+
+#include <tqthread.h>
+#include <tqstring.h>
+
+// KDE includes.
+
+#include <tdeapplication.h>
+
+// Local includes.
+
+#include "dimg.h"
+#include "digikam_export.h"
+
+class TQObject;
+
+namespace Digikam
+{
+
+class DIGIKAM_EXPORT DImgThreadedFilter : public TQThread
+{
+
+public:
+
+/** Class used to post status of computation to parent. */
+class EventData
+{
+ public:
+
+ EventData()
+ {
+ starting = false;
+ success = false;
+ }
+
+ bool starting;
+ bool success;
+ int progress;
+};
+
+public:
+
+ DImgThreadedFilter(DImg *orgImage, TQObject *parent=0,
+ const TQString& name=TQString());
+
+ ~DImgThreadedFilter();
+
+ DImg getTargetImage(void) { return m_destImage; };
+
+ virtual void startComputation(void);
+ virtual void stopComputation(void);
+
+ const TQString &filterName() { return m_name; };
+
+protected:
+
+ /** Start filter operation before threaded method. Must be calls by your constructor. */
+ virtual void initFilter(void);
+
+ /** List of threaded operations by filter. */
+ virtual void run(){ startComputation(); };
+
+ /** Main image filter method. */
+ virtual void filterImage(void){};
+
+ /** Clean up filter data if necessary. Call by stopComputation() method. */
+ virtual void cleanupFilter(void){};
+
+ /** Post Event to parent about progress. Warning: you need to delete
+ 'EventData' instance to 'customEvent' parent implementation. */
+ void postProgress(int progress=0, bool starting=true, bool success=false);
+
+protected:
+
+ /**
+ Support for chaining two filters as master and thread.
+
+ Constructor for slave mode:
+ Constructs a new slave filter with the specified master.
+ The filter will be executed in the current thread.
+ orgImage and destImage will not be copied.
+ progressBegin and progressEnd can indicate the progress span
+ that the slave filter uses in the parent filter's progress.
+ Any derived filter class that is publicly available to other filters
+ should implement an additional constructor using this constructor.
+ */
+ DImgThreadedFilter(DImgThreadedFilter *master, const DImg &orgImage, const DImg &destImage,
+ int progressBegin=0, int progressEnd=100, const TQString& name=TQString());
+
+ /** Inform the master that there is currently a slave. At destruction of the slave, call with slave=0. */
+ void setSlave(DImgThreadedFilter *slave);
+
+ /** This method modulates the progress value from the 0..100 span to the span of this slave.
+ Called by postProgress if master is not null. */
+ virtual int modulateProgress(int progress);
+
+protected:
+
+ /** Used to stop compution loop. */
+ bool m_cancel;
+
+ /** The progress span that a slave filter uses in the parent filter's progress. */
+ int m_progressBegin;
+ int m_progressSpan;
+
+ /** To post event from thread to parent. */
+ TQObject *m_parent;
+
+ /** Filter name.*/
+ TQString m_name;
+
+ /** Copy of original Image data. */
+ DImg m_orgImage;
+
+ /** Output image data. */
+ DImg m_destImage;
+
+ /** The current slave. Any filter might want to use another filter while processing. */
+ DImgThreadedFilter *m_slave;
+
+ /** The master of this slave filter. Progress info will be routed to this one. */
+ DImgThreadedFilter *m_master;
+};
+
+} // NameSpace Digikam
+
+#endif /* DIMGTHREADEDFILTER_H */
diff --git a/src/libs/dimg/filters/hslmodifier.cpp b/src/libs/dimg/filters/hslmodifier.cpp
new file mode 100644
index 00000000..4f924d76
--- /dev/null
+++ b/src/libs/dimg/filters/hslmodifier.cpp
@@ -0,0 +1,240 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-03-06
+ * Description : Hue/Saturation/Lightness image filter.
+ *
+ * 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 CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
+#define CLAMP_0_255(x) TQMAX(TQMIN(x, 255), 0)
+#define CLAMP_0_65535(x) TQMAX(TQMIN(x, 65535), 0)
+
+// C++ includes.
+
+#include <cstdio>
+#include <cmath>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dcolor.h"
+#include "dimg.h"
+#include "hslmodifier.h"
+
+namespace Digikam
+{
+
+class HSLModifierPriv
+{
+public:
+
+ HSLModifierPriv()
+ {
+ modified = false;
+ }
+
+ bool modified;
+
+ int htransfer[256];
+ int ltransfer[256];
+ int stransfer[256];
+
+ int htransfer16[65536];
+ int ltransfer16[65536];
+ int stransfer16[65536];
+};
+
+HSLModifier::HSLModifier()
+{
+ d = new HSLModifierPriv;
+ reset();
+}
+
+HSLModifier::~HSLModifier()
+{
+ delete d;
+}
+
+bool HSLModifier::modified() const
+{
+ return d->modified;
+}
+
+void HSLModifier::reset()
+{
+ // initialize to linear mapping
+
+ for (int i=0; i<65536; i++)
+ {
+ d->htransfer16[i] = i;
+ d->ltransfer16[i] = i;
+ d->stransfer16[i] = i;
+ }
+
+ for (int i=0; i<256; i++)
+ {
+ d->htransfer[i] = i;
+ d->ltransfer[i] = i;
+ d->stransfer[i] = i;
+ }
+
+ d->modified = false;
+}
+
+void HSLModifier::applyHSL(DImg& image)
+{
+ if (!d->modified || image.isNull())
+ return;
+
+ bool sixteenBit = image.sixteenBit();
+ uint numberOfPixels = image.numPixels();
+
+ if (sixteenBit) // 16 bits image.
+ {
+ unsigned short* data = (unsigned short*) image.bits();
+
+ for (uint i=0; i<numberOfPixels; i++)
+ {
+ int hue, sat, lig;
+
+ DColor color(data[2], data[1], data[0], 0, sixteenBit);
+
+ // convert RGB to HSL
+ color.getHSL(&hue, &sat, &lig);
+
+ // convert HSL to RGB
+ color.setRGB(d->htransfer16[hue], d->stransfer16[sat], d->ltransfer16[lig], sixteenBit);
+
+ data[2] = color.red();
+ data[1] = color.green();
+ data[0] = color.blue();
+
+ data += 4;
+ }
+ }
+ else // 8 bits image.
+ {
+ uchar* data = image.bits();
+
+ for (uint i=0; i<numberOfPixels; i++)
+ {
+ int hue, sat, lig;
+
+ DColor color(data[2], data[1], data[0], 0, sixteenBit);
+
+ // convert RGB to HSL
+ color.getHSL(&hue, &sat, &lig);
+
+ // convert HSL to RGB
+ color.setRGB(d->htransfer[hue], d->stransfer[sat], d->ltransfer[lig], sixteenBit);
+
+ data[2] = color.red();
+ data[1] = color.green();
+ data[0] = color.blue();
+
+ data += 4;
+ }
+ }
+}
+
+void HSLModifier::setHue(double val)
+{
+ int value;
+
+ for (int i = 0; i < 65536; i++)
+ {
+ value = lround(val * 65535.0 / 360.0);
+
+ if ((i + value) < 0)
+ d->htransfer16[i] = 65535 + (i + value);
+ else if ((i + value) > 65535)
+ d->htransfer16[i] = i + value - 65535;
+ else
+ d->htransfer16[i] = i + value;
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ value = lround(val * 255.0 / 360.0);
+
+ if ((i + value) < 0)
+ d->htransfer[i] = 255 + (i + value);
+ else if ((i + value) > 255)
+ d->htransfer[i] = i + value - 255;
+ else
+ d->htransfer[i] = i + value;
+ }
+
+ d->modified = true;
+}
+
+void HSLModifier::setSaturation(double val)
+{
+ val = CLAMP(val, -100.0, 100.0);
+ int value;
+
+ for (int i = 0; i < 65536; i++)
+ {
+ value = lround( (i * (100.0 + val)) / 100.0 );
+ d->stransfer16[i] = CLAMP_0_65535(value);
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ value = lround( (i * (100.0 + val)) / 100.0 );
+ d->stransfer[i] = CLAMP_0_255(value);
+ }
+
+ d->modified = true;
+}
+
+void HSLModifier::setLightness(double val)
+{
+ // val needs to be in that range so that the result is in the range 0..65535
+ val = CLAMP(val, -100.0, 100.0);
+
+ if (val < 0)
+ {
+ for (int i = 0; i < 65536; i++)
+ {
+ d->ltransfer16[i] = lround( (i * ( val + 100.0 )) / 100.0);
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ d->ltransfer[i] = lround( (i * ( val + 100.0 )) / 100.0);
+ }
+ }
+ else
+ {
+ for (int i = 0; i < 65536; i++)
+ {
+ d->ltransfer16[i] = lround( i * ( 1.0 - val / 100.0 ) + 65535.0 / 100.0 * val );
+ }
+
+ for (int i = 0; i < 256; i++)
+ {
+ d->ltransfer[i] = lround( i * ( 1.0 - val / 100.0 ) + 255.0 / 100.0 * val );
+ }
+ }
+
+ d->modified = true;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/hslmodifier.h b/src/libs/dimg/filters/hslmodifier.h
new file mode 100644
index 00000000..02a1131d
--- /dev/null
+++ b/src/libs/dimg/filters/hslmodifier.h
@@ -0,0 +1,58 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-03-06
+ * Description : Hue/Saturation/Lightness image filter.
+ *
+ * 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 HSLMODIFIER_H
+#define HSLMODIFIER_H
+
+// Local includes.
+
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class DImg;
+class HSLModifierPriv;
+
+class DIGIKAM_EXPORT HSLModifier
+{
+public:
+
+ HSLModifier();
+ ~HSLModifier();
+
+ void reset();
+ bool modified() const;
+
+ void setHue(double val);
+ void setSaturation(double val);
+ void setLightness(double val);
+ void applyHSL(DImg& image);
+
+private:
+
+ HSLModifierPriv* d;
+};
+
+} // NameSpace Digikam
+
+#endif /* HSLMODIFIER_H */
diff --git a/src/libs/dimg/filters/icctransform.cpp b/src/libs/dimg/filters/icctransform.cpp
new file mode 100644
index 00000000..0a1324db
--- /dev/null
+++ b/src/libs/dimg/filters/icctransform.cpp
@@ -0,0 +1,831 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-18
+ * Description : a class to apply ICC color correction to image.
+ *
+ * Copyright (C) 2005-2006 by F.J. Cruz <[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.
+ *
+ * ============================================================ */
+
+#include <config.h>
+
+// TQt includes.
+
+#include <tqstring.h>
+#include <tqcstring.h>
+#include <tqfile.h>
+
+// KDE includes.
+
+#include <tdeconfig.h>
+#include <tdeapplication.h>
+
+// Lcms includes.
+
+#include LCMS_HEADER
+#if LCMS_VERSION < 114
+#define cmsTakeCopyright(profile) "Unknown"
+#endif // LCMS_VERSION < 114
+
+// Local includes.
+
+#include "ddebug.h"
+#include "icctransform.h"
+
+namespace Digikam
+{
+
+class IccTransformPriv
+{
+public:
+
+ IccTransformPriv()
+ {
+ has_embedded_profile = false;
+ do_proof_profile = false;
+ }
+
+ bool do_proof_profile;
+ bool has_embedded_profile;
+
+ TQByteArray embedded_profile;
+ TQByteArray input_profile;
+ TQByteArray output_profile;
+ TQByteArray proof_profile;
+};
+
+IccTransform::IccTransform()
+{
+ d = new IccTransformPriv;
+ cmsErrorAction(LCMS_ERROR_SHOW);
+}
+
+IccTransform::~IccTransform()
+{
+ delete d;
+}
+
+bool IccTransform::hasInputProfile()
+{
+ return !(d->input_profile.isEmpty());
+}
+
+bool IccTransform::hasOutputProfile()
+{
+ return !(d->output_profile.isEmpty());
+}
+
+TQByteArray IccTransform::embeddedProfile() const
+{
+ return d->embedded_profile;
+}
+
+TQByteArray IccTransform::inputProfile() const
+{
+ return d->input_profile;
+}
+
+TQByteArray IccTransform::outputProfile() const
+{
+ return d->output_profile;
+}
+
+TQByteArray IccTransform::proofProfile() const
+{
+ return d->proof_profile;
+}
+
+void IccTransform::getTransformType(bool do_proof_profile)
+{
+ if (do_proof_profile)
+ {
+ d->do_proof_profile = true;
+ }
+ else
+ {
+ d->do_proof_profile = false;
+ }
+}
+
+void IccTransform::getEmbeddedProfile(const DImg& image)
+{
+ if (!image.getICCProfil().isNull())
+ {
+ d->embedded_profile = image.getICCProfil();
+ d->has_embedded_profile = true;
+ }
+}
+
+TQString IccTransform::getProfileDescription(const TQString& profile)
+{
+ cmsHPROFILE _profile = cmsOpenProfileFromFile(TQFile::encodeName(profile), "r");
+ TQString _description = cmsTakeProductDesc(_profile);
+ cmsCloseProfile(_profile);
+ return _description;
+}
+
+int IccTransform::getRenderingIntent()
+{
+ TDEConfig* config = kapp->config();
+ config->setGroup("Color Management");
+ return config->readNumEntry("RenderingIntent", 0);
+}
+
+bool IccTransform::getUseBPC()
+{
+ TDEConfig* config = kapp->config();
+ config->setGroup("Color Management");
+ return config->readBoolEntry("BPCAlgorithm", false);
+}
+
+TQByteArray IccTransform::loadICCProfilFile(const TQString& filePath)
+{
+ TQFile file(filePath);
+ if ( !file.open(IO_ReadOnly) )
+ return TQByteArray();
+
+ TQByteArray data(file.size());
+ TQDataStream stream( &file );
+ stream.readRawBytes(data.data(), data.size());
+ file.close();
+ return data;
+}
+
+void IccTransform::setProfiles(const TQString& input_profile, const TQString& output_profile)
+{
+ d->input_profile = loadICCProfilFile(input_profile);
+ d->output_profile = loadICCProfilFile(output_profile);
+}
+
+void IccTransform::setProfiles(const TQString& input_profile, const TQString& output_profile,
+ const TQString& proof_profile)
+{
+ d->input_profile = loadICCProfilFile(input_profile);
+ d->output_profile = loadICCProfilFile(output_profile);
+ d->proof_profile = loadICCProfilFile(proof_profile);
+}
+
+void IccTransform::setProfiles(const TQString& output_profile)
+{
+ d->output_profile = loadICCProfilFile(output_profile);
+}
+
+void IccTransform::setProfiles(const TQString& output_profile, const TQString& proof_profile, bool forProof)
+{
+ if (forProof)
+ {
+ d->output_profile = loadICCProfilFile(output_profile);
+ d->proof_profile = loadICCProfilFile(proof_profile);
+ }
+}
+
+TQString IccTransform::getEmbeddedProfileDescriptor()
+{
+ if (d->embedded_profile.isEmpty())
+ return TQString();
+
+ cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->embedded_profile.data(),
+ (DWORD)d->embedded_profile.size());
+ TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile));
+ cmsCloseProfile(tmpProfile);
+ return embeddedProfileDescriptor;
+}
+
+TQString IccTransform::getInputProfileDescriptor()
+{
+ if (d->input_profile.isEmpty()) return TQString();
+ cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->input_profile.data(), (DWORD)d->input_profile.size());
+ TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile));
+ cmsCloseProfile(tmpProfile);
+ return embeddedProfileDescriptor;
+}
+
+TQString IccTransform::getOutpoutProfileDescriptor()
+{
+ if (d->output_profile.isEmpty()) return TQString();
+ cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->output_profile.data(), (DWORD)d->output_profile.size());
+ TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile));
+ cmsCloseProfile(tmpProfile);
+ return embeddedProfileDescriptor;
+}
+
+TQString IccTransform::getProofProfileDescriptor()
+{
+ if (d->proof_profile.isEmpty()) return TQString();
+ cmsHPROFILE tmpProfile = cmsOpenProfileFromMem(d->proof_profile.data(), (DWORD)d->proof_profile.size());
+ TQString embeddedProfileDescriptor = TQString(cmsTakeProductDesc(tmpProfile));
+ cmsCloseProfile(tmpProfile);
+ return embeddedProfileDescriptor;
+}
+
+bool IccTransform::apply(DImg& image)
+{
+ cmsHPROFILE inprofile=0, outprofile=0, proofprofile=0;
+ cmsHTRANSFORM transform;
+ int inputFormat = 0;
+ int intent = INTENT_PERCEPTUAL;
+
+ switch (getRenderingIntent())
+ {
+ case 0:
+ intent = INTENT_PERCEPTUAL;
+ break;
+ case 1:
+ intent = INTENT_RELATIVE_COLORIMETRIC;
+ break;
+ case 2:
+ intent = INTENT_SATURATION;
+ break;
+ case 3:
+ intent = INTENT_ABSOLUTE_COLORIMETRIC;
+ break;
+ }
+
+ //DDebug() << "Intent is: " << intent << endl;
+
+ if (d->has_embedded_profile)
+ {
+ inprofile = cmsOpenProfileFromMem(d->embedded_profile.data(),
+ (DWORD)d->embedded_profile.size());
+ }
+ else
+ {
+ inprofile = cmsOpenProfileFromMem(d->input_profile.data(),
+ (DWORD)d->input_profile.size());
+ }
+ if (inprofile == NULL)
+ {
+ DDebug() << "Error: Input profile is NULL" << endl;
+ cmsCloseProfile(inprofile);
+ return false;
+ }
+
+// if (d->has_embedded_profile)
+// {
+// outprofile = cmsOpenProfileFromMem(d->embedded_profile.data(),
+// (DWORD)d->embedded_profile.size());
+// }
+// else
+// {
+ outprofile = cmsOpenProfileFromMem(d->output_profile.data(),
+ (DWORD)d->output_profile.size());
+// }
+
+ if (outprofile == NULL)
+ {
+ DDebug() << "Error: Output profile is NULL" << endl;
+ cmsCloseProfile(outprofile);
+ return false;
+ }
+
+ if (!d->do_proof_profile)
+ {
+ if (image.sixteenBit())
+ {
+ if (image.hasAlpha())
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAYA_16;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_16;
+ break;
+ default:
+ inputFormat = TYPE_BGRA_16;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGRA_16,
+ intent,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAY_16;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_16;
+ break;
+ default:
+ inputFormat = TYPE_BGR_16;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGR_16,
+ intent,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (image.hasAlpha())
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAYA_8;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_8;
+ break;
+ default:
+ inputFormat = TYPE_BGRA_8;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGRA_8,
+ intent,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAYA_8;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_8;
+ //DDebug() << "input profile: cmyk no alpha" << endl;
+ break;
+ default:
+ inputFormat = TYPE_BGR_8;
+ //DDebug() << "input profile: default no alpha" << endl;
+ }
+
+ transform = cmsCreateTransform(inprofile, inputFormat, outprofile,
+ TYPE_BGR_8, intent,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ proofprofile = cmsOpenProfileFromMem(d->proof_profile.data(),
+ (DWORD)d->proof_profile.size());
+
+ if (proofprofile == NULL)
+ {
+ DDebug() << "Error: Input profile is NULL" << endl;
+ cmsCloseProfile(inprofile);
+ cmsCloseProfile(outprofile);
+ return false;
+ }
+
+ if (image.sixteenBit())
+ {
+ if (image.hasAlpha())
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGRA_16,
+ outprofile,
+ TYPE_BGRA_16,
+ proofprofile,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGR_16,
+ outprofile,
+ TYPE_BGR_16,
+ proofprofile,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (image.hasAlpha())
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGR_8,
+ outprofile,
+ TYPE_BGR_8,
+ proofprofile,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGR_8,
+ outprofile,
+ TYPE_BGR_8,
+ proofprofile,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ INTENT_ABSOLUTE_COLORIMETRIC,
+ cmsFLAGS_WHITEBLACKCOMPENSATION);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ }
+
+ // We need to work using temp pixel buffer to apply ICC transformations.
+ uchar transdata[image.bytesDepth()];
+
+ // Always working with uchar* prevent endianess problem.
+ uchar *data = image.bits();
+
+ // We scan all image pixels one by one.
+ for (uint i=0; i < image.width()*image.height()*image.bytesDepth(); i+=image.bytesDepth())
+ {
+ // Apply ICC transformations.
+ cmsDoTransform( transform, &data[i], &transdata[0], 1);
+
+ // Copy buffer to source to update original image with ICC corrections.
+ // Alpha channel is restored in all cases.
+ memcpy (&data[i], &transdata[0], (image.bytesDepth() == 8) ? 6 : 3);
+ }
+
+ cmsDeleteTransform(transform);
+ cmsCloseProfile(inprofile);
+ cmsCloseProfile(outprofile);
+
+ if (d->do_proof_profile)
+ cmsCloseProfile(proofprofile);
+
+ return true;
+}
+
+bool IccTransform::apply( DImg& image, TQByteArray& profile, int intent, bool useBPC,
+ bool checkGamut, bool useBuiltin )
+{
+ cmsHPROFILE inprofile=0, outprofile=0, proofprofile=0;
+ cmsHTRANSFORM transform;
+ int transformFlags = 0, inputFormat = 0;
+
+ switch (intent)
+ {
+ case 0:
+ intent = INTENT_PERCEPTUAL;
+ break;
+ case 1:
+ intent = INTENT_RELATIVE_COLORIMETRIC;
+ break;
+ case 2:
+ intent = INTENT_SATURATION;
+ break;
+ case 3:
+ intent = INTENT_ABSOLUTE_COLORIMETRIC;
+ break;
+ }
+
+ //DDebug() << "Intent is: " << intent << endl;
+
+ if (!profile.isNull())
+ {
+ inprofile = cmsOpenProfileFromMem(profile.data(),
+ (DWORD)profile.size());
+ }
+ else if (useBuiltin)
+ {
+ inprofile = cmsCreate_sRGBProfile();
+ }
+ else
+ {
+ inprofile = cmsOpenProfileFromMem(d->input_profile.data(),
+ (DWORD)d->input_profile.size());
+ }
+
+ if (inprofile == NULL)
+ {
+ DDebug() << "Error: Input profile is NULL" << endl;
+ return false;
+ }
+
+ outprofile = cmsOpenProfileFromMem(d->output_profile.data(),
+ (DWORD)d->output_profile.size());
+
+ if (outprofile == NULL)
+ {
+ DDebug() << "Error: Output profile is NULL" << endl;
+ cmsCloseProfile(inprofile);
+ return false;
+ }
+
+ if (useBPC)
+ {
+ transformFlags |= cmsFLAGS_WHITEBLACKCOMPENSATION;
+ }
+
+ if (!d->do_proof_profile)
+ {
+ if (image.sixteenBit())
+ {
+ if (image.hasAlpha())
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAYA_16;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_16;
+ break;
+ default:
+ inputFormat = TYPE_BGRA_16;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGRA_16,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAY_16;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_16;
+ break;
+ default:
+ inputFormat = TYPE_BGR_16;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGR_16,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (image.hasAlpha())
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAYA_8;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_8;
+ break;
+ default:
+ inputFormat = TYPE_BGRA_8;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGRA_8,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ switch (cmsGetColorSpace(inprofile))
+ {
+ case icSigGrayData:
+ inputFormat = TYPE_GRAY_8;
+ break;
+ case icSigCmykData:
+ inputFormat = TYPE_CMYK_8;
+ break;
+ default:
+ inputFormat = TYPE_BGR_8;
+ }
+
+ transform = cmsCreateTransform( inprofile,
+ inputFormat,
+ outprofile,
+ TYPE_BGR_8,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ }
+ else
+ {
+ proofprofile = cmsOpenProfileFromMem(d->proof_profile.data(),
+ (DWORD)d->proof_profile.size());
+
+ if (proofprofile == NULL)
+ {
+ DDebug() << "Error: Input profile is NULL" << endl;
+ cmsCloseProfile(inprofile);
+ cmsCloseProfile(outprofile);
+ return false;
+ }
+
+ transformFlags |= cmsFLAGS_SOFTPROOFING;
+ if (checkGamut)
+ {
+ cmsSetAlarmCodes(126, 255, 255);
+ transformFlags |= cmsFLAGS_GAMUTCHECK;
+ }
+
+ if (image.sixteenBit())
+ {
+ if (image.hasAlpha())
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGRA_16,
+ outprofile,
+ TYPE_BGRA_16,
+ proofprofile,
+ intent,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGR_16,
+ outprofile,
+ TYPE_BGR_16,
+ proofprofile,
+ intent,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ if (image.hasAlpha())
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGR_8,
+ outprofile,
+ TYPE_BGR_8,
+ proofprofile,
+ intent,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ else
+ {
+ transform = cmsCreateProofingTransform( inprofile,
+ TYPE_BGR_8,
+ outprofile,
+ TYPE_BGR_8,
+ proofprofile,
+ intent,
+ intent,
+ transformFlags);
+
+ if (!transform)
+ {
+ DDebug() << k_funcinfo << "LCMS internal error: cannot create a color transform instance" << endl;
+ return false;
+ }
+ }
+ }
+ }
+
+ //DDebug() << "Transform flags are: " << transformFlags << endl;
+
+ // We need to work using temp pixel buffer to apply ICC transformations.
+ uchar transdata[image.bytesDepth()];
+
+ // Always working with uchar* prevent endianess problem.
+ uchar *data = image.bits();
+
+ // We scan all image pixels one by one.
+ for (uint i=0; i < image.width()*image.height()*image.bytesDepth(); i+=image.bytesDepth())
+ {
+ // Apply ICC transformations.
+ cmsDoTransform( transform, &data[i], &transdata[0], 1);
+
+ // Copy buffer to source to update original image with ICC corrections.
+ // Alpha channel is restored in all cases.
+ memcpy (&data[i], &transdata[0], (image.bytesDepth() == 8) ? 6 : 3);
+ }
+
+ cmsDeleteTransform(transform);
+ cmsCloseProfile(inprofile);
+ cmsCloseProfile(outprofile);
+
+ if (d->do_proof_profile)
+ cmsCloseProfile(proofprofile);
+
+ return true;
+}
+
+} // NameSpace Digikam
diff --git a/src/libs/dimg/filters/icctransform.h b/src/libs/dimg/filters/icctransform.h
new file mode 100644
index 00000000..0590c44f
--- /dev/null
+++ b/src/libs/dimg/filters/icctransform.h
@@ -0,0 +1,94 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-11-18
+ * Description : a class to apply ICC color correction to image.
+ *
+ * Copyright (C) 2005-2006 by F.J. Cruz <[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.
+ *
+ * ============================================================ */
+
+#ifndef ICCTRANSFORM_H
+#define ICCTRANSFORM_H
+
+// TQt includes.
+
+#include <tqstring.h>
+
+// Local includes.
+
+#include "dimg.h"
+#include "digikam_export.h"
+
+namespace Digikam
+{
+
+class IccTransformPriv;
+
+class DIGIKAM_EXPORT IccTransform
+{
+public:
+
+ IccTransform();
+ ~IccTransform();
+
+ bool apply(DImg& image);
+ bool apply(DImg& image, TQByteArray& profile, int intent,
+ bool useBPC=false, bool checkGamut=false, bool useBuiltin=false);
+
+ void getTransformType(bool do_proof_profile);
+ void getEmbeddedProfile(const DImg& image);
+ int getRenderingIntent();
+ bool getUseBPC();
+
+ bool hasInputProfile();
+ bool hasOutputProfile();
+
+ TQByteArray embeddedProfile() const;
+ TQByteArray inputProfile() const;
+ TQByteArray outputProfile() const;
+ TQByteArray proofProfile() const;
+
+ /** Input profile from file methods */
+ void setProfiles(const TQString& input_profile, const TQString& output_profile);
+ void setProfiles(const TQString& input_profile, const TQString& output_profile, const TQString& proof_profile);
+
+ /** Embedded input profile methods */
+ void setProfiles(const TQString& output_profile);
+ void setProfiles(const TQString& output_profile, const TQString& proof_profile, bool forProof);
+
+ /** Profile info methods */
+ TQString getProfileDescription(const TQString& profile);
+
+ TQString getEmbeddedProfileDescriptor();
+ TQString getInputProfileDescriptor();
+ TQString getOutpoutProfileDescriptor();
+ TQString getProofProfileDescriptor();
+
+private:
+
+ TQByteArray loadICCProfilFile(const TQString& filePath);
+
+private:
+
+ IccTransformPriv* d;
+
+};
+
+} // NameSpace Digikam
+
+#endif // ICCTRANSFORM_H
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 */