/* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 Boudewijn Rempt * * 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 #include #include #include #include #include #include #include #include #include #include #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 QString& name, KisPaintDeviceSP paintDevice); virtual ~KisPaintDeviceCommand() {} virtual void execute() = 0; virtual void unexecute() = 0; protected: void setUndo(bool undo); KisPaintDeviceSP m_paintDevice; }; KisPaintDeviceCommand::KisPaintDeviceCommand(const QString& 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 QPoint& oldpos, const QPoint& newpos); virtual ~MoveCommand(); virtual void execute(); virtual void unexecute(); private: void moveTo(const QPoint& pos); void undoOff(); void undoOn(); private: KisPaintDeviceSP m_device; QPoint m_oldPos; QPoint m_newPos; }; MoveCommand::MoveCommand(KisPaintDeviceSP device, const QPoint& oldpos, const QPoint& 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 QPoint& 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) : QObject(0, name), KShared(), 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(); Q_UINT8* defPixel = new Q_UINT8 [ m_pixelSize ]; colorSpace->fromQColor(Qt::black, OPACITY_TRANSPARENT, defPixel); m_datamanager = new KisDataManager(m_pixelSize, defPixel); delete [] defPixel; Q_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) : QObject(0, name), KShared(), 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(); Q_UINT8* defPixel = new Q_UINT8[ m_pixelSize ]; m_colorSpace->fromQColor(Qt::black, OPACITY_TRANSPARENT, defPixel); m_datamanager = new KisDataManager(m_pixelSize, defPixel); delete [] defPixel; Q_CHECK_PTR(m_datamanager); m_extentIsValid = true; if ( QString ( name ) == QString( "Layer 1" ) ) { m_longRunningFilters = m_colorSpace->createBackgroundFilters(); if (!m_longRunningFilters.isEmpty()) { m_longRunningFilterTimer = new QTimer(this); connect(m_longRunningFilterTimer, SIGNAL(timeout()), this, SLOT(runBackgroundFilters())); m_longRunningFilterTimer->start(2000); } } } KisPaintDevice::KisPaintDevice(const KisPaintDevice& rhs) : QObject(), KShared(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); Q_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; QValueList::iterator it; QValueList::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); Q_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 QRect & 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(Q_INT32 x, Q_INT32 y) { QRect 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 QPoint& pt) { move(pt.x(), pt.y()); } KNamedCommand * KisPaintDevice::moveCommand(Q_INT32 x, Q_INT32 y) { KNamedCommand * cmd = new MoveCommand(this, QPoint(m_x, m_y), QPoint(x, y)); Q_CHECK_PTR(cmd); cmd->execute(); return cmd; } void KisPaintDevice::extent(Q_INT32 &x, Q_INT32 &y, Q_INT32 &w, Q_INT32 &h) const { m_datamanager->extent(x, y, w, h); x += m_x; y += m_y; } QRect KisPaintDevice::extent() const { Q_INT32 x, y, w, h; extent(x, y, w, h); return QRect(x, y, w, h); } bool KisPaintDevice::extentIsValid() const { return m_extentIsValid; } void KisPaintDevice::setExtentIsValid(bool isValid) { m_extentIsValid = isValid; } void KisPaintDevice::exactBounds(Q_INT32 &x, Q_INT32 &y, Q_INT32 &w, Q_INT32 &h) const { QRect r = exactBounds(); x = r.x(); y = r.y(); w = r.width(); h = r.height(); } QRect KisPaintDevice::exactBoundsOldMethod() const { Q_INT32 x, y, w, h, boundX, boundY, boundW, boundH; extent(x, y, w, h); extent(boundX, boundY, boundW, boundH); const Q_UINT8* defaultPixel = m_datamanager->defaultPixel(); bool found = false; for (Q_INT32 y2 = y; y2 < y + h ; ++y2) { KisHLineIterator it = const_cast(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 (Q_INT32 y2 = y + h; y2 > y ; --y2) { KisHLineIterator it = const_cast(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 (Q_INT32 x2 = x; x2 < x + w ; ++x2) { KisVLineIterator it = const_cast(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 (Q_INT32 x2 = x + w; x2 > x ; --x2) { KisVLineIterator it = const_cast(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 QRect(boundX, boundY, boundW, boundH); } QRect KisPaintDevice::exactBoundsImprovedOldMethod() const { // Solution n°2 Q_INT32 x, y, w, h, boundX2, boundY2, boundW2, boundH2; extent(x, y, w, h); extent(boundX2, boundY2, boundW2, boundH2); const Q_UINT8* defaultPixel = m_datamanager->defaultPixel(); bool found = false; { KisHLineIterator it = const_cast(this)->createHLineIterator(x, y, w, false); for (Q_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 (Q_INT32 y2 = y + h; y2 > y ; --y2) { KisHLineIterator it = const_cast(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(this)->createVLineIterator(x, boundY2, boundH2, false); for (Q_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 (Q_INT32 x2 = x + w; x2 > x ; --x2) { KisVLineIterator it = const_cast(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 QRect(boundX2, boundY2, boundW2, boundH2); } QRect KisPaintDevice::exactBounds() const { QRect r2 = exactBoundsImprovedOldMethod(); return r2; } void KisPaintDevice::crop(Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h) { m_datamanager->setExtent(x - m_x, y - m_y, w, h); } void KisPaintDevice::crop(QRect r) { r.moveBy(-m_x, -m_y); m_datamanager->setExtent(r); } void KisPaintDevice::clear() { m_datamanager->clear(); } void KisPaintDevice::fill(Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h, const Q_UINT8 *fillPixel) { m_datamanager->clear(x, y, w, h, fillPixel); } void KisPaintDevice::mirrorX() { QRect r; if (hasSelection()) { r = selection()->selectedRect(); } else { r = exactBounds(); } for (Q_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 */ QRect r; if (hasSelection()) { r = selection()->selectedRect(); } else { r = exactBounds(); } Q_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, Q_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()); Q_INT32 x, y, w, h; extent(x, y, w, h); for (Q_INT32 row = y; row < y + h; ++row) { Q_INT32 column = x; Q_INT32 columnsRemaining = w; while (columnsRemaining > 0) { Q_INT32 numContiguousDstColumns = dst.numContiguousColumns(column, row, row); Q_INT32 numContiguousSrcColumns = numContiguousColumns(column, row, row); Q_INT32 columns = QMIN(numContiguousDstColumns, numContiguousSrcColumns); columns = QMIN(columns, columnsRemaining); //const Q_UINT8 *srcData = pixel(column, row); //Q_UINT8 *dstData = dst.writablePixel(column, row); KisHLineIteratorPixel srcIt = createHLineIterator(column, row, columns, false); KisHLineIteratorPixel dstIt = dst.createHLineIterator(column, row, columns, true); const Q_UINT8 *srcData = srcIt.rawData(); Q_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::convertFromQImage(const QImage& image, const QString &srcProfileName, Q_INT32 offsetX, Q_INT32 offsetY) { QImage img = image; // Krita is little-endian inside. if (img.bitOrder() == QImage::LittleEndian) { img = img.convertBitOrder(QImage::BigEndian); } kdDebug() << k_funcinfo << img.bitOrder()<< endl; // Krita 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 Q_UINT8 * dstData = new Q_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()); // } } QImage KisPaintDevice::convertToQImage(KisProfile * dstProfile, float exposure) { Q_INT32 x1; Q_INT32 y1; Q_INT32 w; Q_INT32 h; x1 = - getX(); y1 = - getY(); if (image()) { w = image()->width(); h = image()->height(); } else { extent(x1, y1, w, h); } return convertToQImage(dstProfile, x1, y1, w, h, exposure); } // XXX: is this faster than building the QImage ourselves? It makes QImage KisPaintDevice::convertToQImage(KisProfile * dstProfile, Q_INT32 x1, Q_INT32 y1, Q_INT32 w, Q_INT32 h, float exposure) { if (w < 0) return QImage(); if (h < 0) return QImage(); Q_UINT8 * data = new Q_UINT8 [w * h * m_pixelSize]; Q_CHECK_PTR(data); // XXX: Is this really faster than converting line by line and building the QImage directly? // This copies potentially a lot of data. readBytes(data, x1, y1, w, h); QImage image = colorSpace()->convertToQImage(data, w, h, dstProfile, INTENT_PERCEPTUAL, exposure); delete[] data; return image; } KisPaintDeviceSP KisPaintDevice::createThumbnailDevice(Q_INT32 w, Q_INT32 h) { KisPaintDeviceSP thumbnail = new KisPaintDevice(colorSpace(), "thumbnail"); thumbnail->clear(); int srcw, srch; if( image() ) { srcw = image()->width(); srch = image()->height(); } else { const QRect e = exactBounds(); srcw = e.width(); srch = e.height(); } if (w > srcw) { w = srcw; h = Q_INT32(double(srcw) / w * h); } if (h > srch) { h = srch; w = Q_INT32(double(srch) / h * w); } if (srcw > srch) h = Q_INT32(double(srch) / srcw * w); else if (srch > srcw) w = Q_INT32(double(srcw) / srch * h); for (Q_INT32 y=0; y < h; ++y) { Q_INT32 iY = (y * srch ) / h; for (Q_INT32 x=0; x < w; ++x) { Q_INT32 iX = (x * srcw ) / w; thumbnail->setPixel(x, y, colorAt(iX, iY)); } } return thumbnail; } QImage KisPaintDevice::createThumbnail(Q_INT32 w, Q_INT32 h) { int srcw, srch; if( image() ) { srcw = image()->width(); srch = image()->height(); } else { const QRect e = extent(); srcw = e.width(); srch = e.height(); } if (w > srcw) { w = srcw; h = Q_INT32(double(srcw) / w * h); } if (h > srch) { h = srch; w = Q_INT32(double(srch) / h * w); } if (srcw > srch) h = Q_INT32(double(srch) / srcw * w); else if (srch > srcw) w = Q_INT32(double(srcw) / srch * h); QColor c; Q_UINT8 opacity; QImage img(w,h,32); for (Q_INT32 y=0; y < h; ++y) { Q_INT32 iY = (y * srch ) / h; for (Q_INT32 x=0; x < w; ++x) { Q_INT32 iX = (x * srcw ) / w; pixel(iX, iY, &c, &opacity); const QRgb rgb = c.rgb(); img.setPixel(x, y, qRgba(qRed(rgb), qGreen(rgb), qBlue(rgb), opacity)); } } return img; } KisRectIteratorPixel KisPaintDevice::createRectIterator(Q_INT32 left, Q_INT32 top, Q_INT32 w, Q_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(Q_INT32 x, Q_INT32 y, Q_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(Q_INT32 x, Q_INT32 y, Q_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(Q_INT32 x, Q_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 QRect& 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); Q_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()); QRect 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(); QRect 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; QRect r = m_selection->selectedExactRect(); if (r.isValid()) { for (Q_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) { QRect r = mask->selectedRect(); crop(r); for (Q_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(Q_INT32 x, Q_INT32 y, QColor *c, Q_UINT8 *opacity) { KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); Q_UINT8 *pix = iter.rawData(); if (!pix) return false; colorSpace()->toQColor(pix, c, opacity); return true; } bool KisPaintDevice::pixel(Q_INT32 x, Q_INT32 y, KisColor * kc) { KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false); Q_UINT8 *pix = iter.rawData(); if (!pix) return false; kc->setColor(pix, m_colorSpace); return true; } KisColor KisPaintDevice::colorAt(Q_INT32 x, Q_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(Q_INT32 x, Q_INT32 y, const QColor& c, Q_UINT8 opacity) { KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true); colorSpace()->fromQColor(c, opacity, iter.rawData()); return true; } bool KisPaintDevice::setPixel(Q_INT32 x, Q_INT32 y, const KisColor& kc) { Q_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; } Q_INT32 KisPaintDevice::numContiguousColumns(Q_INT32 x, Q_INT32 minY, Q_INT32 maxY) { return m_datamanager->numContiguousColumns(x - m_x, minY - m_y, maxY - m_y); } Q_INT32 KisPaintDevice::numContiguousRows(Q_INT32 y, Q_INT32 minX, Q_INT32 maxX) { return m_datamanager->numContiguousRows(y - m_y, minX - m_x, maxX - m_x); } Q_INT32 KisPaintDevice::rowStride(Q_INT32 x, Q_INT32 y) { return m_datamanager->rowStride(x - m_x, y - m_y); } const Q_UINT8* KisPaintDevice::pixel(Q_INT32 x, Q_INT32 y) { return m_datamanager->pixel(x - m_x, y - m_y); } Q_UINT8* KisPaintDevice::writablePixel(Q_INT32 x, Q_INT32 y) { return m_datamanager->writablePixel(x - m_x, y - m_y); } void KisPaintDevice::setX(Q_INT32 x) { m_x = x; if(m_selection && m_selection != this) m_selection->setX(x); } void KisPaintDevice::setY(Q_INT32 y) { m_y = y; if(m_selection && m_selection != this) m_selection->setY(y); } void KisPaintDevice::readBytes(Q_UINT8 * data, Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h) { m_datamanager->readBytes(data, x - m_x, y - m_y, w, h); } void KisPaintDevice::writeBytes(const Q_UINT8 * data, Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_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); QRect rc = extent(); if (!m_longRunningFilters.isEmpty()) { QValueList::iterator it; QValueList::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"