summaryrefslogtreecommitdiffstats
path: root/chalk/core/kis_paint_device.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/core/kis_paint_device.cpp')
-rw-r--r--chalk/core/kis_paint_device.cpp1285
1 files changed, 1285 insertions, 0 deletions
diff --git a/chalk/core/kis_paint_device.cpp b/chalk/core/kis_paint_device.cpp
new file mode 100644
index 000000000..da25caecb
--- /dev/null
+++ b/chalk/core/kis_paint_device.cpp
@@ -0,0 +1,1285 @@
+/*
+ * 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 <tdelocale.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"