diff options
Diffstat (limited to 'src/libs/dimg')
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 */ |