/* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2007 Benjamin Schleimer * * 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 LCMS_HEADER #include #include #include #include #include #include #include #include #include #include #include "kis_image_iface.h" #include "kis_annotation.h" #include "kis_colorspace_factory_registry.h" #include "kis_color.h" #include "kis_command.h" #include "kis_types.h" //#include "kis_guide.h" #include "kis_image.h" #include "kis_paint_device.h" #include "kis_paint_device_action.h" #include "kis_selection.h" #include "kis_painter.h" #include "kis_fill_painter.h" #include "kis_layer.h" #include "kis_group_layer.h" #include "kis_adjustment_layer.h" #include "kis_paint_layer.h" #include "kis_colorspace_convert_visitor.h" #include "kis_background.h" #include "kis_substrate.h" #include "kis_scale_visitor.h" #include "kis_nameserver.h" #include "kis_undo_adapter.h" #include "kis_merge_visitor.h" #include "kis_transaction.h" #include "kis_crop_visitor.h" #include "kis_transform_visitor.h" #include "kis_filter_strategy.h" #include "kis_profile.h" #include "kis_paint_layer.h" #include "kis_perspective_grid.h" #include "kis_change_profile_visitor.h" #include "kis_group_layer.h" #include "kis_iterators_pixel.h" #include "kis_shear_visitor.h" class KisImage::KisImagePrivate { public: KisColor backgroundColor; TQ_UINT32 lockCount; bool sizeChangedWhileLocked; bool selectionChangedWhileLocked; KisSubstrateSP substrate; KisPerspectiveGrid* perspectiveGrid; }; namespace { class KisResizeImageCmd : public KNamedCommand { typedef KNamedCommand super; public: KisResizeImageCmd(KisUndoAdapter *adapter, KisImageSP img, TQ_INT32 width, TQ_INT32 height, TQ_INT32 oldWidth, TQ_INT32 oldHeight) : super(i18n("Resize Image")) { m_adapter = adapter; m_img = img; m_before = TQSize(oldWidth, oldHeight); m_after = TQSize(width, height); } virtual ~KisResizeImageCmd() { } public: virtual void execute() { m_adapter->setUndo(false); m_img->resize(m_after.width(), m_after.height()); m_adapter->setUndo(true); } virtual void unexecute() { m_adapter->setUndo(false); m_img->resize(m_before.width(), m_before.height()); m_adapter->setUndo(true); } private: KisUndoAdapter *m_adapter; KisImageSP m_img; TQSize m_before; TQSize m_after; }; // ------------------------------------------------------- class KisChangeLayersCmd : public KNamedCommand { typedef KNamedCommand super; public: KisChangeLayersCmd(KisUndoAdapter *adapter, KisImageSP img, KisGroupLayerSP oldRootLayer, KisGroupLayerSP newRootLayer, const TQString& name) : super(name) { m_adapter = adapter; m_img = img; m_oldRootLayer = oldRootLayer; m_newRootLayer = newRootLayer; } virtual ~KisChangeLayersCmd() { } public: virtual void execute() { m_adapter->setUndo(false); m_img->setRootLayer(m_newRootLayer); m_adapter->setUndo(true); m_img->notifyLayersChanged(); } virtual void unexecute() { m_adapter->setUndo(false); m_img->setRootLayer(m_oldRootLayer); m_adapter->setUndo(true); m_img->notifyLayersChanged(); } private: KisUndoAdapter *m_adapter; KisImageSP m_img; KisGroupLayerSP m_oldRootLayer; KisGroupLayerSP m_newRootLayer; }; // ------------------------------------------------------- class KisConvertImageTypeCmd : public KNamedCommand { typedef KNamedCommand super; public: KisConvertImageTypeCmd(KisUndoAdapter *adapter, KisImageSP img, KisColorSpace * beforeColorSpace, KisColorSpace * afterColorSpace ) : super(i18n("Convert Image Type")) { m_adapter = adapter; m_img = img; m_beforeColorSpace = beforeColorSpace; m_afterColorSpace = afterColorSpace; } virtual ~KisConvertImageTypeCmd() { } public: virtual void execute() { m_adapter->setUndo(false); m_img->setColorSpace(m_afterColorSpace); m_img->setProfile(m_afterColorSpace->getProfile()); m_adapter->setUndo(true); } virtual void unexecute() { m_adapter->setUndo(false); m_img->setColorSpace(m_beforeColorSpace); m_img->setProfile(m_beforeColorSpace->getProfile()); m_adapter->setUndo(true); } private: KisUndoAdapter *m_adapter; KisImageSP m_img; KisColorSpace * m_beforeColorSpace; KisColorSpace * m_afterColorSpace; }; // ------------------------------------------------------- class KisImageCommand : public KNamedCommand { typedef KNamedCommand super; public: KisImageCommand(const TQString& name, KisImageSP image); virtual ~KisImageCommand() {} virtual void execute() = 0; virtual void unexecute() = 0; protected: void setUndo(bool undo); KisImageSP m_image; }; KisImageCommand::KisImageCommand(const TQString& name, KisImageSP image) : super(name), m_image(image) { } void KisImageCommand::setUndo(bool undo) { if (m_image->undoAdapter()) { m_image->undoAdapter()->setUndo(undo); } } // ------------------------------------------------------- class KisLayerPositionCommand : public KisImageCommand { typedef KisImageCommand super; public: KisLayerPositionCommand(const TQString& name, KisImageSP image, KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) : super(name, image) { m_layer = layer; m_oldParent = layer->parent(); m_oldAboveThis = layer->nextSibling(); m_newParent = parent; m_newAboveThis = aboveThis; } virtual void execute() { setUndo(false); m_image->moveLayer(m_layer, m_newParent, m_newAboveThis); setUndo(true); } virtual void unexecute() { setUndo(false); m_image->moveLayer(m_layer, m_oldParent, m_oldAboveThis); setUndo(true); } private: KisLayerSP m_layer; KisGroupLayerSP m_oldParent; KisLayerSP m_oldAboveThis; KisGroupLayerSP m_newParent; KisLayerSP m_newAboveThis; }; // ------------------------------------------------------- class LayerAddCmd : public KisCommand { typedef KisCommand super; public: LayerAddCmd(KisUndoAdapter *adapter, KisImageSP img, KisLayerSP layer) : super(i18n("Add Layer"), adapter) { m_img = img; m_layer = layer; m_parent = layer->parent(); m_aboveThis = layer->nextSibling(); } virtual ~LayerAddCmd() { } virtual void execute() { adapter()->setUndo(false); m_img->addLayer(m_layer, m_parent.data(), m_aboveThis); adapter()->setUndo(true); } virtual void unexecute() { adapter()->setUndo(false); m_img->removeLayer(m_layer); adapter()->setUndo(true); } private: KisImageSP m_img; KisLayerSP m_layer; KisGroupLayerSP m_parent; KisLayerSP m_aboveThis; }; // ------------------------------------------------------- class LayerRmCmd : public KNamedCommand { typedef KNamedCommand super; public: LayerRmCmd(KisUndoAdapter *adapter, KisImageSP img, KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP wasAbove) : super(i18n("Remove Layer")) { m_adapter = adapter; m_img = img; m_layer = layer; m_prevParent = wasParent; m_prevAbove = wasAbove; } virtual ~LayerRmCmd() { } virtual void execute() { m_adapter->setUndo(false); m_img->removeLayer(m_layer); m_adapter->setUndo(true); } virtual void unexecute() { m_adapter->setUndo(false); m_img->addLayer(m_layer, m_prevParent.data(), m_prevAbove); m_adapter->setUndo(true); } private: KisUndoAdapter *m_adapter; KisImageSP m_img; KisLayerSP m_layer; KisGroupLayerSP m_prevParent; KisLayerSP m_prevAbove; }; class LayerMoveCmd: public KNamedCommand { typedef KNamedCommand super; public: LayerMoveCmd(KisUndoAdapter *adapter, KisImageSP img, KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP wasAbove) : super(i18n("Move Layer")) { m_adapter = adapter; m_img = img; m_layer = layer; m_prevParent = wasParent; m_prevAbove = wasAbove; m_newParent = layer->parent(); m_newAbove = layer->nextSibling(); } virtual ~LayerMoveCmd() { } virtual void execute() { m_adapter->setUndo(false); m_img->moveLayer(m_layer, m_newParent.data(), m_newAbove); m_adapter->setUndo(true); } virtual void unexecute() { m_adapter->setUndo(false); m_img->moveLayer(m_layer, m_prevParent.data(), m_prevAbove); m_adapter->setUndo(true); } private: KisUndoAdapter *m_adapter; KisImageSP m_img; KisLayerSP m_layer; KisGroupLayerSP m_prevParent; KisLayerSP m_prevAbove; KisGroupLayerSP m_newParent; KisLayerSP m_newAbove; }; // ------------------------------------------------------- class LayerPropsCmd : public KNamedCommand { typedef KNamedCommand super; public: LayerPropsCmd(KisLayerSP layer, KisImageSP img, KisUndoAdapter *adapter, const TQString& name, TQ_INT32 opacity, const KisCompositeOp& compositeOp) : super(i18n("Layer Property Changes")) { m_layer = layer; m_img = img; m_adapter = adapter; m_name = name; m_opacity = opacity; m_compositeOp = compositeOp; } virtual ~LayerPropsCmd() { } public: virtual void execute() { TQString name = m_layer->name(); TQ_INT32 opacity = m_layer->opacity(); KisCompositeOp compositeOp = m_layer->compositeOp(); m_adapter->setUndo(false); m_img->setLayerProperties(m_layer, m_opacity, m_compositeOp, m_name); m_adapter->setUndo(true); m_name = name; m_opacity = opacity; m_compositeOp = compositeOp; m_layer->setDirty(); } virtual void unexecute() { execute(); } private: KisUndoAdapter *m_adapter; KisLayerSP m_layer; KisImageSP m_img; TQString m_name; TQ_INT32 m_opacity; KisCompositeOp m_compositeOp; }; // ------------------------------------------------------- class LockImageCommand : public KNamedCommand { typedef KNamedCommand super; public: LockImageCommand(KisImageSP img, bool lockImage) : super("lock image") // Not for translation, this { // is only ever used inside a macro command. m_img = img; m_lockImage = lockImage; } virtual ~LockImageCommand() { } virtual void execute() { if (m_lockImage) { m_img->lock(); } else { m_img->unlock(); } } virtual void unexecute() { if (m_lockImage) { m_img->unlock(); } else { m_img->lock(); } } private: KisImageSP m_img; bool m_lockImage; }; } KisImage::KisImage(KisUndoAdapter *adapter, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorSpace, const TQString& name) : TQObject(0, name.latin1()), TDEShared() { init(adapter, width, height, colorSpace, name); setName(name); m_dcop = 0L; } KisImage::KisImage(const KisImage& rhs) : TQObject(), TDEShared(rhs) { m_dcop = 0L; if (this != &rhs) { m_private = new KisImagePrivate(*rhs.m_private); m_private->perspectiveGrid = new KisPerspectiveGrid(*rhs.m_private->perspectiveGrid); m_uri = rhs.m_uri; m_name = TQString(); m_width = rhs.m_width; m_height = rhs.m_height; m_xres = rhs.m_xres; m_yres = rhs.m_yres; m_unit = rhs.m_unit; m_colorSpace = rhs.m_colorSpace; m_dirty = rhs.m_dirty; m_adapter = rhs.m_adapter; m_bkg = new KisBackground(); TQ_CHECK_PTR(m_bkg); m_rootLayer = static_cast(rhs.m_rootLayer->clone().data()); connect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); m_annotations = rhs.m_annotations; // XXX the annotations would probably need to be deep-copied m_nserver = new KisNameServer(i18n("Layer %1"), rhs.m_nserver->currentSeed() + 1); TQ_CHECK_PTR(m_nserver); //m_guides = rhs.m_guides; // Set this as the current image for the layers m_rootLayer->setImage(this); // Set the active paint layer if(rhs.activeLayer() != NULL) { TQString layerName = rhs.activeLayer()->name(); // kdDebug(12345) << "KisImage::KisImage: active layer = " << layerName << "\n"; KisLayerSP activeLayer = rootLayer()->findLayer(layerName); Q_ASSERT(activeLayer); activate(activeLayer); } else { activate(NULL); } } } DCOPObject * KisImage::dcopObject() { if (!m_dcop) { m_dcop = new KisImageIface(this); TQ_CHECK_PTR(m_dcop); } return m_dcop; } KisImage::~KisImage() { delete m_private->perspectiveGrid; delete m_private; delete m_nserver; delete m_dcop; } TQString KisImage::name() const { return m_name; } void KisImage::setName(const TQString& name) { if (!name.isEmpty()) m_name = name; } TQString KisImage::description() const { return m_description; } void KisImage::setDescription(const TQString& description) { if (!description.isEmpty()) m_description = description; } KisColor KisImage::backgroundColor() const { return m_private->backgroundColor; } void KisImage::setBackgroundColor(const KisColor & color) { m_private->backgroundColor = color; } TQString KisImage::nextLayerName() const { if (m_nserver->currentSeed() == 0) { m_nserver->number(); return i18n("background"); } return m_nserver->name(); } void KisImage::rollBackLayerName() { m_nserver->rollback(); } void KisImage::init(KisUndoAdapter *adapter, TQ_INT32 width, TQ_INT32 height, KisColorSpace * colorSpace, const TQString& name) { Q_ASSERT(colorSpace); if (colorSpace == 0) { colorSpace = KisMetaRegistry::instance()->csRegistry()->getRGB8(); kdWarning(41010) << "No colorspace specified: using RGBA\n"; } m_private = new KisImagePrivate(); m_private->backgroundColor = KisColor(TQt::white, colorSpace); m_private->lockCount = 0; m_private->sizeChangedWhileLocked = false; m_private->selectionChangedWhileLocked = false; m_private->substrate = 0; m_private->perspectiveGrid = new KisPerspectiveGrid(); m_adapter = adapter; m_nserver = new KisNameServer(i18n("Layer %1"), 1); m_name = name; m_colorSpace = colorSpace; m_bkg = new KisBackground(); m_rootLayer = new KisGroupLayer(this,"root", OPACITY_OPAQUE); connect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); m_xres = 1.0; m_yres = 1.0; m_unit = KoUnit::U_PT; m_dirty = false; m_width = width; m_height = height; } bool KisImage::locked() const { return m_private->lockCount != 0; } void KisImage::lock() { if (!locked()) { if (m_rootLayer) disconnect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); m_private->sizeChangedWhileLocked = false; m_private->selectionChangedWhileLocked = false; } m_private->lockCount++; } void KisImage::unlock() { Q_ASSERT(locked()); if (locked()) { m_private->lockCount--; if (m_private->lockCount == 0) { if (m_private->sizeChangedWhileLocked) { // A size change implies a full image update so only send this. emit sigSizeChanged(m_width, m_height); } else { if (m_rootLayer->dirty()) emit sigImageUpdated( m_rootLayer->dirtyRect() ); } if (m_private->selectionChangedWhileLocked) { emit sigActiveSelectionChanged(this); } if (m_rootLayer) connect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); } } } void KisImage::emitSizeChanged() { if (!locked()) { emit sigSizeChanged(m_width, m_height); } else { m_private->sizeChangedWhileLocked = true; } } void KisImage::notifyLayerUpdated(KisLayerSP layer, TQRect rc) { emit sigLayerUpdated(layer, rc); } void KisImage::resize(TQ_INT32 w, TQ_INT32 h, TQ_INT32 x, TQ_INT32 y, bool cropLayers) { if (w != width() || h != height()) { lock(); if (undo()) { if (cropLayers) m_adapter->beginMacro(i18n("Crop Image")); else m_adapter->beginMacro(i18n("Resize Image")); m_adapter->addCommand(new LockImageCommand(this, true)); m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); } m_width = w; m_height = h; if (cropLayers) { KisCropVisitor v(TQRect(x, y, w, h)); m_rootLayer->accept(v); } emitSizeChanged(); unlock(); if (undo()) { m_adapter->addCommand(new LockImageCommand(this, false)); m_adapter->endMacro(); } } } void KisImage::resize(const TQRect& rc, bool cropLayers) { resize(rc.width(), rc.height(), rc.x(), rc.y(), cropLayers); } void KisImage::scale(double sx, double sy, KisProgressDisplayInterface *progress, KisFilterStrategy *filterStrategy) { if (nlayers() == 0) return; // Nothing to scale // New image size. XXX: Pass along to discourage rounding errors? TQ_INT32 w, h; w = (TQ_INT32)(( width() * sx) + 0.5); h = (TQ_INT32)(( height() * sy) + 0.5); if (w != width() || h != height()) { lock(); if (undo()) { m_adapter->beginMacro(i18n("Scale Image")); m_adapter->addCommand(new LockImageCommand(this, true)); } #if 0 if ( colorSpace()->id() == KisID("RGBA") || colorSpace()->id() == KisID("CMYK") || colorSpace()->id() == KisID("GRAYA")) { KisScaleVisitor v (this, sx, sy, progress, filterStrategy); m_rootLayer->accept( v ); } else { #endif KisTransformVisitor visitor (this, sx, sy, 0.0, 0.0, 0.0, 0, 0, progress, filterStrategy); m_rootLayer->accept(visitor); // } if (undo()) { m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); } m_width = w; m_height = h; emitSizeChanged(); unlock(); if (undo()) { m_adapter->addCommand(new LockImageCommand(this, false)); m_adapter->endMacro(); } } } void KisImage::rotate(double radians, KisProgressDisplayInterface *progress) { lock(); TQ_INT32 w = width(); TQ_INT32 h = height(); TQ_INT32 tx = TQ_INT32((w*cos(radians) - h*sin(radians) - w) / 2 + 0.5); TQ_INT32 ty = TQ_INT32((h*cos(radians) + w*sin(radians) - h) / 2 + 0.5); w = (TQ_INT32)(width()*TQABS(cos(radians)) + height()*TQABS(sin(radians)) + 0.5); h = (TQ_INT32)(height()*TQABS(cos(radians)) + width()*TQABS(sin(radians)) + 0.5); tx -= (w - width()) / 2; ty -= (h - height()) / 2; if (undo()) { m_adapter->beginMacro(i18n("Rotate Image")); m_adapter->addCommand(new LockImageCommand(this, true)); } KisFilterStrategy *filter = KisFilterStrategyRegistry::instance()->get(KisID("Triangle")); KisTransformVisitor visitor (this, 1.0, 1.0, 0, 0, radians, -tx, -ty, progress, filter); m_rootLayer->accept(visitor); if (undo()) m_adapter->addCommand(new KisResizeImageCmd(undoAdapter(), this, w, h, width(), height())); m_width = w; m_height = h; emitSizeChanged(); unlock(); if (undo()) { m_adapter->addCommand(new LockImageCommand(this, false)); m_adapter->endMacro(); } } void KisImage::shear(double angleX, double angleY, KisProgressDisplayInterface *m_progress) { const double pi=3.1415926535897932385; //new image size TQ_INT32 w=width(); TQ_INT32 h=height(); if(angleX != 0 || angleY != 0){ double deltaY=height()*TQABS(tan(angleX*pi/180)*tan(angleY*pi/180)); w = (TQ_INT32) ( width() + TQABS(height()*tan(angleX*pi/180)) ); //ugly fix for the problem of having two extra pixels if only a shear along one //axis is done. This has to be fixed in the cropping code in KisRotateVisitor! if (angleX == 0 || angleY == 0) h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180)) ); else if (angleX > 0 && angleY > 0) h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180))- 2 * deltaY + 2 ); else if (angleX < 0 && angleY < 0) h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180))- 2 * deltaY + 2 ); else h = (TQ_INT32) ( height() + TQABS(w*tan(angleY*pi/180)) ); } if (w != width() || h != height()) { lock(); if (undo()) { m_adapter->beginMacro(i18n("Shear Image")); m_adapter->addCommand(new LockImageCommand(this, true)); } KisShearVisitor v(angleX, angleY, m_progress); v.setUndoAdapter(undoAdapter()); rootLayer()->accept(v); if (undo()) m_adapter->addCommand(new KisResizeImageCmd(m_adapter, this, w, h, width(), height())); m_width = w; m_height = h; emitSizeChanged(); unlock(); if (undo()) { m_adapter->addCommand(new LockImageCommand(this, false)); m_adapter->endMacro(); } } } void KisImage::convertTo(KisColorSpace * dstColorSpace, TQ_INT32 renderingIntent) { if ( m_colorSpace == dstColorSpace ) { return; } lock(); KisColorSpace * oldCs = m_colorSpace; if (undo()) { m_adapter->beginMacro(i18n("Convert Image Type")); m_adapter->addCommand(new LockImageCommand(this, true)); } setColorSpace(dstColorSpace); KisColorSpaceConvertVisitor visitor(dstColorSpace, renderingIntent); m_rootLayer->accept(visitor); unlock(); emit sigLayerPropertiesChanged( m_activeLayer ); if (undo()) { m_adapter->addCommand(new KisConvertImageTypeCmd(undoAdapter(), this, oldCs, dstColorSpace)); m_adapter->addCommand(new LockImageCommand(this, false)); m_adapter->endMacro(); } } KisProfile * KisImage::getProfile() const { return colorSpace()->getProfile(); } void KisImage::setProfile(const KisProfile * profile) { if (profile == 0) return; KisColorSpace * dstCs= KisMetaRegistry::instance()->csRegistry()->getColorSpace( colorSpace()->id(), profile); if (dstCs) { lock(); KisColorSpace * oldCs = colorSpace(); setColorSpace(dstCs); emit(sigProfileChanged(const_cast(profile))); KisChangeProfileVisitor visitor(oldCs, dstCs); m_rootLayer->accept(visitor); unlock(); } } double KisImage::xRes() { return m_xres; } double KisImage::yRes() { return m_yres; } void KisImage::setResolution(double xres, double yres) { m_xres = xres; m_yres = yres; } TQ_INT32 KisImage::width() const { return m_width; } TQ_INT32 KisImage::height() const { return m_height; } KisPaintDeviceSP KisImage::activeDevice() { if (KisPaintLayer* layer = dynamic_cast(m_activeLayer.data())) { return layer->paintDeviceOrMask(); } else if (KisAdjustmentLayer* layer = dynamic_cast(m_activeLayer.data())) { if (layer->selection()) { return layer->selection().data(); } } else if (KisGroupLayer * layer = dynamic_cast(m_activeLayer.data())) { // Find first child KisLayerSP child = layer->lastChild(); while(child) { if (KisPaintLayer* layer = dynamic_cast(child.data())) { return layer->paintDevice(); } child = child->prevSibling(); } KisLayerSP sibling = layer->nextSibling(); while (sibling) { if (KisPaintLayer* layer = dynamic_cast(sibling.data())) { return layer->paintDevice(); } sibling = sibling->nextSibling(); } } else if (KisLayerSP layer = m_activeLayer) { // A weird layer -- let's not return it, but a sibling KisLayerSP sibling = layer->nextSibling(); while (sibling) { if (KisPaintLayer* layer = dynamic_cast(sibling.data())) { return layer->paintDevice(); } sibling = sibling->nextSibling(); } } // XXX: We're buggered! return 0; } KisLayerSP KisImage::newLayer(const TQString& name, TQ_UINT8 opacity, const KisCompositeOp& compositeOp, KisColorSpace * colorstrategy) { KisPaintLayer * layer; if (colorstrategy) layer = new KisPaintLayer(this, name, opacity, colorstrategy); else layer = new KisPaintLayer(this, name, opacity); TQ_CHECK_PTR(layer); if (compositeOp.isValid()) layer->setCompositeOp(compositeOp); layer->setVisible(true); if (m_activeLayer != 0) { addLayer(layer, m_activeLayer->parent().data(), m_activeLayer->nextSibling()); } else { addLayer(layer, m_rootLayer, 0); } activate(layer); return layer; } void KisImage::setLayerProperties(KisLayerSP layer, TQ_UINT8 opacity, const KisCompositeOp& compositeOp, const TQString& name) { if (layer && (layer->opacity() != opacity || layer->compositeOp() != compositeOp || layer->name() != name)) { if (undo()) { TQString oldname = layer->name(); TQ_INT32 oldopacity = layer->opacity(); KisCompositeOp oldCompositeOp = layer->compositeOp(); layer->setName(name); layer->setOpacity(opacity); layer->setCompositeOp(compositeOp); m_adapter->addCommand(new LayerPropsCmd(layer, this, m_adapter, oldname, oldopacity, oldCompositeOp)); } else { layer->setName(name); layer->setOpacity(opacity); layer->setCompositeOp(compositeOp); } } } KisGroupLayerSP KisImage::rootLayer() const { return m_rootLayer; } KisLayerSP KisImage::activeLayer() const { return m_activeLayer; } KisPaintDeviceSP KisImage::projection() { return m_rootLayer->projection(TQRect(0, 0, m_width, m_height)); } KisLayerSP KisImage::activate(KisLayerSP layer) { if (layer != m_activeLayer) { if (m_activeLayer) m_activeLayer->deactivate(); m_activeLayer = layer; if (m_activeLayer) m_activeLayer->activate(); emit sigLayerActivated(m_activeLayer); emit sigMaskInfoChanged(); } return layer; } KisLayerSP KisImage::findLayer(const TQString& name) const { return rootLayer()->findLayer(name); } KisLayerSP KisImage::findLayer(int id) const { return rootLayer()->findLayer(id); } bool KisImage::addLayer(KisLayerSP layer, KisGroupLayerSP parent) { return addLayer(layer, parent, parent->firstChild()); } bool KisImage::addLayer(KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) { if (!parent) return false; const bool success = parent->addLayer(layer, aboveThis); if (success) { KisPaintLayerSP player = dynamic_cast(layer.data()); if (player != 0) { // XXX: This should also be done whenever a layer grows! TQValueVector actions = KisMetaRegistry::instance() -> csRegistry()->paintDeviceActionsFor(player->paintDevice()->colorSpace()); for (uint i = 0; i < actions.count(); i++) { actions.at(i)->act(player.data()->paintDevice(), width(), height()); } connect(player, TQ_SIGNAL(sigMaskInfoChanged()), this, TQ_SIGNAL(sigMaskInfoChanged())); } if (layer->extent().isValid()) layer->setDirty(); if (!layer->temporary()) { emit sigLayerAdded(layer); activate(layer); } if (!layer->temporary() && undo()) { m_adapter->addCommand(new LayerAddCmd(m_adapter, this, layer)); } } return success; } bool KisImage::removeLayer(KisLayerSP layer) { if (!layer || layer->image() != this) return false; if (KisGroupLayerSP parent = layer->parent()) { // Adjustment layers should mark the layers underneath them, whose rendering // they have cached, diryt on removal. Otherwise, the group won't be re-rendered. KisAdjustmentLayer * al = dynamic_cast(layer.data()); if (al) { TQRect r = al->extent(); lock(); // Lock the image, because we are going to dirty a lot of layers KisLayerSP l = layer->nextSibling(); while (l) { KisAdjustmentLayer * al2 = dynamic_cast(l.data()); l->setDirty(r, false); if (al2 != 0) break; l = l->nextSibling(); } unlock(); } KisPaintLayerSP player = dynamic_cast(layer.data()); if (player != 0) { disconnect(player, TQ_SIGNAL(sigMaskInfoChanged()), this, TQ_SIGNAL(sigMaskInfoChanged())); } KisLayerSP l = layer->prevSibling(); TQRect r = layer->extent(); while (l) { l->setDirty(r, false); l = l->prevSibling(); } KisLayerSP wasAbove = layer->nextSibling(); KisLayerSP wasBelow = layer->prevSibling(); const bool wasActive = layer == activeLayer(); // sigLayerRemoved can set it to 0, we don't want that in the else of wasActive! KisLayerSP actLayer = activeLayer(); const bool success = parent->removeLayer(layer); if (success) { layer->setImage(0); if (!layer->temporary() && undo()) { m_adapter->addCommand(new LayerRmCmd(m_adapter, this, layer, parent, wasAbove)); } if (!layer->temporary()) { emit sigLayerRemoved(layer, parent, wasAbove); if (wasActive) { if (wasBelow) activate(wasBelow); else if (wasAbove) activate(wasAbove); else if (parent != rootLayer()) activate(parent.data()); else activate(rootLayer()->firstChild()); } else { activate(actLayer); } } } return success; } return false; } bool KisImage::raiseLayer(KisLayerSP layer) { if (!layer) return false; return moveLayer(layer, layer->parent().data(), layer->prevSibling()); } bool KisImage::lowerLayer(KisLayerSP layer) { if (!layer) return false; if (KisLayerSP next = layer->nextSibling()) return moveLayer(layer, layer->parent().data(), next->nextSibling()); return false; } bool KisImage::toTop(KisLayerSP layer) { if (!layer) return false; return moveLayer(layer, rootLayer(), rootLayer()->firstChild()); } bool KisImage::toBottom(KisLayerSP layer) { if (!layer) return false; return moveLayer(layer, rootLayer(), 0); } bool KisImage::moveLayer(KisLayerSP layer, KisGroupLayerSP parent, KisLayerSP aboveThis) { if (!parent) return false; KisGroupLayerSP wasParent = layer->parent(); KisLayerSP wasAbove = layer->nextSibling(); if (wasParent.data() == parent.data() && wasAbove.data() == aboveThis.data()) return false; lock(); if (!wasParent->removeLayer(layer)) { unlock(); return false; } const bool success = parent->addLayer(layer, aboveThis); layer->setDirty(); unlock(); if (success) { emit sigLayerMoved(layer, wasParent, wasAbove); if (undo()) m_adapter->addCommand(new LayerMoveCmd(m_adapter, this, layer, wasParent, wasAbove)); } else //we already removed the layer above, but re-adding it failed, so... { emit sigLayerRemoved(layer, wasParent, wasAbove); if (undo()) m_adapter->addCommand(new LayerRmCmd(m_adapter, this, layer, wasParent, wasAbove)); } return success; } TQ_INT32 KisImage::nlayers() const { return rootLayer()->numLayers() - 1; } TQ_INT32 KisImage::nHiddenLayers() const { return rootLayer()->numLayers(KisLayer::Hidden); } void KisImage::flatten() { KisGroupLayerSP oldRootLayer = m_rootLayer; disconnect(oldRootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); KisPaintLayer *dst = new KisPaintLayer(this, nextLayerName(), OPACITY_OPAQUE, colorSpace()); TQ_CHECK_PTR(dst); TQRect rc = mergedImage()->extent(); KisPainter gc(dst->paintDevice()); gc.bitBlt(rc.x(), rc.y(), COMPOSITE_COPY, mergedImage(), OPACITY_OPAQUE, rc.left(), rc.top(), rc.width(), rc.height()); m_rootLayer = new KisGroupLayer(this, "", OPACITY_OPAQUE); connect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); if (undo()) { m_adapter->beginMacro(i18n("Flatten Image")); m_adapter->addCommand(new LockImageCommand(this, true)); m_adapter->addCommand(new KisChangeLayersCmd(m_adapter, this, oldRootLayer, m_rootLayer, "")); } lock(); addLayer(dst, m_rootLayer, 0); activate(dst); unlock(); notifyLayersChanged(); if (undo()) { m_adapter->addCommand(new LockImageCommand(this, false)); m_adapter->endMacro(); } } void KisImage::mergeLayer(KisLayerSP layer) { KisPaintLayer *player = new KisPaintLayer(this, layer->name(), OPACITY_OPAQUE, colorSpace()); TQ_CHECK_PTR(player); TQRect rc = layer->extent() | layer->nextSibling()->extent(); undoAdapter()->beginMacro(i18n("Merge with Layer Below")); //Abuse the merge visitor to only merge two layers (if either are groups they'll recursively merge) KisMergeVisitor visitor(player->paintDevice(), rc); layer->nextSibling()->accept(visitor); layer->accept(visitor); removeLayer(layer->nextSibling()); addLayer(player, layer->parent(), layer); removeLayer(layer); undoAdapter()->endMacro(); } void KisImage::setModified() { emit sigImageModified(); } void KisImage::renderToPainter(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 x2, TQ_INT32 y2, TQPainter &painter, KisProfile * monitorProfile, PaintFlags paintFlags, float exposure) { TQImage img = convertToTQImage(x1, y1, x2, y2, monitorProfile, exposure); TQ_INT32 w = x2 - x1 + 1; TQ_INT32 h = y2 - y1 + 1; if (paintFlags & PAINT_BACKGROUND) { m_bkg->paintBackground(img, x1, y1); img.setAlphaBuffer(false); } if (paintFlags & PAINT_SELECTION) { if (m_activeLayer != 0) { m_activeLayer->paintSelection(img, x1, y1, w, h); } } if (paintFlags & PAINT_MASKINACTIVELAYERS) { if (m_activeLayer != 0) { m_activeLayer->paintMaskInactiveLayers(img, x1, y1, w, h); } } painter.drawImage(x1, y1, img, 0, 0, w, h); } TQImage KisImage::convertToTQImage(TQ_INT32 x1, TQ_INT32 y1, TQ_INT32 x2, TQ_INT32 y2, KisProfile * profile, float exposure) { TQ_INT32 w = x2 - x1 + 1; TQ_INT32 h = y2 - y1 + 1; KisPaintDeviceSP dev = m_rootLayer->projection(TQRect(x1, y1, w, h)); TQImage img = dev->convertToTQImage(profile, x1, y1, w, h, exposure); if (!img.isNull()) { #ifdef __BIG_ENDIAN__ uchar * data = img.bits(); for (int i = 0; i < w * h; ++i) { uchar r, g, b, a; a = data[0]; b = data[1]; g = data[2]; r = data[3]; data[0] = r; data[1] = g; data[2] = b; data[3] = a; data += 4; } #endif return img; } return TQImage(); } TQImage KisImage::convertToTQImage(const TQRect& r, const TQSize& scaledImageSize, KisProfile *profile, PaintFlags paintFlags, float exposure) { if (r.isEmpty() || scaledImageSize.isEmpty()) { return TQImage(); } TQ_INT32 imageWidth = width(); TQ_INT32 imageHeight = height(); TQ_UINT32 pixelSize = colorSpace()->pixelSize(); double xScale = static_cast(imageWidth) / scaledImageSize.width(); double yScale = static_cast(imageHeight) / scaledImageSize.height(); TQRect srcRect; srcRect.setLeft(static_cast(r.left() * xScale)); srcRect.setRight(static_cast(ceil((r.right() + 1) * xScale)) - 1); srcRect.setTop(static_cast(r.top() * yScale)); srcRect.setBottom(static_cast(ceil((r.bottom() + 1) * yScale)) - 1); KisPaintDeviceSP mergedImage = m_rootLayer->projection(srcRect); TQTime t; t.start(); TQ_UINT8 *scaledImageData = new TQ_UINT8[r.width() * r.height() * pixelSize]; TQ_UINT8 *imageRow = new TQ_UINT8[srcRect.width() * pixelSize]; const TQ_INT32 imageRowX = srcRect.x(); for (TQ_INT32 y = 0; y < r.height(); ++y) { TQ_INT32 dstY = r.y() + y; TQ_INT32 dstX = r.x(); TQ_INT32 srcY = (dstY * imageHeight) / scaledImageSize.height(); mergedImage->readBytes(imageRow, imageRowX, srcY, srcRect.width(), 1); TQ_UINT8 *dstPixel = scaledImageData + (y * r.width() * pixelSize); TQ_UINT32 columnsRemaining = r.width(); while (columnsRemaining > 0) { TQ_INT32 srcX = (dstX * imageWidth) / scaledImageSize.width(); memcpy(dstPixel, imageRow + ((srcX - imageRowX) * pixelSize), pixelSize); ++dstX; dstPixel += pixelSize; --columnsRemaining; } } kdDebug() << "Time elapsed scaling image: " << t.elapsed() << endl; delete [] imageRow; TQImage image = colorSpace()->convertToTQImage(scaledImageData, r.width(), r.height(), profile, INTENT_PERCEPTUAL, exposure); delete [] scaledImageData; #ifdef __BIG_ENDIAN__ uchar * data = image.bits(); for (int i = 0; i < image.width() * image.height(); ++i) { uchar r, g, b, a; a = data[0]; b = data[1]; g = data[2]; r = data[3]; data[0] = r; data[1] = g; data[2] = b; data[3] = a; data += 4; } #endif if (paintFlags & PAINT_BACKGROUND) { m_bkg->paintBackground(image, r, scaledImageSize, TQSize(imageWidth, imageHeight)); image.setAlphaBuffer(false); } if (paintFlags & PAINT_SELECTION) { if (m_activeLayer != 0) { m_activeLayer->paintSelection(image, r, scaledImageSize, TQSize(imageWidth, imageHeight)); } } /*if (paintFlags & PAINT_MASKINACTIVELAYERS) { if (m_activeLayer != 0) { m_activeLayer->paintMaskInactiveLayers(img, x1, y1, w, h); } }*/ return image; } KisPaintDeviceSP KisImage::mergedImage() { return m_rootLayer->projection(TQRect(0, 0, m_width, m_height)); } KisColor KisImage::mergedPixel(TQ_INT32 x, TQ_INT32 y) { return m_rootLayer->projection(TQRect(x, y, 1, 1))->colorAt(x, y); } void KisImage::notifyLayersChanged() { emit sigLayersChanged(rootLayer()); } void KisImage::notifyPropertyChanged(KisLayerSP layer) { emit sigLayerPropertiesChanged(layer); } void KisImage::notifyImageLoaded() { } TQRect KisImage::bounds() const { return TQRect(0, 0, width(), height()); } void KisImage::setUndoAdapter(KisUndoAdapter * adapter) { m_adapter = adapter; } KisUndoAdapter* KisImage::undoAdapter() const { return m_adapter; } bool KisImage::undo() const { return (m_adapter && m_adapter->undo()); } //KisGuideMgr *KisImage::guides() const //{ // return const_cast(&m_guides); //} void KisImage::slotSelectionChanged() { slotSelectionChanged(bounds()); } void KisImage::slotSelectionChanged(const TQRect& r) { TQRect r2(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2); if (!locked()) { emit sigActiveSelectionChanged(this); emit sigSelectionChanged(this); } else { m_private->selectionChangedWhileLocked = true; } } KisColorSpace * KisImage::colorSpace() const { return m_colorSpace; } void KisImage::setColorSpace(KisColorSpace * colorSpace) { m_colorSpace = colorSpace; m_rootLayer->resetProjection(); emit sigColorSpaceChanged(colorSpace); } void KisImage::setRootLayer(KisGroupLayerSP rootLayer) { disconnect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); m_rootLayer = rootLayer; if (!locked()) { connect(m_rootLayer, TQ_SIGNAL(sigDirty(TQRect)), this, TQ_SIGNAL(sigImageUpdated(TQRect))); } activate(m_rootLayer->firstChild()); } void KisImage::addAnnotation(KisAnnotationSP annotation) { // Find the icc annotation, if there is one vKisAnnotationSP_it it = m_annotations.begin(); while (it != m_annotations.end()) { if ((*it)->type() == annotation->type()) { *it = annotation; return; } ++it; } m_annotations.push_back(annotation); } KisAnnotationSP KisImage::annotation(TQString type) { vKisAnnotationSP_it it = m_annotations.begin(); while (it != m_annotations.end()) { if ((*it)->type() == type) { return *it; } ++it; } return 0; } void KisImage::removeAnnotation(TQString type) { vKisAnnotationSP_it it = m_annotations.begin(); while (it != m_annotations.end()) { if ((*it)->type() == type) { m_annotations.erase(it); return; } ++it; } } vKisAnnotationSP_it KisImage::beginAnnotations() { KisProfile * profile = colorSpace()->getProfile(); KisAnnotationSP annotation; if (profile) annotation = profile->annotation(); if (annotation) addAnnotation(annotation); else removeAnnotation("icc"); return m_annotations.begin(); } vKisAnnotationSP_it KisImage::endAnnotations() { return m_annotations.end(); } KisBackgroundSP KisImage::background() const { return m_bkg; } KisPerspectiveGrid* KisImage::perspectiveGrid() { return m_private->perspectiveGrid; } #include "kis_image.moc"