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.
koffice/chalk/core/kis_paint_device.cc

1286 lines
34 KiB

/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
*
* 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 of the License, 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <tqrect.h>
#include <tqwmatrix.h>
#include <tqimage.h>
#include <tqdatetime.h>
#include <tqapplication.h>
#include <tqvaluelist.h>
#include <tqtimer.h>
#include <kcommand.h>
#include <klocale.h>
#include <kdebug.h>
#include <KoStore.h>
#include "kis_global.h"
#include "kis_types.h"
#include "kis_painter.h"
#include "kis_fill_painter.h"
#include "kis_undo_adapter.h"
#include "kis_iterator.h"
#include "kis_iterators_pixel.h"
#include "kis_iteratorpixeltrait.h"
#include "kis_random_accessor.h"
#include "kis_random_sub_accessor.h"
#include "kis_transaction.h"
#include "kis_profile.h"
#include "kis_color.h"
#include "kis_integer_maths.h"
#include "kis_colorspace_factory_registry.h"
#include "kis_selection.h"
#include "kis_layer.h"
#include "kis_paint_device_iface.h"
#include "kis_paint_device.h"
#include "kis_datamanager.h"
#include "kis_memento.h"
#include "kis_selection.h"
#include "kis_exif_info.h"
namespace {
class KisPaintDeviceCommand : public KNamedCommand {
typedef KNamedCommand super;
public:
KisPaintDeviceCommand(const TQString& name, KisPaintDeviceSP paintDevice);
virtual ~KisPaintDeviceCommand() {}
virtual void execute() = 0;
virtual void unexecute() = 0;
protected:
void setUndo(bool undo);
KisPaintDeviceSP m_paintDevice;
};
KisPaintDeviceCommand::KisPaintDeviceCommand(const TQString& name, KisPaintDeviceSP paintDevice) :
super(name), m_paintDevice(paintDevice)
{
}
void KisPaintDeviceCommand::setUndo(bool undo)
{
if (m_paintDevice->undoAdapter()) {
m_paintDevice->undoAdapter()->setUndo(undo);
}
}
class MoveCommand : public KNamedCommand {
typedef KNamedCommand super;
public:
MoveCommand(KisPaintDeviceSP device, const TQPoint& oldpos, const TQPoint& newpos);
virtual ~MoveCommand();
virtual void execute();
virtual void unexecute();
private:
void moveTo(const TQPoint& pos);
void undoOff();
void undoOn();
private:
KisPaintDeviceSP m_device;
TQPoint m_oldPos;
TQPoint m_newPos;
};
MoveCommand::MoveCommand(KisPaintDeviceSP device, const TQPoint& oldpos, const TQPoint& newpos) :
super(i18n("Move Layer"))
{
m_device = device;
m_oldPos = oldpos;
m_newPos = newpos;
}
MoveCommand::~MoveCommand()
{
}
void MoveCommand::undoOff()
{
if (m_device->undoAdapter()) {
m_device->undoAdapter()->setUndo(false);
}
}
void MoveCommand::undoOn()
{
if (m_device->undoAdapter()) {
m_device->undoAdapter()->setUndo(true);
}
}
void MoveCommand::execute()
{
undoOff();
moveTo(m_newPos);
undoOn();
}
void MoveCommand::unexecute()
{
undoOff();
moveTo(m_oldPos);
undoOn();
}
void MoveCommand::moveTo(const TQPoint& pos)
{
m_device->move(pos.x(), pos.y());
}
class KisConvertLayerTypeCmd : public KNamedCommand {
typedef KNamedCommand super;
public:
KisConvertLayerTypeCmd(KisUndoAdapter *adapter, KisPaintDeviceSP paintDevice,
KisDataManagerSP beforeData, KisColorSpace * beforeColorSpace,
KisDataManagerSP afterData, KisColorSpace * afterColorSpace
) : super(i18n("Convert Layer Type"))
{
m_adapter = adapter;
m_paintDevice = paintDevice;
m_beforeData = beforeData;
m_beforeColorSpace = beforeColorSpace;
m_afterData = afterData;
m_afterColorSpace = afterColorSpace;
}
virtual ~KisConvertLayerTypeCmd()
{
}
public:
virtual void execute()
{
m_adapter->setUndo(false);
m_paintDevice->setData(m_afterData, m_afterColorSpace);
m_adapter->setUndo(true);
}
virtual void unexecute()
{
m_adapter->setUndo(false);
m_paintDevice->setData(m_beforeData, m_beforeColorSpace);
m_adapter->setUndo(true);
}
private:
KisUndoAdapter *m_adapter;
KisPaintDeviceSP m_paintDevice;
KisDataManagerSP m_beforeData;
KisColorSpace * m_beforeColorSpace;
KisDataManagerSP m_afterData;
KisColorSpace * m_afterColorSpace;
};
}
KisPaintDevice::KisPaintDevice(KisColorSpace * colorSpace, const char * name) :
TQObject(0, name), TDEShared(), m_exifInfo(0), m_lock( false )
{
if (colorSpace == 0) {
kdWarning(41001) << "Cannot create paint device without colorstrategy!\n";
return;
}
m_longRunningFilterTimer = 0;
m_dcop = 0;
m_x = 0;
m_y = 0;
m_pixelSize = colorSpace->pixelSize();
m_nChannels = colorSpace->nChannels();
TQ_UINT8* defPixel = new TQ_UINT8 [ m_pixelSize ];
colorSpace->fromTQColor(TQt::black, OPACITY_TRANSPARENT, defPixel);
m_datamanager = new KisDataManager(m_pixelSize, defPixel);
delete [] defPixel;
TQ_CHECK_PTR(m_datamanager);
m_extentIsValid = true;
m_parentLayer = 0;
m_colorSpace = colorSpace;
m_hasSelection = false;
m_selectionDeselected = false;
m_selection = 0;
}
KisPaintDevice::KisPaintDevice(KisLayer *parent, KisColorSpace * colorSpace, const char * name) :
TQObject(0, name), TDEShared(), m_exifInfo(0), m_lock( false )
{
m_longRunningFilterTimer = 0;
m_dcop = 0;
m_x = 0;
m_y = 0;
m_hasSelection = false;
m_selectionDeselected = false;
m_selection = 0;
m_parentLayer = parent;
if (colorSpace == 0 && parent != 0 && parent->image() != 0) {
m_colorSpace = parent->image()->colorSpace();
}
else {
m_colorSpace = colorSpace;
}
Q_ASSERT( m_colorSpace );
m_pixelSize = m_colorSpace->pixelSize();
m_nChannels = m_colorSpace->nChannels();
TQ_UINT8* defPixel = new TQ_UINT8[ m_pixelSize ];
m_colorSpace->fromTQColor(TQt::black, OPACITY_TRANSPARENT, defPixel);
m_datamanager = new KisDataManager(m_pixelSize, defPixel);
delete [] defPixel;
TQ_CHECK_PTR(m_datamanager);
m_extentIsValid = true;
if ( TQString ( name ) == TQString( "Layer 1" ) ) {
m_longRunningFilters = m_colorSpace->createBackgroundFilters();
if (!m_longRunningFilters.isEmpty()) {
m_longRunningFilterTimer = new TQTimer(this);
connect(m_longRunningFilterTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(runBackgroundFilters()));
m_longRunningFilterTimer->start(2000);
}
}
}
KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs) : TQObject(), TDEShared(rhs)
{
if (this != &rhs) {
m_longRunningFilterTimer = 0;
m_parentLayer = 0;
m_dcop = rhs.m_dcop;
if (rhs.m_datamanager) {
m_datamanager = new KisDataManager(*rhs.m_datamanager);
TQ_CHECK_PTR(m_datamanager);
}
else {
kdWarning() << "rhs " << rhs.name() << " has no datamanager\n";
}
m_extentIsValid = rhs.m_extentIsValid;
m_x = rhs.m_x;
m_y = rhs.m_y;
m_colorSpace = rhs.m_colorSpace;
m_hasSelection = rhs.m_hasSelection;
if ( m_hasSelection )
m_selection = new KisSelection(*rhs.m_selection);
else
m_selection = 0;
m_pixelSize = rhs.m_pixelSize;
m_nChannels = rhs.m_nChannels;
if(rhs.m_exifInfo)
{
m_exifInfo = new KisExifInfo(*rhs.m_exifInfo);
}
else {
m_exifInfo = 0;
}
}
}
KisPaintDevice::~KisPaintDevice()
{
delete m_dcop;
delete m_longRunningFilterTimer;
TQValueList<KisFilter*>::iterator it;
TQValueList<KisFilter*>::iterator end = m_longRunningFilters.end();
for (it = m_longRunningFilters.begin(); it != end; ++it) {
KisFilter * f = (*it);
delete f;
}
m_longRunningFilters.clear();
//delete m_exifInfo;
}
DCOPObject *KisPaintDevice::dcopObject()
{
if (!m_dcop) {
m_dcop = new KisPaintDeviceIface(this);
TQ_CHECK_PTR(m_dcop);
}
return m_dcop;
}
KisLayer *KisPaintDevice::parentLayer() const
{
return m_parentLayer;
}
void KisPaintDevice::setParentLayer(KisLayer *parentLayer)
{
m_parentLayer = parentLayer;
}
void KisPaintDevice::setDirty(const TQRect & rc)
{
if (m_parentLayer) m_parentLayer->setDirty(rc);
}
void KisPaintDevice::setDirty()
{
if (m_parentLayer) m_parentLayer->setDirty();
}
KisImage *KisPaintDevice::image() const
{
if (m_parentLayer) {
return m_parentLayer->image();
} else {
return 0;
}
}
void KisPaintDevice::move(TQ_INT32 x, TQ_INT32 y)
{
TQRect dirtyRect = extent();
m_x = x;
m_y = y;
dirtyRect |= extent();
if(m_selection)
{
m_selection->setX(x);
m_selection->setY(y);
}
setDirty(dirtyRect);
emit positionChanged(this);
}
void KisPaintDevice::move(const TQPoint& pt)
{
move(pt.x(), pt.y());
}
KNamedCommand * KisPaintDevice::moveCommand(TQ_INT32 x, TQ_INT32 y)
{
KNamedCommand * cmd = new MoveCommand(this, TQPoint(m_x, m_y), TQPoint(x, y));
TQ_CHECK_PTR(cmd);
cmd->execute();
return cmd;
}
void KisPaintDevice::extent(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const
{
m_datamanager->extent(x, y, w, h);
x += m_x;
y += m_y;
}
TQRect KisPaintDevice::extent() const
{
TQ_INT32 x, y, w, h;
extent(x, y, w, h);
return TQRect(x, y, w, h);
}
bool KisPaintDevice::extentIsValid() const
{
return m_extentIsValid;
}
void KisPaintDevice::setExtentIsValid(bool isValid)
{
m_extentIsValid = isValid;
}
void KisPaintDevice::exactBounds(TQ_INT32 &x, TQ_INT32 &y, TQ_INT32 &w, TQ_INT32 &h) const
{
TQRect r = exactBounds();
x = r.x();
y = r.y();
w = r.width();
h = r.height();
}
TQRect KisPaintDevice::exactBoundsOldMethod() const
{
TQ_INT32 x, y, w, h, boundX, boundY, boundW, boundH;
extent(x, y, w, h);
extent(boundX, boundY, boundW, boundH);
const TQ_UINT8* defaultPixel = m_datamanager->defaultPixel();
bool found = false;
for (TQ_INT32 y2 = y; y2 < y + h ; ++y2) {
KisHLineIterator it = const_cast<KisPaintDevice *>(this)->createHLineIterator(x, y2, w, false);
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundY = y2;
found = true;
break;
}
++it;
}
if (found) break;
}
found = false;
for (TQ_INT32 y2 = y + h; y2 > y ; --y2) {
KisHLineIterator it = const_cast<KisPaintDevice *>(this)->createHLineIterator(x, y2, w, false);
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundH = y2 - boundY + 1;
found = true;
break;
}
++it;
}
if (found) break;
}
found = false;
for (TQ_INT32 x2 = x; x2 < x + w ; ++x2) {
KisVLineIterator it = const_cast<KisPaintDevice *>(this)->createVLineIterator(x2, y, h, false);
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundX = x2;
found = true;
break;
}
++it;
}
if (found) break;
}
found = false;
// Look for right edge )
for (TQ_INT32 x2 = x + w; x2 > x ; --x2) {
KisVLineIterator it = const_cast<KisPaintDevice *>(this)->createVLineIterator(x2, y, h, false);
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundW = x2 - boundX + 1; // XXX: I commented this
// +1 out, but why? It
// should be correct, since
// we've found the first
// pixel that should be
// included, and it should
// be added to the width.
found = true;
break;
}
++it;
}
if (found) break;
}
return TQRect(boundX, boundY, boundW, boundH);
}
TQRect KisPaintDevice::exactBoundsImprovedOldMethod() const
{
// Solution n°2
TQ_INT32 x, y, w, h, boundX2, boundY2, boundW2, boundH2;
extent(x, y, w, h);
extent(boundX2, boundY2, boundW2, boundH2);
const TQ_UINT8* defaultPixel = m_datamanager->defaultPixel();
bool found = false;
{
KisHLineIterator it = const_cast<KisPaintDevice *>(this)->createHLineIterator(x, y, w, false);
for (TQ_INT32 y2 = y; y2 < y + h ; ++y2) {
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundY2 = y2;
found = true;
break;
}
++it;
}
if (found) break;
it.nextRow();
}
}
found = false;
for (TQ_INT32 y2 = y + h; y2 > y ; --y2) {
KisHLineIterator it = const_cast<KisPaintDevice *>(this)->createHLineIterator(x, y2, w, false);
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundH2 = y2 - boundY2 + 1;
found = true;
break;
}
++it;
}
if (found) break;
}
found = false;
{
KisVLineIterator it = const_cast<KisPaintDevice *>(this)->createVLineIterator(x, boundY2, boundH2, false);
for (TQ_INT32 x2 = x; x2 < x + w ; ++x2) {
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundX2 = x2;
found = true;
break;
}
++it;
}
if (found) break;
it.nextCol();
}
}
found = false;
// Look for right edge )
{
for (TQ_INT32 x2 = x + w; x2 > x ; --x2) {
KisVLineIterator it = const_cast<KisPaintDevice *>(this)->createVLineIterator(/*x + w*/ x2, boundY2, boundH2, false);
while (!it.isDone() && found == false) {
if (memcmp(it.rawData(), defaultPixel, m_pixelSize) != 0) {
boundW2 = x2 - boundX2 + 1; // XXX: I commented this
// +1 out, but why? It
// should be correct, since
// we've found the first
// pixel that should be
// included, and it should
// be added to the width.
found = true;
break;
}
++it;
}
if (found) break;
}
}
return TQRect(boundX2, boundY2, boundW2, boundH2);
}
TQRect KisPaintDevice::exactBounds() const
{
TQRect r2 = exactBoundsImprovedOldMethod();
return r2;
}
void KisPaintDevice::crop(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
{
m_datamanager->setExtent(x - m_x, y - m_y, w, h);
}
void KisPaintDevice::crop(TQRect r)
{
r.moveBy(-m_x, -m_y); m_datamanager->setExtent(r);
}
void KisPaintDevice::clear()
{
m_datamanager->clear();
}
void KisPaintDevice::fill(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, const TQ_UINT8 *fillPixel)
{
m_datamanager->clear(x, y, w, h, fillPixel);
}
void KisPaintDevice::mirrorX()
{
TQRect r;
if (hasSelection()) {
r = selection()->selectedRect();
}
else {
r = exactBounds();
}
for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) {
KisHLineIteratorPixel srcIt = createHLineIterator(r.x(), y, r.width(), false);
KisHLineIteratorPixel dstIt = createHLineIterator(r.x(), y, r.width(), true);
dstIt += r.width() - 1;
while (!srcIt.isDone()) {
if (srcIt.isSelected()) {
memcpy(dstIt.rawData(), srcIt.oldRawData(), m_pixelSize);
}
++srcIt;
--dstIt;
}
}
if (m_parentLayer) {
m_parentLayer->setDirty(r);
}
}
void KisPaintDevice::mirrorY()
{
/* Read a line from bottom to top and and from top to bottom and write their values to each other */
TQRect r;
if (hasSelection()) {
r = selection()->selectedRect();
}
else {
r = exactBounds();
}
TQ_INT32 y1, y2;
for (y1 = r.top(), y2 = r.bottom(); y1 <= r.bottom(); ++y1, --y2) {
KisHLineIteratorPixel itTop = createHLineIterator(r.x(), y1, r.width(), true);
KisHLineIteratorPixel itBottom = createHLineIterator(r.x(), y2, r.width(), false);
while (!itTop.isDone() && !itBottom.isDone()) {
if (itBottom.isSelected()) {
memcpy(itTop.rawData(), itBottom.oldRawData(), m_pixelSize);
}
++itBottom;
++itTop;
}
}
if (m_parentLayer) {
m_parentLayer->setDirty(r);
}
}
KisMementoSP KisPaintDevice::getMemento()
{
return m_datamanager->getMemento();
}
void KisPaintDevice::rollback(KisMementoSP memento) { m_datamanager->rollback(memento); }
void KisPaintDevice::rollforward(KisMementoSP memento) { m_datamanager->rollforward(memento); }
bool KisPaintDevice::write(KoStore *store)
{
bool retval = m_datamanager->write(store);
emit ioProgress(100);
return retval;
}
bool KisPaintDevice::read(KoStore *store)
{
bool retval = m_datamanager->read(store);
emit ioProgress(100);
return retval;
}
void KisPaintDevice::convertTo(KisColorSpace * dstColorSpace, TQ_INT32 renderingIntent)
{
kdDebug(41004) << "Converting " << name() << " to " << dstColorSpace->id().id() << " from "
<< m_colorSpace->id().id() << "\n";
if ( colorSpace() == dstColorSpace )
{
return;
}
KisPaintDevice dst(dstColorSpace);
dst.setX(getX());
dst.setY(getY());
TQ_INT32 x, y, w, h;
extent(x, y, w, h);
for (TQ_INT32 row = y; row < y + h; ++row) {
TQ_INT32 column = x;
TQ_INT32 columnsRemaining = w;
while (columnsRemaining > 0) {
TQ_INT32 numContiguousDstColumns = dst.numContiguousColumns(column, row, row);
TQ_INT32 numContiguousSrcColumns = numContiguousColumns(column, row, row);
TQ_INT32 columns = TQMIN(numContiguousDstColumns, numContiguousSrcColumns);
columns = TQMIN(columns, columnsRemaining);
//const TQ_UINT8 *srcData = pixel(column, row);
//TQ_UINT8 *dstData = dst.writablePixel(column, row);
KisHLineIteratorPixel srcIt = createHLineIterator(column, row, columns, false);
KisHLineIteratorPixel dstIt = dst.createHLineIterator(column, row, columns, true);
const TQ_UINT8 *srcData = srcIt.rawData();
TQ_UINT8 *dstData = dstIt.rawData();
m_colorSpace->convertPixelsTo(srcData, dstData, dstColorSpace, columns, renderingIntent);
column += columns;
columnsRemaining -= columns;
}
}
KisDataManagerSP oldData = m_datamanager;
KisColorSpace *oldColorSpace = m_colorSpace;
setData(dst.m_datamanager, dstColorSpace);
if (undoAdapter() && undoAdapter()->undo()) {
undoAdapter()->addCommand(new KisConvertLayerTypeCmd(undoAdapter(), this, oldData, oldColorSpace, m_datamanager, m_colorSpace));
}
}
void KisPaintDevice::setProfile(KisProfile * profile)
{
if (profile == 0) return;
KisColorSpace * dstSpace =
KisMetaRegistry::instance()->csRegistry()->getColorSpace( colorSpace()->id(),
profile);
if (dstSpace)
m_colorSpace = dstSpace;
}
void KisPaintDevice::setData(KisDataManagerSP data, KisColorSpace * colorSpace)
{
m_datamanager = data;
m_colorSpace = colorSpace;
m_pixelSize = m_colorSpace->pixelSize();
m_nChannels = m_colorSpace->nChannels();
if (m_parentLayer) {
m_parentLayer->setDirty(extent());
m_parentLayer->notifyPropertyChanged();
}
}
KisUndoAdapter *KisPaintDevice::undoAdapter() const
{
if (m_parentLayer && m_parentLayer->image()) {
return m_parentLayer->image()->undoAdapter();
}
return 0;
}
void KisPaintDevice::convertFromTQImage(const TQImage& image, const TQString &srcProfileName,
TQ_INT32 offsetX, TQ_INT32 offsetY)
{
TQImage img = image;
// Chalk is little-endian inside.
if (img.bitOrder() == TQImage::LittleEndian) {
img = img.convertBitOrder(TQImage::BigEndian);
}
kdDebug() << k_funcinfo << img.bitOrder()<< endl;
// Chalk likes bgra (convertDepth returns *this is the img is alread 32 bits)
img = img.convertDepth( 32 );
#if 0
// XXX: Apply import profile
if (colorSpace() == KisMetaRegistry::instance()->csRegistry() ->getColorSpace(KisID("RGBA",""),"")) {
writeBytes(img.bits(), 0, 0, img.width(), img.height());
}
else {
#endif
TQ_UINT8 * dstData = new TQ_UINT8[img.width() * img.height() * pixelSize()];
KisMetaRegistry::instance()->csRegistry()
->getColorSpace(KisID("RGBA",""),srcProfileName)->
convertPixelsTo(img.bits(), dstData, colorSpace(), img.width() * img.height());
writeBytes(dstData, offsetX, offsetY, img.width(), img.height());
// }
}
TQImage KisPaintDevice::convertToTQImage(KisProfile * dstProfile, float exposure)
{
TQ_INT32 x1;
TQ_INT32 y1;
TQ_INT32 w;
TQ_INT32 h;
x1 = - getX();
y1 = - getY();
if (image()) {
w = image()->width();
h = image()->height();
}
else {
extent(x1, y1, w, h);
}
return convertToTQImage(dstProfile, x1, y1, w, h, exposure);
}
// XXX: is this faster than building the TQImage ourselves? It makes
TQImage KisPaintDevice::convertToTQImage(KisProfile * dstProfile, TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 w, TQ_INT32 h, float exposure)
{
if (w < 0)
return TQImage();
if (h < 0)
return TQImage();
TQ_UINT8 * data = new TQ_UINT8 [w * h * m_pixelSize];
TQ_CHECK_PTR(data);
// XXX: Is this really faster than converting line by line and building the TQImage directly?
// This copies potentially a lot of data.
readBytes(data, x1, y1, w, h);
TQImage image = colorSpace()->convertToTQImage(data, w, h, dstProfile, INTENT_PERCEPTUAL, exposure);
delete[] data;
return image;
}
KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(TQ_INT32 w, TQ_INT32 h)
{
KisPaintDeviceSP thumbnail = new KisPaintDevice(colorSpace(), "thumbnail");
thumbnail->clear();
int srcw, srch;
if( image() )
{
srcw = image()->width();
srch = image()->height();
}
else
{
const TQRect e = exactBounds();
srcw = e.width();
srch = e.height();
}
if (w > srcw)
{
w = srcw;
h = TQ_INT32(double(srcw) / w * h);
}
if (h > srch)
{
h = srch;
w = TQ_INT32(double(srch) / h * w);
}
if (srcw > srch)
h = TQ_INT32(double(srch) / srcw * w);
else if (srch > srcw)
w = TQ_INT32(double(srcw) / srch * h);
for (TQ_INT32 y=0; y < h; ++y) {
TQ_INT32 iY = (y * srch ) / h;
for (TQ_INT32 x=0; x < w; ++x) {
TQ_INT32 iX = (x * srcw ) / w;
thumbnail->setPixel(x, y, colorAt(iX, iY));
}
}
return thumbnail;
}
TQImage KisPaintDevice::createThumbnail(TQ_INT32 w, TQ_INT32 h)
{
int srcw, srch;
if( image() )
{
srcw = image()->width();
srch = image()->height();
}
else
{
const TQRect e = extent();
srcw = e.width();
srch = e.height();
}
if (w > srcw)
{
w = srcw;
h = TQ_INT32(double(srcw) / w * h);
}
if (h > srch)
{
h = srch;
w = TQ_INT32(double(srch) / h * w);
}
if (srcw > srch)
h = TQ_INT32(double(srch) / srcw * w);
else if (srch > srcw)
w = TQ_INT32(double(srcw) / srch * h);
TQColor c;
TQ_UINT8 opacity;
TQImage img(w,h,32);
for (TQ_INT32 y=0; y < h; ++y) {
TQ_INT32 iY = (y * srch ) / h;
for (TQ_INT32 x=0; x < w; ++x) {
TQ_INT32 iX = (x * srcw ) / w;
pixel(iX, iY, &c, &opacity);
const TQRgb rgb = c.rgb();
img.setPixel(x, y, tqRgba(tqRed(rgb), tqGreen(rgb), tqBlue(rgb), opacity));
}
}
return img;
}
KisRectIteratorPixel KisPaintDevice::createRectIterator(TQ_INT32 left, TQ_INT32 top, TQ_INT32 w, TQ_INT32 h, bool writable)
{
if(hasSelection())
return KisRectIteratorPixel(this, m_datamanager, m_selection->m_datamanager, left, top, w, h, m_x, m_y, writable);
else
return KisRectIteratorPixel(this, m_datamanager, NULL, left, top, w, h, m_x, m_y, writable);
}
KisHLineIteratorPixel KisPaintDevice::createHLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable)
{
if(hasSelection())
return KisHLineIteratorPixel(this, m_datamanager, m_selection->m_datamanager, x, y, w, m_x, m_y, writable);
else
return KisHLineIteratorPixel(this, m_datamanager, NULL, x, y, w, m_x, m_y, writable);
}
KisVLineIteratorPixel KisPaintDevice::createVLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 h, bool writable)
{
if(hasSelection())
return KisVLineIteratorPixel(this, m_datamanager, m_selection->m_datamanager, x, y, h, m_x, m_y, writable);
else
return KisVLineIteratorPixel(this, m_datamanager, NULL, x, y, h, m_x, m_y, writable);
}
KisRandomAccessorPixel KisPaintDevice::createRandomAccessor(TQ_INT32 x, TQ_INT32 y, bool writable) {
if(hasSelection())
return KisRandomAccessorPixel(m_datamanager, m_selection->m_datamanager, x, y, m_x, m_y, writable);
else
return KisRandomAccessorPixel(m_datamanager, NULL, x, y, m_x, m_y, writable);
}
KisRandomSubAccessorPixel KisPaintDevice::createRandomSubAccessor()
{
return KisRandomSubAccessorPixel(this);
}
void KisPaintDevice::emitSelectionChanged()
{
if (m_parentLayer && m_parentLayer->image()) {
m_parentLayer->image()->slotSelectionChanged();
}
}
void KisPaintDevice::emitSelectionChanged(const TQRect& r)
{
if (m_parentLayer && m_parentLayer->image()) {
m_parentLayer->image()->slotSelectionChanged(r);
}
}
KisSelectionSP KisPaintDevice::selection()
{
if ( m_selectionDeselected && m_selection ) {
m_selectionDeselected = false;
}
else if (!m_selection) {
m_selection = new KisSelection(this);
TQ_CHECK_PTR(m_selection);
m_selection->setX(m_x);
m_selection->setY(m_y);
}
m_hasSelection = true;
return m_selection;
}
bool KisPaintDevice::hasSelection()
{
return m_hasSelection;
}
bool KisPaintDevice::selectionDeselected()
{
return m_selectionDeselected;
}
void KisPaintDevice::deselect()
{
if (m_selection && m_hasSelection) {
m_hasSelection = false;
m_selectionDeselected = true;
}
}
void KisPaintDevice::reselect()
{
m_hasSelection = true;
m_selectionDeselected = false;
}
void KisPaintDevice::addSelection(KisSelectionSP selection) {
KisPainter painter(this->selection().data());
TQRect r = selection->selectedExactRect();
painter.bitBlt(r.x(), r.y(), COMPOSITE_OVER, selection.data(), r.x(), r.y(), r.width(), r.height());
painter.end();
}
void KisPaintDevice::subtractSelection(KisSelectionSP selection) {
KisPainter painter(this->selection().data());
selection->invert();
TQRect r = selection->selectedExactRect();
painter.bitBlt(r.x(), r.y(), COMPOSITE_ERASE, selection.data(), r.x(), r.y(), r.width(), r.height());
selection->invert();
painter.end();
}
void KisPaintDevice::clearSelection()
{
if (!hasSelection()) return;
TQRect r = m_selection->selectedExactRect();
if (r.isValid()) {
for (TQ_INT32 y = 0; y < r.height(); y++) {
KisHLineIterator devIt = createHLineIterator(r.x(), r.y() + y, r.width(), true);
KisHLineIterator selectionIt = m_selection->createHLineIterator(r.x(), r.y() + y, r.width(), false);
while (!devIt.isDone()) {
// XXX: Optimize by using stretches
m_colorSpace->applyInverseAlphaU8Mask( devIt.rawData(), selectionIt.rawData(), 1);
++devIt;
++selectionIt;
}
}
if (m_parentLayer) {
m_parentLayer->setDirty(r);
}
}
}
void KisPaintDevice::applySelectionMask(KisSelectionSP mask)
{
TQRect r = mask->selectedRect();
crop(r);
for (TQ_INT32 y = r.top(); y <= r.bottom(); ++y) {
KisHLineIterator pixelIt = createHLineIterator(r.x(), y, r.width(), true);
KisHLineIterator maskIt = mask->createHLineIterator(r.x(), y, r.width(), false);
while (!pixelIt.isDone()) {
// XXX: Optimize by using stretches
m_colorSpace->applyAlphaU8Mask( pixelIt.rawData(), maskIt.rawData(), 1);
++pixelIt;
++maskIt;
}
}
}
KisSelectionSP KisPaintDevice::setSelection( KisSelectionSP selection)
{
if (selection) {
KisSelectionSP oldSelection = m_selection;
m_selection = selection;
m_hasSelection = true;
return oldSelection;
}
else return 0;
}
bool KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y, TQColor *c, TQ_UINT8 *opacity)
{
KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false);
TQ_UINT8 *pix = iter.rawData();
if (!pix) return false;
colorSpace()->toTQColor(pix, c, opacity);
return true;
}
bool KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y, KisColor * kc)
{
KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false);
TQ_UINT8 *pix = iter.rawData();
if (!pix) return false;
kc->setColor(pix, m_colorSpace);
return true;
}
KisColor KisPaintDevice::colorAt(TQ_INT32 x, TQ_INT32 y)
{
//return KisColor(m_datamanager->pixel(x - m_x, y - m_y), m_colorSpace);
KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true);
return KisColor(iter.rawData(), m_colorSpace);
}
bool KisPaintDevice::setPixel(TQ_INT32 x, TQ_INT32 y, const TQColor& c, TQ_UINT8 opacity)
{
KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true);
colorSpace()->fromTQColor(c, opacity, iter.rawData());
return true;
}
bool KisPaintDevice::setPixel(TQ_INT32 x, TQ_INT32 y, const KisColor& kc)
{
TQ_UINT8 * pix;
if (kc.colorSpace() != m_colorSpace) {
KisColor kc2 (kc, m_colorSpace);
pix = kc2.data();
}
else {
pix = kc.data();
}
KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true);
memcpy(iter.rawData(), pix, m_colorSpace->pixelSize());
return true;
}
TQ_INT32 KisPaintDevice::numContiguousColumns(TQ_INT32 x, TQ_INT32 minY, TQ_INT32 maxY)
{
return m_datamanager->numContiguousColumns(x - m_x, minY - m_y, maxY - m_y);
}
TQ_INT32 KisPaintDevice::numContiguousRows(TQ_INT32 y, TQ_INT32 minX, TQ_INT32 maxX)
{
return m_datamanager->numContiguousRows(y - m_y, minX - m_x, maxX - m_x);
}
TQ_INT32 KisPaintDevice::rowStride(TQ_INT32 x, TQ_INT32 y)
{
return m_datamanager->rowStride(x - m_x, y - m_y);
}
const TQ_UINT8* KisPaintDevice::pixel(TQ_INT32 x, TQ_INT32 y)
{
return m_datamanager->pixel(x - m_x, y - m_y);
}
TQ_UINT8* KisPaintDevice::writablePixel(TQ_INT32 x, TQ_INT32 y)
{
return m_datamanager->writablePixel(x - m_x, y - m_y);
}
void KisPaintDevice::setX(TQ_INT32 x)
{
m_x = x;
if(m_selection && m_selection != this)
m_selection->setX(x);
}
void KisPaintDevice::setY(TQ_INT32 y)
{
m_y = y;
if(m_selection && m_selection != this)
m_selection->setY(y);
}
void KisPaintDevice::readBytes(TQ_UINT8 * data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
{
m_datamanager->readBytes(data, x - m_x, y - m_y, w, h);
}
void KisPaintDevice::writeBytes(const TQ_UINT8 * data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
{
m_datamanager->writeBytes( data, x - m_x, y - m_y, w, h);
}
KisDataManagerSP KisPaintDevice::dataManager() const
{
return m_datamanager;
}
KisExifInfo* KisPaintDevice::exifInfo()
{
if(!m_exifInfo)
m_exifInfo = new KisExifInfo();
return m_exifInfo;
}
void KisPaintDevice::runBackgroundFilters()
{
if ( m_lock ) return;
KisTransaction * cmd = new KisTransaction("Running autofilters", this);
TQRect rc = extent();
if (!m_longRunningFilters.isEmpty()) {
TQValueList<KisFilter*>::iterator it;
TQValueList<KisFilter*>::iterator end = m_longRunningFilters.end();
for (it = m_longRunningFilters.begin(); it != end; ++it) {
(*it)->process(this, this, 0, rc);
}
}
if (cmd && undoAdapter()) undoAdapter()->addCommand(cmd);
if (m_parentLayer) m_parentLayer->setDirty(rc);
}
#include "kis_paint_device.moc"