You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/chalk/core/kis_image.cc

1703 lines
46 KiB

/*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (c) 2007 Benjamin Schleimer <bensch128@yahoo.com>
*
* 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 <stdlib.h>
#include <math.h>
#include <config.h>
#include LCMS_HEADER
#include <tqimage.h>
#include <tqpainter.h>
#include <tqsize.h>
#include <tqtl.h>
#include <tqapplication.h>
#include <tqthread.h>
#include <tqdatetime.h>
#include <kcommand.h>
#include <kdebug.h>
#include <tdelocale.h>
#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<KisGroupLayer*>(rhs.m_rootLayer->clone().data());
connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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<KisProfile *>(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<KisPaintLayer*>(m_activeLayer.data())) {
return layer->paintDeviceOrMask();
}
else if (KisAdjustmentLayer* layer = dynamic_cast<KisAdjustmentLayer*>(m_activeLayer.data())) {
if (layer->selection()) {
return layer->selection().data();
}
}
else if (KisGroupLayer * layer = dynamic_cast<KisGroupLayer*>(m_activeLayer.data())) {
// Find first child
KisLayerSP child = layer->lastChild();
while(child)
{
if (KisPaintLayer* layer = dynamic_cast<KisPaintLayer*>(child.data())) {
return layer->paintDevice();
}
child = child->prevSibling();
}
KisLayerSP sibling = layer->nextSibling();
while (sibling) {
if (KisPaintLayer* layer = dynamic_cast<KisPaintLayer*>(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<KisPaintLayer*>(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<KisPaintLayer*>(layer.data());
if (player != 0) {
// XXX: This should also be done whenever a layer grows!
TQValueVector<KisPaintDeviceAction *> 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, TQT_SIGNAL(sigMaskInfoChanged()), this, TQT_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<KisAdjustmentLayer*>(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<KisAdjustmentLayer*>(l.data());
l->setDirty(r, false);
if (al2 != 0) break;
l = l->nextSibling();
}
unlock();
}
KisPaintLayerSP player = dynamic_cast<KisPaintLayer*>(layer.data());
if (player != 0) {
disconnect(player, TQT_SIGNAL(sigMaskInfoChanged()),
this, TQT_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, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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<double>(imageWidth) / scaledImageSize.width();
double yScale = static_cast<double>(imageHeight) / scaledImageSize.height();
TQRect srcRect;
srcRect.setLeft(static_cast<int>(r.left() * xScale));
srcRect.setRight(static_cast<int>(ceil((r.right() + 1) * xScale)) - 1);
srcRect.setTop(static_cast<int>(r.top() * yScale));
srcRect.setBottom(static_cast<int>(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<KisGuideMgr*>(&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, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_SIGNAL(sigImageUpdated(TQRect)));
m_rootLayer = rootLayer;
if (!locked()) {
connect(m_rootLayer, TQT_SIGNAL(sigDirty(TQRect)), this, TQT_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"