You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
digikam/digikam/libs/dimg/dimg.cpp

1698 lines
40 KiB

/* ============================================================
*
* 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