chalk

kis_merge_visitor.h
1/*
2 * Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
3 * Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
4 * Copyright (c) 2006 Bart Coppens <kde@bartcoppens.be>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 */
20#ifndef KIS_MERGE_H_
21#define KIS_MERGE_H_
22
23#include <tqrect.h>
24
25#include "kis_types.h"
26#include "kis_paint_device.h"
27#include "kis_layer_visitor.h"
28#include "kis_painter.h"
29#include "kis_image.h"
30#include "kis_layer.h"
31#include "kis_group_layer.h"
32#include "kis_adjustment_layer.h"
33#include "kis_paint_layer.h"
34#include "kis_part_layer_iface.h"
35#include "kis_filter.h"
36#include "kis_filter_configuration.h"
37#include "kis_filter_registry.h"
38#include "kis_selection.h"
39#include "kis_transaction.h"
40#include "kis_iterators_pixel.h"
41
42class KisMergeVisitor : public KisLayerVisitor {
43public:
48 KisMergeVisitor(KisPaintDeviceSP projection, const TQRect& rc) :
49 KisLayerVisitor()
50 {
51 Q_ASSERT(projection);
52
53 m_projection = projection;
54 m_rc = rc;
55 }
56
57private:
58 // Helper for the indirect painting (keep above to inhibit gcc-2.95 ICE)
59 template<class Target>
60 TDESharedPtr<Target> paintIndirect(KisPaintDeviceSP source,
61 TDESharedPtr<Target> target,
62 KisLayerSupportsIndirectPainting* layer,
63 TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 dx, TQ_INT32 dy,
64 TQ_INT32 w, TQ_INT32 h) {
65 KisPainter gc2(target.data());
66 gc2.bitBlt(dx, dy, COMPOSITE_COPY, source,
67 OPACITY_OPAQUE, sx, sy, w, h);
68 gc2.bitBlt(dx, dy, layer->temporaryCompositeOp(), layer->temporaryTarget(),
69 layer->temporaryOpacity(), sx, sy, w, h);
70 gc2.end();
71 return target;
72 }
73
74public:
75 virtual bool visit(KisPaintLayer *layer)
76 {
77
78 if (m_projection == 0) {
79 return false;
80 }
81
82 kdDebug(41010) << "Visiting on paint layer " << layer->name() << ", visible: " << layer->visible()
83 << ", temporary: " << layer->temporary() << ", extent: "
84 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
85 if (!layer->visible())
86 return true;
87
88 TQ_INT32 sx, sy, dx, dy, w, h;
89
90 TQRect rc = layer->paintDevice()->extent() & m_rc;
91
92 // Indirect painting?
93 KisPaintDeviceSP tempTarget = layer->temporaryTarget();
94 if (tempTarget) {
95 rc = (layer->paintDevice()->extent() | tempTarget->extent()) & m_rc;
96 }
97
98 sx = rc.left();
99 sy = rc.top();
100 w = rc.width();
101 h = rc.height();
102 dx = sx;
103 dy = sy;
104
105 KisPainter gc(m_projection);
106 KisPaintDeviceSP source = layer->paintDevice();
107
108 if (!layer->hasMask()) {
109 if (tempTarget) {
110 KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
111 source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h);
112 }
113
114 gc.bitBlt(dx, dy, layer->compositeOp(), source, layer->opacity(), sx, sy, w, h);
115 } else {
116 if (layer->renderMask()) {
117 // To display the mask, we don't do things with composite op and opacity
118 // This is like the gimp does it, I guess that's ok?
119
120 // Note that here we'll use m_rc, because even if the extent of the device is
121 // empty, we want a full mask to be drawn! (we don't change rc, since
122 // it'd mess with setClean). This is because KisPainter::bitBlt &'s with
123 // the source device's extent. This is ok in normal circumstances, but
124 // we changed the default tile. Fixing this properly would mean fixing it there.
125 sx = m_rc.left();
126 sy = m_rc.top();
127 w = m_rc.width();
128 h = m_rc.height();
129 dx = sx;
130 dy = sy;
131
132 // The problem is that the extent of the layer mask might not be extended
133 // enough. Check if that is the case
134 KisPaintDeviceSP mask = layer->getMask();
135 TQRect mextent = mask->extent();
136 if ((mextent & m_rc) != m_rc) {
137 // Iterate over all pixels in the m_rc area. With just accessing the
138 // tiles in read-write mode, we ensure that the tiles get created if they
139 // do not exist. If they do, they'll remain untouched since we don't
140 // actually write data to it.
141 // XXX Admission: this is actually kind of a hack :-(
142 KisRectIteratorPixel it = mask->createRectIterator(sx, sy, w, h, true);
143 while (!it.isDone())
144 ++it;
145 }
146 if (tempTarget) {
147 KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
148 mask = paintIndirect(mask, temp, layer, sx, sy, dx, dy, w, h);
149 }
150
151 gc.bitBlt(dx, dy, COMPOSITE_OVER, mask, OPACITY_OPAQUE, sx, sy, w, h);
152 } else {
153 KisSelectionSP mask = layer->getMaskAsSelection();
154 // The indirect painting happens on the mask
155 if (tempTarget && layer->editMask()) {
156 KisPaintDeviceSP maskSrc = layer->getMask();
157 KisPaintDeviceSP temp = new KisPaintDevice(maskSrc->colorSpace());
158 temp = paintIndirect(maskSrc, temp, layer, sx, sy, dx, dy, w, h);
159 // Blegh
160 KisRectIteratorPixel srcIt = temp->createRectIterator(sx, sy, w, h, false);
161 KisRectIteratorPixel dstIt = mask->createRectIterator(sx, sy, w, h, true);
162
163 while(!dstIt.isDone()) {
164 // Same as in convertMaskToSelection
165 *dstIt.rawData() = *srcIt.rawData();
166 ++srcIt;
167 ++dstIt;
168 }
169 } else if (tempTarget) {
170 // We have a mask, and paint indirect, but not on the mask
171 KisPaintDeviceSP temp = new KisPaintDevice(source->colorSpace());
172 source = paintIndirect(source, temp, layer, sx, sy, dx, dy, w, h);
173 }
174
175 gc.bltSelection(dx, dy,
176 layer->compositeOp(),
177 source,
178 mask,
179 layer->opacity(), sx, sy, w, h);
180 }
181 }
182
183 layer->setClean( rc );
184 return true;
185 }
186
187 virtual bool visit(KisGroupLayer *layer)
188 {
189
190 if (m_projection == 0) {
191 return false;
192 }
193
194 kdDebug(41010) << "Visiting on group layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
195 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
196
197 if (!layer->visible())
198 return true;
199
200 TQ_INT32 sx, sy, dx, dy, w, h;
201
202 // This automatically makes sure the projection is up-to-date for the specified rect.
203 KisPaintDeviceSP dev = layer->projection(m_rc);
204 TQRect rc = dev->extent() & m_rc;
205
206 sx = rc.left();
207 sy = rc.top();
208 w = rc.width();
209 h = rc.height();
210 dx = sx;
211 dy = sy;
212
213 KisPainter gc(m_projection);
214 gc.bitBlt(dx, dy, layer->compositeOp(), dev, layer->opacity(), sx, sy, w, h);
215
216 return true;
217 }
218
219 virtual bool visit(KisPartLayer* layer)
220 {
221
222 kdDebug(41010) << "Visiting on part layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
223 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
224
225 if (m_projection == 0) {
226 return false;
227 }
228 if (!layer->visible())
229 return true;
230
231 KisPaintDeviceSP dev(layer->prepareProjection(m_projection, m_rc));
232 if (!dev)
233 return true;
234
235 TQ_INT32 sx, sy, dx, dy, w, h;
236
237 TQRect rc = dev->extent() & m_rc;
238
239 sx= rc.left();
240 sy = rc.top();
241 w = rc.width();
242 h = rc.height();
243 dx = sx;
244 dy = sy;
245
246 KisPainter gc(m_projection);
247 gc.bitBlt(dx, dy, layer->compositeOp() , dev, layer->opacity(), sx, sy, w, h);
248
249 layer->setClean(rc);
250 return true;
251 }
252
253 virtual bool visit(KisAdjustmentLayer* layer)
254 {
255 kdDebug(41010) << "Visiting on adjustment layer " << layer->name() << ", visible: " << layer->visible() << ", extent: "
256 << layer->extent() << ", dirty: " << layer->dirtyRect() << ", paint rect: " << m_rc << endl;
257
258 if (m_projection == 0) {
259 return true;
260 }
261
262 if (!layer->visible())
263 return true;
264
265 KisPaintDeviceSP tempTarget = layer->temporaryTarget();
266 if (tempTarget) {
267 m_rc = (layer->extent() | tempTarget->extent()) & m_rc;
268 }
269
270 if (m_rc.width() == 0 || m_rc.height() == 0) // Don't even try
271 return true;
272
273 KisFilterConfiguration * cfg = layer->filter();
274 if (!cfg) return false;
275
276
277 KisFilter * f = KisFilterRegistry::instance()->get( cfg->name() );
278 if (!f) return false;
279
280 // Possibly enlarge the rect that changed (like for convolution filters)
281 // m_rc = f->enlargeRect(m_rc, cfg);
282 KisSelectionSP selection = layer->selection();
283
284 // Copy of the projection -- use the copy-on-write trick. XXX NO COPY ON WRITE YET =(
285 //KisPaintDeviceSP tmp = new KisPaintDevice(*m_projection);
286 KisPaintDeviceSP tmp = 0;
287 KisSelectionSP sel = selection;
288 // If there's a selection, only keep the selected bits
289 if (selection != 0) {
290 tmp = new KisPaintDevice(m_projection->colorSpace());
291
292 KisPainter gc(tmp);
293 TQRect selectedRect = selection->selectedRect();
294 selectedRect &= m_rc;
295
296 if (selectedRect.width() == 0 || selectedRect.height() == 0) // Don't even try
297 return true;
298
299 // Don't forget that we need to take into account the extended sourcing area as well
300 //selectedRect = f->enlargeRect(selectedRect, cfg);
301
302 //kdDebug() << k_funcinfo << selectedRect << endl;
303 tmp->setX(selection->getX());
304 tmp->setY(selection->getY());
305
306 // Indirect painting
307 if (tempTarget) {
308 sel = new KisSelection();
309 sel = paintIndirect(selection.data(), sel, layer, m_rc.left(), m_rc.top(),
310 m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
311 }
312
313 gc.bitBlt(selectedRect.x(), selectedRect.y(), COMPOSITE_COPY, m_projection,
314 selectedRect.x(), selectedRect.y(),
315 selectedRect.width(), selectedRect.height());
316 gc.end();
317 } else {
318 tmp = new KisPaintDevice(*m_projection);
319 }
320
321 // Some filters will require usage of oldRawData, which is not available without
322 // a transaction!
323 KisTransaction* cmd = new KisTransaction("", tmp);
324
325 // Filter the temporary paint device -- remember, these are only the selected bits,
326 // if there was a selection.
327 f->process(tmp, tmp, cfg, m_rc);
328
329 delete cmd;
330
331 // Copy the filtered bits onto the projection
332 KisPainter gc(m_projection);
333 if (selection)
334 gc.bltSelection(m_rc.left(), m_rc.top(),
335 COMPOSITE_OVER, tmp, sel, layer->opacity(),
336 m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
337 else
338 gc.bitBlt(m_rc.left(), m_rc.top(),
339 COMPOSITE_OVER, tmp, layer->opacity(),
340 m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
341 gc.end();
342
343 // Copy the finished projection onto the cache
344 gc.begin(layer->cachedPaintDevice());
345 gc.bitBlt(m_rc.left(), m_rc.top(),
346 COMPOSITE_COPY, m_projection, OPACITY_OPAQUE,
347 m_rc.left(), m_rc.top(), m_rc.width(), m_rc.height());
348 layer->setClean(m_rc);
349 return true;
350 }
351
352private:
353 KisPaintDeviceSP m_projection;
354 TQRect m_rc;
355};
356
357#endif // KIS_MERGE_H_
358
virtual TQRect extent() const
Returns an approximation of where the bounds on actual data are in this layer.
const TQString & name() const
Get the unique, language independent name of the filter.
_T get(const KisID &name) const
This function allow to get an object from its KisID.
virtual KisPaintDeviceSP projection(const TQRect &rect)
will copy from to, if !0, CoW!!
virtual TQRect extent() const
Returns an approximation of where the bounds on actual data are in this layer.
virtual void setClean(const TQRect &rect)
Set the specified rect to clean.
virtual TQRect extent() const =0
Returns an approximation of where the bounds on actual data are in this layer.
virtual TQRect extent() const
Returns an approximation of where the bounds on actual data are in this layer.
KisPaintDeviceSP getMask()
Returns the layer mask's device. Creates one if there is currently none.
bool editMask() const
When this returns true, the KisPaintDevice returned in paintDevice will actually be the layer mask (i...
KisSelectionSP getMaskAsSelection()
Returns the layer mask's device, converted to a selection. Creates one if there is currently none.
bool renderMask() const
Returns true if the masked part of the mask will be rendered instead of being transparent.
bool hasMask() const
Does this layer have a layer mask?
TQ_UINT8 * rawData() const
returns a pointer to the pixel data. Do NOT interpret the data - leave that to a colorstrategy
bool isDone() const
returns true when iterators has reached the end