chalk

kis_paint_layer.cpp
1/*
2 * Copyright (c) 2005 Casper Boemann <cbr@boemann.dk>
3 * Copyright (c) 2006 Bart Coppens <kde@bartcoppens.be>
4 *
5 * this program is free software; you can redistribute it and/or modify
6 * it under the terms of the gnu general public license as published by
7 * the free software foundation; either version 2 of the license, or
8 * (at your option) any later version.
9 *
10 * this program is distributed in the hope that it will be useful,
11 * but without any warranty; without even the implied warranty of
12 * merchantability or fitness for a particular purpose. see the
13 * gnu general public license for more details.
14 *
15 * you should have received a copy of the gnu general public license
16 * along with this program; if not, write to the free software
17 * foundation, inc., 675 mass ave, cambridge, ma 02139, usa.
18 */
19
20#include <kdebug.h>
21#include <tqimage.h>
22
23#include "kis_debug_areas.h"
24#include "kis_image.h"
25#include "kis_paint_layer.h"
26#include "kis_selection.h"
27#include "kis_painter.h"
28#include "kis_undo_adapter.h"
29#include "kis_iterators_pixel.h"
30#include "kis_paint_device.h"
31#include "kis_meta_registry.h"
32#include "kis_colorspace_factory_registry.h"
33#include "kis_datamanager.h"
34#include "kis_undo_adapter.h"
35
36KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity, KisPaintDeviceSP dev)
37 : super(img, name, opacity)
38{
39 Q_ASSERT(img);
40 Q_ASSERT(dev);
41 m_paintdev = dev;
42 m_mask = 0;
43 m_maskAsSelection = 0;
44 m_paintdev->setParentLayer(this);
45 m_renderMask = false;
46 m_editMask = true;
47}
48
49
50KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity)
51 : super(img, name, opacity)
52{
53 Q_ASSERT(img);
54 m_paintdev = new KisPaintDevice(this, img->colorSpace(), name.latin1());
55 m_mask = 0;
56 m_maskAsSelection = 0;
57 m_renderMask = false;
58 m_editMask = true;
59}
60
61KisPaintLayer::KisPaintLayer(KisImage *img, const TQString& name, TQ_UINT8 opacity, KisColorSpace * colorSpace)
62 : super(img, name, opacity)
63{
64 Q_ASSERT(img);
65 Q_ASSERT(colorSpace);
66 m_paintdev = new KisPaintDevice(this, colorSpace, name.latin1());
67 m_mask = 0;
68 m_maskAsSelection = 0;
69 m_renderMask = false;
70 m_editMask = true;
71}
72
73KisPaintLayer::KisPaintLayer(const KisPaintLayer& rhs) :
74 KisLayer(rhs), KisLayerSupportsIndirectPainting(rhs)
75{
76 m_paintdev = new KisPaintDevice( *rhs.m_paintdev.data() );
77 m_paintdev->setParentLayer(this);
78 if (rhs.hasMask()) {
79 m_mask = new KisPaintDevice(*rhs.m_mask.data());
80 m_mask->setParentLayer(this);
81 }
82 m_renderMask = rhs.m_renderMask;
83 m_editMask = rhs.m_editMask;
84}
85
86KisLayerSP KisPaintLayer::clone() const
87{
88 return new KisPaintLayer(*this);
89}
90
91KisPaintLayer::~KisPaintLayer()
92{
93 if (m_paintdev != 0) {
94 m_paintdev->setParentLayer(0);
95 }
96 if (m_mask != 0) {
97 m_mask->setParentLayer(0);
98 }
99}
100
101void KisPaintLayer::paintSelection(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
102{
103 if (m_paintdev && m_paintdev->hasSelection()) {
104 m_paintdev->selection()->paintSelection(img, x, y, w, h);
105 } else if (m_mask && m_editMask && m_mask->hasSelection()) {
106 m_mask->selection()->paintSelection(img, x, y, w, h);
107 }
108}
109
110void KisPaintLayer::paintSelection(TQImage &img, const TQRect& scaledImageRect, const TQSize& scaledImageSize, const TQSize& imageSize)
111{
112 if (m_paintdev && m_paintdev->hasSelection()) {
113 m_paintdev->selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize);
114 } else if (m_mask && m_editMask && m_mask->hasSelection()) {
115 m_mask->selection()->paintSelection(img, scaledImageRect, scaledImageSize, imageSize);
116 }
117}
118
119void KisPaintLayer::paintMaskInactiveLayers(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
120{
121 uchar *j = img.bits();
122
123 KisColorSpace *cs = m_paintdev->colorSpace();
124
125 for (TQ_INT32 y2 = y; y2 < h + y; ++y2) {
126 KisHLineIteratorPixel it = m_paintdev->createHLineIterator(x, y2, w, false);
127 while ( ! it.isDone()) {
128 TQ_UINT8 s = cs->getAlpha(it.rawData());
129 if(s==0)
130 {
131 TQ_UINT8 g = (*(j + 0) + *(j + 1 ) + *(j + 2 )) / 9;
132
133 *(j+0) = 128+g ;
134 *(j+1) = 165+g;
135 *(j+2) = 128+g;
136 }
137 j+=4;
138 ++it;
139 }
140 }
141}
142
143TQImage KisPaintLayer::createThumbnail(TQ_INT32 w, TQ_INT32 h)
144{
145 if (m_paintdev)
146 return m_paintdev->createThumbnail(w, h);
147 else
148 return TQImage();
149}
150
151
152TQ_INT32 KisPaintLayer::x() const {
153 if (m_paintdev)
154 return m_paintdev->getX();
155 else return 0;
156}
157
158void KisPaintLayer::setX(TQ_INT32 x)
159{
160 if (m_paintdev)
161 m_paintdev->setX(x);
162}
163
164TQ_INT32 KisPaintLayer::y() const {
165 if (m_paintdev)
166 return m_paintdev->getY();
167 else
168 return 0;
169}
170
171void KisPaintLayer::setY(TQ_INT32 y) {
172 if (m_paintdev)
173 m_paintdev->setY(y);
174}
175
176TQRect KisPaintLayer::extent() const {
177 if (m_paintdev)
178 return m_paintdev->extent();
179 else
180 return TQRect();
181}
182
184 if (m_paintdev)
185 return m_paintdev->exactBounds();
186 else
187 return TQRect();
188}
189
191 if (!hasMask())
192 return;
193
194 m_mask->setParentLayer(0);
195 m_mask = 0;
196 m_maskAsSelection = 0;
197 setDirty();
198
199 emit sigMaskInfoChanged();
200}
201
202// ### XXX Do we apply the mask outside the image boundaries too? I'd say no, but I'm not sure
204 if (!hasMask())
205 return;
206
207 int x, y, w, h;
208 m_paintdev->extent(x, y, w, h);
209
210 // A bit slow; but it works
211 KisPaintDeviceSP temp = new KisPaintDevice(m_paintdev->colorSpace());
212 KisPainter gc(temp);
213 gc.bltSelection(x, y, COMPOSITE_OVER, m_paintdev, m_maskAsSelection, OPACITY_OPAQUE, x, y, w, h);
214 gc.end();
215 gc.begin(m_paintdev);
216 gc.bitBlt(x, y, COMPOSITE_COPY, temp, OPACITY_OPAQUE, x, y, w, h);
217 gc.end();
218
219 removeMask();
220}
221
222KisPaintDeviceSP KisPaintLayer::createMask() {
223 if (hasMask())
224 return m_mask;
225
226 kdDebug() << k_funcinfo << endl;
227 // Grey8 nicely fits our needs of being intuitively comparable to other apps'
228 // mask layer interfaces. It does have an alpha component though, which is a bit
229 // less appropriate in this context.
230 m_mask = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()
231 ->getColorSpace(KisID("GRAYA"), 0));
232
233 genericMaskCreationHelper();
234
235 return m_mask;
236}
237
238// FIXME If from is a paint device is not grey8!!
239void KisPaintLayer::createMaskFromPaintDevice(KisPaintDeviceSP from) {
240 if (hasMask())
241 return; // Or overwrite? XXX
242
243 kdDebug() << k_funcinfo << endl;
244 m_mask = from; // KisPaintDevice(*from); XXX
245
246 genericMaskCreationHelper();
247}
248
249void KisPaintLayer::createMaskFromSelection(KisSelectionSP from) {
250 kdDebug() << k_funcinfo << endl;
251 m_mask = new KisPaintDevice(KisMetaRegistry::instance()->csRegistry()
252 ->getColorSpace(KisID("GRAYA"), 0));
253 m_mask->setParentLayer(this);
254
255 m_maskAsSelection = new KisSelection(); // Anonymous selection is good enough
256
257 // Default pixel is opaque white == don't mask?
258 TQ_UINT8 const defPixel[] = { 255, 255 };
259 m_mask->dataManager()->setDefaultPixel(defPixel);
260
261 if (from) {
262 TQRect r(extent());
263
264 int w = r.width();
265 int h = r.height();
266 for (int y = r.y(); y < h; y++) {
267 KisHLineIteratorPixel srcIt = from->createHLineIterator(r.x(), y, w, false);
268 KisHLineIteratorPixel dstIt = m_mask->createHLineIterator(r.x(), y, w, true);
269
270 while(!dstIt.isDone()) {
271 // XXX same remark as in convertMaskToSelection
272 *dstIt.rawData() = *srcIt.rawData();
273 ++srcIt;
274 ++dstIt;
275 }
276 }
277 }
278
279 convertMaskToSelection(extent());
280 m_paintdev->deselect();
281
282 setDirty();
283 emit sigMaskInfoChanged();
284}
285
286KisPaintDeviceSP KisPaintLayer::getMask() {
287 createMask();
288 kdDebug() << k_funcinfo << endl;
289 return m_mask;
290}
291
293 createMask();
294 kdDebug() << k_funcinfo << endl;
295 return m_maskAsSelection;
296}
297
299 m_editMask = b;
300 emit sigMaskInfoChanged();
301}
302
304 m_renderMask = b;
305
306 if (hasMask())
307 setDirty();
308
309 emit sigMaskInfoChanged();
310}
311
312void KisPaintLayer::convertMaskToSelection(const TQRect& r) {
313 KisRectIteratorPixel srcIt = m_mask->createRectIterator(r.x(), r.y(),
314 r.width(), r.height(), false);
315 KisRectIteratorPixel dstIt = m_maskAsSelection->createRectIterator(r.x(), r.y(),
316 r.width(), r.height(), true);
317
318 while(!dstIt.isDone()) {
319 // src is grey8 (grey + alpha), dst is alpha8. We convert the grey value to
320 // alpha8 manually and ignore the alpha (that's why we don't convert using default
321 // functions, and interpret the data raw!) [ XXX ]
322 *dstIt.rawData() = *srcIt.rawData();
323 ++srcIt;
324 ++dstIt;
325 }
326}
327
328void KisPaintLayer::genericMaskCreationHelper() {
329 m_mask->setParentLayer(this);
330
331 m_maskAsSelection = new KisSelection(); // Anonymous selection is good enough
332
333 // Default pixel is opaque white == don't mask?
334 TQ_UINT8 const defPixel[] = { 255, 255 };
335 m_mask->dataManager()->setDefaultPixel(defPixel);
336
337 setDirty();
338 emit sigMaskInfoChanged();
339}
340
341void KisPaintLayer::setDirty(bool propagate) {
342 if (hasMask())
343 convertMaskToSelection(extent());
344 super::setDirty(propagate);
345}
346
347void KisPaintLayer::setDirty(const TQRect & rect, bool propagate) {
348 if (hasMask())
349 convertMaskToSelection(rect);
350 super::setDirty(rect, propagate);
351}
352
353// Undoable versions code
354namespace {
355 class KisCreateMaskCommand : public KNamedCommand {
356 typedef KNamedCommand super;
357 KisPaintLayerSP m_layer;
358 KisPaintDeviceSP m_mask;
359 public:
360 KisCreateMaskCommand(const TQString& name, KisPaintLayer* layer)
361 : super(name), m_layer(layer) {}
362 virtual void execute() {
363 kdDebug() << k_funcinfo << endl;
364 if (!m_mask)
365 m_mask = m_layer->createMask();
366 else
367 m_layer->createMaskFromPaintDevice(m_mask);
368 }
369 virtual void unexecute() {
370 m_layer->removeMask();
371 }
372 };
373
374 class KisMaskFromSelectionCommand : public KNamedCommand {
375 typedef KNamedCommand super;
376 KisPaintLayerSP m_layer;
377 KisPaintDeviceSP m_maskBefore;
378 KisPaintDeviceSP m_maskAfter;
379 KisSelectionSP m_selection;
380 public:
381 KisMaskFromSelectionCommand(const TQString& name, KisPaintLayer* layer)
382 : super(name), m_layer(layer) {
383 if (m_layer->hasMask())
384 m_maskBefore = m_layer->getMask();
385 else
386 m_maskBefore = 0;
387 m_maskAfter = 0;
388 if (m_layer->paintDevice()->hasSelection())
389 m_selection = m_layer->paintDevice()->selection();
390 else
391 m_selection = 0;
392 }
393 virtual void execute() {
394 if (!m_maskAfter) {
395 m_layer->createMaskFromSelection(m_selection);
396 m_maskAfter = m_layer->getMask();
397 } else {
398 m_layer->paintDevice()->deselect();
399 m_layer->createMaskFromPaintDevice(m_maskAfter);
400 }
401 }
402 virtual void unexecute() {
403 m_layer->paintDevice()->setSelection(m_selection);
404 if (m_maskBefore)
405 m_layer->createMaskFromPaintDevice(m_maskBefore);
406 else
407 m_layer->removeMask();
408 }
409 };
410
411 class KisMaskToSelectionCommand : public KNamedCommand {
412 typedef KNamedCommand super;
413 KisPaintLayerSP m_layer;
414 KisPaintDeviceSP m_mask;
415 KisSelectionSP m_selection;
416 public:
417 KisMaskToSelectionCommand(const TQString& name, KisPaintLayer* layer)
418 : super(name), m_layer(layer) {
419 m_mask = m_layer->getMask();
420 if (m_layer->paintDevice()->hasSelection())
421 m_selection = m_layer->paintDevice()->selection();
422 else
423 m_selection = 0;
424 }
425 virtual void execute() {
426 m_layer->paintDevice()->setSelection(m_layer->getMaskAsSelection());
427 m_layer->removeMask();
428 }
429 virtual void unexecute() {
430 if (m_selection)
431 m_layer->paintDevice()->setSelection(m_selection);
432 else
433 m_layer->paintDevice()->deselect();
434 m_layer->createMaskFromPaintDevice(m_mask);
435 }
436 };
437
438 class KisRemoveMaskCommand : public KNamedCommand {
439 typedef KNamedCommand super;
440 KisPaintLayerSP m_layer;
441 KisPaintDeviceSP m_mask;
442 public:
443 KisRemoveMaskCommand(const TQString& name, KisPaintLayer* layer)
444 : super(name), m_layer(layer) {
445 m_mask = m_layer->getMask();
446 }
447 virtual void execute() {
448 kdDebug() << k_funcinfo << endl;
449 m_layer->removeMask();
450 }
451 virtual void unexecute() {
452 // I hope that if the undo stack unwinds, it will end up here in the right
453 // state again; taking a deep-copy sounds like wasteful to me
454 m_layer->createMaskFromPaintDevice(m_mask);
455 }
456 };
457
458 class KisApplyMaskCommand : public KNamedCommand {
459 typedef KNamedCommand super;
460 KisPaintLayerSP m_layer;
461 KisPaintDeviceSP m_mask;
462 KisPaintDeviceSP m_original;
463 public:
464 KisApplyMaskCommand(const TQString& name, KisPaintLayer* layer)
465 : super(name), m_layer(layer) {
466 m_mask = m_layer->getMask();
467 m_original = new KisPaintDevice(*m_layer->paintDevice());
468 }
469 virtual void execute() {
470 m_layer->applyMask();
471 }
472 virtual void unexecute() {
473 // I hope that if the undo stack unwinds, it will end up here in the right
474 // state again; taking a deep-copy sounds like wasteful to me
475 KisPainter gc(m_layer->paintDevice());
476 int x, y, w, h;
477 m_layer->paintDevice()->extent(x, y, w, h);
478
479 gc.bitBlt(x, y, COMPOSITE_COPY, m_original, OPACITY_OPAQUE, x, y, w, h);
480 gc.end();
481
482 m_layer->createMaskFromPaintDevice(m_mask);
483 }
484 };
485}
486
488 return new KisCreateMaskCommand(i18n("Create Layer Mask"), this);
489}
490
492 return new KisMaskFromSelectionCommand(i18n("Mask From Selection"), this);
493}
494
496 return new KisMaskToSelectionCommand(i18n("Mask to Selection"), this);
497}
498
499
501 return new KisRemoveMaskCommand(i18n("Remove Layer Mask"), this);
502}
503
505 return new KisApplyMaskCommand(i18n("Apply Layer Mask"), this);
506}
507
508
509#include "kis_paint_layer.moc"
A colorspace is the definition of a certain color model in Chalk.
virtual TQ_UINT8 getAlpha(const TQ_UINT8 *pixel) const =0
Get the alpha value of the given pixel, downscaled to an 8-bit value.
The pixel iterators are high level iterarators.
Chalk has a large number of extensible resources.
Definition kis_id.h:57
Abstract class that represents the concept of a Layer in Chalk.
Definition kis_layer.h:42
virtual void setDirty(bool propagate=true)
Set the entire layer extent dirty; this percolates up to parent layers all the way to the root layer.
A paint device contains the actual pixel data and offers methods to read and write pixels.
This layer is of a type that can be painted on.
virtual TQRect extent() const
Returns an approximation of where the bounds on actual data are in this layer.
KisPaintDeviceSP createMask()
Create a mask if it does not yet exist, and return it.
virtual KisLayerSP clone() const
Return a copy of this layer.
KisPaintDeviceSP getMask()
Returns the layer mask's device. Creates one if there is currently none.
void setEditMask(bool b)
Sets the editMask property.
virtual void setDirty(bool propagate=true)
Overridden to call the private convertMaskToSelection.
KNamedCommand * createMaskCommand()
Undoable version of createMask.
void createMaskFromPaintDevice(KisPaintDeviceSP from)
Convert the from argument to the mask.
void setRenderMask(bool b)
Set the renderMask property.
void sigMaskInfoChanged()
When the mask is created/destroyed or the editmask or rendermask is changed.
virtual void paintMaskInactiveLayers(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
paints where no data is on this layer. Useful when it is a transparent layer stacked on top of anothe...
virtual TQImage createThumbnail(TQ_INT32 w, TQ_INT32 h)
Returns a thumbnail in requested size.
KNamedCommand * applyMaskCommand()
Undoable version of applyMask.
KNamedCommand * maskFromSelectionCommand()
Undoable version of createMaskFromSelection.
KisSelectionSP getMaskAsSelection()
Returns the layer mask's device, converted to a selection. Creates one if there is currently none.
void removeMask()
Remove the layer mask.
KNamedCommand * removeMaskCommand()
Undoable version of removeMask.
virtual void paintSelection(TQImage &img, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
paints a mask where the selection on this layer resides
void applyMask()
Apply the layer mask to the paint device, this removes the mask afterwards.
void createMaskFromSelection(KisSelectionSP from)
Convert the from selection to a paint device (should convert the getMaskAsSelection result back to th...
virtual TQRect exactBounds() const
Returns the exact bounds of where the actual data resides in this layer.
bool hasMask() const
Does this layer have a layer mask?
KNamedCommand * maskToSelectionCommand()
Undoable, removes the current mask, but converts it to the current selection.
KisPainter contains the graphics primitives necessary to draw on a KisPaintDevice.
Definition kis_painter.h:56
KCommand * end()
Finish painting on the current device.
void bitBlt(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh)
Blast the specified region from src onto the current paint device.
void begin(KisPaintDeviceSP device)
Start painting on the specified device.
void bltSelection(TQ_INT32 dx, TQ_INT32 dy, const KisCompositeOp &op, KisPaintDeviceSP src, KisSelectionSP selMask, TQ_UINT8 opacity, TQ_INT32 sx, TQ_INT32 sy, TQ_INT32 sw, TQ_INT32 sh)
A version of bitBlt that renders using an external selection mask, ignoring the src device's own sele...
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
KisSelection contains a byte-map representation of a layer, where the value of a byte signifies wheth...