krita

kis_selection.cc

00001 /*
00002  *  Copyright (c) 2004 Boudewijn Rempt <boud@valdyas.org>
00003  *
00004  *  this program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the gnu general public license as published by
00006  *  the free software foundation; either version 2 of the license, or
00007  *  (at your option) any later version.
00008  *
00009  *  this program is distributed in the hope that it will be useful,
00010  *  but without any warranty; without even the implied warranty of
00011  *  merchantability or fitness for a particular purpose.  see the
00012  *  gnu general public license for more details.
00013  *
00014  *  you should have received a copy of the gnu general public license
00015  *  along with this program; if not, write to the free software
00016  *  foundation, inc., 675 mass ave, cambridge, ma 02139, usa.
00017  */
00018 
00019 #include <qimage.h>
00020 
00021 #include <kdebug.h>
00022 #include <klocale.h>
00023 #include <qcolor.h>
00024 
00025 #include "kis_layer.h"
00026 #include "kis_debug_areas.h"
00027 #include "kis_types.h"
00028 #include "kis_colorspace_factory_registry.h"
00029 #include "kis_fill_painter.h"
00030 #include "kis_iterators_pixel.h"
00031 #include "kis_integer_maths.h"
00032 #include "kis_image.h"
00033 #include "kis_datamanager.h"
00034 #include "kis_fill_painter.h"
00035 #include "kis_selection.h"
00036 
00037 KisSelection::KisSelection(KisPaintDeviceSP dev)
00038     : super(dev->parentLayer(), KisMetaRegistry::instance()->csRegistry()->getAlpha8(), (QString("selection for ") + dev->name()).latin1())
00039     , m_parentPaintDevice(dev)
00040 {
00041     Q_ASSERT(dev);
00042 }
00043 
00044 KisSelection::KisSelection()
00045     : super(KisMetaRegistry::instance()->csRegistry()->getAlpha8(), "anonymous selection")
00046     , m_parentPaintDevice(0)
00047 {
00048 }
00049 
00050 KisSelection::KisSelection(const KisSelection& rhs)
00051     : super(rhs)
00052 {
00053     m_parentPaintDevice = rhs.m_parentPaintDevice;
00054 }
00055 
00056 KisSelection::~KisSelection()
00057 {
00058 }
00059 
00060 Q_UINT8 KisSelection::selected(Q_INT32 x, Q_INT32 y)
00061 {
00062     KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, false);
00063 
00064     Q_UINT8 *pix = iter.rawData();
00065 
00066     return *pix;
00067 }
00068 
00069 void KisSelection::setSelected(Q_INT32 x, Q_INT32 y, Q_UINT8 s)
00070 {
00071     KisHLineIteratorPixel iter = createHLineIterator(x, y, 1, true);
00072 
00073     Q_UINT8 *pix = iter.rawData();
00074 
00075     *pix = s;
00076 }
00077 
00078 QImage KisSelection::maskImage()
00079 {
00080     // If part of a KisAdjustmentLayer, there may be no parent device.
00081     QImage img;
00082     Q_INT32 x, y, w, h, y2, x2;
00083     if (m_parentPaintDevice) {
00084 
00085         m_parentPaintDevice->exactBounds(x, y, w, h);
00086         img = QImage(w, h, 32);
00087     }
00088     else {
00089         x = 0;
00090         y = 0;
00091         w = image()->width();
00092         h = image()->width();
00093         img = QImage(w, h, 32);
00094     }
00095 
00096     for (y2 = y; y2 < h - y; ++y2) {
00097             KisHLineIteratorPixel it = createHLineIterator(x, y2, w, false);
00098             x2 = 0;
00099             while (!it.isDone()) {
00100                     Q_UINT8 s = MAX_SELECTED - *(it.rawData());
00101                     Q_INT32 c = qRgb(s, s, s);
00102                     img.setPixel(x2, y2, c);
00103                     ++x2;
00104                     ++it;
00105             }
00106     }
00107     return img;
00108 }
00109 void KisSelection::select(QRect r)
00110 {
00111     KisFillPainter painter(this);
00112     KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8();
00113     painter.fillRect(r, KisColor(Qt::white, cs), MAX_SELECTED);
00114     Q_INT32 x, y, w, h;
00115     extent(x, y, w, h);
00116 }
00117 
00118 void KisSelection::clear(QRect r)
00119 {
00120     KisFillPainter painter(this);
00121     KisColorSpace * cs = KisMetaRegistry::instance()->csRegistry()->getRGB8();
00122     painter.fillRect(r, KisColor(Qt::white, cs), MIN_SELECTED);
00123 }
00124 
00125 void KisSelection::clear()
00126 {
00127     Q_UINT8 defPixel = MIN_SELECTED;
00128     m_datamanager->setDefaultPixel(&defPixel);
00129     m_datamanager->clear();
00130 }
00131 
00132 void KisSelection::invert()
00133 {
00134     Q_INT32 x,y,w,h;
00135 
00136     extent(x, y, w, h);
00137     KisRectIterator it = createRectIterator(x, y, w, h, true);
00138     while ( ! it.isDone() )
00139     {
00140         // CBR this is wrong only first byte is inverted
00141         // BSAR: But we have always only one byte in this color model :-).
00142         *(it.rawData()) = MAX_SELECTED - *(it.rawData());
00143         ++it;
00144     }
00145     Q_UINT8 defPixel = MAX_SELECTED - *(m_datamanager->defaultPixel());
00146     m_datamanager->setDefaultPixel(&defPixel);
00147 }
00148 
00149 bool KisSelection::isTotallyUnselected(QRect r)
00150 {
00151     if(*(m_datamanager->defaultPixel()) != MIN_SELECTED)
00152         return false;
00153     QRect sr = selectedExactRect();
00154     return ! r.intersects(sr);
00155 }
00156 
00157 QRect KisSelection::selectedRect()
00158 {
00159     if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice)
00160         return extent();
00161     else
00162         return extent().unite(m_parentPaintDevice->extent());
00163 }
00164 
00165 QRect KisSelection::selectedExactRect()
00166 {
00167     if(*(m_datamanager->defaultPixel()) == MIN_SELECTED || !m_parentPaintDevice)
00168         return exactBounds();
00169     else
00170         return exactBounds().unite(m_parentPaintDevice->exactBounds());
00171 }
00172 
00173 void KisSelection::paintUniformSelectionRegion(QImage img, const QRect& imageRect, const QRegion& uniformRegion)
00174 {
00175     Q_ASSERT(img.size() == imageRect.size());
00176     Q_ASSERT(imageRect.contains(uniformRegion.boundingRect()));
00177 
00178     if (img.isNull() || img.size() != imageRect.size() || !imageRect.contains(uniformRegion.boundingRect())) {
00179         return;
00180     }
00181 
00182     if (*m_datamanager->defaultPixel() == MIN_SELECTED) {
00183 
00184         QRegion region = uniformRegion & QRegion(imageRect);
00185 
00186         if (!region.isEmpty()) {
00187             QMemArray<QRect> rects = region.rects();
00188 
00189             for (unsigned int i = 0; i < rects.count(); i++) {
00190                 QRect r = rects[i];
00191 
00192                 for (Q_INT32 y = 0; y < r.height(); ++y) {
00193 
00194                     QRgb *imagePixel = reinterpret_cast<QRgb *>(img.scanLine(r.y() - imageRect.y() + y));
00195                     imagePixel += r.x() - imageRect.x();
00196 
00197                     Q_INT32 numPixels = r.width();
00198 
00199                     while (numPixels > 0) {
00200 
00201                         QRgb srcPixel = *imagePixel;
00202                         Q_UINT8 srcGrey = (qRed(srcPixel) + qGreen(srcPixel) + qBlue(srcPixel)) / 9;
00203                         Q_UINT8 srcAlpha = qAlpha(srcPixel);
00204 
00205                         srcGrey = UINT8_MULT(srcGrey, srcAlpha);
00206                         Q_UINT8 dstAlpha = QMAX(srcAlpha, 192);
00207 
00208                         QRgb dstPixel = qRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha);
00209                         *imagePixel = dstPixel;
00210 
00211                         ++imagePixel;
00212                         --numPixels;
00213                     }
00214                 }
00215             }
00216         }
00217     }
00218 }
00219 
00220 void KisSelection::paintSelection(QImage img, Q_INT32 imageRectX, Q_INT32 imageRectY, Q_INT32 imageRectWidth, Q_INT32 imageRectHeight)
00221 {
00222     Q_ASSERT(img.size() == QSize(imageRectWidth, imageRectHeight));
00223 
00224     if (img.isNull() || img.size() != QSize(imageRectWidth, imageRectHeight)) {
00225         return;
00226     }
00227 
00228     QRect imageRect(imageRectX, imageRectY, imageRectWidth, imageRectHeight);
00229     QRect selectionExtent = extent();
00230 
00231     selectionExtent.setLeft(selectionExtent.left() - 1);
00232     selectionExtent.setTop(selectionExtent.top() - 1);
00233     selectionExtent.setWidth(selectionExtent.width() + 2);
00234     selectionExtent.setHeight(selectionExtent.height() + 2);
00235 
00236     QRegion uniformRegion = QRegion(imageRect);
00237     uniformRegion -= QRegion(selectionExtent);
00238 
00239     if (!uniformRegion.isEmpty()) {
00240         paintUniformSelectionRegion(img, imageRect, uniformRegion);
00241     }
00242 
00243     QRect nonuniformRect = imageRect & selectionExtent;
00244 
00245     if (!nonuniformRect.isEmpty()) {
00246 
00247         const Q_INT32 imageRectOffsetX = nonuniformRect.x() - imageRectX;
00248         const Q_INT32 imageRectOffsetY = nonuniformRect.y() - imageRectY;
00249 
00250         imageRectX = nonuniformRect.x();
00251         imageRectY = nonuniformRect.y();
00252         imageRectWidth = nonuniformRect.width();
00253         imageRectHeight = nonuniformRect.height();
00254 
00255         const Q_INT32 NUM_SELECTION_ROWS = 3;
00256     
00257         Q_UINT8 *selectionRow[NUM_SELECTION_ROWS];
00258     
00259         Q_INT32 aboveRowIndex = 0;
00260         Q_INT32 centreRowIndex = 1;
00261         Q_INT32 belowRowIndex = 2;
00262     
00263         selectionRow[aboveRowIndex] = new Q_UINT8[imageRectWidth + 2];
00264         selectionRow[centreRowIndex] = new Q_UINT8[imageRectWidth + 2];
00265         selectionRow[belowRowIndex] = new Q_UINT8[imageRectWidth + 2];
00266     
00267         readBytes(selectionRow[centreRowIndex], imageRectX - 1, imageRectY - 1, imageRectWidth + 2, 1);
00268         readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY, imageRectWidth + 2, 1);
00269     
00270         for (Q_INT32 y = 0; y < imageRectHeight; ++y) {
00271     
00272             Q_INT32 oldAboveRowIndex = aboveRowIndex;
00273             aboveRowIndex = centreRowIndex;
00274             centreRowIndex = belowRowIndex;
00275             belowRowIndex = oldAboveRowIndex;
00276     
00277             readBytes(selectionRow[belowRowIndex], imageRectX - 1, imageRectY + y + 1, imageRectWidth + 2, 1);
00278     
00279             const Q_UINT8 *aboveRow = selectionRow[aboveRowIndex] + 1;
00280             const Q_UINT8 *centreRow = selectionRow[centreRowIndex] + 1;
00281             const Q_UINT8 *belowRow = selectionRow[belowRowIndex] + 1;
00282     
00283             QRgb *imagePixel = reinterpret_cast<QRgb *>(img.scanLine(imageRectOffsetY + y));
00284             imagePixel += imageRectOffsetX;
00285     
00286             for (Q_INT32 x = 0; x < imageRectWidth; ++x) {
00287     
00288                 Q_UINT8 centre = *centreRow;
00289     
00290                 if (centre != MAX_SELECTED) {
00291     
00292                     // this is where we come if the pixels should be blue or bluish
00293     
00294                     QRgb srcPixel = *imagePixel;
00295                     Q_UINT8 srcGrey = (qRed(srcPixel) + qGreen(srcPixel) + qBlue(srcPixel)) / 9;
00296                     Q_UINT8 srcAlpha = qAlpha(srcPixel);
00297     
00298                     // Colour influence is proportional to alphaPixel.
00299                     srcGrey = UINT8_MULT(srcGrey, srcAlpha);
00300     
00301                     QRgb dstPixel;
00302     
00303                     if (centre == MIN_SELECTED) {
00304                         //this is where we come if the pixels should be blue (or red outline)
00305     
00306                         Q_UINT8 left = *(centreRow - 1);
00307                         Q_UINT8 right = *(centreRow + 1);
00308                         Q_UINT8 above = *aboveRow;
00309                         Q_UINT8 below = *belowRow;
00310     
00311                         // Stop unselected transparent areas from appearing the same
00312                         // as selected transparent areas.
00313                         Q_UINT8 dstAlpha = QMAX(srcAlpha, 192);
00314     
00315                         // now for a simple outline based on 4-connectivity
00316                         if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) {
00317                             dstPixel = qRgba(255, 0, 0, dstAlpha);
00318                         } else {
00319                             dstPixel = qRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha);
00320                         }
00321                     } else {
00322                         dstPixel = qRgba(UINT8_BLEND(qRed(srcPixel), srcGrey + 128, centre),
00323                                          UINT8_BLEND(qGreen(srcPixel), srcGrey + 128, centre),
00324                                          UINT8_BLEND(qBlue(srcPixel), srcGrey + 165, centre), 
00325                                          srcAlpha);
00326                     }
00327     
00328                     *imagePixel = dstPixel;
00329                 }
00330     
00331                 aboveRow++;
00332                 centreRow++;
00333                 belowRow++;
00334                 imagePixel++;
00335             }
00336         }
00337     
00338         delete [] selectionRow[aboveRowIndex];
00339         delete [] selectionRow[centreRowIndex];
00340         delete [] selectionRow[belowRowIndex];
00341     }
00342 }
00343 
00344 void KisSelection::paintSelection(QImage img, const QRect& scaledImageRect, const QSize& scaledImageSize, const QSize& imageSize)
00345 {
00346     if (img.isNull() || scaledImageRect.isEmpty() || scaledImageSize.isEmpty() || imageSize.isEmpty()) {
00347         return;
00348     }
00349 
00350     Q_ASSERT(img.size() == scaledImageRect.size());
00351 
00352     if (img.size() != scaledImageRect.size()) {
00353         return;
00354     }
00355 
00356     Q_INT32 imageWidth = imageSize.width();
00357     Q_INT32 imageHeight = imageSize.height();
00358 
00359     QRect selectionExtent = extent();
00360 
00361     selectionExtent.setLeft(selectionExtent.left() - 1);
00362     selectionExtent.setTop(selectionExtent.top() - 1);
00363     selectionExtent.setWidth(selectionExtent.width() + 2);
00364     selectionExtent.setHeight(selectionExtent.height() + 2);
00365 
00366     double xScale = static_cast<double>(scaledImageSize.width()) / imageWidth;
00367     double yScale = static_cast<double>(scaledImageSize.height()) / imageHeight;
00368 
00369     QRect scaledSelectionExtent;
00370 
00371     scaledSelectionExtent.setLeft(static_cast<int>(selectionExtent.left() * xScale));
00372     scaledSelectionExtent.setRight(static_cast<int>(ceil((selectionExtent.right() + 1) * xScale)) - 1);
00373     scaledSelectionExtent.setTop(static_cast<int>(selectionExtent.top() * yScale));
00374     scaledSelectionExtent.setBottom(static_cast<int>(ceil((selectionExtent.bottom() + 1) * yScale)) - 1);
00375 
00376     QRegion uniformRegion = QRegion(scaledImageRect);
00377     uniformRegion -= QRegion(scaledSelectionExtent);
00378 
00379     if (!uniformRegion.isEmpty()) {
00380         paintUniformSelectionRegion(img, scaledImageRect, uniformRegion);
00381     }
00382 
00383     QRect nonuniformRect = scaledImageRect & scaledSelectionExtent;
00384 
00385     if (!nonuniformRect.isEmpty()) {
00386 
00387         const Q_INT32 scaledImageRectXOffset = nonuniformRect.x() - scaledImageRect.x();
00388         const Q_INT32 scaledImageRectYOffset = nonuniformRect.y() - scaledImageRect.y();
00389 
00390         const Q_INT32 scaledImageRectX = nonuniformRect.x();
00391         const Q_INT32 scaledImageRectY = nonuniformRect.y();
00392         const Q_INT32 scaledImageRectWidth = nonuniformRect.width();
00393         const Q_INT32 scaledImageRectHeight = nonuniformRect.height();
00394 
00395         const Q_INT32 imageRowLeft = static_cast<Q_INT32>(scaledImageRectX / xScale);
00396         const Q_INT32 imageRowRight = static_cast<Q_INT32>((ceil((scaledImageRectX + scaledImageRectWidth - 1 + 1) / xScale)) - 1);
00397 
00398         const Q_INT32 imageRowWidth = imageRowRight - imageRowLeft + 1;
00399         const Q_INT32 imageRowStride = imageRowWidth + 2;
00400 
00401         const Q_INT32 NUM_SELECTION_ROWS = 3;
00402 
00403         Q_INT32 aboveRowIndex = 0;
00404         Q_INT32 centreRowIndex = 1;
00405         Q_INT32 belowRowIndex = 2;
00406 
00407         Q_INT32 aboveRowSrcY = -3;
00408         Q_INT32 centreRowSrcY = -3;
00409         Q_INT32 belowRowSrcY = -3;
00410 
00411         Q_UINT8 *selectionRows = new Q_UINT8[imageRowStride * NUM_SELECTION_ROWS];
00412         Q_UINT8 *selectionRow[NUM_SELECTION_ROWS];
00413 
00414         selectionRow[0] = selectionRows + 1;
00415         selectionRow[1] = selectionRow[0] + imageRowStride;
00416         selectionRow[2] = selectionRow[0] + (2 * imageRowStride);
00417 
00418         for (Q_INT32 y = 0; y < scaledImageRectHeight; ++y) {
00419 
00420             Q_INT32 scaledY = scaledImageRectY + y;
00421             Q_INT32 srcY = (scaledY * imageHeight) / scaledImageSize.height();
00422 
00423             Q_UINT8 *aboveRow;
00424             Q_UINT8 *centreRow;
00425             Q_UINT8 *belowRow;
00426 
00427             if (srcY - 1 == aboveRowSrcY) {
00428                 aboveRow = selectionRow[aboveRowIndex];
00429                 centreRow = selectionRow[centreRowIndex];
00430                 belowRow = selectionRow[belowRowIndex];
00431             } else if (srcY - 1 == centreRowSrcY) {
00432 
00433                 Q_INT32 oldAboveRowIndex = aboveRowIndex;
00434 
00435                 aboveRowIndex = centreRowIndex;
00436                 centreRowIndex = belowRowIndex;
00437                 belowRowIndex = oldAboveRowIndex;
00438 
00439                 aboveRow = selectionRow[aboveRowIndex];
00440                 centreRow = selectionRow[centreRowIndex];
00441                 belowRow = selectionRow[belowRowIndex];
00442 
00443                 readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1);
00444 
00445             } else if (srcY - 1 == belowRowSrcY) {
00446 
00447                 Q_INT32 oldAboveRowIndex = aboveRowIndex;
00448                 Q_INT32 oldCentreRowIndex = centreRowIndex;
00449 
00450                 aboveRowIndex = belowRowIndex;
00451                 centreRowIndex = oldAboveRowIndex;
00452                 belowRowIndex = oldCentreRowIndex;
00453 
00454                 aboveRow = selectionRow[aboveRowIndex];
00455                 centreRow = selectionRow[centreRowIndex];
00456                 belowRow = selectionRow[belowRowIndex];
00457 
00458                 if (belowRowIndex == centreRowIndex + 1) {
00459                     readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 2);
00460                 } else {
00461                     readBytes(centreRow - 1, imageRowLeft - 1, srcY, imageRowStride, 1);
00462                     readBytes(belowRow - 1, imageRowLeft - 1, srcY + 1, imageRowStride, 1);
00463                 }
00464 
00465             } else {
00466 
00467                 aboveRowIndex = 0;
00468                 centreRowIndex = 1;
00469                 belowRowIndex = 2;
00470 
00471                 aboveRow = selectionRow[aboveRowIndex];
00472                 centreRow = selectionRow[centreRowIndex];
00473                 belowRow = selectionRow[belowRowIndex];
00474 
00475                 readBytes(selectionRows, imageRowLeft - 1, srcY - 1, imageRowStride, NUM_SELECTION_ROWS);
00476             }
00477 
00478             aboveRowSrcY = srcY - 1;
00479             centreRowSrcY = aboveRowSrcY + 1;
00480             belowRowSrcY = centreRowSrcY + 1;
00481 
00482             QRgb *imagePixel = reinterpret_cast<QRgb *>(img.scanLine(scaledImageRectYOffset + y));
00483             imagePixel += scaledImageRectXOffset;
00484 
00485             for (Q_INT32 x = 0; x < scaledImageRectWidth; ++x) {
00486 
00487                 Q_INT32 scaledX = scaledImageRectX + x;
00488                 Q_INT32 srcX = (scaledX * imageWidth) / scaledImageSize.width();
00489 
00490                 Q_UINT8 centre = *(centreRow + srcX - imageRowLeft);
00491 
00492                 if (centre != MAX_SELECTED) {
00493 
00494                     // this is where we come if the pixels should be blue or bluish
00495 
00496                     QRgb srcPixel = *imagePixel;
00497                     Q_UINT8 srcGrey = (qRed(srcPixel) + qGreen(srcPixel) + qBlue(srcPixel)) / 9;
00498                     Q_UINT8 srcAlpha = qAlpha(srcPixel);
00499 
00500                     // Colour influence is proportional to alphaPixel.
00501                     srcGrey = UINT8_MULT(srcGrey, srcAlpha);
00502 
00503                     QRgb dstPixel;
00504 
00505                     if (centre == MIN_SELECTED) {
00506                         //this is where we come if the pixels should be blue (or red outline)
00507 
00508                         Q_UINT8 left = *(centreRow + (srcX - imageRowLeft) - 1);
00509                         Q_UINT8 right = *(centreRow + (srcX - imageRowLeft) + 1);
00510                         Q_UINT8 above = *(aboveRow + (srcX - imageRowLeft));
00511                         Q_UINT8 below = *(belowRow + (srcX - imageRowLeft));
00512 
00513                         // Stop unselected transparent areas from appearing the same
00514                         // as selected transparent areas.
00515                         Q_UINT8 dstAlpha = QMAX(srcAlpha, 192);
00516 
00517                         // now for a simple outline based on 4-connectivity
00518                         if (left != MIN_SELECTED || right != MIN_SELECTED || above != MIN_SELECTED || below != MIN_SELECTED) {
00519                             dstPixel = qRgba(255, 0, 0, dstAlpha);
00520                         } else {
00521                             dstPixel = qRgba(128 + srcGrey, 128 + srcGrey, 165 + srcGrey, dstAlpha);
00522                         }
00523                     } else {
00524                         dstPixel = qRgba(UINT8_BLEND(qRed(srcPixel), srcGrey + 128, centre),
00525                                          UINT8_BLEND(qGreen(srcPixel), srcGrey + 128, centre),
00526                                          UINT8_BLEND(qBlue(srcPixel), srcGrey + 165, centre), 
00527                                          srcAlpha);
00528                     }
00529 
00530                     *imagePixel = dstPixel;
00531                 }
00532 
00533                 imagePixel++;
00534             }
00535         }
00536 
00537         delete [] selectionRows;
00538     }
00539 }
00540 
KDE Home | KDE Accessibility Home | Description of Access Keys