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         QRect r =  m_device->selection()->selectedRect().intersect(QRect(x, y, w, h));
00096         x = r.x();
00097         y = r.y();
00098         w = r.width();
00099         h = r.height();
00100 
00101     }
00102 
00103     if ( w == 0 && h == 0 ) return;
00104 
00105     // Determine the kernel's extent from the center pixel
00106     Q_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
00107     kw = kernel->width;
00108     kh = kernel->height;
00109     khalfWidth = (kw - 1) / 2;
00110     khalfHeight = (kh - 1) / 2;
00111 
00112     xLastMinuskhw = x + (w - khalfWidth);
00113     yLastMinuskhh = y + (h - khalfHeight);
00114 
00115     // 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.
00116     if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return;
00117 
00118     m_cancelRequested = false;
00119     int lastProgressPercent = 0;
00120     emit notifyProgress(0);
00121 
00122     KisColorSpace * cs = m_device->colorSpace();
00123 
00124     // Determine whether we convolve border pixels, or not.
00125     switch (borderOp) {
00126         case BORDER_DEFAULT_FILL :
00127             break;
00128         case BORDER_REPEAT:
00129             applyMatrixRepeat(kernel, x, y, w, h, channelFlags);
00130             return;
00131         case BORDER_WRAP:
00132         case BORDER_AVOID:
00133         default :
00134             x += khalfWidth;
00135             y += khalfHeight;
00136             w -= kw - 1;
00137             h -= kh - 1;
00138     }
00139 
00140     // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.
00141 
00142     int cacheSize = kw * kh;
00143     int cdepth = cs -> pixelSize();
00144     Q_UINT8** pixelPtrCache = new Q_UINT8*[cacheSize];
00145     for (int i = 0; i < cacheSize; i++)
00146         pixelPtrCache[i] = new Q_UINT8[cdepth];
00147 //     pixelPtrCache.fill(0);
00148 
00149     // row == the y position of the pixel we want to change in the paint device
00150     int row = y;
00151 
00152     for (; row < y + h; ++row) {
00153 
00154         // col = the x position of the pixel we want to change
00155         int col = x;
00156 
00157         KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true);
00158         bool needFull = true;
00159         while (!hit.isDone()) {
00160 
00161             // Iterate over all contributing pixels that are covered by the kernel
00162             // krow = the y position in the kernel matrix
00163             if(needFull)
00164             {
00165                 Q_INT32 i = 0;
00166                 for (Q_INT32 krow = 0; krow <  kh; ++krow) {
00167 
00168                     // col - khalfWidth = the left starting point of the kernel as centered on our pixel
00169                     // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
00170                     // kw = the width of the kernel
00171 
00172                     // Fill the cache with pointers to the pixels under the kernel
00173                     KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw, false);
00174                     while (!kit.isDone()) {
00175                         memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
00176                         ++kit;
00177                         ++i;
00178                     }
00179                 }
00180                 needFull = false;
00181                 Q_ASSERT (i==kw*kh);
00182             } else {
00183                 for (Q_INT32 krow = 0; krow <  kh; ++krow) { // shift the cache to the left
00184                     Q_UINT8** d = pixelPtrCache + krow * kw;
00185                     //memmove( d, d + 1, (kw-1)*sizeof(Q_UINT8*));
00186                     for (int i = 0; i < (kw-1); i++) {
00187                         memcpy(d[i], d[i+1], cdepth);
00188                     }
00189                 }
00190                 Q_INT32 i = kw - 1;
00191                 KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh, false);
00192                 while (!kit.isDone()) {
00193                     memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
00194                     ++kit;
00195                     i += kw;
00196                 }
00197             }
00198             if (hit.isSelected()) {
00199                 cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
00200 //                 pixelPtrCache.fill(0);
00201             }
00202             ++col;
00203             ++hit;
00204         }
00205 
00206         int progressPercent = 100 - ((((y + h) - row) * 100) / h);
00207 
00208         if (progressPercent > lastProgressPercent) {
00209             emit notifyProgress(progressPercent);
00210             lastProgressPercent = progressPercent;
00211 
00212             if (m_cancelRequested) {
00213                 for (int i = 0; i < cacheSize; i++)
00214                     delete[] pixelPtrCache[i];
00215                 delete[] pixelPtrCache;
00216 
00217                 return;
00218             }
00219         }
00220 
00221     }
00222 
00223     addDirtyRect(QRect(x, y, w, h));
00224 
00225     emit notifyProgressDone();
00226 
00227     for (int i = 0; i < cacheSize; i++)
00228         delete[] pixelPtrCache[i];
00229     delete[] pixelPtrCache;
00230 }
00231 
00232 void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, Q_INT32 x, Q_INT32 y, Q_INT32 w, Q_INT32 h,
00233                            KisChannelInfo::enumChannelFlags channelFlags)
00234 {
00235     int lastProgressPercent = 0;
00236     // Determine the kernel's extent from the center pixel
00237     Q_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
00238     kw = kernel->width;
00239     kh = kernel->height;
00240     khalfWidth = (kw - 1) / 2;
00241     khalfHeight = (kh - 1) / 2;
00242 
00243     xLastMinuskhw = x + (w - khalfWidth);
00244     yLastMinuskhh = y + (h - khalfHeight);
00245 
00246     KisColorSpace * cs = m_device->colorSpace();
00247 
00248     // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.
00249 
00250     int cacheSize = kw * kh;
00251     int cdepth = cs -> pixelSize();
00252     Q_UINT8** pixelPtrCache = new Q_UINT8*[cacheSize];
00253     for (int i = 0; i < cacheSize; i++)
00254         pixelPtrCache[i] = new Q_UINT8[cdepth];
00255 
00256     // row == the y position of the pixel we want to change in the paint device
00257     int row = y;
00258 
00259     for (; row < y + h; ++row) {
00260 
00261         // col = the x position of the pixel we want to change
00262         int col = x;
00263 
00264         KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true);
00265         bool needFull = true;
00266 
00267         Q_INT32 itStart = row - khalfHeight;
00268         Q_INT32 itH = kh;
00269         if(itStart < 0)
00270         {
00271             itH += itStart;
00272             itStart = 0;
00273         } else if(itStart + kh > yLastMinuskhh)
00274         {
00275             itH -= itStart + kh - yLastMinuskhh;
00276         }
00277         KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
00278         while (!hit.isDone()) {
00279 
00280             // Iterate over all contributing pixels that are covered by the kernel
00281             // krow = the y position in the kernel matrix
00282             if(needFull) // The cache has not been fill, so we need to fill it
00283             {
00284                 Q_INT32 i = 0;
00285                 Q_INT32 krow = 0;
00286                 if( row < khalfHeight )
00287                 {
00288                     // We are just outside the layer, all the row in the cache will be identical
00289                     // so we need to create them only once, and then to copy them
00290                     if( x < khalfWidth)
00291                     { // the left pixels are outside of the layer, in the corner
00292                         Q_INT32 kcol = 0;
00293                         KisHLineIteratorPixel kit = m_device->createHLineIterator(0, 0, kw, false);
00294                         for(; kcol < (khalfWidth - x) + 1; ++kcol)
00295                         { // First copy the address of the topleft pixel
00296                             memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
00297                         }
00298                         for(; kcol < kw; ++kcol)
00299                         { // Then copy the address of the rest of the line
00300                             ++kit;
00301                             memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
00302                         }
00303                     } else {
00304                         uint kcol = 0;
00305                         KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw, false);
00306                         while (!kit.isDone()) {
00307                             memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
00308                             ++kit;
00309                             ++kcol;
00310                         }
00311                     }
00312                     krow = 1; // we have allready done the first krow
00313                     for(;krow < (khalfHeight - row); ++krow)
00314                     {
00315                         //    Copy the first line in the current line
00316                         for (int i = 0; i < kw; i++)
00317                             memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[i], cdepth);
00318                     }
00319                     i = krow * kw;
00320                 }
00321                 Q_INT32 itH = kh;
00322                 if(row + khalfHeight > yLastMinuskhh)
00323                 {
00324                     itH += yLastMinuskhh - row - khalfHeight;
00325                 }
00326                 for (; krow <  itH; ++krow) {
00327 
00328                     // col - khalfWidth = the left starting point of the kernel as centered on our pixel
00329                     // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
00330                     // kw = the width of the kernel
00331 
00332                     // Fill the cache with pointers to the pixels under the kernel
00333                     Q_INT32 itHStart = col - khalfWidth;
00334                     Q_INT32 itW = kw;
00335                     if(itHStart < 0)
00336                     {
00337                         itW += itHStart;
00338                         itHStart = 0;
00339                     }
00340                     KisHLineIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW, false);
00341                     if( col < khalfWidth )
00342                     {
00343                         for(; i <  krow * kw + ( kw - itW ); i+= 1)
00344                         {
00345                             memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
00346                         }
00347                     }
00348                     while (!kit.isDone()) {
00349                         memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
00350                         ++kit;
00351                         ++i;
00352                     }
00353                 }
00354                 Q_INT32 lastvalid = i - kw;
00355                 for(; krow < kh; ++krow) {
00356                     // Copy the last valid line in the current line
00357                     for (int i = 0; i < kw; i++)
00358                         memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[lastvalid + i],
00359                                 cdepth);
00360                 }
00361                 needFull = false;
00362             } else {
00363 /*                for (Q_INT32 krow = 0; krow <  kh; ++krow) { // shift the cache to the left
00364                     Q_UINT8** d = pixelPtrCache + krow * kw;
00365 //                     memmove( d, d + 1, (kw-1)*sizeof(Q_UINT8*));
00366                     for (int i = 0; i < (kw-1); i++) {
00367                         memcpy(d[i], d[i+1], cdepth);
00368                     }
00369                 }*/
00370                 Q_UINT8* firstincache = pixelPtrCache[0];
00371                 memmove(pixelPtrCache, pixelPtrCache + 1, (cacheSize - 1) * sizeof(Q_UINT8*) );
00372                 pixelPtrCache[cacheSize - 1] = firstincache;
00373                 if(col < xLastMinuskhw)
00374                 {
00375                     Q_INT32 i = kw - 1;
00376 //                         KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
00377                     kit.nextCol();
00378                     if( row < khalfHeight )
00379                     {
00380                         for(; i < (khalfHeight- row ) * kw; i+=kw)
00381                         {
00382                             memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
00383                         }
00384                     }
00385                     while (!kit.isDone()) {
00386                         memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
00387                         ++kit;
00388                         i += kw;
00389                     }
00390                     Q_INT32 lastvalid = i - kw;
00391                     for(;i < kw*kh; i+=kw)
00392                     {
00393                         memcpy(pixelPtrCache[i], pixelPtrCache[lastvalid], cdepth);
00394                     }
00395                 }
00396             }
00397             if (hit.isSelected()) {
00398                 cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
00399             }
00400             ++col;
00401             ++hit;
00402         }
00403 
00404         int progressPercent = 100 - ((((y + h) - row) * 100) / h);
00405 
00406         if (progressPercent > lastProgressPercent) {
00407             emit notifyProgress(progressPercent);
00408             lastProgressPercent = progressPercent;
00409 
00410             if (m_cancelRequested) {
00411                 for (int i = 0; i < cacheSize; i++)
00412                     delete[] pixelPtrCache[i];
00413                 delete[] pixelPtrCache;
00414                 return;
00415             }
00416         }
00417 
00418     }
00419 
00420     addDirtyRect(QRect(x, y, w, h));
00421 
00422     emit notifyProgressDone();
00423     for (int i = 0; i < cacheSize; i++)
00424         delete[] pixelPtrCache[i];
00425     delete[] pixelPtrCache;
00426 }
KDE Home | KDE Accessibility Home | Description of Access Keys