summaryrefslogtreecommitdiffstats
path: root/digikam/libs/dimg/dimg.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'digikam/libs/dimg/dimg.cpp')
-rw-r--r--digikam/libs/dimg/dimg.cpp1697
1 files changed, 1697 insertions, 0 deletions
diff --git a/digikam/libs/dimg/dimg.cpp b/digikam/libs/dimg/dimg.cpp
new file mode 100644
index 0000000..45f5013
--- /dev/null
+++ b/digikam/libs/dimg/dimg.cpp
@@ -0,0 +1,1697 @@
+/* ============================================================
+ *
+ * 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 <renchi@pooh.tam.uiuc.edu>
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel.wiesweg@gmx.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.
+ *
+ * ============================================================ */
+
+// C ANSI includes.
+
+extern "C"
+{
+#include <stdint.h>
+}
+
+// C++ includes.
+
+#include <cstdio>
+
+// Qt includes.
+
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qmap.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 QCString& filePath, DImgLoaderObserver *observer,
+ DRawDecoding rawDecodingSettings)
+ : m_priv(new DImgPrivate)
+{
+ load(filePath, observer, rawDecodingSettings);
+}
+
+DImg::DImg(const QString& 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 QImage& image)
+ : m_priv(new DImgPrivate)
+{
+ if (!image.isNull())
+ {
+ QImage 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] = qBlue(*sptr);
+ dptr[1] = qGreen(*sptr);
+ dptr[2] = qRed(*sptr);
+ dptr[3] = qAlpha(*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 (QMap<int, QByteArray>::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 QString& 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 << " : QIMAGE file identified" << endl;
+ QImageLoader 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 QString& filePath, const QString& format, DImgLoaderObserver *observer)
+{
+ if (isNull())
+ return false;
+
+ if (format.isEmpty())
+ return false;
+
+ QString 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);
+ QImageLoader loader(this);
+ return loader.save(filePath, observer);
+ }
+
+ return false;
+}
+
+DImg::FORMAT DImg::fileFormat(const QString& 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.
+
+ QFileInfo fileInfo(filePath);
+ if (!fileInfo.exists())
+ {
+ DDebug() << k_funcinfo << "File \"" << filePath << "\" does not exist" << endl;
+ return NONE;
+ }
+
+#if KDCRAW_VERSION < 0x000106
+ QString rawFilesExt(KDcrawIface::DcrawBinary::instance()->rawFiles());
+#else
+ QString rawFilesExt(KDcrawIface::KDcraw::rawFiles());
+#endif
+ QString ext = fileInfo.extension(false).upper();
+
+ if (!ext.isEmpty())
+ {
+ if (ext == QString("JPEG") || ext == QString("JPG") || ext == QString("JPE"))
+ return JPEG;
+ else if (ext == QString("PNG"))
+ return PNG;
+ else if (ext == QString("TIFF") || ext == QString("TIF"))
+ return TIFF;
+ else if (rawFilesExt.upper().contains(ext))
+ return RAW;
+ if (ext == QString("JP2") || ext == QString("JPX") || // JPEG2000 file format
+ ext == QString("JPC") || // JPEG2000 code stream
+ ext == QString("PGX")) // JPEG2000 WM format
+ return JP2K;
+ }
+
+ // In second, we trying to parse file header.
+
+ FILE* f = fopen(QFile::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(QFile::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, QImage will be used to try to open file.
+ return QIMAGE;
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// 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;
+}
+
+QSize DImg::size() const
+{
+ return QSize(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 QString& filePath)
+{
+ QFile file(filePath);
+ if ( !file.open(IO_ReadOnly) )
+ return false;
+
+ QByteArray data(file.size());
+ QDataStream stream( &file );
+ stream.readRawBytes(data.data(), data.size());
+ setICCProfil(data);
+ file.close();
+ return true;
+}
+
+bool DImg::setICCProfilToFile(const QString& filePath)
+{
+ QFile file(filePath);
+ if ( !file.open(IO_WriteOnly) )
+ return false;
+
+ QByteArray data(getICCProfil());
+ QDataStream stream( &file );
+ stream.writeRawBytes(data.data(), data.size());
+ file.close();
+ return true;
+}
+
+QByteArray DImg::getComments() const
+{
+ return metadata(COM);
+}
+
+QByteArray DImg::getExif() const
+{
+ return metadata(EXIF);
+}
+
+QByteArray DImg::getIptc() const
+{
+ return metadata(IPTC);
+}
+
+QByteArray DImg::getICCProfil() const
+{
+ return metadata(ICC);
+}
+
+void DImg::setComments(const QByteArray& commentsData)
+{
+ m_priv->metaData.replace(COM, commentsData);
+}
+
+void DImg::setExif(const QByteArray& exifData)
+{
+ m_priv->metaData.replace(EXIF, exifData);
+}
+
+void DImg::setIptc(const QByteArray& iptcData)
+{
+ m_priv->metaData.replace(IPTC, iptcData);
+}
+
+void DImg::setICCProfil(const QByteArray& profile)
+{
+ m_priv->metaData.replace(ICC, profile);
+}
+
+QByteArray DImg::metadata(DImg::METADATA key) const
+{
+ typedef QMap<int, QByteArray> MetaDataMap;
+
+ for (MetaDataMap::iterator it = m_priv->metaData.begin(); it != m_priv->metaData.end(); ++it)
+ {
+ if (it.key() == key)
+ return it.data();
+ }
+
+ return QByteArray();
+}
+
+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 QString& key, const QVariant& value)
+{
+ m_priv->attributes.insert(key, value);
+}
+
+QVariant DImg::attribute(const QString& key) const
+{
+ if (m_priv->attributes.contains(key))
+ return m_priv->attributes[key];
+
+ return QVariant();
+}
+
+void DImg::setEmbeddedText(const QString& key, const QString& text)
+{
+ m_priv->embeddedText.insert(key, text);
+}
+
+QString DImg::embeddedText(const QString& key) const
+{
+ if (m_priv->embeddedText.contains(key))
+ return m_priv->embeddedText[key];
+
+ return QString();
+}
+
+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(QRect 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);
+ }
+ }
+}
+
+
+//---------------------------------------------------------------------------------------------------
+// QImage / QPixmap access
+
+
+QImage DImg::copyQImage()
+{
+ if (isNull())
+ return QImage();
+
+ if (sixteenBit())
+ {
+ DImg img(*this);
+ img.detach();
+ img.convertDepth(32);
+ return img.copyQImage();
+ }
+
+ QImage img(width(), height(), 32);
+
+ uchar* sptr = bits();
+ uint* dptr = (uint*)img.bits();
+
+ for (uint i=0; i < width()*height(); i++)
+ {
+ *dptr++ = qRgba(sptr[2], sptr[1], sptr[0], sptr[3]);
+ sptr += 4;
+ }
+
+ if (hasAlpha())
+ {
+ img.setAlphaBuffer(true);
+ }
+
+ return img;
+}
+
+QImage DImg::copyQImage(QRect rect)
+{
+ return (copyQImage(rect.x(), rect.y(), rect.width(), rect.height()));
+}
+
+QImage DImg::copyQImage(int x, int y, int w, int h)
+{
+ if (isNull())
+ return QImage();
+
+ DImg img = copy(x, y, w, h);
+
+ if (img.sixteenBit())
+ img.convertDepth(32);
+
+ return img.copyQImage();
+}
+
+QPixmap DImg::convertToPixmap()
+{
+ if (isNull())
+ return QPixmap();
+
+ if (sixteenBit())
+ {
+ // make fastaaaa..
+ return QPixmap(copyQImage(0, 0, width(), height()));
+ }
+
+ if (QImage::systemByteOrder() == QImage::BigEndian)
+ {
+ QImage img(width(), height(), 32);
+
+ uchar* sptr = bits();
+ uint* dptr = (uint*)img.bits();
+
+ for (uint i=0; i<width()*height(); i++)
+ {
+ *dptr++ = qRgba(sptr[2], sptr[1], sptr[0], sptr[3]);
+ sptr += 4;
+ }
+
+ if (hasAlpha())
+ {
+ img.setAlphaBuffer(true);
+ }
+
+ return QPixmap(img);
+ }
+ else
+ {
+ QImage img(bits(), width(), height(), 32, 0, 0, QImage::IgnoreEndian);
+
+ if (hasAlpha())
+ {
+ img.setAlphaBuffer(true);
+ }
+
+ return QPixmap(img);
+ }
+}
+
+QPixmap DImg::convertToPixmap(IccTransform *monitorICCtrans)
+{
+ if (isNull())
+ return QPixmap();
+
+ if (!monitorICCtrans->hasOutputProfile())
+ {
+ DDebug() << k_funcinfo << " : no monitor ICC profile available!" << endl;
+ return convertToPixmap();
+ }
+
+ DImg img = copy();
+
+ // Without embedded profile
+ if (img.getICCProfil().isNull())
+ {
+ QByteArray 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());
+}
+
+QImage DImg::pureColorMask(ExposureSettingsContainer *expoSettings)
+{
+ if (isNull() || (!expoSettings->underExposureIndicator && !expoSettings->overExposureIndicator))
+ return QImage();
+
+ QImage 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(QRect 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