diff options
Diffstat (limited to 'chalk/core/kis_paint_device.cpp')
-rw-r--r-- | chalk/core/kis_paint_device.cpp | 1285 |
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 00000000..da25caec --- /dev/null +++ b/chalk/core/kis_paint_device.cpp @@ -0,0 +1,1285 @@ +/* + * Copyright (c) 2002 Patrick Julien <[email protected]> + * Copyright (c) 2004 Boudewijn Rempt <[email protected]> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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" |