26#include "kis_debug_areas.h"
28#include "kis_colorspace_factory_registry.h"
29#include "kis_fill_painter.h"
30#include "kis_iterators_pixel.h"
31#include "kis_integer_maths.h"
33#include "kis_datamanager.h"
34#include "kis_fill_painter.h"
35#include "kis_selection.h"
40 , (TQString(
"selection for ") + dev->name()).latin1())
41 , m_parentPaintDevice(dev)
42 , m_doCacheExactRect(false)
49 : super(
KisMetaRegistry::instance()->csRegistry()->getAlpha8(),
"anonymous selection")
50 , m_parentPaintDevice(0), m_dirty(false)
55 : super(rhs), m_parentPaintDevice(rhs.m_parentPaintDevice), m_doCacheExactRect(rhs.m_doCacheExactRect),
56 m_cachedExactRect(rhs.m_cachedExactRect), m_dirty(rhs.m_dirty)
60KisSelection::~KisSelection()
64TQ_UINT8 KisSelection::selected(TQ_INT32 x, TQ_INT32 y)
68 TQ_UINT8 *pix = iter.rawData();
73void KisSelection::setSelected(TQ_INT32 x, TQ_INT32 y, TQ_UINT8 s)
77 TQ_UINT8 *pix = iter.rawData();
82TQImage KisSelection::maskImage()
87 if (m_parentPaintDevice) {
89 bounds = m_parentPaintDevice->exactBounds();
90 bounds = bounds.intersect( m_parentPaintDevice->image()->bounds() );
91 img = TQImage(bounds.width(), bounds.height(), 32);
94 bounds = TQRect( 0, 0,
image()->width(),
image()->height());
95 img = TQImage(bounds.width(), bounds.height(), 32);
98 KisHLineIteratorPixel it =
createHLineIterator(bounds.x(), bounds.y(), bounds.width(),
false);
99 for (
int y2 = bounds.y(); y2 < bounds.height() - bounds.y(); ++y2) {
101 while (!it.isDone()) {
102 TQ_UINT8 s = MAX_SELECTED - *(it.rawData());
103 TQ_INT32 c = tqRgb(s, s, s);
104 img.setPixel(x2, y2, c);
112void KisSelection::select(TQRect r)
114 KisFillPainter painter(
this);
115 KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->
getRGB8();
116 painter.fillRect(r, KisColor(TQt::white, cs), MAX_SELECTED);
123 KisFillPainter painter(
this);
124 KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->
getRGB8();
125 painter.fillRect(r, KisColor(TQt::white, cs), MIN_SELECTED);
130 TQ_UINT8 defPixel = MIN_SELECTED;
131 m_datamanager->setDefaultPixel(&defPixel);
132 m_datamanager->clear();
135void KisSelection::invert()
148 TQ_UINT8 defPixel = MAX_SELECTED - *(m_datamanager->defaultPixel());
149 m_datamanager->setDefaultPixel(&defPixel);
154 if(*(m_datamanager->defaultPixel()) != MIN_SELECTED)
157 return ! r.intersects(sr);
162 if(*(m_datamanager->defaultPixel()) != MIN_SELECTED)
165 return ! r.intersects(sr);
171 if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice)
174 return extent().unite(m_parentPaintDevice->extent());
179 if(m_doCacheExactRect)
180 return m_cachedExactRect;
181 else if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice)
182 return exactBounds();
184 return exactBounds().unite(m_parentPaintDevice->exactBounds());
187void KisSelection::stopCachingExactRect()
189 kdDebug() <<
"stop caching the exact rect" << endl;
190 m_doCacheExactRect =
false;
194void KisSelection::startCachingExactRect()
196 kdDebug() <<
"start caching the exact rect" << endl;
197 if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice)
198 m_cachedExactRect = exactBounds();
200 m_cachedExactRect = exactBounds().unite(m_parentPaintDevice->exactBounds());
201 m_doCacheExactRect =
true;
204void KisSelection::paintUniformSelectionRegion(TQImage img,
const TQRect& imageRect,
const TQRegion& uniformRegion)
206 Q_ASSERT(img.size() == imageRect.size());
207 Q_ASSERT(imageRect.contains(uniformRegion.boundingRect()));
209 if (img.isNull() || img.size() != imageRect.size() || !imageRect.contains(uniformRegion.boundingRect())) {
213 if (*m_datamanager->defaultPixel() == MIN_SELECTED) {
215 TQRegion region = uniformRegion & TQRegion(imageRect);
217 if (!region.isEmpty()) {
218 TQMemArray<TQRect> rects = region.rects();
220 for (
unsigned int i = 0; i < rects.count(); i++) {
223 for (TQ_INT32 y = 0; y < r.height(); ++y) {
225 TQRgb *imagePixel =
reinterpret_cast<TQRgb *
>(img.scanLine(r.y() - imageRect.y() + y));
226 imagePixel += r.x() - imageRect.x();
228 TQ_INT32 numPixels = r.width();
230 while (numPixels > 0) {
232 TQRgb srcPixel = *imagePixel;
233 TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9;
234 TQ_UINT8 srcAlpha = tqAlpha(srcPixel);
236 srcGrey = UINT8_MULT(srcGrey, srcAlpha);
237 TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192);
239 TQRgb dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha);
240 *imagePixel = dstPixel;
251void KisSelection::paintSelection(TQImage img, TQ_INT32 imageRectX, TQ_INT32 imageRectY, TQ_INT32 imageRectWidth, TQ_INT32 imageRectHeight)
253 Q_ASSERT(img.size() == TQSize(imageRectWidth, imageRectHeight));
255 if (img.isNull() || img.size() != TQSize(imageRectWidth, imageRectHeight)) {
259 TQRect imageRect(imageRectX, imageRectY, imageRectWidth, imageRectHeight);
260 TQRect selectionExtent = extent();
262 selectionExtent.setLeft(selectionExtent.left() - 1);
263 selectionExtent.setTop(selectionExtent.top() - 1);
264 selectionExtent.setWidth(selectionExtent.width() + 2);
265 selectionExtent.setHeight(selectionExtent.height() + 2);
267 TQRegion uniformRegion = TQRegion(imageRect);
268 uniformRegion -= TQRegion(selectionExtent);
270 if (!uniformRegion.isEmpty()) {
271 paintUniformSelectionRegion(img, imageRect, uniformRegion);
274 TQRect nonuniformRect = imageRect & selectionExtent;
276 if (!nonuniformRect.isEmpty()) {
278 const TQ_INT32 imageRectOffsetX = nonuniformRect.x() - imageRectX;
279 const TQ_INT32 imageRectOffsetY = nonuniformRect.y() - imageRectY;
281 imageRectX = nonuniformRect.x();
282 imageRectY = nonuniformRect.y();
283 imageRectWidth = nonuniformRect.width();
284 imageRectHeight = nonuniformRect.height();
286 const TQ_INT32 NUM_SELECTION_ROWS = 3;
288 TQ_UINT8 *selectionRow[NUM_SELECTION_ROWS];
290 TQ_INT32 aboveRowIndex = 0;
291 TQ_INT32 centreRowIndex = 1;
292 TQ_INT32 belowRowIndex = 2;
294 selectionRow[aboveRowIndex] =
new TQ_UINT8[imageRectWidth + 2];
295 selectionRow[centreRowIndex] =
new TQ_UINT8[imageRectWidth + 2];
296 selectionRow[belowRowIndex] =
new TQ_UINT8[imageRectWidth + 2];
298 readBytes(selectionRow[centreRowIndex], imageRectX - 1, imageRectY - 1, imageRectWidth + 2, 1);
299 readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY, imageRectWidth + 2, 1);
301 for (TQ_INT32 y = 0; y < imageRectHeight; ++y) {
303 TQ_INT32 oldAboveRowIndex = aboveRowIndex;
304 aboveRowIndex = centreRowIndex;
305 centreRowIndex = belowRowIndex;
306 belowRowIndex = oldAboveRowIndex;
308 readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY + y + 1, imageRectWidth + 2, 1);
310 const TQ_UINT8 *aboveRow = selectionRow[aboveRowIndex] + 1;
311 const TQ_UINT8 *centreRow = selectionRow[centreRowIndex] + 1;
312 const TQ_UINT8 *belowRow = selectionRow[belowRowIndex] + 1;
314 TQRgb *imagePixel =
reinterpret_cast<TQRgb *
>(img.scanLine(imageRectOffsetY + y));
315 imagePixel += imageRectOffsetX;
317 for (TQ_INT32 x = 0; x < imageRectWidth; ++x) {
319 TQ_UINT8 centre = *centreRow;
321 if (centre != MAX_SELECTED) {
325 TQRgb srcPixel = *imagePixel;
326 TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9;
327 TQ_UINT8 srcAlpha = tqAlpha(srcPixel);
330 srcGrey = UINT8_MULT(srcGrey, srcAlpha);
334 if (centre == MIN_SELECTED) {
337 TQ_UINT8 left = *(centreRow - 1);
338 TQ_UINT8 right = *(centreRow + 1);
339 TQ_UINT8 above = *aboveRow;
340 TQ_UINT8 below = *belowRow;
344 TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192);
347 if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) {
348 dstPixel = tqRgba(255, 0, 0, dstAlpha);
350 dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha);
353 dstPixel = tqRgba(UINT8_BLEND(tqRed(srcPixel), srcGrey + 128, centre),
354 UINT8_BLEND(tqGreen(srcPixel), srcGrey + 128, centre),
355 UINT8_BLEND(tqBlue(srcPixel), srcGrey + 165, centre),
359 *imagePixel = dstPixel;
369 delete [] selectionRow[aboveRowIndex];
370 delete [] selectionRow[centreRowIndex];
371 delete [] selectionRow[belowRowIndex];
375void KisSelection::paintSelection(TQImage img,
const TQRect& scaledImageRect,
const TQSize& scaledImageSize,
const TQSize& imageSize)
377 if (img.isNull() || scaledImageRect.isEmpty() || scaledImageSize.isEmpty() || imageSize.isEmpty()) {
381 Q_ASSERT(img.size() == scaledImageRect.size());
383 if (img.size() != scaledImageRect.size()) {
387 TQ_INT32 imageWidth = imageSize.width();
388 TQ_INT32 imageHeight = imageSize.height();
390 TQRect selectionExtent = extent();
392 selectionExtent.setLeft(selectionExtent.left() - 1);
393 selectionExtent.setTop(selectionExtent.top() - 1);
394 selectionExtent.setWidth(selectionExtent.width() + 2);
395 selectionExtent.setHeight(selectionExtent.height() + 2);
397 double xScale =
static_cast<double>(scaledImageSize.width()) / imageWidth;
398 double yScale =
static_cast<double>(scaledImageSize.height()) / imageHeight;
400 TQRect scaledSelectionExtent;
402 scaledSelectionExtent.setLeft(
static_cast<int>(selectionExtent.left() * xScale));
403 scaledSelectionExtent.setRight(
static_cast<int>(ceil((selectionExtent.right() + 1) * xScale)) - 1);
404 scaledSelectionExtent.setTop(
static_cast<int>(selectionExtent.top() * yScale));
405 scaledSelectionExtent.setBottom(
static_cast<int>(ceil((selectionExtent.bottom() + 1) * yScale)) - 1);
407 TQRegion uniformRegion = TQRegion(scaledImageRect);
408 uniformRegion -= TQRegion(scaledSelectionExtent);
410 if (!uniformRegion.isEmpty()) {
411 paintUniformSelectionRegion(img, scaledImageRect, uniformRegion);
414 TQRect nonuniformRect = scaledImageRect & scaledSelectionExtent;
416 if (!nonuniformRect.isEmpty()) {
418 const TQ_INT32 scaledImageRectXOffset = nonuniformRect.x() - scaledImageRect.x();
419 const TQ_INT32 scaledImageRectYOffset = nonuniformRect.y() - scaledImageRect.y();
421 const TQ_INT32 scaledImageRectX = nonuniformRect.x();
422 const TQ_INT32 scaledImageRectY = nonuniformRect.y();
423 const TQ_INT32 scaledImageRectWidth = nonuniformRect.width();
424 const TQ_INT32 scaledImageRectHeight = nonuniformRect.height();
426 const TQ_INT32 imageRowLeft =
static_cast<TQ_INT32
>(scaledImageRectX / xScale);
427 const TQ_INT32 imageRowRight =
static_cast<TQ_INT32
>((ceil((scaledImageRectX + scaledImageRectWidth - 1 + 1) / xScale)) - 1);
429 const TQ_INT32 imageRowWidth = imageRowRight - imageRowLeft + 1;
430 const TQ_INT32 imageRowStride = imageRowWidth + 2;
432 const TQ_INT32 NUM_SELECTION_ROWS = 3;
434 TQ_INT32 aboveRowIndex = 0;
435 TQ_INT32 centreRowIndex = 1;
436 TQ_INT32 belowRowIndex = 2;
438 TQ_INT32 aboveRowSrcY = -3;
439 TQ_INT32 centreRowSrcY = -3;
440 TQ_INT32 belowRowSrcY = -3;
442 TQ_UINT8 *selectionRows =
new TQ_UINT8[imageRowStride * NUM_SELECTION_ROWS];
443 TQ_UINT8 *selectionRow[NUM_SELECTION_ROWS];
445 selectionRow[0] = selectionRows + 1;
446 selectionRow[1] = selectionRow[0] + imageRowStride;
447 selectionRow[2] = selectionRow[0] + (2 * imageRowStride);
449 for (TQ_INT32 y = 0; y < scaledImageRectHeight; ++y) {
451 TQ_INT32 scaledY = scaledImageRectY + y;
452 TQ_INT32 srcY = (scaledY * imageHeight) / scaledImageSize.height();
458 if (srcY - 1 == aboveRowSrcY) {
459 aboveRow = selectionRow[aboveRowIndex];
460 centreRow = selectionRow[centreRowIndex];
461 belowRow = selectionRow[belowRowIndex];
462 }
else if (srcY - 1 == centreRowSrcY) {
464 TQ_INT32 oldAboveRowIndex = aboveRowIndex;
466 aboveRowIndex = centreRowIndex;
467 centreRowIndex = belowRowIndex;
468 belowRowIndex = oldAboveRowIndex;
470 aboveRow = selectionRow[aboveRowIndex];
471 centreRow = selectionRow[centreRowIndex];
472 belowRow = selectionRow[belowRowIndex];
474 readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1);
476 }
else if (srcY - 1 == belowRowSrcY) {
478 TQ_INT32 oldAboveRowIndex = aboveRowIndex;
479 TQ_INT32 oldCentreRowIndex = centreRowIndex;
481 aboveRowIndex = belowRowIndex;
482 centreRowIndex = oldAboveRowIndex;
483 belowRowIndex = oldCentreRowIndex;
485 aboveRow = selectionRow[aboveRowIndex];
486 centreRow = selectionRow[centreRowIndex];
487 belowRow = selectionRow[belowRowIndex];
489 if (belowRowIndex == centreRowIndex + 1) {
490 readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 2);
492 readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 1);
493 readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1);
502 aboveRow = selectionRow[aboveRowIndex];
503 centreRow = selectionRow[centreRowIndex];
504 belowRow = selectionRow[belowRowIndex];
506 readBytes(selectionRows, imageRowLeft - 1, srcY - 1, imageRowStride, NUM_SELECTION_ROWS);
509 aboveRowSrcY = srcY - 1;
510 centreRowSrcY = aboveRowSrcY + 1;
511 belowRowSrcY = centreRowSrcY + 1;
513 TQRgb *imagePixel =
reinterpret_cast<TQRgb *
>(img.scanLine(scaledImageRectYOffset + y));
514 imagePixel += scaledImageRectXOffset;
516 for (TQ_INT32 x = 0; x < scaledImageRectWidth; ++x) {
518 TQ_INT32 scaledX = scaledImageRectX + x;
519 TQ_INT32 srcX = (scaledX * imageWidth) / scaledImageSize.width();
521 TQ_UINT8 centre = *(centreRow + srcX - imageRowLeft);
523 if (centre != MAX_SELECTED) {
527 TQRgb srcPixel = *imagePixel;
528 TQ_UINT8 srcGrey = (tqRed(srcPixel) + tqGreen(srcPixel) + tqBlue(srcPixel)) / 9;
529 TQ_UINT8 srcAlpha = tqAlpha(srcPixel);
532 srcGrey = UINT8_MULT(srcGrey, srcAlpha);
536 if (centre == MIN_SELECTED) {
539 TQ_UINT8 left = *(centreRow + (srcX - imageRowLeft) - 1);
540 TQ_UINT8 right = *(centreRow + (srcX - imageRowLeft) + 1);
541 TQ_UINT8 above = *(aboveRow + (srcX - imageRowLeft));
542 TQ_UINT8 below = *(belowRow + (srcX - imageRowLeft));
546 TQ_UINT8 dstAlpha = TQMAX(srcAlpha, 192);
549 if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) {
550 dstPixel = tqRgba(255, 0, 0, dstAlpha);
552 dstPixel = tqRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha);
555 dstPixel = tqRgba(UINT8_BLEND(tqRed(srcPixel), srcGrey + 128, centre),
556 UINT8_BLEND(tqGreen(srcPixel), srcGrey + 128, centre),
557 UINT8_BLEND(tqBlue(srcPixel), srcGrey + 165, centre),
561 *imagePixel = dstPixel;
568 delete [] selectionRows;
KisColorSpace * getRGB8()
Convenience method to get an RGB colorspace with the default lcms profile.
KisImage * image() const
Return the image that contains this paint device, or 0 if it is not part of an image.
KisRectIteratorPixel createRectIterator(TQ_INT32 left, TQ_INT32 top, TQ_INT32 w, TQ_INT32 h, bool writable)
This function return an iterator which points to the first pixel of an rectangle.
KisLayer * parentLayer() const
Returns the KisLayer that contains this paint device, or 0 if this is not part of a layer.
virtual void readBytes(TQ_UINT8 *data, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h)
Read the bytes representing the rectangle described by x, y, w, h into data.
virtual void setDirty()
Set the parent layer completely dirty, if this paint device has one.
KisHLineIteratorPixel createHLineIterator(TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, bool writable)
This function return an iterator which points to the first pixel of a horizontal line.
The KisRectIterator iterators over a rectangular area in the most efficient order.
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
TQRect selectedRect() const
Rough, but fastish way of determining the area of the tiles used by the selection.
TQRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.
KisSelection()
Create a new KisSelection.
virtual void setDirty()
Set the parent layer completely dirty, if this paint device has one.
bool isTotallyUnselected(TQRect r)
Tests if the the rect is totally outside the selection.
void clear()
Complete erase the current paint device.
KisSelection(KisPaintDeviceSP dev)
Create a new KisSelection.
bool isProbablyTotallyUnselected(TQRect r)
Tests if the the rect is totally outside the selection, but uses selectedRect instead of selectedRect...