From 698569f8428ca088f764d704034a1330517b98c0 Mon Sep 17 00:00:00 2001 From: tpearson Date: Sun, 26 Jun 2011 00:41:16 +0000 Subject: Finish rebranding of Krita as Chalk git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1238363 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- chalk/core/kis_convolution_painter.cc | 426 ++++++++++++++++++++++++++++++++++ 1 file changed, 426 insertions(+) create mode 100644 chalk/core/kis_convolution_painter.cc (limited to 'chalk/core/kis_convolution_painter.cc') diff --git a/chalk/core/kis_convolution_painter.cc b/chalk/core/kis_convolution_painter.cc new file mode 100644 index 00000000..ea0351df --- /dev/null +++ b/chalk/core/kis_convolution_painter.cc @@ -0,0 +1,426 @@ +/* + * Copyright (c) 2005 Cyrille Berger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include + +#include "tqbrush.h" +#include "tqcolor.h" +#include "tqfontinfo.h" +#include "tqfontmetrics.h" +#include "tqpen.h" +#include "tqregion.h" +#include "tqwmatrix.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include "kis_brush.h" +#include "kis_global.h" +#include "kis_image.h" +#include "kis_iterators_pixel.h" +#include "kis_layer.h" +#include "kis_paint_device.h" +#include "kis_painter.h" +#include "kis_pattern.h" +#include "kis_rect.h" +#include "kis_colorspace.h" +#include "kis_types.h" +#include "kis_vec.h" +#include "kis_selection.h" +#include "kis_convolution_painter.h" + + +KisKernelSP KisKernel::fromTQImage(const TQImage& img) +{ + KisKernelSP k = new KisKernel; + k->width = img.width(); + k->height = img.height(); + k->offset = 0; + uint count = k->width * k->height; + k->data = new TQ_INT32[count]; + TQ_INT32* itData = k->data; + TQ_UINT8* itImg = (TQ_UINT8*)img.bits(); + k->factor = 0; + for(uint i = 0; i < count; ++i , ++itData, itImg+=4) + { + *itData = 255 - ( *itImg + *(itImg+1) + *(itImg+2) ) / 3; + k->factor += *itData; + } + return k; +} + + +KisConvolutionPainter::KisConvolutionPainter() + : super() +{ +} + +KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device) : super(device) +{ +} + +void KisConvolutionPainter::applyMatrix(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, + KisConvolutionBorderOp borderOp, + KisChannelInfo::enumChannelFlags channelFlags ) +{ + // Make the area we cover as small as possible + if (m_device->hasSelection()) { + + TQRect r = m_device->selection()->selectedRect().intersect(TQRect(x, y, w, h)); + x = r.x(); + y = r.y(); + w = r.width(); + h = r.height(); + + } + + if ( w == 0 && h == 0 ) return; + + // Determine the kernel's extent from the center pixel + TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh; + kw = kernel->width; + kh = kernel->height; + khalfWidth = (kw - 1) / 2; + khalfHeight = (kh - 1) / 2; + + xLastMinuskhw = x + (w - khalfWidth); + yLastMinuskhh = y + (h - khalfHeight); + + // 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. + if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return; + + m_cancelRequested = false; + int lastProgressPercent = 0; + emit notifyProgress(0); + + KisColorSpace * cs = m_device->colorSpace(); + + // Determine whether we convolve border pixels, or not. + switch (borderOp) { + case BORDER_DEFAULT_FILL : + break; + case BORDER_REPEAT: + applyMatrixRepeat(kernel, x, y, w, h, channelFlags); + return; + case BORDER_WRAP: + case BORDER_AVOID: + default : + x += khalfWidth; + y += khalfHeight; + w -= kw - 1; + h -= kh - 1; + } + + // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy. + + int cacheSize = kw * kh; + int cdepth = cs -> pixelSize(); + TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize]; + for (int i = 0; i < cacheSize; i++) + pixelPtrCache[i] = new TQ_UINT8[cdepth]; +// pixelPtrCache.fill(0); + + // row == the y position of the pixel we want to change in the paint device + int row = y; + + for (; row < y + h; ++row) { + + // col = the x position of the pixel we want to change + int col = x; + + KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true); + bool needFull = true; + while (!hit.isDone()) { + + // Iterate over all contributing pixels that are covered by the kernel + // krow = the y position in the kernel matrix + if(needFull) + { + TQ_INT32 i = 0; + for (TQ_INT32 krow = 0; krow < kh; ++krow) { + + // col - khalfWidth = the left starting point of the kernel as centered on our pixel + // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel + // kw = the width of the kernel + + // Fill the cache with pointers to the pixels under the kernel + KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw, false); + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + ++i; + } + } + needFull = false; + Q_ASSERT (i==kw*kh); + } else { + for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left + TQ_UINT8** d = pixelPtrCache + krow * kw; + //memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*)); + for (int i = 0; i < (kw-1); i++) { + memcpy(d[i], d[i+1], cdepth); + } + } + TQ_INT32 i = kw - 1; + KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh, false); + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + i += kw; + } + } + if (hit.isSelected()) { + cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh); +// pixelPtrCache.fill(0); + } + ++col; + ++hit; + } + + int progressPercent = 100 - ((((y + h) - row) * 100) / h); + + if (progressPercent > lastProgressPercent) { + emit notifyProgress(progressPercent); + lastProgressPercent = progressPercent; + + if (m_cancelRequested) { + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; + + return; + } + } + + } + + addDirtyRect(TQRect(x, y, w, h)); + + emit notifyProgressDone(); + + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; +} + +void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h, + KisChannelInfo::enumChannelFlags channelFlags) +{ + int lastProgressPercent = 0; + // Determine the kernel's extent from the center pixel + TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh; + kw = kernel->width; + kh = kernel->height; + khalfWidth = (kw - 1) / 2; + khalfHeight = (kh - 1) / 2; + + xLastMinuskhw = x + (w - khalfWidth); + yLastMinuskhh = y + (h - khalfHeight); + + KisColorSpace * cs = m_device->colorSpace(); + + // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy. + + int cacheSize = kw * kh; + int cdepth = cs -> pixelSize(); + TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize]; + for (int i = 0; i < cacheSize; i++) + pixelPtrCache[i] = new TQ_UINT8[cdepth]; + + // row == the y position of the pixel we want to change in the paint device + int row = y; + + for (; row < y + h; ++row) { + + // col = the x position of the pixel we want to change + int col = x; + + KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true); + bool needFull = true; + + TQ_INT32 itStart = row - khalfHeight; + TQ_INT32 itH = kh; + if(itStart < 0) + { + itH += itStart; + itStart = 0; + } else if(itStart + kh > yLastMinuskhh) + { + itH -= itStart + kh - yLastMinuskhh; + } + KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false); + while (!hit.isDone()) { + + // Iterate over all contributing pixels that are covered by the kernel + // krow = the y position in the kernel matrix + if(needFull) // The cache has not been fill, so we need to fill it + { + TQ_INT32 i = 0; + TQ_INT32 krow = 0; + if( row < khalfHeight ) + { + // We are just outside the layer, all the row in the cache will be identical + // so we need to create them only once, and then to copy them + if( x < khalfWidth) + { // the left pixels are outside of the layer, in the corner + TQ_INT32 kcol = 0; + KisHLineIteratorPixel kit = m_device->createHLineIterator(0, 0, kw, false); + for(; kcol < (khalfWidth - x) + 1; ++kcol) + { // First copy the address of the topleft pixel + memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); + } + for(; kcol < kw; ++kcol) + { // Then copy the address of the rest of the line + ++kit; + memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); + } + } else { + uint kcol = 0; + KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw, false); + while (!kit.isDone()) { + memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth); + ++kit; + ++kcol; + } + } + krow = 1; // we have allready done the first krow + for(;krow < (khalfHeight - row); ++krow) + { + // Copy the first line in the current line + for (int i = 0; i < kw; i++) + memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[i], cdepth); + } + i = krow * kw; + } + TQ_INT32 itH = kh; + if(row + khalfHeight > yLastMinuskhh) + { + itH += yLastMinuskhh - row - khalfHeight; + } + for (; krow < itH; ++krow) { + + // col - khalfWidth = the left starting point of the kernel as centered on our pixel + // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel + // kw = the width of the kernel + + // Fill the cache with pointers to the pixels under the kernel + TQ_INT32 itHStart = col - khalfWidth; + TQ_INT32 itW = kw; + if(itHStart < 0) + { + itW += itHStart; + itHStart = 0; + } + KisHLineIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW, false); + if( col < khalfWidth ) + { + for(; i < krow * kw + ( kw - itW ); i+= 1) + { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + } + } + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + ++i; + } + } + TQ_INT32 lastvalid = i - kw; + for(; krow < kh; ++krow) { + // Copy the last valid line in the current line + for (int i = 0; i < kw; i++) + memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[lastvalid + i], + cdepth); + } + needFull = false; + } else { +/* for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left + TQ_UINT8** d = pixelPtrCache + krow * kw; +// memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*)); + for (int i = 0; i < (kw-1); i++) { + memcpy(d[i], d[i+1], cdepth); + } + }*/ + TQ_UINT8* firstincache = pixelPtrCache[0]; + memmove(pixelPtrCache, pixelPtrCache + 1, (cacheSize - 1) * sizeof(TQ_UINT8*) ); + pixelPtrCache[cacheSize - 1] = firstincache; + if(col < xLastMinuskhw) + { + TQ_INT32 i = kw - 1; +// KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false); + kit.nextCol(); + if( row < khalfHeight ) + { + for(; i < (khalfHeight- row ) * kw; i+=kw) + { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + } + } + while (!kit.isDone()) { + memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth); + ++kit; + i += kw; + } + TQ_INT32 lastvalid = i - kw; + for(;i < kw*kh; i+=kw) + { + memcpy(pixelPtrCache[i], pixelPtrCache[lastvalid], cdepth); + } + } + } + if (hit.isSelected()) { + cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh); + } + ++col; + ++hit; + } + + int progressPercent = 100 - ((((y + h) - row) * 100) / h); + + if (progressPercent > lastProgressPercent) { + emit notifyProgress(progressPercent); + lastProgressPercent = progressPercent; + + if (m_cancelRequested) { + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; + return; + } + } + + } + + addDirtyRect(TQRect(x, y, w, h)); + + emit notifyProgressDone(); + for (int i = 0; i < cacheSize; i++) + delete[] pixelPtrCache[i]; + delete[] pixelPtrCache; +} -- cgit v1.2.1