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.
359 lines
13 KiB
359 lines
13 KiB
/*
|
|
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
|
|
* Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
|
|
* Copyright (c) 2006 Bart Coppens <kde@bartcoppens.be>
|
|
*
|
|
* 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.
|
|
*/
|
|
#ifndef KIS_MERGE_H_
|
|
#define KIS_MERGE_H_
|
|
|
|
#include <tqrect.h>
|
|
|
|
#include "kis_types.h"
|
|
#include "kis_paint_device.h"
|
|
#include "kis_layer_visitor.h"
|
|
#include "kis_painter.h"
|
|
#include "kis_image.h"
|
|
#include "kis_layer.h"
|
|
#include "kis_group_layer.h"
|
|
#include "kis_adjustment_layer.h"
|
|
#include "kis_paint_layer.h"
|
|
#include "kis_part_layer_iface.h"
|
|
#include "kis_filter.h"
|
|
#include "kis_filter_configuration.h"
|
|
#include "kis_filter_registry.h"
|
|
#include "kis_selection.h"
|
|
#include "kis_transaction.h"
|
|
#include "kis_iterators_pixel.h"
|
|
|
|
class KisMergeVisitor : public KisLayerVisitor {
|
|
public:
|
|
/**
|
|
* Don't even _think_ of creating a merge visitor without a projection; without a projection,
|
|
* the adjustmentlayers won't work.
|
|
*/
|
|
KisMergeVisitor(KisPaintDeviceSP projection, const TQRect& rc) :
|
|
KisLayerVisitor()
|
|
{
|
|
Q_ASSERT(projection);
|
|
|
|
m_projection = projection;
|
|
m_rc = rc;
|
|
}
|
|
|
|
private:
|
|
// Helper for the indirect painting (keep above to inhibit gcc-2.95 ICE)
|
|
template<class Target>
|
|
KSharedPtr<Target> paintIndirect(KisPaintDeviceSP source,
|
|
KSharedPtr<Target> target,
|
|
KisLayerSupportsIndirectPainting* layer,
|
|
TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 dx, TQ_INT32 dy,
|
|
TQ_INT32 w, TQ_INT32 h) {
|
|
KisPainter gc2(target.data());
|
|
gc2.bitBlt(dx, dy, COMPOSITE_COPY, source,
|
|
OPACITY_OPAQUE, sx, sy, w, h);
|
|
gc2.bitBlt(dx, dy, layer->temporaryCompositeOp(), layer->temporaryTarget(),
|
|
layer->temporaryOpacity(), sx, sy, w, h);
|
|
gc2.end();
|
|
return target;
|
|
}
|
|
|
|
public:
|
|
virtual bool visit(KisPaintLayer *layer)
|
|
{
|
|
|
|
if (m_projection == 0) {
|
|
return false;
|
|
}
|
|
|
|
kdDebug(41010) << "Visiting on paint layer " << layer->name() << ", visible: " << layer->visible()
|
|
<< ", temporary: " << layer->temporary() << ", extent: "
|
|
<< layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
|
|
if (!layer->visible())
|
|
return true;
|
|
|
|
TQ_INT32 sx, sy, dx, dy, w, h;
|
|
|
|
TQRect rc = layer->paintDevice()->extent() & m_rc;
|
|
|
|
// Indirect painting?
|
|
KisPaintDeviceSP tempTarget = layer->temporaryTarget();
|
|
if (tempTarget) {
|
|
rc = (layer->paintDevice()->extent() | tempTarget->extent()) & m_rc;
|
|
}
|
|
|
|
sx = rc.left();
|
|
sy = rc.top();
|
|
w = rc.width();
|
|
h = rc.height();
|
|
dx = sx;
|
|
dy = sy;
|
|
|
|
KisPainter gc(m_projection);
|
|
KisPaintDeviceSP source = layer->paintDevice();
|
|
|
|
if (!layer->hasMask()) {
|
|
if (tempTarget) {
|
|
KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
|
|
source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h);
|
|
}
|
|
|
|
gc.bitBlt(dx, dy, layer->compositeOp(), source, layer->opacity(), sx, sy, w, h);
|
|
} else {
|
|
if (layer->renderMask()) {
|
|
// To display the mask, we don't do things with composite op and opacity
|
|
// This is like the gimp does it, I guess that's ok?
|
|
|
|
// Note that here we'll use m_rc, because even if the extent of the device is
|
|
// empty, we want a full mask to be drawn! (we don't change rc, since
|
|
// it'd mess with setClean). This is because KisPainter::bitBlt &'s with
|
|
// the source device's extent. This is ok in normal circumstances, but
|
|
// we changed the default tile. Fixing this properly would mean fixing it there.
|
|
sx = m_rc.left();
|
|
sy = m_rc.top();
|
|
w = m_rc.width();
|
|
h = m_rc.height();
|
|
dx = sx;
|
|
dy = sy;
|
|
|
|
// The problem is that the extent of the layer mask might not be extended
|
|
// enough. Check if that is the case
|
|
KisPaintDeviceSP mask = layer->getMask();
|
|
TQRect mextent = mask->extent();
|
|
if ((mextent & m_rc) != m_rc) {
|
|
// Iterate over all pixels in the m_rc area. With just accessing the
|
|
// tiles in read-write mode, we ensure that the tiles get created if they
|
|
// do not exist. If they do, they'll remain untouched since we don't
|
|
// actually write data to it.
|
|
// XXX Admission: this is actually kind of a hack :-(
|
|
KisRectIteratorPixel it = mask->createRectIterator(sx, sy, w, h, true);
|
|
while (!it.isDone())
|
|
++it;
|
|
}
|
|
if (tempTarget) {
|
|
KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
|
|
mask = paintIndirect(mask, temp, layer, sx, sy, dx, dy, w, h);
|
|
}
|
|
|
|
gc.bitBlt(dx, dy, COMPOSITE_OVER, mask, OPACITY_OPAQUE, sx, sy, w, h);
|
|
} else {
|
|
KisSelectionSP mask = layer->getMaskAsSelection();
|
|
// The indirect painting happens on the mask
|
|
if (tempTarget && layer->editMask()) {
|
|
KisPaintDeviceSP maskSrc = layer->getMask();
|
|
KisPaintDeviceSP temp = new KisPaintDevice(maskSrc->colorSpace());
|
|
temp = paintIndirect(maskSrc, temp, layer, sx, sy, dx, dy, w, h);
|
|
// Blegh
|
|
KisRectIteratorPixel srcIt = temp->createRectIterator(sx, sy, w, h, false);
|
|
KisRectIteratorPixel dstIt = mask->createRectIterator(sx, sy, w, h, true);
|
|
|
|
while(!dstIt.isDone()) {
|
|
// Same as in convertMaskToSelection
|
|
*dstIt.rawData() = *srcIt.rawData();
|
|
++srcIt;
|
|
++dstIt;
|
|
}
|
|
} else if (tempTarget) {
|
|
// We have a mask, and paint indirect, but not on the mask
|
|
KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
|
|
source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h);
|
|
}
|
|
|
|
gc.bltSelection(dx, dy,
|
|
layer->compositeOp(),
|
|
source,
|
|
mask,
|
|
layer->opacity(), sx, sy, w, h);
|
|
}
|
|
}
|
|
|
|
layer->setClean( rc );
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(KisGroupLayer *layer)
|
|
{
|
|
|
|
if (m_projection == 0) {
|
|
return false;
|
|
}
|
|
|
|
kdDebug(41010) << "Visiting on group layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
|
|
<< layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
|
|
|
|
if (!layer->visible())
|
|
return true;
|
|
|
|
TQ_INT32 sx, sy, dx, dy, w, h;
|
|
|
|
// This automatically makes sure the projection is up-to-date for the specified rect.
|
|
KisPaintDeviceSP dev = layer->projection(m_rc);
|
|
TQRect rc = dev->extent() & m_rc;
|
|
|
|
sx = rc.left();
|
|
sy = rc.top();
|
|
w = rc.width();
|
|
h = rc.height();
|
|
dx = sx;
|
|
dy = sy;
|
|
|
|
KisPainter gc(m_projection);
|
|
gc.bitBlt(dx, dy, layer->compositeOp(), dev, layer->opacity(), sx, sy, w, h);
|
|
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(KisPartLayer* layer)
|
|
{
|
|
|
|
kdDebug(41010) << "Visiting on part layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
|
|
<< layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
|
|
|
|
if (m_projection == 0) {
|
|
return false;
|
|
}
|
|
if (!layer->visible())
|
|
return true;
|
|
|
|
KisPaintDeviceSP dev(layer->prepareProjection(m_projection, m_rc));
|
|
if (!dev)
|
|
return true;
|
|
|
|
TQ_INT32 sx, sy, dx, dy, w, h;
|
|
|
|
TQRect rc = dev->extent() & m_rc;
|
|
|
|
sx= rc.left();
|
|
sy = rc.top();
|
|
w = rc.width();
|
|
h = rc.height();
|
|
dx = sx;
|
|
dy = sy;
|
|
|
|
KisPainter gc(m_projection);
|
|
gc.bitBlt(dx, dy, layer->compositeOp() , dev, layer->opacity(), sx, sy, w, h);
|
|
|
|
layer->setClean(rc);
|
|
return true;
|
|
}
|
|
|
|
virtual bool visit(KisAdjustmentLayer* layer)
|
|
{
|
|
kdDebug(41010) << "Visiting on adjustment layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
|
|
<< layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
|
|
|
|
if (m_projection == 0) {
|
|
return true;
|
|
}
|
|
|
|
if (!layer->visible())
|
|
return true;
|
|
|
|
KisPaintDeviceSP tempTarget = layer->temporaryTarget();
|
|
if (tempTarget) {
|
|
m_rc = (layer->extent() | tempTarget->extent()) & m_rc;
|
|
}
|
|
|
|
if (m_rc.width() == 0 || m_rc.height() == 0) // Don't even try
|
|
return true;
|
|
|
|
KisFilterConfiguration * cfg = layer->filter();
|
|
if (!cfg) return false;
|
|
|
|
|
|
KisFilter * f = KisFilterRegistry::instance()->get( cfg->name() );
|
|
if (!f) return false;
|
|
|
|
// Possibly enlarge the rect that changed (like for convolution filters)
|
|
// m_rc = f->enlargeRect(m_rc, cfg);
|
|
KisSelectionSP selection = layer->selection();
|
|
|
|
// Copy of the projection -- use the copy-on-write trick. XXX NO COPY ON WRITE YET =(
|
|
//KisPaintDeviceSP tmp = new KisPaintDevice(*m_projection);
|
|
KisPaintDeviceSP tmp = 0;
|
|
KisSelectionSP sel = selection;
|
|
// If there's a selection, only keep the selected bits
|
|
if (selection != 0) {
|
|
tmp = new KisPaintDevice(m_projection->colorSpace());
|
|
|
|
KisPainter gc(tmp);
|
|
TQRect selectedRect = selection->selectedRect();
|
|
selectedRect &= m_rc;
|
|
|
|
if (selectedRect.width() == 0 || selectedRect.height() == 0) // Don't even try
|
|
return true;
|
|
|
|
// Don't forget that we need to take into account the extended sourcing area as well
|
|
//selectedRect = f->enlargeRect(selectedRect, cfg);
|
|
|
|
//kdDebug() << k_funcinfo << selectedRect << endl;
|
|
tmp->setX(selection->getX());
|
|
tmp->setY(selection->getY());
|
|
|
|
// Indirect painting
|
|
if (tempTarget) {
|
|
sel = new KisSelection();
|
|
sel = paintIndirect(selection.data(), sel, layer, m_rc.left(), m_rc.top(),
|
|
m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
|
|
}
|
|
|
|
gc.bitBlt(selectedRect.x(), selectedRect.y(), COMPOSITE_COPY, m_projection,
|
|
selectedRect.x(), selectedRect.y(),
|
|
selectedRect.width(), selectedRect.height());
|
|
gc.end();
|
|
} else {
|
|
tmp = new KisPaintDevice(*m_projection);
|
|
}
|
|
|
|
// Some filters will require usage of oldRawData, which is not available without
|
|
// a transaction!
|
|
KisTransaction* cmd = new KisTransaction("", tmp);
|
|
|
|
// Filter the temporary paint device -- remember, these are only the selected bits,
|
|
// if there was a selection.
|
|
f->process(tmp, tmp, cfg, m_rc);
|
|
|
|
delete cmd;
|
|
|
|
// Copy the filtered bits onto the projection
|
|
KisPainter gc(m_projection);
|
|
if (selection)
|
|
gc.bltSelection(m_rc.left(), m_rc.top(),
|
|
COMPOSITE_OVER, tmp, sel, layer->opacity(),
|
|
m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
|
|
else
|
|
gc.bitBlt(m_rc.left(), m_rc.top(),
|
|
COMPOSITE_OVER, tmp, layer->opacity(),
|
|
m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
|
|
gc.end();
|
|
|
|
// Copy the finished projection onto the cache
|
|
gc.begin(layer->cachedPaintDevice());
|
|
gc.bitBlt(m_rc.left(), m_rc.top(),
|
|
COMPOSITE_COPY, m_projection, OPACITY_OPAQUE,
|
|
m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
|
|
layer->setClean(m_rc);
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
KisPaintDeviceSP m_projection;
|
|
TQRect m_rc;
|
|
};
|
|
|
|
#endif // KIS_MERGE_H_
|
|
|