diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-06-26 00:41:16 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2011-06-26 00:41:16 +0000 |
commit | 698569f8428ca088f764d704034a1330517b98c0 (patch) | |
tree | bf45be6946ebbbee9cce5a5bcf838f4c952d87e6 /chalk/core/kis_merge_visitor.h | |
parent | 2785103a6bd4de55bd26d79e34d0fdd4b329a73a (diff) | |
download | koffice-698569f8428ca088f764d704034a1330517b98c0.tar.gz koffice-698569f8428ca088f764d704034a1330517b98c0.zip |
Finish rebranding of Krita as Chalk
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1238363 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'chalk/core/kis_merge_visitor.h')
-rw-r--r-- | chalk/core/kis_merge_visitor.h | 358 |
1 files changed, 358 insertions, 0 deletions
diff --git a/chalk/core/kis_merge_visitor.h b/chalk/core/kis_merge_visitor.h new file mode 100644 index 00000000..68053384 --- /dev/null +++ b/chalk/core/kis_merge_visitor.h @@ -0,0 +1,358 @@ +/* + * 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 tqmask, 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 tqmask 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 tqmask might not be extended + // enough. Check if that is the case + KisPaintDeviceSP tqmask = layer->getMask(); + TQRect mextent = tqmask->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 = tqmask->createRectIterator(sx, sy, w, h, true); + while (!it.isDone()) + ++it; + } + if (tempTarget) { + KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace()); + tqmask = paintIndirect(tqmask, temp, layer, sx, sy, dx, dy, w, h); + } + + gc.bitBlt(dx, dy, COMPOSITE_OVER, tqmask, OPACITY_OPAQUE, sx, sy, w, h); + } else { + KisSelectionSP tqmask = layer->getMaskAsSelection(); + // The indirect painting happens on the tqmask + if (tempTarget && layer->editMask()) { + KisPaintDeviceSP tqmaskSrc = layer->getMask(); + KisPaintDeviceSP temp = new KisPaintDevice(tqmaskSrc->colorSpace()); + temp = paintIndirect(tqmaskSrc, temp, layer, sx, sy, dx, dy, w, h); + // Blegh + KisRectIteratorPixel srcIt = temp->createRectIterator(sx, sy, w, h, false); + KisRectIteratorPixel dstIt = tqmask->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 tqmask, and paint indirect, but not on the tqmask + KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace()); + source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h); + } + + gc.bltSelection(dx, dy, + layer->compositeOp(), + source, + tqmask, + 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_ + |