krita

kis_convolution_painter.cc

00001 /*
00002  *  Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 #include <stdlib.h>
00020 #include <string.h>
00021 #include <cfloat>
00022 
00023 #include "qbrush.h"
00024 #include "qcolor.h"
00025 #include "qfontinfo.h"
00026 #include "qfontmetrics.h"
00027 #include "qpen.h"
00028 #include "qregion.h"
00029 #include "qwmatrix.h"
00030 #include <qimage.h>
00031 #include <qmap.h>
00032 #include <qpainter.h>
00033 #include <qpixmap.h>
00034 #include <qpointarray.h>
00035 #include <qrect.h>
00036 #include <qstring.h>
00037 
00038 #include <kdebug.h>
00039 #include <klocale.h>
00040 
00041 #include <qcolor.h>
00042 
00043 #include "kis_brush.h"
00044 #include "kis_global.h"
00045 #include "kis_image.h"
00046 #include "kis_iterators_pixel.h"
00047 #include "kis_layer.h"
00048 #include "kis_paint_device.h"
00049 #include "kis_painter.h"
00050 #include "kis_pattern.h"
00051 #include "kis_rect.h"
00052 #include "kis_colorspace.h"
00053 #include "kis_types.h"
00054 #include "kis_vec.h"
00055 #include "kis_selection.h"
00056 #include "kis_convolution_painter.h"
00057 
00058 
00059 KisKernelSP KisKernel::fromQImage(const QImage& img)
00060 {
00061     KisKernelSP k = new KisKernel;
00062     k->width = img.width();
00063     k->height = img.height();
00064     k->offset = 0;
00065     uint count = k->width * k->height;
00066     k->data = new Q_INT32[count];
00067     Q_INT32* itData = k->data;
00068     Q_UINT8* itImg = img.bits();
00069     k->factor = 0;
00070     for(uint i = 0; i < count; ++i , ++itData, itImg+=4)
00071     {
00072         *itData = 255 - ( *itImg + *(itImg+1) + *(itImg+2) ) / 3;
00073         k->factor += *itData;
00074     }
00075     return k;
00076 }
00077 
00078 
00079 KisConvolutionPainter::KisConvolutionPainter()
00080     : super()
00081 {
00082 }
00083 
00084 KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device) : super(device)
00085 {
00086 }
00087 
00088 void KisConvolutionPainter::applyMatrix(KisKernelSP kernel, Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h,
00089                     KisConvolutionBorderOp borderOp,
00090                     KisChannelInfo::enumChannelFlags  channelFlags )
00091 {
00092     // Make the area we cover as small as possible
00093     if (m_device->hasSelection()) {
00094 
00095         if (m_device->selection()->isTotallyUnselected(QRect(x, y, w, h))) {
00096             return;
00097         }
00098 
00099         QRect r = m_device->selection()->extent().intersect(QRect(x, y, w, h));
00100         x = r.x();
00101         y = r.y();
00102         w = r.width();
00103         h = r.height();
00104 
00105     }
00106 
00107     // Determine the kernel's extent from the center pixel
00108     Q_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
00109     kw = kernel->width;
00110     kh = kernel->height;
00111     khalfWidth = (kw - 1) / 2;
00112     khalfHeight = (kh - 1) / 2;
00113 
00114     xLastMinuskhw = x + (w - khalfWidth);
00115     yLastMinuskhh = y + (h - khalfHeight);
00116 
00117     // Don't try to convolve on an area smaller than the kernel, or with a kernel that is not square or has no center pixel.
00118     if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return;
00119 
00120     m_cancelRequested = false;
00121     int lastProgressPercent = 0;
00122     emit notifyProgress(0);
00123 
00124     KisColorSpace * cs = m_device->colorSpace();
00125 
00126     // Determine whether we convolve border pixels, or not.
00127     switch (borderOp) {
00128         case BORDER_DEFAULT_FILL :
00129             break;
00130         case BORDER_REPEAT:
00131             applyMatrixRepeat(kernel, x, y, w, h, channelFlags);
00132             return;
00133         case BORDER_WRAP:
00134         case BORDER_AVOID:
00135         default :
00136             x += khalfWidth;
00137             y += khalfHeight;
00138             w -= kw - 1;
00139             h -= kh - 1;
00140     }
00141 
00142     // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.
00143 
00144     QMemArray<Q_UINT8 *> pixelPtrCache(kw * kh);
00145 //     pixelPtrCache.fill(0);
00146 
00147     // row == the y position of the pixel we want to change in the paint device
00148     int row = y;
00149 
00150     for (; row < y + h; ++row) {
00151 
00152         // col = the x position of the pixel we want to change
00153         int col = x;
00154 
00155         KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true);
00156         bool needFull = true;
00157         while (!hit.isDone()) {
00158             if (hit.isSelected()) {
00159 
00160                 // Iterate over all contributing pixels that are covered by the kernel
00161                 // krow = the y position in the kernel matrix
00162                 if(needFull)
00163                 {
00164                     Q_INT32 i = 0;
00165                     for (Q_INT32 krow = 0; krow <  kh; ++krow) {
00166 
00167                         // col - khalfWidth = the left starting point of the kernel as centered on our pixel
00168                         // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
00169                         // kw = the width of the kernel
00170 
00171                         // Fill the cache with pointers to the pixels under the kernel
00172                         KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw, false);
00173                         while (!kit.isDone()) {
00174                             pixelPtrCache[i] = const_cast<Q_UINT8 *>(kit.oldRawData());
00175                             ++kit;
00176                             ++i;
00177                         }
00178                     }
00179                     needFull = false;
00180                     Q_ASSERT (i==kw*kh);
00181                 } else {
00182                     for (Q_INT32 krow = 0; krow <  kh; ++krow) { // shift the cache to the left
00183                         Q_UINT8** d = pixelPtrCache.data() + krow * kw;
00184                         memmove( d, d + 1, (kw-1)*sizeof(Q_UINT8*));
00185                     }
00186                     Q_INT32 i = kw - 1;
00187                     KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh, false);
00188                     while (!kit.isDone()) {
00189                         pixelPtrCache[i] = const_cast<Q_UINT8 *>(kit.oldRawData());
00190                         ++kit;
00191                         i += kw;
00192                     }
00193                 }
00194                 cs->convolveColors(pixelPtrCache.data(), kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
00195 //                 pixelPtrCache.fill(0);
00196             }
00197             ++col;
00198             ++hit;
00199         }
00200 
00201         int progressPercent = 100 - ((((y + h) - row) * 100) / h);
00202 
00203         if (progressPercent > lastProgressPercent) {
00204             emit notifyProgress(progressPercent);
00205             lastProgressPercent = progressPercent;
00206 
00207             if (m_cancelRequested) {
00208                 return;
00209             }
00210         }
00211 
00212     }
00213 
00214     addDirtyRect(QRect(x, y, w, h));
00215 
00216     emit notifyProgressDone();
00217 }
00218 
00219 void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h,
00220                            KisChannelInfo::enumChannelFlags channelFlags)
00221 {
00222     int lastProgressPercent = 0;
00223     // Determine the kernel's extent from the center pixel
00224     Q_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
00225     kw = kernel->width;
00226     kh = kernel->height;
00227     khalfWidth = (kw - 1) / 2;
00228     khalfHeight = (kh - 1) / 2;
00229 
00230     xLastMinuskhw = x + (w - khalfWidth);
00231     yLastMinuskhh = y + (h - khalfHeight);
00232 
00233     KisColorSpace * cs = m_device->colorSpace();
00234 
00235     // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.
00236 
00237     QMemArray<Q_UINT8 *> pixelPtrCache(kw * kh);
00238 
00239     // row == the y position of the pixel we want to change in the paint device
00240     int row = y;
00241 
00242     for (; row < y + h; ++row) {
00243 
00244         // col = the x position of the pixel we want to change
00245         int col = x;
00246 
00247         KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true);
00248         bool needFull = true;
00249         
00250         Q_INT32 itStart = row - khalfHeight;
00251         Q_INT32 itH = kh;
00252         if(itStart < 0)
00253         {
00254             itH += itStart;
00255             itStart = 0;
00256         } else if(itStart + kh > yLastMinuskhh)
00257         {
00258             itH -= itStart + kh - yLastMinuskhh;
00259         }
00260         KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
00261         while (!hit.isDone()) {
00262             if (hit.isSelected()) {
00263 
00264                 // Iterate over all contributing pixels that are covered by the kernel
00265                 // krow = the y position in the kernel matrix
00266                 if(needFull) // The cache has not been fill, so we need to fill it
00267                 {
00268                     Q_INT32 i = 0;
00269                     Q_INT32 krow = 0;
00270                     if( row < khalfHeight )
00271                     {
00272                         // We are just outside the layer, all the row in the cache will be identical
00273                         // so we need to create them only once, and then to copy them
00274                         if( x < khalfWidth)
00275                         { // the left pixels are outside of the layer, in the corner
00276                             Q_INT32 kcol = 0;
00277                             KisHLineIteratorPixel kit = m_device->createHLineIterator(0, 0, kw, false);
00278                             for(; kcol < (khalfWidth - x) + 1; ++kcol)
00279                             { // First copy the address of the topleft pixel
00280                                 pixelPtrCache[kcol] = const_cast<Q_UINT8 *>(kit.oldRawData());
00281                             }
00282                             for(; kcol < kw; ++kcol)
00283                             { // Then copy the address of the rest of the line
00284                                 ++kit;
00285                                 pixelPtrCache[kcol] = const_cast<Q_UINT8 *>(kit.oldRawData());
00286                             }
00287                         } else {
00288                             uint kcol = 0;
00289                             KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw, false);
00290                             while (!kit.isDone()) {
00291                                 pixelPtrCache[kcol] = const_cast<Q_UINT8 *>(kit.oldRawData());
00292                                 ++kit;
00293                                 ++kcol;
00294                             }
00295                         }
00296                         krow = 1; // we have allready done the first krow
00297                         for(;krow < (khalfHeight - row); ++krow)
00298                         {
00299                             memcpy( pixelPtrCache.data() + krow * kw, pixelPtrCache.data(), kw*sizeof(Q_UINT8*)); //    Copy the first line in the current line
00300                         }
00301                         i = krow * kw;
00302                     }
00303                     Q_INT32 itH = kh;
00304                     if(row + khalfHeight > yLastMinuskhh)
00305                     {
00306                         itH += yLastMinuskhh - row - khalfHeight;
00307                     }
00308                     for (; krow <  itH; ++krow) {
00309 
00310                         // col - khalfWidth = the left starting point of the kernel as centered on our pixel
00311                         // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
00312                         // kw = the width of the kernel
00313 
00314                         // Fill the cache with pointers to the pixels under the kernel
00315                         Q_INT32 itHStart = col - khalfWidth;
00316                         Q_INT32 itW = kw;
00317                         if(itHStart < 0)
00318                         {
00319                             itW += itHStart;
00320                             itHStart = 0;
00321                         }
00322                         KisHLineIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW, false);
00323                         if( col < khalfWidth )
00324                         {
00325                             for(; i <  krow * kw + ( kw - itW ); i+= 1)
00326                             {
00327                                 pixelPtrCache[i] = const_cast<Q_UINT8 *>(kit.oldRawData());
00328                             }
00329                         }
00330                         while (!kit.isDone()) {
00331                             pixelPtrCache[i] = const_cast<Q_UINT8 *>(kit.oldRawData());
00332                             ++kit;
00333                             ++i;
00334                         }
00335                     }
00336                     Q_INT32 lastvalid = i - kw;
00337                     for(; krow < kh; ++krow) {
00338                         memcpy( pixelPtrCache.data() + krow * kw,
00339                                 pixelPtrCache.data() + lastvalid,
00340                                 kw * sizeof(Q_UINT8*)); // Copy the last valid line in the current line
00341                     }
00342                     needFull = false;
00343                 } else {
00344                     for (Q_INT32 krow = 0; krow <  kh; ++krow) { // shift the cache to the left
00345                         Q_UINT8** d = pixelPtrCache.data() + krow * kw;
00346                         memmove( d, d + 1, (kw-1)*sizeof(Q_UINT8*));
00347                     }
00348                     if(col < xLastMinuskhw)
00349                     {
00350                         Q_INT32 i = kw - 1;
00351 //                         KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
00352                         kit.nextCol();
00353                         if( row < khalfHeight )
00354                         {
00355                             for(; i < (khalfHeight- row ) * kw; i+=kw)
00356                             {
00357                                 pixelPtrCache[i] = const_cast<Q_UINT8 *>(kit.oldRawData());
00358                             }
00359                         }
00360                         while (!kit.isDone()) {
00361                             pixelPtrCache[i] = const_cast<Q_UINT8 *>(kit.oldRawData());
00362                             ++kit;
00363                             i += kw;
00364                         }
00365                         Q_INT32 lastvalid = i - kw;
00366                         for(;i < kw*kh; i+=kw)
00367                         {
00368                             pixelPtrCache[i] = pixelPtrCache[lastvalid];
00369                         }
00370                     }
00371                 }
00372                 cs->convolveColors(pixelPtrCache.data(), kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
00373             }
00374             ++col;
00375             ++hit;
00376         }
00377 
00378         int progressPercent = 100 - ((((y + h) - row) * 100) / h);
00379 
00380         if (progressPercent > lastProgressPercent) {
00381             emit notifyProgress(progressPercent);
00382             lastProgressPercent = progressPercent;
00383 
00384             if (m_cancelRequested) {
00385                 return;
00386             }
00387         }
00388 
00389     }
00390 
00391     addDirtyRect(QRect(x, y, w, h));
00392 
00393     emit notifyProgressDone();
00394 }
KDE Home | KDE Accessibility Home | Description of Access Keys