chalk

kis_convolution_painter.cpp
1/*
2 * Copyright (c) 2005 Cyrille Berger <cberger@cberger.net>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19#include <stdlib.h>
20#include <string.h>
21#include <cfloat>
22
23#include "tqbrush.h"
24#include "tqcolor.h"
25#include "tqfontinfo.h"
26#include "tqfontmetrics.h"
27#include "tqpen.h"
28#include "tqregion.h"
29#include "tqwmatrix.h"
30#include <tqimage.h>
31#include <tqmap.h>
32#include <tqpainter.h>
33#include <tqpixmap.h>
34#include <tqpointarray.h>
35#include <tqrect.h>
36#include <tqstring.h>
37
38#include <kdebug.h>
39#include <tdelocale.h>
40
41#include <tqcolor.h>
42
43#include "kis_brush.h"
44#include "kis_global.h"
45#include "kis_image.h"
46#include "kis_iterators_pixel.h"
47#include "kis_layer.h"
48#include "kis_paint_device.h"
49#include "kis_painter.h"
50#include "kis_pattern.h"
51#include "kis_rect.h"
52#include "kis_colorspace.h"
53#include "kis_types.h"
54#include "kis_vec.h"
55#include "kis_selection.h"
56#include "kis_convolution_painter.h"
57
58
59KisKernelSP KisKernel::fromTQImage(const TQImage& img)
60{
61 KisKernelSP k = new KisKernel;
62 k->width = img.width();
63 k->height = img.height();
64 k->offset = 0;
65 uint count = k->width * k->height;
66 k->data = new TQ_INT32[count];
67 TQ_INT32* itData = k->data;
68 TQ_UINT8* itImg = (TQ_UINT8*)img.bits();
69 k->factor = 0;
70 for(uint i = 0; i < count; ++i , ++itData, itImg+=4)
71 {
72 *itData = 255 - ( *itImg + *(itImg+1) + *(itImg+2) ) / 3;
73 k->factor += *itData;
74 }
75 return k;
76}
77
78
79KisConvolutionPainter::KisConvolutionPainter()
80 : super()
81{
82}
83
84KisConvolutionPainter::KisConvolutionPainter(KisPaintDeviceSP device) : super(device)
85{
86}
87
88void KisConvolutionPainter::applyMatrix(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h,
89 KisConvolutionBorderOp borderOp,
90 KisChannelInfo::enumChannelFlags channelFlags )
91{
92 // Make the area we cover as small as possible
93 if (m_device->hasSelection()) {
94
95 TQRect r = m_device->selection()->selectedRect().intersect(TQRect(x, y, w, h));
96 x = r.x();
97 y = r.y();
98 w = r.width();
99 h = r.height();
100
101 }
102
103 if ( w == 0 && h == 0 ) return;
104
105 // Determine the kernel's extent from the center pixel
106 TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
107 kw = kernel->width;
108 kh = kernel->height;
109 khalfWidth = (kw - 1) / 2;
110 khalfHeight = (kh - 1) / 2;
111
112 xLastMinuskhw = x + (w - khalfWidth);
113 yLastMinuskhh = y + (h - khalfHeight);
114
115 // 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.
116 if (w < kw || h < kh || (kw&1) == 0 || (kh&1) == 0 || kernel->factor == 0 ) return;
117
118 m_cancelRequested = false;
119 int lastProgressPercent = 0;
120 emit notifyProgress(0);
121
122 KisColorSpace * cs = m_device->colorSpace();
123
124 // Determine whether we convolve border pixels, or not.
125 switch (borderOp) {
126 case BORDER_DEFAULT_FILL :
127 break;
128 case BORDER_REPEAT:
129 applyMatrixRepeat(kernel, x, y, w, h, channelFlags);
130 return;
131 case BORDER_WRAP:
132 case BORDER_AVOID:
133 default :
134 x += khalfWidth;
135 y += khalfHeight;
136 w -= kw - 1;
137 h -= kh - 1;
138 }
139
140 // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.
141
142 int cacheSize = kw * kh;
143 int cdepth = cs -> pixelSize();
144 TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize];
145 for (int i = 0; i < cacheSize; i++)
146 pixelPtrCache[i] = new TQ_UINT8[cdepth];
147// pixelPtrCache.fill(0);
148
149 // row == the y position of the pixel we want to change in the paint device
150 int row = y;
151
152 for (; row < y + h; ++row) {
153
154 // col = the x position of the pixel we want to change
155 int col = x;
156
157 KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true);
158 bool needFull = true;
159 while (!hit.isDone()) {
160
161 // Iterate over all contributing pixels that are covered by the kernel
162 // krow = the y position in the kernel matrix
163 if(needFull)
164 {
165 TQ_INT32 i = 0;
166 for (TQ_INT32 krow = 0; krow < kh; ++krow) {
167
168 // col - khalfWidth = the left starting point of the kernel as centered on our pixel
169 // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
170 // kw = the width of the kernel
171
172 // Fill the cache with pointers to the pixels under the kernel
173 KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, (row - khalfHeight) + krow, kw, false);
174 while (!kit.isDone()) {
175 memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
176 ++kit;
177 ++i;
178 }
179 }
180 needFull = false;
181 Q_ASSERT (i==kw*kh);
182 } else {
183 for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left
184 TQ_UINT8** d = pixelPtrCache + krow * kw;
185 //memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*));
186 for (int i = 0; i < (kw-1); i++) {
187 memcpy(d[i], d[i+1], cdepth);
188 }
189 }
190 TQ_INT32 i = kw - 1;
191 KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, row - khalfHeight, kh, false);
192 while (!kit.isDone()) {
193 memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
194 ++kit;
195 i += kw;
196 }
197 }
198 if (hit.isSelected()) {
199 cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
200// pixelPtrCache.fill(0);
201 }
202 ++col;
203 ++hit;
204 }
205
206 int progressPercent = 100 - ((((y + h) - row) * 100) / h);
207
208 if (progressPercent > lastProgressPercent) {
209 emit notifyProgress(progressPercent);
210 lastProgressPercent = progressPercent;
211
212 if (m_cancelRequested) {
213 for (int i = 0; i < cacheSize; i++)
214 delete[] pixelPtrCache[i];
215 delete[] pixelPtrCache;
216
217 return;
218 }
219 }
220
221 }
222
223 addDirtyRect(TQRect(x, y, w, h));
224
225 emit notifyProgressDone();
226
227 for (int i = 0; i < cacheSize; i++)
228 delete[] pixelPtrCache[i];
229 delete[] pixelPtrCache;
230}
231
232void KisConvolutionPainter::applyMatrixRepeat(KisKernelSP kernel, TQ_INT32 x, TQ_INT32 y, TQ_INT32 w, TQ_INT32 h,
233 KisChannelInfo::enumChannelFlags channelFlags)
234{
235 int lastProgressPercent = 0;
236 // Determine the kernel's extent from the center pixel
237 TQ_INT32 kw, kh, khalfWidth, khalfHeight, xLastMinuskhw, yLastMinuskhh;
238 kw = kernel->width;
239 kh = kernel->height;
240 khalfWidth = (kw - 1) / 2;
241 khalfHeight = (kh - 1) / 2;
242
243 xLastMinuskhw = x + (w - khalfWidth);
244 yLastMinuskhh = y + (h - khalfHeight);
245
246 KisColorSpace * cs = m_device->colorSpace();
247
248 // Iterate over all pixels in our rect, create a cache of pixels around the current pixel and convolve them in the colorstrategy.
249
250 int cacheSize = kw * kh;
251 int cdepth = cs -> pixelSize();
252 TQ_UINT8** pixelPtrCache = new TQ_UINT8*[cacheSize];
253 for (int i = 0; i < cacheSize; i++)
254 pixelPtrCache[i] = new TQ_UINT8[cdepth];
255
256 // row == the y position of the pixel we want to change in the paint device
257 int row = y;
258
259 for (; row < y + h; ++row) {
260
261 // col = the x position of the pixel we want to change
262 int col = x;
263
264 KisHLineIteratorPixel hit = m_device->createHLineIterator(x, row, w, true);
265 bool needFull = true;
266
267 TQ_INT32 itStart = row - khalfHeight;
268 TQ_INT32 itH = kh;
269 if(itStart < 0)
270 {
271 itH += itStart;
272 itStart = 0;
273 } else if(itStart + kh > yLastMinuskhh)
274 {
275 itH -= itStart + kh - yLastMinuskhh;
276 }
277 KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
278 while (!hit.isDone()) {
279
280 // Iterate over all contributing pixels that are covered by the kernel
281 // krow = the y position in the kernel matrix
282 if(needFull) // The cache has not been fill, so we need to fill it
283 {
284 TQ_INT32 i = 0;
285 TQ_INT32 krow = 0;
286 if( row < khalfHeight )
287 {
288 // We are just outside the layer, all the row in the cache will be identical
289 // so we need to create them only once, and then to copy them
290 if( x < khalfWidth)
291 { // the left pixels are outside of the layer, in the corner
292 TQ_INT32 kcol = 0;
293 KisHLineIteratorPixel kit = m_device->createHLineIterator(0, 0, kw, false);
294 for(; kcol < (khalfWidth - x) + 1; ++kcol)
295 { // First copy the address of the topleft pixel
296 memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
297 }
298 for(; kcol < kw; ++kcol)
299 { // Then copy the address of the rest of the line
300 ++kit;
301 memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
302 }
303 } else {
304 uint kcol = 0;
305 KisHLineIteratorPixel kit = m_device->createHLineIterator(col - khalfWidth, 0, kw, false);
306 while (!kit.isDone()) {
307 memcpy(pixelPtrCache[kcol], kit.oldRawData(), cdepth);
308 ++kit;
309 ++kcol;
310 }
311 }
312 krow = 1; // we have allready done the first krow
313 for(;krow < (khalfHeight - row); ++krow)
314 {
315 // Copy the first line in the current line
316 for (int i = 0; i < kw; i++)
317 memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[i], cdepth);
318 }
319 i = krow * kw;
320 }
321 TQ_INT32 itH = kh;
322 if(row + khalfHeight > yLastMinuskhh)
323 {
324 itH += yLastMinuskhh - row - khalfHeight;
325 }
326 for (; krow < itH; ++krow) {
327
328 // col - khalfWidth = the left starting point of the kernel as centered on our pixel
329 // krow - khalfHeight = the offset for the top of the kernel as centered on our pixel
330 // kw = the width of the kernel
331
332 // Fill the cache with pointers to the pixels under the kernel
333 TQ_INT32 itHStart = col - khalfWidth;
334 TQ_INT32 itW = kw;
335 if(itHStart < 0)
336 {
337 itW += itHStart;
338 itHStart = 0;
339 }
340 KisHLineIteratorPixel kit = m_device->createHLineIterator(itHStart, (row - khalfHeight) + krow, itW, false);
341 if( col < khalfWidth )
342 {
343 for(; i < krow * kw + ( kw - itW ); i+= 1)
344 {
345 memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
346 }
347 }
348 while (!kit.isDone()) {
349 memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
350 ++kit;
351 ++i;
352 }
353 }
354 TQ_INT32 lastvalid = i - kw;
355 for(; krow < kh; ++krow) {
356 // Copy the last valid line in the current line
357 for (int i = 0; i < kw; i++)
358 memcpy(pixelPtrCache[krow * kw + i], pixelPtrCache[lastvalid + i],
359 cdepth);
360 }
361 needFull = false;
362 } else {
363/* for (TQ_INT32 krow = 0; krow < kh; ++krow) { // shift the cache to the left
364 TQ_UINT8** d = pixelPtrCache + krow * kw;
365// memmove( d, d + 1, (kw-1)*sizeof(TQ_UINT8*));
366 for (int i = 0; i < (kw-1); i++) {
367 memcpy(d[i], d[i+1], cdepth);
368 }
369 }*/
370 TQ_UINT8* firstincache = pixelPtrCache[0];
371 memmove(pixelPtrCache, pixelPtrCache + 1, (cacheSize - 1) * sizeof(TQ_UINT8*) );
372 pixelPtrCache[cacheSize - 1] = firstincache;
373 if(col < xLastMinuskhw)
374 {
375 TQ_INT32 i = kw - 1;
376// KisVLineIteratorPixel kit = m_device->createVLineIterator(col + khalfWidth, itStart, itH, false);
377 kit.nextCol();
378 if( row < khalfHeight )
379 {
380 for(; i < (khalfHeight- row ) * kw; i+=kw)
381 {
382 memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
383 }
384 }
385 while (!kit.isDone()) {
386 memcpy(pixelPtrCache[i], kit.oldRawData(), cdepth);
387 ++kit;
388 i += kw;
389 }
390 TQ_INT32 lastvalid = i - kw;
391 for(;i < kw*kh; i+=kw)
392 {
393 memcpy(pixelPtrCache[i], pixelPtrCache[lastvalid], cdepth);
394 }
395 }
396 }
397 if (hit.isSelected()) {
398 cs->convolveColors(pixelPtrCache, kernel->data, channelFlags, hit.rawData(), kernel->factor, kernel->offset, kw * kh);
399 }
400 ++col;
401 ++hit;
402 }
403
404 int progressPercent = 100 - ((((y + h) - row) * 100) / h);
405
406 if (progressPercent > lastProgressPercent) {
407 emit notifyProgress(progressPercent);
408 lastProgressPercent = progressPercent;
409
410 if (m_cancelRequested) {
411 for (int i = 0; i < cacheSize; i++)
412 delete[] pixelPtrCache[i];
413 delete[] pixelPtrCache;
414 return;
415 }
416 }
417
418 }
419
420 addDirtyRect(TQRect(x, y, w, h));
421
422 emit notifyProgressDone();
423 for (int i = 0; i < cacheSize; i++)
424 delete[] pixelPtrCache[i];
425 delete[] pixelPtrCache;
426}
virtual void convolveColors(TQ_UINT8 **colors, TQ_INT32 *kernelValues, KisChannelInfo::enumChannelFlags channelFlags, TQ_UINT8 *dst, TQ_INT32 factor, TQ_INT32 offset, TQ_INT32 nPixels) const =0
Convolve the given array of pointers to pixels and return the result in dst.
TQRect addDirtyRect(TQRect r)
Add the r to the current dirty rect, and return the dirtyRect after adding r to it.