• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdefx
 

tdefx

  • tdefx
kimageeffect.cpp
1/* This file is part of the KDE libraries
2 Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley <mosfet@kde.org>
3 (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
4 (C) 1998, 1999 Dirk Mueller <mueller@kde.org>
5 (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
6 (C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
7 (C) 2004 Zack Rusin <zack@kde.org>
8
9Redistribution and use in source and binary forms, with or without
10modification, are permitted provided that the following conditions
11are met:
12
131. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
152. Redistributions in binary form must reproduce the above copyright
16 notice, this list of conditions and the following disclaimer in the
17 documentation and/or other materials provided with the distribution.
18
19THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30*/
31
32// $Id$
33
34#include <math.h>
35#include <assert.h>
36
37#include <tqimage.h>
38#include <stdlib.h>
39#include <iostream>
40
41#include "kimageeffect.h"
42#include "kcpuinfo.h"
43
44#include <config.h>
45
46#if 0
47//disabled until #74478 fixed.
48
49#if defined(__i386__) && ( defined(__GNUC__) || defined(__INTEL_COMPILER) )
50# if defined( HAVE_X86_MMX )
51# define USE_MMX_INLINE_ASM
52# endif
53# if defined( HAVE_X86_SSE2 )
54# define USE_SSE2_INLINE_ASM
55# endif
56#endif
57
58#endif
59//======================================================================
60//
61// Utility stuff for effects ported from ImageMagick to TQImage
62//
63//======================================================================
64#define MaxRGB 255L
65#define DegreesToRadians(x) ((x)*M_PI/180.0)
66#define MagickSQ2PI 2.50662827463100024161235523934010416269302368164062
67#define MagickEpsilon 1.0e-12
68#define MagickPI 3.14159265358979323846264338327950288419716939937510
69#define MOD(x, y) ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y))
70
76#define FXCLAMP(x,low,high) fxClamp(x,low,high)
77template<class T>
78inline const T& fxClamp( const T& x, const T& low, const T& high )
79{
80 if ( x < low ) return low;
81 else if ( x > high ) return high;
82 else return x;
83}
84
85static inline unsigned int intensityValue(unsigned int color)
86{
87 return((unsigned int)((0.299*tqRed(color) +
88 0.587*tqGreen(color) +
89 0.1140000000000001*tqBlue(color))));
90}
91
92template<typename T>
93static inline void liberateMemory(T **memory)
94{
95 assert(memory != NULL);
96 if(*memory == NULL) return;
97 free((char*)*memory);
98 *memory=NULL;
99}
100
101struct double_packet
102{
103 double red;
104 double green;
105 double blue;
106 double alpha;
107};
108
109struct short_packet
110{
111 unsigned short int red;
112 unsigned short int green;
113 unsigned short int blue;
114 unsigned short int alpha;
115};
116
117
118//======================================================================
119//
120// Gradient effects
121//
122//======================================================================
123
124TQImage KImageEffect::gradient(const TQSize &size, const TQColor &ca,
125 const TQColor &cb, GradientType eff, int ncols)
126{
127 int rDiff, gDiff, bDiff;
128 int rca, gca, bca, rcb, gcb, bcb;
129
130 TQImage image(size, 32);
131
132 if (size.width() == 0 || size.height() == 0) {
133#ifndef NDEBUG
134 std::cerr << "WARNING: KImageEffect::gradient: invalid image" << std::endl;
135#endif
136 return image;
137 }
138
139 int x, y;
140
141 rDiff = (rcb = cb.red()) - (rca = ca.red());
142 gDiff = (gcb = cb.green()) - (gca = ca.green());
143 bDiff = (bcb = cb.blue()) - (bca = ca.blue());
144
145 if( eff == VerticalGradient || eff == HorizontalGradient ){
146
147 uint *p;
148 uint rgb;
149
150 int rl = rca << 16;
151 int gl = gca << 16;
152 int bl = bca << 16;
153
154 if( eff == VerticalGradient ) {
155
156 int rcdelta = ((1<<16) / size.height()) * rDiff;
157 int gcdelta = ((1<<16) / size.height()) * gDiff;
158 int bcdelta = ((1<<16) / size.height()) * bDiff;
159
160 for ( y = 0; y < size.height(); y++ ) {
161 p = (uint *) image.scanLine(y);
162
163 rl += rcdelta;
164 gl += gcdelta;
165 bl += bcdelta;
166
167 rgb = tqRgb( (rl>>16), (gl>>16), (bl>>16) );
168
169 for( x = 0; x < size.width(); x++ ) {
170 *p = rgb;
171 p++;
172 }
173 }
174
175 }
176 else { // must be HorizontalGradient
177
178 unsigned int *o_src = (unsigned int *)image.scanLine(0);
179 unsigned int *src = o_src;
180
181 int rcdelta = ((1<<16) / size.width()) * rDiff;
182 int gcdelta = ((1<<16) / size.width()) * gDiff;
183 int bcdelta = ((1<<16) / size.width()) * bDiff;
184
185 for( x = 0; x < size.width(); x++) {
186
187 rl += rcdelta;
188 gl += gcdelta;
189 bl += bcdelta;
190
191 *src++ = tqRgb( (rl>>16), (gl>>16), (bl>>16));
192 }
193
194 src = o_src;
195
196 // Believe it or not, manually copying in a for loop is faster
197 // than calling memcpy for each scanline (on the order of ms...).
198 // I think this is due to the function call overhead (mosfet).
199
200 for (y = 1; y < size.height(); ++y) {
201
202 p = (unsigned int *)image.scanLine(y);
203 src = o_src;
204 for(x=0; x < size.width(); ++x)
205 *p++ = *src++;
206 }
207 }
208 }
209
210 else {
211
212 float rfd, gfd, bfd;
213 float rd = rca, gd = gca, bd = bca;
214
215 unsigned char *xtable[3];
216 unsigned char *ytable[3];
217
218 unsigned int w = size.width(), h = size.height();
219 xtable[0] = new unsigned char[w];
220 xtable[1] = new unsigned char[w];
221 xtable[2] = new unsigned char[w];
222 ytable[0] = new unsigned char[h];
223 ytable[1] = new unsigned char[h];
224 ytable[2] = new unsigned char[h];
225 w*=2, h*=2;
226
227 if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) {
228 // Diagonal dgradient code inspired by BlackBox (mosfet)
229 // BlackBox dgradient is (C) Brad Hughes, <bhughes@tcac.net> and
230 // Mike Cole <mike@mydot.com>.
231
232 rfd = (float)rDiff/w;
233 gfd = (float)gDiff/w;
234 bfd = (float)bDiff/w;
235
236 int dir;
237 for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) {
238 dir = eff == DiagonalGradient? x : size.width() - x - 1;
239 xtable[0][dir] = (unsigned char) rd;
240 xtable[1][dir] = (unsigned char) gd;
241 xtable[2][dir] = (unsigned char) bd;
242 }
243 rfd = (float)rDiff/h;
244 gfd = (float)gDiff/h;
245 bfd = (float)bDiff/h;
246 rd = gd = bd = 0;
247 for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) {
248 ytable[0][y] = (unsigned char) rd;
249 ytable[1][y] = (unsigned char) gd;
250 ytable[2][y] = (unsigned char) bd;
251 }
252
253 for (y = 0; y < size.height(); y++) {
254 unsigned int *scanline = (unsigned int *)image.scanLine(y);
255 for (x = 0; x < size.width(); x++) {
256 scanline[x] = tqRgb(xtable[0][x] + ytable[0][y],
257 xtable[1][x] + ytable[1][y],
258 xtable[2][x] + ytable[2][y]);
259 }
260 }
261 }
262
263 else if (eff == RectangleGradient ||
264 eff == PyramidGradient ||
265 eff == PipeCrossGradient ||
266 eff == EllipticGradient)
267 {
268 int rSign = rDiff>0? 1: -1;
269 int gSign = gDiff>0? 1: -1;
270 int bSign = bDiff>0? 1: -1;
271
272 rfd = (float)rDiff / size.width();
273 gfd = (float)gDiff / size.width();
274 bfd = (float)bDiff / size.width();
275
276 rd = (float)rDiff/2;
277 gd = (float)gDiff/2;
278 bd = (float)bDiff/2;
279
280 for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd)
281 {
282 xtable[0][x] = (unsigned char) abs((int)rd);
283 xtable[1][x] = (unsigned char) abs((int)gd);
284 xtable[2][x] = (unsigned char) abs((int)bd);
285 }
286
287 rfd = (float)rDiff/size.height();
288 gfd = (float)gDiff/size.height();
289 bfd = (float)bDiff/size.height();
290
291 rd = (float)rDiff/2;
292 gd = (float)gDiff/2;
293 bd = (float)bDiff/2;
294
295 for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd)
296 {
297 ytable[0][y] = (unsigned char) abs((int)rd);
298 ytable[1][y] = (unsigned char) abs((int)gd);
299 ytable[2][y] = (unsigned char) abs((int)bd);
300 }
301
302 int h = (size.height()+1)>>1;
303 for (y = 0; y < h; y++) {
304 unsigned int *sl1 = (unsigned int *)image.scanLine(y);
305 unsigned int *sl2 = (unsigned int *)image.scanLine(TQMAX(size.height()-y-1, y));
306
307 int w = (size.width()+1)>>1;
308 int x2 = size.width()-1;
309
310 for (x = 0; x < w; x++, x2--) {
311 unsigned int rgb = 0;
312 if (eff == PyramidGradient) {
313 rgb = tqRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
314 gcb-gSign*(xtable[1][x]+ytable[1][y]),
315 bcb-bSign*(xtable[2][x]+ytable[2][y]));
316 }
317 if (eff == RectangleGradient) {
318 rgb = tqRgb(rcb - rSign *
319 TQMAX(xtable[0][x], ytable[0][y]) * 2,
320 gcb - gSign *
321 TQMAX(xtable[1][x], ytable[1][y]) * 2,
322 bcb - bSign *
323 TQMAX(xtable[2][x], ytable[2][y]) * 2);
324 }
325 if (eff == PipeCrossGradient) {
326 rgb = tqRgb(rcb - rSign *
327 TQMIN(xtable[0][x], ytable[0][y]) * 2,
328 gcb - gSign *
329 TQMIN(xtable[1][x], ytable[1][y]) * 2,
330 bcb - bSign *
331 TQMIN(xtable[2][x], ytable[2][y]) * 2);
332 }
333 if (eff == EllipticGradient) {
334 rgb = tqRgb(rcb - rSign *
335 (int)sqrt((xtable[0][x]*xtable[0][x] +
336 ytable[0][y]*ytable[0][y])*2.0),
337 gcb - gSign *
338 (int)sqrt((xtable[1][x]*xtable[1][x] +
339 ytable[1][y]*ytable[1][y])*2.0),
340 bcb - bSign *
341 (int)sqrt((xtable[2][x]*xtable[2][x] +
342 ytable[2][y]*ytable[2][y])*2.0));
343 }
344
345 sl1[x] = sl2[x] = rgb;
346 sl1[x2] = sl2[x2] = rgb;
347 }
348 }
349 }
350
351 delete [] xtable[0];
352 delete [] xtable[1];
353 delete [] xtable[2];
354 delete [] ytable[0];
355 delete [] ytable[1];
356 delete [] ytable[2];
357 }
358
359 // dither if necessary
360 if (ncols && (TQPixmap::defaultDepth() < 15 )) {
361 if ( ncols < 2 || ncols > 256 )
362 ncols = 3;
363 TQColor *dPal = new TQColor[ncols];
364 for (int i=0; i<ncols; i++) {
365 dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
366 gca + gDiff * i / ( ncols - 1 ),
367 bca + bDiff * i / ( ncols - 1 ) );
368 }
369 dither(image, dPal, ncols);
370 delete [] dPal;
371 }
372
373 return image;
374}
375
376
377// -----------------------------------------------------------------------------
378
379//CT this was (before Dirk A. Mueller's speedup changes)
380// merely the same code as in the above method, but it's supposedly
381// way less performant since it introduces a lot of supplementary tests
382// and simple math operations for the calculus of the balance.
383// (surprizingly, it isn't less performant, in the contrary :-)
384// Yes, I could have merged them, but then the excellent performance of
385// the balanced code would suffer with no other gain than a mere
386// source code and byte code size economy.
387
388TQImage KImageEffect::unbalancedGradient(const TQSize &size, const TQColor &ca,
389 const TQColor &cb, GradientType eff, int xfactor, int yfactor,
390 int ncols)
391{
392 int dir; // general parameter used for direction switches
393
394 bool _xanti = false , _yanti = false;
395
396 if (xfactor < 0) _xanti = true; // negative on X direction
397 if (yfactor < 0) _yanti = true; // negative on Y direction
398
399 xfactor = abs(xfactor);
400 yfactor = abs(yfactor);
401
402 if (!xfactor) xfactor = 1;
403 if (!yfactor) yfactor = 1;
404
405 if (xfactor > 200 ) xfactor = 200;
406 if (yfactor > 200 ) yfactor = 200;
407
408
409 // float xbal = xfactor/5000.;
410 // float ybal = yfactor/5000.;
411 float xbal = xfactor/30./size.width();
412 float ybal = yfactor/30./size.height();
413 float rat;
414
415 int rDiff, gDiff, bDiff;
416 int rca, gca, bca, rcb, gcb, bcb;
417
418 TQImage image(size, 32);
419
420 if (size.width() == 0 || size.height() == 0) {
421#ifndef NDEBUG
422 std::cerr << "WARNING: KImageEffect::unbalancedGradient : invalid image\n";
423#endif
424 return image;
425 }
426
427 int x, y;
428 unsigned int *scanline;
429
430 rDiff = (rcb = cb.red()) - (rca = ca.red());
431 gDiff = (gcb = cb.green()) - (gca = ca.green());
432 bDiff = (bcb = cb.blue()) - (bca = ca.blue());
433
434 if( eff == VerticalGradient || eff == HorizontalGradient){
435 TQColor cRow;
436
437 uint *p;
438 uint rgbRow;
439
440 if( eff == VerticalGradient) {
441 for ( y = 0; y < size.height(); y++ ) {
442 dir = _yanti ? y : size.height() - 1 - y;
443 p = (uint *) image.scanLine(dir);
444 rat = 1 - exp( - (float)y * ybal );
445
446 cRow.setRgb( rcb - (int) ( rDiff * rat ),
447 gcb - (int) ( gDiff * rat ),
448 bcb - (int) ( bDiff * rat ) );
449
450 rgbRow = cRow.rgb();
451
452 for( x = 0; x < size.width(); x++ ) {
453 *p = rgbRow;
454 p++;
455 }
456 }
457 }
458 else {
459
460 unsigned int *src = (unsigned int *)image.scanLine(0);
461 for(x = 0; x < size.width(); x++ )
462 {
463 dir = _xanti ? x : size.width() - 1 - x;
464 rat = 1 - exp( - (float)x * xbal );
465
466 src[dir] = tqRgb(rcb - (int) ( rDiff * rat ),
467 gcb - (int) ( gDiff * rat ),
468 bcb - (int) ( bDiff * rat ));
469 }
470
471 // Believe it or not, manually copying in a for loop is faster
472 // than calling memcpy for each scanline (on the order of ms...).
473 // I think this is due to the function call overhead (mosfet).
474
475 for(y = 1; y < size.height(); ++y)
476 {
477 scanline = (unsigned int *)image.scanLine(y);
478 for(x=0; x < size.width(); ++x)
479 scanline[x] = src[x];
480 }
481 }
482 }
483
484 else {
485 int w=size.width(), h=size.height();
486
487 unsigned char *xtable[3];
488 unsigned char *ytable[3];
489 xtable[0] = new unsigned char[w];
490 xtable[1] = new unsigned char[w];
491 xtable[2] = new unsigned char[w];
492 ytable[0] = new unsigned char[h];
493 ytable[1] = new unsigned char[h];
494 ytable[2] = new unsigned char[h];
495
496 if ( eff == DiagonalGradient || eff == CrossDiagonalGradient)
497 {
498 for (x = 0; x < w; x++) {
499 dir = _xanti ? x : w - 1 - x;
500 rat = 1 - exp( - (float)x * xbal );
501
502 xtable[0][dir] = (unsigned char) ( rDiff/2 * rat );
503 xtable[1][dir] = (unsigned char) ( gDiff/2 * rat );
504 xtable[2][dir] = (unsigned char) ( bDiff/2 * rat );
505 }
506
507 for (y = 0; y < h; y++) {
508 dir = _yanti ? y : h - 1 - y;
509 rat = 1 - exp( - (float)y * ybal );
510
511 ytable[0][dir] = (unsigned char) ( rDiff/2 * rat );
512 ytable[1][dir] = (unsigned char) ( gDiff/2 * rat );
513 ytable[2][dir] = (unsigned char) ( bDiff/2 * rat );
514 }
515
516 for (y = 0; y < h; y++) {
517 unsigned int *scanline = (unsigned int *)image.scanLine(y);
518 for (x = 0; x < w; x++) {
519 scanline[x] = tqRgb(rcb - (xtable[0][x] + ytable[0][y]),
520 gcb - (xtable[1][x] + ytable[1][y]),
521 bcb - (xtable[2][x] + ytable[2][y]));
522 }
523 }
524 }
525
526 else if (eff == RectangleGradient ||
527 eff == PyramidGradient ||
528 eff == PipeCrossGradient ||
529 eff == EllipticGradient)
530 {
531 int rSign = rDiff>0? 1: -1;
532 int gSign = gDiff>0? 1: -1;
533 int bSign = bDiff>0? 1: -1;
534
535 for (x = 0; x < w; x++)
536 {
537 dir = _xanti ? x : w - 1 - x;
538 rat = 1 - exp( - (float)x * xbal );
539
540 xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
541 xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
542 xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
543 }
544
545 for (y = 0; y < h; y++)
546 {
547 dir = _yanti ? y : h - 1 - y;
548
549 rat = 1 - exp( - (float)y * ybal );
550
551 ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
552 ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
553 ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
554 }
555
556 for (y = 0; y < h; y++) {
557 unsigned int *scanline = (unsigned int *)image.scanLine(y);
558 for (x = 0; x < w; x++) {
559 if (eff == PyramidGradient)
560 {
561 scanline[x] = tqRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
562 gcb-gSign*(xtable[1][x]+ytable[1][y]),
563 bcb-bSign*(xtable[2][x]+ytable[2][y]));
564 }
565 else if (eff == RectangleGradient)
566 {
567 scanline[x] = tqRgb(rcb - rSign *
568 TQMAX(xtable[0][x], ytable[0][y]) * 2,
569 gcb - gSign *
570 TQMAX(xtable[1][x], ytable[1][y]) * 2,
571 bcb - bSign *
572 TQMAX(xtable[2][x], ytable[2][y]) * 2);
573 }
574 else if (eff == PipeCrossGradient)
575 {
576 scanline[x] = tqRgb(rcb - rSign *
577 TQMIN(xtable[0][x], ytable[0][y]) * 2,
578 gcb - gSign *
579 TQMIN(xtable[1][x], ytable[1][y]) * 2,
580 bcb - bSign *
581 TQMIN(xtable[2][x], ytable[2][y]) * 2);
582 }
583 else if (eff == EllipticGradient)
584 {
585 scanline[x] = tqRgb(rcb - rSign *
586 (int)sqrt((xtable[0][x]*xtable[0][x] +
587 ytable[0][y]*ytable[0][y])*2.0),
588 gcb - gSign *
589 (int)sqrt((xtable[1][x]*xtable[1][x] +
590 ytable[1][y]*ytable[1][y])*2.0),
591 bcb - bSign *
592 (int)sqrt((xtable[2][x]*xtable[2][x] +
593 ytable[2][y]*ytable[2][y])*2.0));
594 }
595 }
596 }
597 }
598
599 if (ncols && (TQPixmap::defaultDepth() < 15 )) {
600 if ( ncols < 2 || ncols > 256 )
601 ncols = 3;
602 TQColor *dPal = new TQColor[ncols];
603 for (int i=0; i<ncols; i++) {
604 dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
605 gca + gDiff * i / ( ncols - 1 ),
606 bca + bDiff * i / ( ncols - 1 ) );
607 }
608 dither(image, dPal, ncols);
609 delete [] dPal;
610 }
611
612 delete [] xtable[0];
613 delete [] xtable[1];
614 delete [] xtable[2];
615 delete [] ytable[0];
616 delete [] ytable[1];
617 delete [] ytable[2];
618
619 }
620
621 return image;
622}
623
627namespace {
628
629struct KIE4Pack
630{
631 TQ_UINT16 data[4];
632};
633
634struct KIE8Pack
635{
636 TQ_UINT16 data[8];
637};
638
639}
640
641//======================================================================
642//
643// Intensity effects
644//
645//======================================================================
646
647
648/* This builds a 256 byte unsigned char lookup table with all
649 * the possible percent values prior to applying the effect, then uses
650 * integer math for the pixels. For any image larger than 9x9 this will be
651 * less expensive than doing a float operation on the 3 color components of
652 * each pixel. (mosfet)
653 */
654TQImage& KImageEffect::intensity(TQImage &image, float percent)
655{
656 if (image.width() == 0 || image.height() == 0) {
657#ifndef NDEBUG
658 std::cerr << "WARNING: KImageEffect::intensity : invalid image\n";
659#endif
660 return image;
661 }
662
663 int segColors = image.depth() > 8 ? 256 : image.numColors();
664 int pixels = image.depth() > 8 ? image.width()*image.height() :
665 image.numColors();
666 unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
667 (unsigned int *)image.colorTable();
668
669 bool brighten = (percent >= 0);
670 if(percent < 0)
671 percent = -percent;
672
673#ifdef USE_MMX_INLINE_ASM
674 bool haveMMX = KCPUInfo::haveExtension( KCPUInfo::IntelMMX );
675
676 if(haveMMX)
677 {
678 TQ_UINT16 p = TQ_UINT16(256.0f*(percent));
679 KIE4Pack mult = {{p,p,p,0}};
680
681 __asm__ __volatile__(
682 "pxor %%mm7, %%mm7\n\t" // zero mm7 for unpacking
683 "movq (%0), %%mm6\n\t" // copy intensity change to mm6
684 : : "r"(&mult), "m"(mult));
685
686 unsigned int rem = pixels % 4;
687 pixels -= rem;
688 TQ_UINT32 *end = ( data + pixels );
689
690 if (brighten)
691 {
692 while ( data != end ) {
693 __asm__ __volatile__(
694 "movq (%0), %%mm0\n\t"
695 "movq 8(%0), %%mm4\n\t" // copy 4 pixels of data to mm0 and mm4
696 "movq %%mm0, %%mm1\n\t"
697 "movq %%mm0, %%mm3\n\t"
698 "movq %%mm4, %%mm5\n\t" // copy to registers for unpacking
699 "punpcklbw %%mm7, %%mm0\n\t"
700 "punpckhbw %%mm7, %%mm1\n\t" // unpack the two pixels from mm0
701 "pmullw %%mm6, %%mm0\n\t"
702 "punpcklbw %%mm7, %%mm4\n\t"
703 "pmullw %%mm6, %%mm1\n\t" // multiply by intensity*256
704 "psrlw $8, %%mm0\n\t" // divide by 256
705 "pmullw %%mm6, %%mm4\n\t"
706 "psrlw $8, %%mm1\n\t"
707 "psrlw $8, %%mm4\n\t"
708 "packuswb %%mm1, %%mm0\n\t" // pack solution into mm0. saturates at 255
709 "movq %%mm5, %%mm1\n\t"
710
711 "punpckhbw %%mm7, %%mm1\n\t" // unpack 4th pixel in mm1
712
713 "pmullw %%mm6, %%mm1\n\t"
714 "paddusb %%mm3, %%mm0\n\t" // add intesity result to original of mm0
715 "psrlw $8, %%mm1\n\t"
716 "packuswb %%mm1, %%mm4\n\t" // pack upper two pixels into mm4
717
718 "movq %%mm0, (%0)\n\t" // rewrite to memory lower two pixels
719 "paddusb %%mm5, %%mm4\n\t"
720 "movq %%mm4, 8(%0)\n\t" // rewrite upper two pixels
721 : : "r"(data) );
722 data += 4;
723 }
724
725 end += rem;
726 while ( data != end ) {
727 __asm__ __volatile__(
728 "movd (%0), %%mm0\n\t" // repeat above but for
729 "punpcklbw %%mm7, %%mm0\n\t" // one pixel at a time
730 "movq %%mm0, %%mm3\n\t"
731 "pmullw %%mm6, %%mm0\n\t"
732 "psrlw $8, %%mm0\n\t"
733 "paddw %%mm3, %%mm0\n\t"
734 "packuswb %%mm0, %%mm0\n\t"
735 "movd %%mm0, (%0)\n\t"
736 : : "r"(data) );
737 data++;
738 }
739 }
740 else
741 {
742 while ( data != end ) {
743 __asm__ __volatile__(
744 "movq (%0), %%mm0\n\t"
745 "movq 8(%0), %%mm4\n\t"
746 "movq %%mm0, %%mm1\n\t"
747 "movq %%mm0, %%mm3\n\t"
748
749 "movq %%mm4, %%mm5\n\t"
750
751 "punpcklbw %%mm7, %%mm0\n\t"
752 "punpckhbw %%mm7, %%mm1\n\t"
753 "pmullw %%mm6, %%mm0\n\t"
754 "punpcklbw %%mm7, %%mm4\n\t"
755 "pmullw %%mm6, %%mm1\n\t"
756 "psrlw $8, %%mm0\n\t"
757 "pmullw %%mm6, %%mm4\n\t"
758 "psrlw $8, %%mm1\n\t"
759 "psrlw $8, %%mm4\n\t"
760 "packuswb %%mm1, %%mm0\n\t"
761 "movq %%mm5, %%mm1\n\t"
762
763 "punpckhbw %%mm7, %%mm1\n\t"
764
765 "pmullw %%mm6, %%mm1\n\t"
766 "psubusb %%mm0, %%mm3\n\t" // subtract darkening amount
767 "psrlw $8, %%mm1\n\t"
768 "packuswb %%mm1, %%mm4\n\t"
769
770 "movq %%mm3, (%0)\n\t"
771 "psubusb %%mm4, %%mm5\n\t" // only change for this version is
772 "movq %%mm5, 8(%0)\n\t" // subtraction here as we are darkening image
773 : : "r"(data) );
774 data += 4;
775 }
776
777 end += rem;
778 while ( data != end ) {
779 __asm__ __volatile__(
780 "movd (%0), %%mm0\n\t"
781 "punpcklbw %%mm7, %%mm0\n\t"
782 "movq %%mm0, %%mm3\n\t"
783 "pmullw %%mm6, %%mm0\n\t"
784 "psrlw $8, %%mm0\n\t"
785 "psubusw %%mm0, %%mm3\n\t"
786 "packuswb %%mm3, %%mm3\n\t"
787 "movd %%mm3, (%0)\n\t"
788 : : "r"(data) );
789 data++;
790 }
791 }
792 __asm__ __volatile__("emms"); // clear mmx state
793 }
794 else
795#endif // USE_MMX_INLINE_ASM
796 {
797 unsigned char *segTbl = new unsigned char[segColors];
798 int tmp;
799 if(brighten){ // keep overflow check out of loops
800 for(int i=0; i < segColors; ++i){
801 tmp = (int)(i*percent);
802 if(tmp > 255)
803 tmp = 255;
804 segTbl[i] = tmp;
805 }
806 }
807 else{
808 for(int i=0; i < segColors; ++i){
809 tmp = (int)(i*percent);
810 if(tmp < 0)
811 tmp = 0;
812 segTbl[i] = tmp;
813 }
814 }
815
816 if(brighten){ // same here
817 for(int i=0; i < pixels; ++i){
818 int r = tqRed(data[i]);
819 int g = tqGreen(data[i]);
820 int b = tqBlue(data[i]);
821 int a = tqAlpha(data[i]);
822 r = r + segTbl[r] > 255 ? 255 : r + segTbl[r];
823 g = g + segTbl[g] > 255 ? 255 : g + segTbl[g];
824 b = b + segTbl[b] > 255 ? 255 : b + segTbl[b];
825 data[i] = tqRgba(r, g, b,a);
826 }
827 }
828 else{
829 for(int i=0; i < pixels; ++i){
830 int r = tqRed(data[i]);
831 int g = tqGreen(data[i]);
832 int b = tqBlue(data[i]);
833 int a = tqAlpha(data[i]);
834 r = r - segTbl[r] < 0 ? 0 : r - segTbl[r];
835 g = g - segTbl[g] < 0 ? 0 : g - segTbl[g];
836 b = b - segTbl[b] < 0 ? 0 : b - segTbl[b];
837 data[i] = tqRgba(r, g, b, a);
838 }
839 }
840 delete [] segTbl;
841 }
842
843 return image;
844}
845
846TQImage& KImageEffect::channelIntensity(TQImage &image, float percent,
847 RGBComponent channel)
848{
849 if (image.width() == 0 || image.height() == 0) {
850#ifndef NDEBUG
851 std::cerr << "WARNING: KImageEffect::channelIntensity : invalid image\n";
852#endif
853 return image;
854 }
855
856 int segColors = image.depth() > 8 ? 256 : image.numColors();
857 unsigned char *segTbl = new unsigned char[segColors];
858 int pixels = image.depth() > 8 ? image.width()*image.height() :
859 image.numColors();
860 unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
861 (unsigned int *)image.colorTable();
862 bool brighten = (percent >= 0);
863 if(percent < 0)
864 percent = -percent;
865
866 if(brighten){ // keep overflow check out of loops
867 for(int i=0; i < segColors; ++i){
868 int tmp = (int)(i*percent);
869 if(tmp > 255)
870 tmp = 255;
871 segTbl[i] = tmp;
872 }
873 }
874 else{
875 for(int i=0; i < segColors; ++i){
876 int tmp = (int)(i*percent);
877 if(tmp < 0)
878 tmp = 0;
879 segTbl[i] = tmp;
880 }
881 }
882
883 if(brighten){ // same here
884 if(channel == Red){ // and here ;-)
885 for(int i=0; i < pixels; ++i){
886 int c = tqRed(data[i]);
887 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
888 data[i] = tqRgba(c, tqGreen(data[i]), tqBlue(data[i]), tqAlpha(data[i]));
889 }
890 }
891 else if(channel == Green){
892 for(int i=0; i < pixels; ++i){
893 int c = tqGreen(data[i]);
894 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
895 data[i] = tqRgba(tqRed(data[i]), c, tqBlue(data[i]), tqAlpha(data[i]));
896 }
897 }
898 else{
899 for(int i=0; i < pixels; ++i){
900 int c = tqBlue(data[i]);
901 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
902 data[i] = tqRgba(tqRed(data[i]), tqGreen(data[i]), c, tqAlpha(data[i]));
903 }
904 }
905
906 }
907 else{
908 if(channel == Red){
909 for(int i=0; i < pixels; ++i){
910 int c = tqRed(data[i]);
911 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
912 data[i] = tqRgba(c, tqGreen(data[i]), tqBlue(data[i]), tqAlpha(data[i]));
913 }
914 }
915 else if(channel == Green){
916 for(int i=0; i < pixels; ++i){
917 int c = tqGreen(data[i]);
918 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
919 data[i] = tqRgba(tqRed(data[i]), c, tqBlue(data[i]), tqAlpha(data[i]));
920 }
921 }
922 else{
923 for(int i=0; i < pixels; ++i){
924 int c = tqBlue(data[i]);
925 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
926 data[i] = tqRgba(tqRed(data[i]), tqGreen(data[i]), c, tqAlpha(data[i]));
927 }
928 }
929 }
930 delete [] segTbl;
931
932 return image;
933}
934
935// Modulate an image with an RBG channel of another image
936//
937TQImage& KImageEffect::modulate(TQImage &image, TQImage &modImage, bool reverse,
938 ModulationType type, int factor, RGBComponent channel)
939{
940 if (image.width() == 0 || image.height() == 0 ||
941 modImage.width() == 0 || modImage.height() == 0) {
942#ifndef NDEBUG
943 std::cerr << "WARNING: KImageEffect::modulate : invalid image\n";
944#endif
945 return image;
946 }
947
948 int r, g, b, h, s, v, a;
949 TQColor clr;
950 int mod=0;
951 unsigned int x1, x2, y1, y2;
952 int x, y;
953
954 // for image, we handle only depth 32
955 if (image.depth()<32) image = image.convertDepth(32);
956
957 // for modImage, we handle depth 8 and 32
958 if (modImage.depth()<8) modImage = modImage.convertDepth(8);
959
960 unsigned int *colorTable2 = (modImage.depth()==8) ?
961 modImage.colorTable():0;
962 unsigned int *data1, *data2;
963 unsigned char *data2b;
964 unsigned int color1, color2;
965
966 x1 = image.width(); y1 = image.height();
967 x2 = modImage.width(); y2 = modImage.height();
968
969 for (y = 0; y < (int)y1; y++) {
970 data1 = (unsigned int *) image.scanLine(y);
971 data2 = (unsigned int *) modImage.scanLine( y%y2 );
972 data2b = (unsigned char *) modImage.scanLine( y%y2 );
973
974 x=0;
975 while(x < (int)x1) {
976 color2 = (colorTable2) ? colorTable2[*data2b] : *data2;
977 if (reverse) {
978 color1 = color2;
979 color2 = *data1;
980 }
981 else
982 color1 = *data1;
983
984 if (type == Intensity || type == Contrast) {
985 r = tqRed(color1);
986 g = tqGreen(color1);
987 b = tqBlue(color1);
988 if (channel != All) {
989 mod = (channel == Red) ? tqRed(color2) :
990 (channel == Green) ? tqGreen(color2) :
991 (channel == Blue) ? tqBlue(color2) :
992 (channel == Gray) ? tqGray(color2) : 0;
993 mod = mod*factor/50;
994 }
995
996 if (type == Intensity) {
997 if (channel == All) {
998 r += r * factor/50 * tqRed(color2)/256;
999 g += g * factor/50 * tqGreen(color2)/256;
1000 b += b * factor/50 * tqBlue(color2)/256;
1001 }
1002 else {
1003 r += r * mod/256;
1004 g += g * mod/256;
1005 b += b * mod/256;
1006 }
1007 }
1008 else { // Contrast
1009 if (channel == All) {
1010 r += (r-128) * factor/50 * tqRed(color2)/128;
1011 g += (g-128) * factor/50 * tqGreen(color2)/128;
1012 b += (b-128) * factor/50 * tqBlue(color2)/128;
1013 }
1014 else {
1015 r += (r-128) * mod/128;
1016 g += (g-128) * mod/128;
1017 b += (b-128) * mod/128;
1018 }
1019 }
1020
1021 if (r<0) r=0; if (r>255) r=255;
1022 if (g<0) g=0; if (g>255) g=255;
1023 if (b<0) b=0; if (b>255) b=255;
1024 a = tqAlpha(*data1);
1025 *data1 = tqRgba(r, g, b, a);
1026 }
1027 else if (type == Saturation || type == HueShift) {
1028 clr.setRgb(color1);
1029 clr.hsv(&h, &s, &v);
1030 mod = (channel == Red) ? tqRed(color2) :
1031 (channel == Green) ? tqGreen(color2) :
1032 (channel == Blue) ? tqBlue(color2) :
1033 (channel == Gray) ? tqGray(color2) : 0;
1034 mod = mod*factor/50;
1035
1036 if (type == Saturation) {
1037 s -= s * mod/256;
1038 if (s<0) s=0; if (s>255) s=255;
1039 }
1040 else { // HueShift
1041 h += mod;
1042 while(h<0) h+=360;
1043 h %= 360;
1044 }
1045
1046 clr.setHsv(h, s, v);
1047 a = tqAlpha(*data1);
1048 *data1 = clr.rgb() | ((uint)(a & 0xff) << 24);
1049 }
1050 data1++; data2++; data2b++; x++;
1051 if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; }
1052 }
1053 }
1054 return image;
1055}
1056
1057
1058
1059//======================================================================
1060//
1061// Blend effects
1062//
1063//======================================================================
1064
1065
1066// Nice and fast direct pixel manipulation
1067TQImage& KImageEffect::blend(const TQColor& clr, TQImage& dst, float opacity)
1068{
1069 if (dst.width() <= 0 || dst.height() <= 0)
1070 return dst;
1071
1072 if (opacity < 0.0 || opacity > 1.0) {
1073#ifndef NDEBUG
1074 std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
1075#endif
1076 return dst;
1077 }
1078
1079 if (dst.depth() != 32)
1080 dst = dst.convertDepth(32);
1081
1082 int pixels = dst.width() * dst.height();
1083
1084#ifdef USE_SSE2_INLINE_ASM
1085 if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
1086 TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 );
1087
1088 KIE8Pack packedalpha = { { alpha, alpha, alpha, 256,
1089 alpha, alpha, alpha, 256 } };
1090
1091 TQ_UINT16 red = TQ_UINT16( clr.red() * 256 * opacity );
1092 TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity );
1093 TQ_UINT16 blue = TQ_UINT16( clr.blue() * 256 * opacity );
1094
1095 KIE8Pack packedcolor = { { blue, green, red, 0,
1096 blue, green, red, 0 } };
1097
1098 // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending
1099 __asm__ __volatile__(
1100 "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
1101 "movdqu (%0), %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6
1102 "movdqu (%1), %%xmm5\n\t" // Set up color * alpha * 256 in XMM5
1103 : : "r"(&packedalpha), "r"(&packedcolor),
1104 "m"(packedcolor), "m"(packedalpha) );
1105
1106 TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1107
1108 // Check how many pixels we need to process to achieve 16 byte alignment
1109 int offset = (16 - (TQ_UINT32( data ) & 0x0f)) / 4;
1110
1111 // The main loop processes 8 pixels / iteration
1112 int remainder = (pixels - offset) % 8;
1113 pixels -= remainder;
1114
1115 // Alignment loop
1116 for ( int i = 0; i < offset; i++ ) {
1117 __asm__ __volatile__(
1118 "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1
1119 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1120 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256
1121 "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
1122 "psrlw $8, %%xmm0\n\t" // Divide by 256
1123 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1124 "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image
1125 : : "r"(data), "r"(i) );
1126 }
1127
1128 // Main loop
1129 for ( int i = offset; i < pixels; i += 8 ) {
1130 __asm__ __volatile(
1131 // Load 8 pixels to XMM registers 1 - 4
1132 "movq (%0,%1,4), %%xmm0\n\t" // Load pixels 1 and 2 to XMM1
1133 "movq 8(%0,%1,4), %%xmm1\n\t" // Load pixels 3 and 4 to XMM2
1134 "movq 16(%0,%1,4), %%xmm2\n\t" // Load pixels 5 and 6 to XMM3
1135 "movq 24(%0,%1,4), %%xmm3\n\t" // Load pixels 7 and 8 to XMM4
1136
1137 // Prefetch the pixels for next iteration
1138 "prefetchnta 32(%0,%1,4) \n\t"
1139
1140 // Blend pixels 1 and 2
1141 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixels
1142 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixels with (1 - alpha) * 256
1143 "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
1144 "psrlw $8, %%xmm0\n\t" // Divide by 256
1145
1146 // Blend pixels 3 and 4
1147 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixels
1148 "pmullw %%xmm6, %%xmm1\n\t" // Multiply the pixels with (1 - alpha) * 256
1149 "paddw %%xmm5, %%xmm1\n\t" // Add color * alpha * 256 to the result
1150 "psrlw $8, %%xmm1\n\t" // Divide by 256
1151
1152 // Blend pixels 5 and 6
1153 "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the pixels
1154 "pmullw %%xmm6, %%xmm2\n\t" // Multiply the pixels with (1 - alpha) * 256
1155 "paddw %%xmm5, %%xmm2\n\t" // Add color * alpha * 256 to the result
1156 "psrlw $8, %%xmm2\n\t" // Divide by 256
1157
1158 // Blend pixels 7 and 8
1159 "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the pixels
1160 "pmullw %%xmm6, %%xmm3\n\t" // Multiply the pixels with (1 - alpha) * 256
1161 "paddw %%xmm5, %%xmm3\n\t" // Add color * alpha * 256 to the result
1162 "psrlw $8, %%xmm3\n\t" // Divide by 256
1163
1164 // Pack the pixels into 2 double quadwords
1165 "packuswb %%xmm1, %%xmm0\n\t" // Pack pixels 1 - 4 to a double qword
1166 "packuswb %%xmm3, %%xmm2\n\t" // Pack pixles 5 - 8 to a double qword
1167
1168 // Write the pixels back to the image
1169 "movdqa %%xmm0, (%0,%1,4)\n\t" // Store pixels 1 - 4
1170 "movdqa %%xmm2, 16(%0,%1,4)\n\t" // Store pixels 5 - 8
1171 : : "r"(data), "r"(i) );
1172 }
1173
1174 // Cleanup loop
1175 for ( int i = pixels; i < pixels + remainder; i++ ) {
1176 __asm__ __volatile__(
1177 "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1
1178 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1179 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256
1180 "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
1181 "psrlw $8, %%xmm0\n\t" // Divide by 256
1182 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1183 "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image
1184 : : "r"(data), "r"(i) );
1185 }
1186 } else
1187#endif
1188
1189#ifdef USE_MMX_INLINE_ASM
1190 if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
1191 TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 );
1192 KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } };
1193
1194 TQ_UINT16 red = TQ_UINT16( clr.red() * 256 * opacity );
1195 TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity );
1196 TQ_UINT16 blue = TQ_UINT16( clr.blue() * 256 * opacity );
1197
1198 KIE4Pack packedcolor = { { blue, green, red, 0 } };
1199
1200 __asm__ __volatile__(
1201 "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking
1202 "movq (%0), %%mm6\n\t" // Set up (1 - alpha) * 256 in MM6
1203 "movq (%1), %%mm5\n\t" // Set up color * alpha * 256 in MM5
1204 : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) );
1205
1206 TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1207
1208 // The main loop processes 4 pixels / iteration
1209 int remainder = pixels % 4;
1210 pixels -= remainder;
1211
1212 // Main loop
1213 for ( int i = 0; i < pixels; i += 4 ) {
1214 __asm__ __volatile__(
1215 // Load 4 pixels to MM registers 1 - 4
1216 "movd (%0,%1,4), %%mm0\n\t" // Load the 1st pixel to MM0
1217 "movd 4(%0,%1,4), %%mm1\n\t" // Load the 2nd pixel to MM1
1218 "movd 8(%0,%1,4), %%mm2\n\t" // Load the 3rd pixel to MM2
1219 "movd 12(%0,%1,4), %%mm3\n\t" // Load the 4th pixel to MM3
1220
1221 // Blend the first pixel
1222 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel
1223 "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with (1 - alpha) * 256
1224 "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result
1225 "psrlw $8, %%mm0\n\t" // Divide by 256
1226
1227 // Blend the second pixel
1228 "punpcklbw %%mm7, %%mm1\n\t" // Unpack the pixel
1229 "pmullw %%mm6, %%mm1\n\t" // Multiply the pixel with (1 - alpha) * 256
1230 "paddw %%mm5, %%mm1\n\t" // Add color * alpha * 256 to the result
1231 "psrlw $8, %%mm1\n\t" // Divide by 256
1232
1233 // Blend the third pixel
1234 "punpcklbw %%mm7, %%mm2\n\t" // Unpack the pixel
1235 "pmullw %%mm6, %%mm2\n\t" // Multiply the pixel with (1 - alpha) * 256
1236 "paddw %%mm5, %%mm2\n\t" // Add color * alpha * 256 to the result
1237 "psrlw $8, %%mm2\n\t" // Divide by 256
1238
1239 // Blend the fourth pixel
1240 "punpcklbw %%mm7, %%mm3\n\t" // Unpack the pixel
1241 "pmullw %%mm6, %%mm3\n\t" // Multiply the pixel with (1 - alpha) * 256
1242 "paddw %%mm5, %%mm3\n\t" // Add color * alpha * 256 to the result
1243 "psrlw $8, %%mm3\n\t" // Divide by 256
1244
1245 // Pack the pixels into 2 quadwords
1246 "packuswb %%mm1, %%mm0\n\t" // Pack pixels 1 and 2 to a qword
1247 "packuswb %%mm3, %%mm2\n\t" // Pack pixels 3 and 4 to a qword
1248
1249 // Write the pixels back to the image
1250 "movq %%mm0, (%0,%1,4)\n\t" // Store pixels 1 and 2
1251 "movq %%mm2, 8(%0,%1,4)\n\t" // Store pixels 3 and 4
1252 : : "r"(data), "r"(i) );
1253 }
1254
1255 // Cleanup loop
1256 for ( int i = pixels; i < pixels + remainder; i++ ) {
1257 __asm__ __volatile__(
1258 "movd (%0,%1,4), %%mm0\n\t" // Load one pixel to MM1
1259 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel
1260 "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with 1 - alpha * 256
1261 "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result
1262 "psrlw $8, %%mm0\n\t" // Divide by 256
1263 "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword
1264 "movd %%mm0, (%0,%1,4)\n\t" // Write the pixel to the image
1265 : : "r"(data), "r"(i) );
1266 }
1267
1268 // Empty the MMX state
1269 __asm__ __volatile__("emms");
1270 } else
1271#endif // USE_MMX_INLINE_ASM
1272
1273 {
1274 int rcol, gcol, bcol;
1275 clr.rgb(&rcol, &gcol, &bcol);
1276
1277#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
1278 unsigned char *data = (unsigned char *)dst.bits() + 1;
1279#else // BGRA
1280 unsigned char *data = (unsigned char *)dst.bits();
1281#endif
1282
1283 for (int i=0; i<pixels; i++)
1284 {
1285#ifdef WORDS_BIGENDIAN
1286 *data += (unsigned char)((rcol - *data) * opacity);
1287 data++;
1288 *data += (unsigned char)((gcol - *data) * opacity);
1289 data++;
1290 *data += (unsigned char)((bcol - *data) * opacity);
1291 data++;
1292#else
1293 *data += (unsigned char)((bcol - *data) * opacity);
1294 data++;
1295 *data += (unsigned char)((gcol - *data) * opacity);
1296 data++;
1297 *data += (unsigned char)((rcol - *data) * opacity);
1298 data++;
1299#endif
1300 data++; // skip alpha
1301 }
1302 }
1303
1304 return dst;
1305}
1306
1307// Nice and fast direct pixel manipulation
1308TQImage& KImageEffect::blend(TQImage& src, TQImage& dst, float opacity)
1309{
1310 if (src.width() <= 0 || src.height() <= 0)
1311 return dst;
1312 if (dst.width() <= 0 || dst.height() <= 0)
1313 return dst;
1314
1315 if (src.width() != dst.width() || src.height() != dst.height()) {
1316#ifndef NDEBUG
1317 std::cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n";
1318#endif
1319 return dst;
1320 }
1321
1322 if (opacity < 0.0 || opacity > 1.0) {
1323#ifndef NDEBUG
1324 std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
1325#endif
1326 return dst;
1327 }
1328
1329 if (src.depth() != 32) src = src.convertDepth(32);
1330 if (dst.depth() != 32) dst = dst.convertDepth(32);
1331
1332 int pixels = src.width() * src.height();
1333
1334#ifdef USE_SSE2_INLINE_ASM
1335 if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
1336 TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 );
1337 KIE8Pack packedalpha = { { alpha, alpha, alpha, 0,
1338 alpha, alpha, alpha, 0 } };
1339
1340 // Prepare the XMM6 and XMM7 registers for unpacking and blending
1341 __asm__ __volatile__(
1342 "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
1343 "movdqu (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6
1344 : : "r"(&packedalpha), "m"(packedalpha) );
1345
1346 TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() );
1347 TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1348
1349 // Check how many pixels we need to process to achieve 16 byte alignment
1350 int offset = (16 - (TQ_UINT32( data2 ) & 0x0f)) / 4;
1351
1352 // The main loop processes 4 pixels / iteration
1353 int remainder = (pixels - offset) % 4;
1354 pixels -= remainder;
1355
1356 // Alignment loop
1357 for ( int i = 0; i < offset; i++ ) {
1358 __asm__ __volatile__(
1359 "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1
1360 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel
1361 "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0
1362 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1363 "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
1364 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
1365 "psllw $8, %%xmm1\n\t" // Multiply dst with 256
1366 "paddw %%xmm1, %%xmm0\n\t" // Add dst to result
1367 "psrlw $8, %%xmm0\n\t" // Divide by 256
1368 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1369 "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image
1370 : : "r"(data1), "r"(data2), "r"(i) );
1371 }
1372
1373 // Main loop
1374 for ( int i = offset; i < pixels; i += 4 ) {
1375 __asm__ __volatile__(
1376 // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3
1377 "movq (%0,%2,4), %%xmm0\n\t" // Load two src pixels to XMM0
1378 "movq (%1,%2,4), %%xmm1\n\t" // Load two dst pixels to XMM1
1379 "movq 8(%0,%2,4), %%xmm2\n\t" // Load two src pixels to XMM2
1380 "movq 8(%1,%2,4), %%xmm3\n\t" // Load two dst pixels to XMM3
1381
1382 // Prefetch the pixels for the iteration after the next one
1383 "prefetchnta 32(%0,%2,4) \n\t"
1384 "prefetchnta 32(%1,%2,4) \n\t"
1385
1386 // Blend the first two pixels
1387 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the dst pixels
1388 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the src pixels
1389 "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
1390 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
1391 "psllw $8, %%xmm1\n\t" // Multiply dst with 256
1392 "paddw %%xmm1, %%xmm0\n\t" // Add dst to the result
1393 "psrlw $8, %%xmm0\n\t" // Divide by 256
1394
1395 // Blend the next two pixels
1396 "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the dst pixels
1397 "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the src pixels
1398 "psubw %%xmm3, %%xmm2\n\t" // Subtract dst from src
1399 "pmullw %%xmm6, %%xmm2\n\t" // Multiply the result with alpha * 256
1400 "psllw $8, %%xmm3\n\t" // Multiply dst with 256
1401 "paddw %%xmm3, %%xmm2\n\t" // Add dst to the result
1402 "psrlw $8, %%xmm2\n\t" // Divide by 256
1403
1404 // Write the pixels back to the image
1405 "packuswb %%xmm2, %%xmm0\n\t" // Pack the pixels to a double qword
1406 "movdqa %%xmm0, (%1,%2,4)\n\t" // Store the pixels
1407 : : "r"(data1), "r"(data2), "r"(i) );
1408 }
1409
1410 // Cleanup loop
1411 for ( int i = pixels; i < pixels + remainder; i++ ) {
1412 __asm__ __volatile__(
1413 "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1
1414 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel
1415 "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0
1416 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1417 "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
1418 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
1419 "psllw $8, %%xmm1\n\t" // Multiply dst with 256
1420 "paddw %%xmm1, %%xmm0\n\t" // Add dst to result
1421 "psrlw $8, %%xmm0\n\t" // Divide by 256
1422 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1423 "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image
1424 : : "r"(data1), "r"(data2), "r"(i) );
1425 }
1426 } else
1427#endif // USE_SSE2_INLINE_ASM
1428
1429#ifdef USE_MMX_INLINE_ASM
1430 if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
1431 TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 );
1432 KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } };
1433
1434 // Prepare the MM6 and MM7 registers for blending and unpacking
1435 __asm__ __volatile__(
1436 "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking
1437 "movq (%0), %%mm6\n\t" // Set up alpha * 256 in MM6
1438 : : "r"(&packedalpha), "m"(packedalpha) );
1439
1440 TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() );
1441 TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1442
1443 // The main loop processes 2 pixels / iteration
1444 int remainder = pixels % 2;
1445 pixels -= remainder;
1446
1447 // Main loop
1448 for ( int i = 0; i < pixels; i += 2 ) {
1449 __asm__ __volatile__(
1450 // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3
1451 "movd (%0,%2,4), %%mm0\n\t" // Load the 1st src pixel to MM0
1452 "movd (%1,%2,4), %%mm1\n\t" // Load the 1st dst pixel to MM1
1453 "movd 4(%0,%2,4), %%mm2\n\t" // Load the 2nd src pixel to MM2
1454 "movd 4(%1,%2,4), %%mm3\n\t" // Load the 2nd dst pixel to MM3
1455
1456 // Blend the first pixel
1457 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel
1458 "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel
1459 "psubw %%mm1, %%mm0\n\t" // Subtract dst from src
1460 "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256
1461 "psllw $8, %%mm1\n\t" // Multiply dst with 256
1462 "paddw %%mm1, %%mm0\n\t" // Add dst to the result
1463 "psrlw $8, %%mm0\n\t" // Divide by 256
1464
1465 // Blend the second pixel
1466 "punpcklbw %%mm7, %%mm2\n\t" // Unpack the src pixel
1467 "punpcklbw %%mm7, %%mm3\n\t" // Unpack the dst pixel
1468 "psubw %%mm3, %%mm2\n\t" // Subtract dst from src
1469 "pmullw %%mm6, %%mm2\n\t" // Multiply the result with alpha * 256
1470 "psllw $8, %%mm3\n\t" // Multiply dst with 256
1471 "paddw %%mm3, %%mm2\n\t" // Add dst to the result
1472 "psrlw $8, %%mm2\n\t" // Divide by 256
1473
1474 // Write the pixels back to the image
1475 "packuswb %%mm2, %%mm0\n\t" // Pack the pixels to a qword
1476 "movq %%mm0, (%1,%2,4)\n\t" // Store the pixels
1477 : : "r"(data1), "r"(data2), "r"(i) );
1478 }
1479
1480 // Blend the remaining pixel (if there is one)
1481 if ( remainder ) {
1482 __asm__ __volatile__(
1483 "movd (%0), %%mm0\n\t" // Load one src pixel to MM0
1484 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel
1485 "movd (%1), %%mm1\n\t" // Load one dst pixel to MM1
1486 "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel
1487 "psubw %%mm1, %%mm0\n\t" // Subtract dst from src
1488 "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256
1489 "psllw $8, %%mm1\n\t" // Multiply dst with 256
1490 "paddw %%mm1, %%mm0\n\t" // Add dst to result
1491 "psrlw $8, %%mm0\n\t" // Divide by 256
1492 "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword
1493 "movd %%mm0, (%1)\n\t" // Write the pixel to the image
1494 : : "r"(data1 + pixels), "r"(data2 + pixels) );
1495 }
1496
1497 // Empty the MMX state
1498 __asm__ __volatile__("emms");
1499 } else
1500#endif // USE_MMX_INLINE_ASM
1501
1502 {
1503#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
1504 unsigned char *data1 = (unsigned char *)dst.bits() + 1;
1505 unsigned char *data2 = (unsigned char *)src.bits() + 1;
1506#else // BGRA
1507 unsigned char *data1 = (unsigned char *)dst.bits();
1508 unsigned char *data2 = (unsigned char *)src.bits();
1509#endif
1510
1511 for (int i=0; i<pixels; i++)
1512 {
1513#ifdef WORDS_BIGENDIAN
1514 *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1515 data1++;
1516 *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1517 data1++;
1518 *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1519 data1++;
1520#else
1521 *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1522 data1++;
1523 *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1524 data1++;
1525 *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1526 data1++;
1527#endif
1528 data1++; // skip alpha
1529 data2++;
1530 }
1531 }
1532
1533 return dst;
1534}
1535
1536
1537TQImage& KImageEffect::blend(TQImage &image, float initial_intensity,
1538 const TQColor &bgnd, GradientType eff,
1539 bool anti_dir)
1540{
1541 if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
1542#ifndef NDEBUG
1543 std::cerr << "WARNING: KImageEffect::blend : invalid image\n";
1544#endif
1545 return image;
1546 }
1547
1548 int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
1549 int r, g, b;
1550 int ind;
1551
1552 unsigned int xi, xf, yi, yf;
1553 unsigned int a;
1554
1555 // check the boundaries of the initial intesity param
1556 float unaffected = 1;
1557 if (initial_intensity > 1) initial_intensity = 1;
1558 if (initial_intensity < -1) initial_intensity = -1;
1559 if (initial_intensity < 0) {
1560 unaffected = 1. + initial_intensity;
1561 initial_intensity = 0;
1562 }
1563
1564
1565 float intensity = initial_intensity;
1566 float var = 1. - initial_intensity;
1567
1568 if (anti_dir) {
1569 initial_intensity = intensity = 1.;
1570 var = -var;
1571 }
1572
1573 int x, y;
1574
1575 unsigned int *data = (unsigned int *)image.bits();
1576
1577 int image_width = image.width(); //Those can't change
1578 int image_height = image.height();
1579
1580
1581 if( eff == VerticalGradient || eff == HorizontalGradient ) {
1582
1583 // set the image domain to apply the effect to
1584 xi = 0, xf = image_width;
1585 yi = 0, yf = image_height;
1586 if (eff == VerticalGradient) {
1587 if (anti_dir) yf = (int)(image_height * unaffected);
1588 else yi = (int)(image_height * (1 - unaffected));
1589 }
1590 else {
1591 if (anti_dir) xf = (int)(image_width * unaffected);
1592 else xi = (int)(image_height * (1 - unaffected));
1593 }
1594
1595 var /= (eff == VerticalGradient?yf-yi:xf-xi);
1596
1597 int ind_base;
1598 for (y = yi; y < (int)yf; y++) {
1599 intensity = eff == VerticalGradient? intensity + var :
1600 initial_intensity;
1601 ind_base = image_width * y ;
1602 for (x = xi; x < (int)xf ; x++) {
1603 if (eff == HorizontalGradient) intensity += var;
1604 ind = x + ind_base;
1605 r = tqRed (data[ind]) + (int)(intensity *
1606 (r_bgnd - tqRed (data[ind])));
1607 g = tqGreen(data[ind]) + (int)(intensity *
1608 (g_bgnd - tqGreen(data[ind])));
1609 b = tqBlue (data[ind]) + (int)(intensity *
1610 (b_bgnd - tqBlue (data[ind])));
1611 if (r > 255) r = 255; if (r < 0 ) r = 0;
1612 if (g > 255) g = 255; if (g < 0 ) g = 0;
1613 if (b > 255) b = 255; if (b < 0 ) b = 0;
1614 a = tqAlpha(data[ind]);
1615 data[ind] = tqRgba(r, g, b, a);
1616 }
1617 }
1618 }
1619 else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) {
1620 float xvar = var / 2 / image_width; // / unaffected;
1621 float yvar = var / 2 / image_height; // / unaffected;
1622 float tmp;
1623
1624 for (x = 0; x < image_width ; x++) {
1625 tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1);
1626 ind = x;
1627 for (y = 0; y < image_height ; y++) {
1628 intensity = initial_intensity + tmp + yvar * y;
1629
1630 r = tqRed (data[ind]) + (int)(intensity *
1631 (r_bgnd - tqRed (data[ind])));
1632 g = tqGreen(data[ind]) + (int)(intensity *
1633 (g_bgnd - tqGreen(data[ind])));
1634 b = tqBlue (data[ind]) + (int)(intensity *
1635 (b_bgnd - tqBlue (data[ind])));
1636 if (r > 255) r = 255; if (r < 0 ) r = 0;
1637 if (g > 255) g = 255; if (g < 0 ) g = 0;
1638 if (b > 255) b = 255; if (b < 0 ) b = 0;
1639 a = tqAlpha(data[ind]);
1640 data[ind] = tqRgba(r, g, b, a);
1641
1642 ind += image_width;
1643 }
1644 }
1645 }
1646
1647 else if (eff == RectangleGradient || eff == EllipticGradient) {
1648 float xvar;
1649 float yvar;
1650
1651 for (x = 0; x < image_width / 2 + image_width % 2; x++) {
1652 xvar = var / image_width * (image_width - x*2/unaffected-1);
1653 for (y = 0; y < image_height / 2 + image_height % 2; y++) {
1654 yvar = var / image_height * (image_height - y*2/unaffected -1);
1655
1656 if (eff == RectangleGradient)
1657 intensity = initial_intensity + TQMAX(xvar, yvar);
1658 else
1659 intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
1660 if (intensity > 1) intensity = 1;
1661 if (intensity < 0) intensity = 0;
1662
1663 //NW
1664 ind = x + image_width * y ;
1665 r = tqRed (data[ind]) + (int)(intensity *
1666 (r_bgnd - tqRed (data[ind])));
1667 g = tqGreen(data[ind]) + (int)(intensity *
1668 (g_bgnd - tqGreen(data[ind])));
1669 b = tqBlue (data[ind]) + (int)(intensity *
1670 (b_bgnd - tqBlue (data[ind])));
1671 if (r > 255) r = 255; if (r < 0 ) r = 0;
1672 if (g > 255) g = 255; if (g < 0 ) g = 0;
1673 if (b > 255) b = 255; if (b < 0 ) b = 0;
1674 a = tqAlpha(data[ind]);
1675 data[ind] = tqRgba(r, g, b, a);
1676
1677 //NE
1678 ind = image_width - x - 1 + image_width * y ;
1679 r = tqRed (data[ind]) + (int)(intensity *
1680 (r_bgnd - tqRed (data[ind])));
1681 g = tqGreen(data[ind]) + (int)(intensity *
1682 (g_bgnd - tqGreen(data[ind])));
1683 b = tqBlue (data[ind]) + (int)(intensity *
1684 (b_bgnd - tqBlue (data[ind])));
1685 if (r > 255) r = 255; if (r < 0 ) r = 0;
1686 if (g > 255) g = 255; if (g < 0 ) g = 0;
1687 if (b > 255) b = 255; if (b < 0 ) b = 0;
1688 a = tqAlpha(data[ind]);
1689 data[ind] = tqRgba(r, g, b, a);
1690 }
1691 }
1692
1693 //CT loop is doubled because of stupid central row/column issue.
1694 // other solution?
1695 for (x = 0; x < image_width / 2; x++) {
1696 xvar = var / image_width * (image_width - x*2/unaffected-1);
1697 for (y = 0; y < image_height / 2; y++) {
1698 yvar = var / image_height * (image_height - y*2/unaffected -1);
1699
1700 if (eff == RectangleGradient)
1701 intensity = initial_intensity + TQMAX(xvar, yvar);
1702 else
1703 intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
1704 if (intensity > 1) intensity = 1;
1705 if (intensity < 0) intensity = 0;
1706
1707 //SW
1708 ind = x + image_width * (image_height - y -1) ;
1709 r = tqRed (data[ind]) + (int)(intensity *
1710 (r_bgnd - tqRed (data[ind])));
1711 g = tqGreen(data[ind]) + (int)(intensity *
1712 (g_bgnd - tqGreen(data[ind])));
1713 b = tqBlue (data[ind]) + (int)(intensity *
1714 (b_bgnd - tqBlue (data[ind])));
1715 if (r > 255) r = 255; if (r < 0 ) r = 0;
1716 if (g > 255) g = 255; if (g < 0 ) g = 0;
1717 if (b > 255) b = 255; if (b < 0 ) b = 0;
1718 a = tqAlpha(data[ind]);
1719 data[ind] = tqRgba(r, g, b, a);
1720
1721 //SE
1722 ind = image_width-x-1 + image_width * (image_height - y - 1) ;
1723 r = tqRed (data[ind]) + (int)(intensity *
1724 (r_bgnd - tqRed (data[ind])));
1725 g = tqGreen(data[ind]) + (int)(intensity *
1726 (g_bgnd - tqGreen(data[ind])));
1727 b = tqBlue (data[ind]) + (int)(intensity *
1728 (b_bgnd - tqBlue (data[ind])));
1729 if (r > 255) r = 255; if (r < 0 ) r = 0;
1730 if (g > 255) g = 255; if (g < 0 ) g = 0;
1731 if (b > 255) b = 255; if (b < 0 ) b = 0;
1732 a = tqAlpha(data[ind]);
1733 data[ind] = tqRgba(r, g, b, a);
1734 }
1735 }
1736 }
1737#ifndef NDEBUG
1738 else std::cerr << "KImageEffect::blend effect not implemented" << std::endl;
1739#endif
1740 return image;
1741}
1742
1743// Not very efficient as we create a third big image...
1744//
1745TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2,
1746 GradientType gt, int xf, int yf)
1747{
1748 if (image1.width() == 0 || image1.height() == 0 ||
1749 image2.width() == 0 || image2.height() == 0)
1750 return image1;
1751
1752 TQImage image3;
1753
1754 image3 = KImageEffect::unbalancedGradient(image1.size(),
1755 TQColor(0,0,0), TQColor(255,255,255),
1756 gt, xf, yf, 0);
1757
1758 return blend(image1,image2,image3, Red); // Channel to use is arbitrary
1759}
1760
1761// Blend image2 into image1, using an RBG channel of blendImage
1762//
1763TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2,
1764 TQImage &blendImage, RGBComponent channel)
1765{
1766 if (image1.width() == 0 || image1.height() == 0 ||
1767 image2.width() == 0 || image2.height() == 0 ||
1768 blendImage.width() == 0 || blendImage.height() == 0) {
1769#ifndef NDEBUG
1770 std::cerr << "KImageEffect::blend effect invalid image" << std::endl;
1771#endif
1772 return image1;
1773 }
1774
1775 int r, g, b;
1776 int ind1, ind2, ind3;
1777
1778 unsigned int x1, x2, x3, y1, y2, y3;
1779 unsigned int a;
1780
1781 int x, y;
1782
1783 // for image1 and image2, we only handle depth 32
1784 if (image1.depth()<32) image1 = image1.convertDepth(32);
1785 if (image2.depth()<32) image2 = image2.convertDepth(32);
1786
1787 // for blendImage, we handle depth 8 and 32
1788 if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
1789
1790 unsigned int *colorTable3 = (blendImage.depth()==8) ?
1791 blendImage.colorTable():0;
1792
1793 unsigned int *data1 = (unsigned int *)image1.bits();
1794 unsigned int *data2 = (unsigned int *)image2.bits();
1795 unsigned int *data3 = (unsigned int *)blendImage.bits();
1796 unsigned char *data3b = (unsigned char *)blendImage.bits();
1797 unsigned int color3;
1798
1799 x1 = image1.width(); y1 = image1.height();
1800 x2 = image2.width(); y2 = image2.height();
1801 x3 = blendImage.width(); y3 = blendImage.height();
1802
1803 for (y = 0; y < (int)y1; y++) {
1804 ind1 = x1*y;
1805 ind2 = x2*(y%y2);
1806 ind3 = x3*(y%y3);
1807
1808 x=0;
1809 while(x < (int)x1) {
1810 color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
1811
1812 a = (channel == Red) ? tqRed(color3) :
1813 (channel == Green) ? tqGreen(color3) :
1814 (channel == Blue) ? tqBlue(color3) : tqGray(color3);
1815
1816 r = (a*tqRed(data1[ind1]) + (256-a)*tqRed(data2[ind2]))/256;
1817 g = (a*tqGreen(data1[ind1]) + (256-a)*tqGreen(data2[ind2]))/256;
1818 b = (a*tqBlue(data1[ind1]) + (256-a)*tqBlue(data2[ind2]))/256;
1819
1820 a = tqAlpha(data1[ind1]);
1821 data1[ind1] = tqRgba(r, g, b, a);
1822
1823 ind1++; ind2++; ind3++; x++;
1824 if ( (x%x2) ==0) ind2 -= x2;
1825 if ( (x%x3) ==0) ind3 -= x3;
1826 }
1827 }
1828 return image1;
1829}
1830
1831
1832//======================================================================
1833//
1834// Hash effects
1835//
1836//======================================================================
1837
1838unsigned int KImageEffect::lHash(unsigned int c)
1839{
1840 unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c);
1841 unsigned char nr, ng, nb;
1842 nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
1843 ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
1844 nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
1845
1846 return tqRgba(nr, ng, nb, a);
1847}
1848
1849
1850// -----------------------------------------------------------------------------
1851
1852unsigned int KImageEffect::uHash(unsigned int c)
1853{
1854 unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c);
1855 unsigned char nr, ng, nb;
1856 nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
1857 ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
1858 nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
1859
1860 return tqRgba(nr, ng, nb, a);
1861}
1862
1863
1864// -----------------------------------------------------------------------------
1865
1866TQImage& KImageEffect::hash(TQImage &image, Lighting lite, unsigned int spacing)
1867{
1868 if (image.width() == 0 || image.height() == 0) {
1869#ifndef NDEBUG
1870 std::cerr << "KImageEffect::hash effect invalid image" << std::endl;
1871#endif
1872 return image;
1873 }
1874
1875 int x, y;
1876 unsigned int *data = (unsigned int *)image.bits();
1877 unsigned int ind;
1878
1879 //CT no need to do it if not enough space
1880 if ((lite == NorthLite ||
1881 lite == SouthLite)&&
1882 (unsigned)image.height() < 2+spacing) return image;
1883 if ((lite == EastLite ||
1884 lite == WestLite)&&
1885 (unsigned)image.height() < 2+spacing) return image;
1886
1887 if (lite == NorthLite || lite == SouthLite) {
1888 for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
1889 for (x = 0; x < image.width(); x++) {
1890 ind = x + image.width() * y;
1891 data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
1892
1893 ind = ind + image.width();
1894 data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
1895 }
1896 }
1897 }
1898
1899 else if (lite == EastLite || lite == WestLite) {
1900 for (y = 0 ; y < image.height(); y++) {
1901 for (x = 0; x < image.width(); x = x + 2 + spacing) {
1902 ind = x + image.width() * y;
1903 data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
1904
1905 ind++;
1906 data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
1907 }
1908 }
1909 }
1910
1911 else if (lite == NWLite || lite == SELite) {
1912 for (y = 0 ; y < image.height(); y++) {
1913 for (x = 0;
1914 x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
1915 x = x + 2 + spacing) {
1916 ind = x + image.width() * y + ((y & 1)? 1 : 0);
1917 data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
1918
1919 ind++;
1920 data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
1921 }
1922 }
1923 }
1924
1925 else if (lite == SWLite || lite == NELite) {
1926 for (y = 0 ; y < image.height(); y++) {
1927 for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
1928 ind = x + image.width() * y - ((y & 1)? 1 : 0);
1929 data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
1930
1931 ind++;
1932 data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
1933 }
1934 }
1935 }
1936
1937 return image;
1938}
1939
1940
1941//======================================================================
1942//
1943// Flatten effects
1944//
1945//======================================================================
1946
1947TQImage& KImageEffect::flatten(TQImage &img, const TQColor &ca,
1948 const TQColor &cb, int ncols)
1949{
1950 if (img.width() == 0 || img.height() == 0)
1951 return img;
1952
1953 // a bitmap is easy...
1954 if (img.depth() == 1) {
1955 img.setColor(0, ca.rgb());
1956 img.setColor(1, cb.rgb());
1957 return img;
1958 }
1959
1960 int r1 = ca.red(); int r2 = cb.red();
1961 int g1 = ca.green(); int g2 = cb.green();
1962 int b1 = ca.blue(); int b2 = cb.blue();
1963 int min = 0, max = 255;
1964
1965 TQRgb col;
1966
1967 // Get minimum and maximum greylevel.
1968 if (img.numColors()) {
1969 // pseudocolor
1970 for (int i = 0; i < img.numColors(); i++) {
1971 col = img.color(i);
1972 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
1973 min = TQMIN(min, mean);
1974 max = TQMAX(max, mean);
1975 }
1976 } else {
1977 // truecolor
1978 for (int y=0; y < img.height(); y++)
1979 for (int x=0; x < img.width(); x++) {
1980 col = img.pixel(x, y);
1981 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
1982 min = TQMIN(min, mean);
1983 max = TQMAX(max, mean);
1984 }
1985 }
1986
1987 // Conversion factors
1988 float sr = ((float) r2 - r1) / (max - min);
1989 float sg = ((float) g2 - g1) / (max - min);
1990 float sb = ((float) b2 - b1) / (max - min);
1991
1992
1993 // Repaint the image
1994 if (img.numColors()) {
1995 for (int i=0; i < img.numColors(); i++) {
1996 col = img.color(i);
1997 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
1998 int r = (int) (sr * (mean - min) + r1 + 0.5);
1999 int g = (int) (sg * (mean - min) + g1 + 0.5);
2000 int b = (int) (sb * (mean - min) + b1 + 0.5);
2001 img.setColor(i, tqRgba(r, g, b, tqAlpha(col)));
2002 }
2003 } else {
2004 for (int y=0; y < img.height(); y++)
2005 for (int x=0; x < img.width(); x++) {
2006 col = img.pixel(x, y);
2007 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
2008 int r = (int) (sr * (mean - min) + r1 + 0.5);
2009 int g = (int) (sg * (mean - min) + g1 + 0.5);
2010 int b = (int) (sb * (mean - min) + b1 + 0.5);
2011 img.setPixel(x, y, tqRgba(r, g, b, tqAlpha(col)));
2012 }
2013 }
2014
2015
2016 // Dither if necessary
2017 if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
2018 return img;
2019
2020 if (ncols == 1) ncols++;
2021 if (ncols > 256) ncols = 256;
2022
2023 TQColor *pal = new TQColor[ncols];
2024 sr = ((float) r2 - r1) / (ncols - 1);
2025 sg = ((float) g2 - g1) / (ncols - 1);
2026 sb = ((float) b2 - b1) / (ncols - 1);
2027
2028 for (int i=0; i<ncols; i++)
2029 pal[i] = TQColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
2030
2031 dither(img, pal, ncols);
2032
2033 delete[] pal;
2034 return img;
2035}
2036
2037
2038//======================================================================
2039//
2040// Fade effects
2041//
2042//======================================================================
2043
2044TQImage& KImageEffect::fade(TQImage &img, float val, const TQColor &color)
2045{
2046 if (img.width() == 0 || img.height() == 0)
2047 return img;
2048
2049 // We don't handle bitmaps
2050 if (img.depth() == 1)
2051 return img;
2052
2053 unsigned char tbl[256];
2054 for (int i=0; i<256; i++)
2055 tbl[i] = (int) (val * i + 0.5);
2056
2057 int red = color.red();
2058 int green = color.green();
2059 int blue = color.blue();
2060
2061 TQRgb col;
2062 int r, g, b, cr, cg, cb;
2063
2064 if (img.depth() <= 8) {
2065 // pseudo color
2066 for (int i=0; i<img.numColors(); i++) {
2067 col = img.color(i);
2068 cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col);
2069 if (cr > red)
2070 r = cr - tbl[cr - red];
2071 else
2072 r = cr + tbl[red - cr];
2073 if (cg > green)
2074 g = cg - tbl[cg - green];
2075 else
2076 g = cg + tbl[green - cg];
2077 if (cb > blue)
2078 b = cb - tbl[cb - blue];
2079 else
2080 b = cb + tbl[blue - cb];
2081 img.setColor(i, tqRgba(r, g, b, tqAlpha(col)));
2082 }
2083
2084 } else {
2085 // truecolor
2086 for (int y=0; y<img.height(); y++) {
2087 TQRgb *data = (TQRgb *) img.scanLine(y);
2088 for (int x=0; x<img.width(); x++) {
2089 col = *data;
2090 cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col);
2091 if (cr > red)
2092 r = cr - tbl[cr - red];
2093 else
2094 r = cr + tbl[red - cr];
2095 if (cg > green)
2096 g = cg - tbl[cg - green];
2097 else
2098 g = cg + tbl[green - cg];
2099 if (cb > blue)
2100 b = cb - tbl[cb - blue];
2101 else
2102 b = cb + tbl[blue - cb];
2103 *data++ = tqRgba(r, g, b, tqAlpha(col));
2104 }
2105 }
2106 }
2107
2108 return img;
2109}
2110
2111//======================================================================
2112//
2113// Color effects
2114//
2115//======================================================================
2116
2117// This code is adapted from code (C) Rik Hemsley <rik@kde.org>
2118//
2119// The formula used (r + b + g) /3 is different from the tqGray formula
2120// used by Qt. This is because our formula is much much faster. If,
2121// however, it turns out that this is producing sub-optimal images,
2122// then it will have to change (kurt)
2123//
2124// It does produce lower quality grayscale ;-) Use fast == true for the fast
2125// algorithm, false for the higher quality one (mosfet).
2126TQImage& KImageEffect::toGray(TQImage &img, bool fast)
2127{
2128 if (img.width() == 0 || img.height() == 0)
2129 return img;
2130
2131 if(fast){
2132 if (img.depth() == 32) {
2133 uchar * r(img.bits());
2134 uchar * g(img.bits() + 1);
2135 uchar * b(img.bits() + 2);
2136
2137 uchar * end(img.bits() + img.numBytes());
2138
2139 while (r != end) {
2140
2141 *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
2142
2143 r += 4;
2144 g += 4;
2145 b += 4;
2146 }
2147 }
2148 else
2149 {
2150 for (int i = 0; i < img.numColors(); i++)
2151 {
2152 uint r = tqRed(img.color(i));
2153 uint g = tqGreen(img.color(i));
2154 uint b = tqBlue(img.color(i));
2155
2156 uint gray = (((r + g) >> 1) + b) >> 1;
2157 img.setColor(i, tqRgba(gray, gray, gray, tqAlpha(img.color(i))));
2158 }
2159 }
2160 }
2161 else{
2162 int pixels = img.depth() > 8 ? img.width()*img.height() :
2163 img.numColors();
2164 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
2165 (unsigned int *)img.colorTable();
2166 int val, i;
2167 for(i=0; i < pixels; ++i){
2168 val = tqGray(data[i]);
2169 data[i] = tqRgba(val, val, val, tqAlpha(data[i]));
2170 }
2171 }
2172 return img;
2173}
2174
2175// CT 29Jan2000 - desaturation algorithms
2176TQImage& KImageEffect::desaturate(TQImage &img, float desat)
2177{
2178 if (img.width() == 0 || img.height() == 0)
2179 return img;
2180
2181 if (desat < 0) desat = 0.;
2182 if (desat > 1) desat = 1.;
2183 int pixels = img.depth() > 8 ? img.width()*img.height() :
2184 img.numColors();
2185 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
2186 (unsigned int *)img.colorTable();
2187 int h, s, v, i;
2188 TQColor clr; // keep constructor out of loop (mosfet)
2189 for(i=0; i < pixels; ++i){
2190 clr.setRgb(data[i]);
2191 clr.hsv(&h, &s, &v);
2192 clr.setHsv(h, (int)(s * (1. - desat)), v);
2193 data[i] = clr.rgb();
2194 }
2195 return img;
2196}
2197
2198// Contrast stuff (mosfet)
2199TQImage& KImageEffect::contrast(TQImage &img, int c)
2200{
2201 if (img.width() == 0 || img.height() == 0)
2202 return img;
2203
2204 if(c > 255)
2205 c = 255;
2206 if(c < -255)
2207 c = -255;
2208 int pixels = img.depth() > 8 ? img.width()*img.height() :
2209 img.numColors();
2210 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
2211 (unsigned int *)img.colorTable();
2212 int i, r, g, b;
2213 for(i=0; i < pixels; ++i){
2214 r = tqRed(data[i]);
2215 g = tqGreen(data[i]);
2216 b = tqBlue(data[i]);
2217 if(tqGray(data[i]) <= 127){
2218 if(r - c > 0)
2219 r -= c;
2220 else
2221 r = 0;
2222 if(g - c > 0)
2223 g -= c;
2224 else
2225 g = 0;
2226 if(b - c > 0)
2227 b -= c;
2228 else
2229 b = 0;
2230 }
2231 else{
2232 if(r + c <= 255)
2233 r += c;
2234 else
2235 r = 255;
2236 if(g + c <= 255)
2237 g += c;
2238 else
2239 g = 255;
2240 if(b + c <= 255)
2241 b += c;
2242 else
2243 b = 255;
2244 }
2245 data[i] = tqRgba(r, g, b, tqAlpha(data[i]));
2246 }
2247 return(img);
2248}
2249
2250//======================================================================
2251//
2252// Dithering effects
2253//
2254//======================================================================
2255
2256// adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
2257//
2258// Floyd-Steinberg dithering
2259// Ref: Bitmapped Graphics Programming in C++
2260// Marv Luse, Addison-Wesley Publishing, 1993.
2261TQImage& KImageEffect::dither(TQImage &img, const TQColor *palette, int size)
2262{
2263 if (img.width() == 0 || img.height() == 0 ||
2264 palette == 0 || img.depth() <= 8)
2265 return img;
2266
2267 TQImage dImage( img.width(), img.height(), 8, size );
2268 int i;
2269
2270 dImage.setNumColors( size );
2271 for ( i = 0; i < size; i++ )
2272 dImage.setColor( i, palette[ i ].rgb() );
2273
2274 int *rerr1 = new int [ img.width() * 2 ];
2275 int *gerr1 = new int [ img.width() * 2 ];
2276 int *berr1 = new int [ img.width() * 2 ];
2277
2278 memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
2279 memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
2280 memset( berr1, 0, sizeof( int ) * img.width() * 2 );
2281
2282 int *rerr2 = rerr1 + img.width();
2283 int *gerr2 = gerr1 + img.width();
2284 int *berr2 = berr1 + img.width();
2285
2286 for ( int j = 0; j < img.height(); j++ )
2287 {
2288 uint *ip = (uint * )img.scanLine( j );
2289 uchar *dp = dImage.scanLine( j );
2290
2291 for ( i = 0; i < img.width(); i++ )
2292 {
2293 rerr1[i] = rerr2[i] + tqRed( *ip );
2294 rerr2[i] = 0;
2295 gerr1[i] = gerr2[i] + tqGreen( *ip );
2296 gerr2[i] = 0;
2297 berr1[i] = berr2[i] + tqBlue( *ip );
2298 berr2[i] = 0;
2299 ip++;
2300 }
2301
2302 *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
2303
2304 for ( i = 1; i < img.width()-1; i++ )
2305 {
2306 int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
2307 *dp = indx;
2308
2309 int rerr = rerr1[i];
2310 rerr -= palette[indx].red();
2311 int gerr = gerr1[i];
2312 gerr -= palette[indx].green();
2313 int berr = berr1[i];
2314 berr -= palette[indx].blue();
2315
2316 // diffuse red error
2317 rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
2318 rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
2319 rerr2[ i ] += ( rerr * 5 ) >> 4;
2320 rerr2[ i+1 ] += ( rerr ) >> 4;
2321
2322 // diffuse green error
2323 gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
2324 gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
2325 gerr2[ i ] += ( gerr * 5 ) >> 4;
2326 gerr2[ i+1 ] += ( gerr ) >> 4;
2327
2328 // diffuse red error
2329 berr1[ i+1 ] += ( berr * 7 ) >> 4;
2330 berr2[ i-1 ] += ( berr * 3 ) >> 4;
2331 berr2[ i ] += ( berr * 5 ) >> 4;
2332 berr2[ i+1 ] += ( berr ) >> 4;
2333
2334 dp++;
2335 }
2336
2337 *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
2338 }
2339
2340 delete [] rerr1;
2341 delete [] gerr1;
2342 delete [] berr1;
2343
2344 img = dImage;
2345 return img;
2346}
2347
2348int KImageEffect::nearestColor( int r, int g, int b, const TQColor *palette, int size )
2349{
2350 if (palette == 0)
2351 return 0;
2352
2353 int dr = palette[0].red() - r;
2354 int dg = palette[0].green() - g;
2355 int db = palette[0].blue() - b;
2356
2357 int minDist = dr*dr + dg*dg + db*db;
2358 int nearest = 0;
2359
2360 for (int i = 1; i < size; i++ )
2361 {
2362 dr = palette[i].red() - r;
2363 dg = palette[i].green() - g;
2364 db = palette[i].blue() - b;
2365
2366 int dist = dr*dr + dg*dg + db*db;
2367
2368 if ( dist < minDist )
2369 {
2370 minDist = dist;
2371 nearest = i;
2372 }
2373 }
2374
2375 return nearest;
2376}
2377
2378bool KImageEffect::blend(
2379 const TQImage & upper,
2380 const TQImage & lower,
2381 TQImage & output
2382)
2383{
2384 if (
2385 upper.width() > lower.width() ||
2386 upper.height() > lower.height() ||
2387 upper.depth() != 32 ||
2388 lower.depth() != 32
2389 )
2390 {
2391#ifndef NDEBUG
2392 std::cerr << "KImageEffect::blend : Sizes not correct\n" ;
2393#endif
2394 return false;
2395 }
2396
2397 output = lower.copy();
2398
2399 uchar *i, *o;
2400 int a;
2401 int col;
2402 int w = upper.width();
2403 int row(upper.height() - 1);
2404
2405 do {
2406
2407 i = const_cast<TQImage&>(upper).scanLine(row);
2408 o = const_cast<TQImage&>(output).scanLine(row);
2409
2410 col = w << 2;
2411 --col;
2412
2413 do {
2414
2415 while (!(a = i[col]) && (col != 3)) {
2416 --col; --col; --col; --col;
2417 }
2418
2419 --col;
2420 o[col] += ((i[col] - o[col]) * a) >> 8;
2421
2422 --col;
2423 o[col] += ((i[col] - o[col]) * a) >> 8;
2424
2425 --col;
2426 o[col] += ((i[col] - o[col]) * a) >> 8;
2427
2428 } while (col--);
2429
2430 } while (row--);
2431
2432 return true;
2433}
2434
2435#if 0
2436// Not yet...
2437bool KImageEffect::blend(
2438 const TQImage & upper,
2439 const TQImage & lower,
2440 TQImage & output,
2441 const TQRect & destRect
2442)
2443{
2444 output = lower.copy();
2445 return output;
2446}
2447
2448#endif
2449
2450bool KImageEffect::blend(
2451 int &x, int &y,
2452 const TQImage & upper,
2453 const TQImage & lower,
2454 TQImage & output
2455)
2456{
2457 int cx=0, cy=0, cw=upper.width(), ch=upper.height();
2458
2459 if ( upper.width() + x > lower.width() ||
2460 upper.height() + y > lower.height() ||
2461 x < 0 || y < 0 ||
2462 upper.depth() != 32 || lower.depth() != 32 )
2463 {
2464 if ( x > lower.width() || y > lower.height() ) return false;
2465 if ( upper.width()<=0 || upper.height() <= 0 ) return false;
2466 if ( lower.width()<=0 || lower.height() <= 0 ) return false;
2467
2468 if (x<0) {cx=-x; cw+=x; x=0; };
2469 if (cw + x > lower.width()) { cw=lower.width()-x; };
2470 if (y<0) {cy=-y; ch+=y; y=0; };
2471 if (ch + y > lower.height()) { ch=lower.height()-y; };
2472
2473 if ( cx >= upper.width() || cy >= upper.height() ) return true;
2474 if ( cw <= 0 || ch <= 0 ) return true;
2475 }
2476
2477 output.create(cw,ch,32);
2478// output.setAlphaBuffer(true); // I should do some benchmarks to see if
2479 // this is worth the effort
2480
2481 TQRgb *i, *o, *b;
2482
2483 int a;
2484 int j,k;
2485 for (j=0; j<ch; j++)
2486 {
2487 b=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ]);
2488 i=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ]);
2489 o=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(output).scanLine(j) [ cw << 2 ]);
2490
2491 k=cw-1;
2492 --b; --i; --o;
2493 do
2494 {
2495 while ( !(a=tqAlpha(*i)) && k>0 )
2496 {
2497 i--;
2498// *o=0;
2499 *o=*b;
2500 --o; --b;
2501 k--;
2502 };
2503// *o=0xFF;
2504 *o = tqRgb(tqRed(*b) + (((tqRed(*i) - tqRed(*b)) * a) >> 8),
2505 tqGreen(*b) + (((tqGreen(*i) - tqGreen(*b)) * a) >> 8),
2506 tqBlue(*b) + (((tqBlue(*i) - tqBlue(*b)) * a) >> 8));
2507 --i; --o; --b;
2508 } while (k--);
2509 }
2510
2511 return true;
2512}
2513
2514bool KImageEffect::blendOnLower(
2515 int x, int y,
2516 const TQImage & upper,
2517 const TQImage & lower
2518)
2519{
2520 int cx=0, cy=0, cw=upper.width(), ch=upper.height();
2521
2522 if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
2523 if ( x + cw > lower.width() ||
2524 y + ch > lower.height() ||
2525 x < 0 || y < 0 )
2526 {
2527 if ( x > lower.width() || y > lower.height() ) return true;
2528 if ( upper.width()<=0 || upper.height() <= 0 ) return true;
2529 if ( lower.width()<=0 || lower.height() <= 0 ) return true;
2530
2531 if (x<0) {cx=-x; cw+=x; x=0; };
2532 if (cw + x > lower.width()) { cw=lower.width()-x; };
2533 if (y<0) {cy=-y; ch+=y; y=0; };
2534 if (ch + y > lower.height()) { ch=lower.height()-y; };
2535
2536 if ( cx >= upper.width() || cy >= upper.height() ) return true;
2537 if ( cw <= 0 || ch <= 0 ) return true;
2538 }
2539
2540 uchar *i, *b;
2541 int a;
2542 int k;
2543
2544 for (int j=0; j<ch; j++)
2545 {
2546 b=&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ];
2547 i=&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ];
2548
2549 k=cw-1;
2550 --b; --i;
2551 do
2552 {
2553#ifndef WORDS_BIGENDIAN
2554 while ( !(a=*i) && k>0 )
2555#else
2556 while ( !(a=*(i-3)) && k>0 )
2557#endif
2558 {
2559 i-=4; b-=4; k--;
2560 };
2561
2562#ifndef WORDS_BIGENDIAN
2563 --i; --b;
2564 *b += ( ((*i - *b) * a) >> 8 );
2565 --i; --b;
2566 *b += ( ((*i - *b) * a) >> 8 );
2567 --i; --b;
2568 *b += ( ((*i - *b) * a) >> 8 );
2569 --i; --b;
2570#else
2571 *b += ( ((*i - *b) * a) >> 8 );
2572 --i; --b;
2573 *b += ( ((*i - *b) * a) >> 8 );
2574 --i; --b;
2575 *b += ( ((*i - *b) * a) >> 8 );
2576 i -= 2; b -= 2;
2577#endif
2578 } while (k--);
2579 }
2580
2581 return true;
2582}
2583
2584void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset,
2585 TQImage &lower, const TQRect &lowerRect)
2586{
2587 // clip rect
2588 TQRect lr = lowerRect & lower.rect();
2589 lr.setWidth( TQMIN(lr.width(), upper.width()-upperOffset.x()) );
2590 lr.setHeight( TQMIN(lr.height(), upper.height()-upperOffset.y()) );
2591 if ( !lr.isValid() ) return;
2592
2593 // blend
2594 for (int y = 0; y < lr.height(); y++) {
2595 for (int x = 0; x < lr.width(); x++) {
2596 TQRgb *b = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(TQRgb));
2597 TQRgb *d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(TQRgb));
2598 int a = tqAlpha(*d);
2599 *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
2600 tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
2601 tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
2602 }
2603 }
2604}
2605
2606void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset,
2607 TQImage &lower, const TQRect &lowerRect, float opacity)
2608{
2609 // clip rect
2610 TQRect lr = lowerRect & lower.rect();
2611 lr.setWidth( TQMIN(lr.width(), upper.width()-upperOffset.x()) );
2612 lr.setHeight( TQMIN(lr.height(), upper.height()-upperOffset.y()) );
2613 if ( !lr.isValid() ) return;
2614
2615 // blend
2616 for (int y = 0; y < lr.height(); y++) {
2617 for (int x = 0; x < lr.width(); x++) {
2618 TQRgb *b = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(TQRgb));
2619 TQRgb *d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(TQRgb));
2620 int a = tqRound(opacity * tqAlpha(*d));
2621 *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
2622 tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
2623 tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
2624 }
2625 }
2626}
2627
2628TQRect KImageEffect::computeDestinationRect(const TQSize &lowerSize,
2629 Disposition disposition, TQImage &upper)
2630{
2631 int w = lowerSize.width();
2632 int h = lowerSize.height();
2633 int ww = upper.width();
2634 int wh = upper.height();
2635 TQRect d;
2636
2637 switch (disposition) {
2638 case NoImage:
2639 break;
2640 case Centered:
2641 d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
2642 break;
2643 case Tiled:
2644 d.setRect(0, 0, w, h);
2645 break;
2646 case CenterTiled:
2647 d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh,
2648 w-1, h-1);
2649 break;
2650 case Scaled:
2651 upper = upper.smoothScale(w, h);
2652 d.setRect(0, 0, w, h);
2653 break;
2654 case CenteredAutoFit:
2655 if( ww <= w && wh <= h ) {
2656 d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered
2657 break;
2658 }
2659 // fall through
2660 case CenteredMaxpect: {
2661 double sx = (double) w / ww;
2662 double sy = (double) h / wh;
2663 if (sx > sy) {
2664 ww = (int)(sy * ww);
2665 wh = h;
2666 } else {
2667 wh = (int)(sx * wh);
2668 ww = w;
2669 }
2670 upper = upper.smoothScale(ww, wh);
2671 d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
2672 break;
2673 }
2674 case TiledMaxpect: {
2675 double sx = (double) w / ww;
2676 double sy = (double) h / wh;
2677 if (sx > sy) {
2678 ww = (int)(sy * ww);
2679 wh = h;
2680 } else {
2681 wh = (int)(sx * wh);
2682 ww = w;
2683 }
2684 upper = upper.smoothScale(ww, wh);
2685 d.setRect(0, 0, w, h);
2686 break;
2687 }
2688 }
2689
2690 return d;
2691}
2692
2693void KImageEffect::blendOnLower(TQImage &upper, TQImage &lower,
2694 Disposition disposition, float opacity)
2695{
2696 TQRect r = computeDestinationRect(lower.size(), disposition, upper);
2697 for (int y = r.top(); y<r.bottom(); y += upper.height())
2698 for (int x = r.left(); x<r.right(); x += upper.width())
2699 blendOnLower(upper, TQPoint(-TQMIN(x, 0), -TQMIN(y, 0)),
2700 lower, TQRect(x, y, upper.width(), upper.height()), opacity);
2701}
2702
2703
2704// For selected icons
2705TQImage& KImageEffect::selectedImage( TQImage &img, const TQColor &col )
2706{
2707 return blend( col, img, 0.5);
2708}
2709
2710//
2711// ===================================================================
2712// Effects originally ported from ImageMagick for PixiePlus, plus a few
2713// new ones. (mosfet 05/26/2003)
2714// ===================================================================
2715//
2716/*
2717 Portions of this software are based on ImageMagick. Such portions are clearly
2718marked as being ported from ImageMagick. ImageMagick is copyrighted under the
2719following conditions:
2720
2721Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to
2722making software imaging solutions freely available.
2723
2724Permission is hereby granted, free of charge, to any person obtaining a copy
2725of this software and associated documentation files ("ImageMagick"), to deal
2726in ImageMagick without restriction, including without limitation the rights
2727to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2728copies of ImageMagick, and to permit persons to whom the ImageMagick is
2729furnished to do so, subject to the following conditions:
2730
2731The above copyright notice and this permission notice shall be included in all
2732copies or substantial portions of ImageMagick.
2733
2734The software is provided "as is", without warranty of any kind, express or
2735implied, including but not limited to the warranties of merchantability,
2736fitness for a particular purpose and noninfringement. In no event shall
2737ImageMagick Studio be liable for any claim, damages or other liability,
2738whether in an action of contract, tort or otherwise, arising from, out of or
2739in connection with ImageMagick or the use or other dealings in ImageMagick.
2740
2741Except as contained in this notice, the name of the ImageMagick Studio shall
2742not be used in advertising or otherwise to promote the sale, use or other
2743dealings in ImageMagick without prior written authorization from the
2744ImageMagick Studio.
2745*/
2746
2747TQImage KImageEffect::sample(TQImage &src, int w, int h)
2748{
2749 if(w == src.width() && h == src.height())
2750 return(src);
2751
2752 int depth = src.depth();
2753 TQImage dest(w, h, depth, depth <= 8 ? src.numColors() : 0,
2754 depth == 1 ? TQImage::LittleEndian : TQImage::IgnoreEndian);
2755 int *x_offset = (int *)malloc(w*sizeof(int));
2756 int *y_offset = (int *)malloc(h*sizeof(int));
2757 if(!x_offset || !y_offset){
2758#ifndef NDEBUG
2759 tqWarning("KImageEffect::sample(): Unable to allocate pixel buffer");
2760#endif
2761 free(x_offset);
2762 free(y_offset);
2763 return(src);
2764 }
2765
2766 // init pixel offsets
2767 for(int x=0; x < w; ++x)
2768 x_offset[x] = (int)(x*src.width()/((double)w));
2769 for(int y=0; y < h; ++y)
2770 y_offset[y] = (int)(y*src.height()/((double)h));
2771
2772 if(depth > 8){ // DirectClass source image
2773 for(int y=0; y < h; ++y){
2774 unsigned int *destData = (unsigned int *)dest.scanLine(y);
2775 unsigned int *srcData = (unsigned int *)src.scanLine(y_offset[y]);
2776 for(int x=0; x < w; ++x)
2777 destData[x] = srcData[x_offset[x]];
2778 }
2779 }
2780 else if(depth == 1) {
2781 int r = src.bitOrder() == TQImage::LittleEndian;
2782 memcpy(dest.colorTable(), src.colorTable(), src.numColors()*sizeof(TQRgb));
2783 for(int y=0; y < h; ++y){
2784 unsigned char *destData = dest.scanLine(y);
2785 unsigned char *srcData = src.scanLine(y_offset[y]);
2786 for(int x=0; x < w; ++x){
2787 int k = x_offset[x];
2788 int l = r ? (k & 7) : (7 - (k&7));
2789 if(srcData[k >> 3] & (1 << l))
2790 destData[x >> 3] |= 1 << (x & 7);
2791 else
2792 destData[x >> 3] &= ~(1 << (x & 7));
2793 }
2794 }
2795 }
2796 else{ // PseudoClass source image
2797 memcpy(dest.colorTable(), src.colorTable(), src.numColors()*sizeof(TQRgb));
2798 for(int y=0; y < h; ++y){
2799 unsigned char *destData = dest.scanLine(y);
2800 unsigned char *srcData = src.scanLine(y_offset[y]);
2801 for(int x=0; x < w; ++x)
2802 destData[x] = srcData[x_offset[x]];
2803 }
2804 }
2805 free(x_offset);
2806 free(y_offset);
2807 return(dest);
2808}
2809
2810void KImageEffect::threshold(TQImage &img, unsigned int threshold)
2811{
2812 int i, count;
2813 unsigned int *data;
2814 if(img.depth() > 8){ // DirectClass
2815 count = img.width()*img.height();
2816 data = (unsigned int *)img.bits();
2817 }
2818 else{ // PsudeoClass
2819 count = img.numColors();
2820 data = (unsigned int *)img.colorTable();
2821 }
2822 for(i=0; i < count; ++i)
2823 data[i] = intensityValue(data[i]) < threshold ? TQColor(TQt::black).rgb() : TQColor(TQt::white).rgb();
2824}
2825
2826void KImageEffect::hull(const int x_offset, const int y_offset,
2827 const int polarity, const int columns,
2828 const int rows,
2829 unsigned int *f, unsigned int *g)
2830{
2831 int x, y;
2832
2833 unsigned int *p, *q, *r, *s;
2834 unsigned int v;
2835 if(f == NULL || g == NULL)
2836 return;
2837 p=f+(columns+2);
2838 q=g+(columns+2);
2839 r=p+(y_offset*(columns+2)+x_offset);
2840 for (y=0; y < rows; y++){
2841 p++;
2842 q++;
2843 r++;
2844 if(polarity > 0)
2845 for (x=0; x < columns; x++){
2846 v=(*p);
2847 if (*r > v)
2848 v++;
2849 *q=v;
2850 p++;
2851 q++;
2852 r++;
2853 }
2854 else
2855 for(x=0; x < columns; x++){
2856 v=(*p);
2857 if (v > (unsigned int) (*r+1))
2858 v--;
2859 *q=v;
2860 p++;
2861 q++;
2862 r++;
2863 }
2864 p++;
2865 q++;
2866 r++;
2867 }
2868 p=f+(columns+2);
2869 q=g+(columns+2);
2870 r=q+(y_offset*(columns+2)+x_offset);
2871 s=q-(y_offset*(columns+2)+x_offset);
2872 for(y=0; y < rows; y++){
2873 p++;
2874 q++;
2875 r++;
2876 s++;
2877 if(polarity > 0)
2878 for(x=0; x < (int) columns; x++){
2879 v=(*q);
2880 if (((unsigned int) (*s+1) > v) && (*r > v))
2881 v++;
2882 *p=v;
2883 p++;
2884 q++;
2885 r++;
2886 s++;
2887 }
2888 else
2889 for (x=0; x < columns; x++){
2890 v=(*q);
2891 if (((unsigned int) (*s+1) < v) && (*r < v))
2892 v--;
2893 *p=v;
2894 p++;
2895 q++;
2896 r++;
2897 s++;
2898 }
2899 p++;
2900 q++;
2901 r++;
2902 s++;
2903 }
2904}
2905
2906TQImage KImageEffect::despeckle(TQImage &src)
2907{
2908 int i, j, x, y;
2909 unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
2910 *alpha_channel;
2911 int packets;
2912 static const int
2913 X[4]= {0, 1, 1,-1},
2914 Y[4]= {1, 0, 1, 1};
2915
2916 unsigned int *destData;
2917 TQImage dest(src.width(), src.height(), 32);
2918
2919 packets = (src.width()+2)*(src.height()+2);
2920 red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2921 green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2922 blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2923 alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2924 buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
2925 if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
2926 !buffer){
2927 free(red_channel);
2928 free(green_channel);
2929 free(blue_channel);
2930 free(alpha_channel);
2931 free(buffer);
2932 return(src);
2933 }
2934
2935 // copy image pixels to color component buffers
2936 j = src.width()+2;
2937 if(src.depth() > 8){ // DirectClass source image
2938 unsigned int *srcData;
2939 for(y=0; y < src.height(); ++y){
2940 srcData = (unsigned int *)src.scanLine(y);
2941 ++j;
2942 for(x=0; x < src.width(); ++x){
2943 red_channel[j] = tqRed(srcData[x]);
2944 green_channel[j] = tqGreen(srcData[x]);
2945 blue_channel[j] = tqBlue(srcData[x]);
2946 alpha_channel[j] = tqAlpha(srcData[x]);
2947 ++j;
2948 }
2949 ++j;
2950 }
2951 }
2952 else{ // PsudeoClass source image
2953 unsigned char *srcData;
2954 unsigned int *cTable = src.colorTable();
2955 unsigned int pixel;
2956 for(y=0; y < src.height(); ++y){
2957 srcData = (unsigned char *)src.scanLine(y);
2958 ++j;
2959 for(x=0; x < src.width(); ++x){
2960 pixel = *(cTable+srcData[x]);
2961 red_channel[j] = tqRed(pixel);
2962 green_channel[j] = tqGreen(pixel);
2963 blue_channel[j] = tqBlue(pixel);
2964 alpha_channel[j] = tqAlpha(pixel);
2965 ++j;
2966 }
2967 ++j;
2968 }
2969 }
2970 // reduce speckle in red channel
2971 for(i=0; i < 4; i++){
2972 hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
2973 hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
2974 hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
2975 hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
2976 }
2977 // reduce speckle in green channel
2978 for (i=0; i < packets; i++)
2979 buffer[i]=0;
2980 for (i=0; i < 4; i++){
2981 hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
2982 hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
2983 hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
2984 hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
2985 }
2986 // reduce speckle in blue channel
2987 for (i=0; i < packets; i++)
2988 buffer[i]=0;
2989 for (i=0; i < 4; i++){
2990 hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
2991 hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
2992 hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
2993 hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
2994 }
2995 // copy color component buffers to despeckled image
2996 j = dest.width()+2;
2997 for(y=0; y < dest.height(); ++y)
2998 {
2999 destData = (unsigned int *)dest.scanLine(y);
3000 ++j;
3001 for (x=0; x < dest.width(); ++x)
3002 {
3003 destData[x] = tqRgba(red_channel[j], green_channel[j],
3004 blue_channel[j], alpha_channel[j]);
3005 ++j;
3006 }
3007 ++j;
3008 }
3009 free(buffer);
3010 free(red_channel);
3011 free(green_channel);
3012 free(blue_channel);
3013 free(alpha_channel);
3014 return(dest);
3015}
3016
3017unsigned int KImageEffect::generateNoise(unsigned int pixel,
3018 NoiseType noise_type)
3019{
3020#define NoiseEpsilon 1.0e-5
3021#define NoiseMask 0x7fff
3022#define SigmaUniform 4.0
3023#define SigmaGaussian 4.0
3024#define SigmaImpulse 0.10
3025#define SigmaLaplacian 10.0
3026#define SigmaMultiplicativeGaussian 0.5
3027#define SigmaPoisson 0.05
3028#define TauGaussian 20.0
3029
3030 double alpha, beta, sigma, value;
3031 alpha=(double) (rand() & NoiseMask)/NoiseMask;
3032 if (alpha == 0.0)
3033 alpha=1.0;
3034 switch(noise_type){
3035 case UniformNoise:
3036 default:
3037 {
3038 value=(double) pixel+SigmaUniform*(alpha-0.5);
3039 break;
3040 }
3041 case GaussianNoise:
3042 {
3043 double tau;
3044
3045 beta=(double) (rand() & NoiseMask)/NoiseMask;
3046 sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
3047 tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
3048 value=(double) pixel+
3049 (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
3050 break;
3051 }
3052 case MultiplicativeGaussianNoise:
3053 {
3054 if (alpha <= NoiseEpsilon)
3055 sigma=MaxRGB;
3056 else
3057 sigma=sqrt(-2.0*log(alpha));
3058 beta=(rand() & NoiseMask)/NoiseMask;
3059 value=(double) pixel+
3060 pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
3061 break;
3062 }
3063 case ImpulseNoise:
3064 {
3065 if (alpha < (SigmaImpulse/2.0))
3066 value=0;
3067 else
3068 if (alpha >= (1.0-(SigmaImpulse/2.0)))
3069 value=MaxRGB;
3070 else
3071 value=pixel;
3072 break;
3073 }
3074 case LaplacianNoise:
3075 {
3076 if (alpha <= 0.5)
3077 {
3078 if (alpha <= NoiseEpsilon)
3079 value=(double) pixel-MaxRGB;
3080 else
3081 value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
3082 break;
3083 }
3084 beta=1.0-alpha;
3085 if (beta <= (0.5*NoiseEpsilon))
3086 value=(double) pixel+MaxRGB;
3087 else
3088 value=(double) pixel-SigmaLaplacian*log(2.0*beta);
3089 break;
3090 }
3091 case PoissonNoise:
3092 {
3093 int
3094 i;
3095
3096 for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
3097 {
3098 beta=(double) (rand() & NoiseMask)/NoiseMask;
3099 alpha=alpha*beta;
3100 }
3101 value=i/SigmaPoisson;
3102 break;
3103 }
3104 }
3105 if(value < 0.0)
3106 return(0);
3107 if(value > MaxRGB)
3108 return(MaxRGB);
3109 return((unsigned int) (value+0.5));
3110}
3111
3112TQImage KImageEffect::addNoise(TQImage &src, NoiseType noise_type)
3113{
3114 int x, y;
3115 TQImage dest(src.width(), src.height(), 32);
3116 unsigned int *destData;
3117
3118 if(src.depth() > 8){ // DirectClass source image
3119 unsigned int *srcData;
3120 for(y=0; y < src.height(); ++y){
3121 srcData = (unsigned int *)src.scanLine(y);
3122 destData = (unsigned int *)dest.scanLine(y);
3123 for(x=0; x < src.width(); ++x){
3124 destData[x] = tqRgba(generateNoise(tqRed(srcData[x]), noise_type),
3125 generateNoise(tqGreen(srcData[x]), noise_type),
3126 generateNoise(tqBlue(srcData[x]), noise_type),
3127 tqAlpha(srcData[x]));
3128 }
3129 }
3130 }
3131 else{ // PsudeoClass source image
3132 unsigned char *srcData;
3133 unsigned int *cTable = src.colorTable();
3134 unsigned int pixel;
3135 for(y=0; y < src.height(); ++y){
3136 srcData = (unsigned char *)src.scanLine(y);
3137 destData = (unsigned int *)dest.scanLine(y);
3138 for(x=0; x < src.width(); ++x){
3139 pixel = *(cTable+srcData[x]);
3140 destData[x] = tqRgba(generateNoise(tqRed(pixel), noise_type),
3141 generateNoise(tqGreen(pixel), noise_type),
3142 generateNoise(tqBlue(pixel), noise_type),
3143 tqAlpha(pixel));
3144 }
3145 }
3146
3147 }
3148 return(dest);
3149}
3150
3151unsigned int KImageEffect::interpolateColor(TQImage *image, double x_offset,
3152 double y_offset,
3153 unsigned int background)
3154{
3155 double alpha, beta;
3156 unsigned int p, q, r, s;
3157 int x, y;
3158
3159 x = (int)x_offset;
3160 y = (int)y_offset;
3161 if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
3162 return(background);
3163 if(image->depth() > 8){
3164 if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
3165 unsigned int *t = (unsigned int *)image->scanLine(y);
3166 p = t[x];
3167 q = t[x+1];
3168 r = t[x+image->width()];
3169 s = t[x+image->width()+1];
3170 }
3171 else{
3172 unsigned int *t = (unsigned int *)image->scanLine(y);
3173 p = background;
3174 if((x >= 0) && (y >= 0)){
3175 p = t[x];
3176 }
3177 q = background;
3178 if(((x+1) < image->width()) && (y >= 0)){
3179 q = t[x+1];
3180 }
3181 r = background;
3182 if((x >= 0) && ((y+1) < image->height())){
3183 t = (unsigned int *)image->scanLine(y+1);
3184 r = t[x+image->width()];
3185 }
3186 s = background;
3187 if(((x+1) < image->width()) && ((y+1) < image->height())){
3188 t = (unsigned int *)image->scanLine(y+1);
3189 s = t[x+image->width()+1];
3190 }
3191
3192 }
3193 }
3194 else{
3195 unsigned int *colorTable = (unsigned int *)image->colorTable();
3196 if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
3197 unsigned char *t;
3198 t = (unsigned char *)image->scanLine(y);
3199 p = *(colorTable+t[x]);
3200 q = *(colorTable+t[x+1]);
3201 t = (unsigned char *)image->scanLine(y+1);
3202 r = *(colorTable+t[x]);
3203 s = *(colorTable+t[x+1]);
3204 }
3205 else{
3206 unsigned char *t;
3207 p = background;
3208 if((x >= 0) && (y >= 0)){
3209 t = (unsigned char *)image->scanLine(y);
3210 p = *(colorTable+t[x]);
3211 }
3212 q = background;
3213 if(((x+1) < image->width()) && (y >= 0)){
3214 t = (unsigned char *)image->scanLine(y);
3215 q = *(colorTable+t[x+1]);
3216 }
3217 r = background;
3218 if((x >= 0) && ((y+1) < image->height())){
3219 t = (unsigned char *)image->scanLine(y+1);
3220 r = *(colorTable+t[x]);
3221 }
3222 s = background;
3223 if(((x+1) < image->width()) && ((y+1) < image->height())){
3224 t = (unsigned char *)image->scanLine(y+1);
3225 s = *(colorTable+t[x+1]);
3226 }
3227
3228 }
3229
3230 }
3231 x_offset -= floor(x_offset);
3232 y_offset -= floor(y_offset);
3233 alpha = 1.0-x_offset;
3234 beta = 1.0-y_offset;
3235
3236 return(tqRgba((unsigned char)(beta*(alpha*tqRed(p)+x_offset*tqRed(q))+y_offset*(alpha*tqRed(r)+x_offset*tqRed(s))),
3237 (unsigned char)(beta*(alpha*tqGreen(p)+x_offset*tqGreen(q))+y_offset*(alpha*tqGreen(r)+x_offset*tqGreen(s))),
3238 (unsigned char)(beta*(alpha*tqBlue(p)+x_offset*tqBlue(q))+y_offset*(alpha*tqBlue(r)+x_offset*tqBlue(s))),
3239 (unsigned char)(beta*(alpha*tqAlpha(p)+x_offset*tqAlpha(q))+y_offset*(alpha*tqAlpha(r)+x_offset*tqAlpha(s)))));
3240}
3241
3242TQImage KImageEffect::implode(TQImage &src, double factor,
3243 unsigned int background)
3244{
3245 double amount, distance, radius;
3246 double x_center, x_distance, x_scale;
3247 double y_center, y_distance, y_scale;
3248 unsigned int *destData;
3249 int x, y;
3250
3251 TQImage dest(src.width(), src.height(), 32);
3252
3253 // compute scaling factor
3254 x_scale = 1.0;
3255 y_scale = 1.0;
3256 x_center = (double)0.5*src.width();
3257 y_center = (double)0.5*src.height();
3258 radius=x_center;
3259 if(src.width() > src.height())
3260 y_scale = (double)src.width()/src.height();
3261 else if(src.width() < src.height()){
3262 x_scale = (double) src.height()/src.width();
3263 radius = y_center;
3264 }
3265 amount=factor/10.0;
3266 if(amount >= 0)
3267 amount/=10.0;
3268 if(src.depth() > 8){ // DirectClass source image
3269 unsigned int *srcData;
3270 for(y=0; y < src.height(); ++y){
3271 srcData = (unsigned int *)src.scanLine(y);
3272 destData = (unsigned int *)dest.scanLine(y);
3273 y_distance=y_scale*(y-y_center);
3274 for(x=0; x < src.width(); ++x){
3275 destData[x] = srcData[x];
3276 x_distance = x_scale*(x-x_center);
3277 distance= x_distance*x_distance+y_distance*y_distance;
3278 if(distance < (radius*radius)){
3279 double factor;
3280 // Implode the pixel.
3281 factor=1.0;
3282 if(distance > 0.0)
3283 factor=
3284 pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
3285 destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
3286 factor*y_distance/y_scale+y_center,
3287 background);
3288 }
3289 }
3290 }
3291 }
3292 else{ // PsudeoClass source image
3293 unsigned char *srcData;
3294 unsigned char idx;
3295 unsigned int *cTable = src.colorTable();
3296 for(y=0; y < src.height(); ++y){
3297 srcData = (unsigned char *)src.scanLine(y);
3298 destData = (unsigned int *)dest.scanLine(y);
3299 y_distance=y_scale*(y-y_center);
3300 for(x=0; x < src.width(); ++x){
3301 idx = srcData[x];
3302 destData[x] = cTable[idx];
3303 x_distance = x_scale*(x-x_center);
3304 distance= x_distance*x_distance+y_distance*y_distance;
3305 if(distance < (radius*radius)){
3306 double factor;
3307 // Implode the pixel.
3308 factor=1.0;
3309 if(distance > 0.0)
3310 factor=
3311 pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
3312 destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
3313 factor*y_distance/y_scale+y_center,
3314 background);
3315 }
3316 }
3317 }
3318
3319 }
3320 return(dest);
3321}
3322
3323TQImage KImageEffect::rotate(TQImage &img, RotateDirection r)
3324{
3325 TQImage dest;
3326 int x, y;
3327 if(img.depth() > 8){
3328 unsigned int *srcData, *destData;
3329 switch(r){
3330 case Rotate90:
3331 dest.create(img.height(), img.width(), img.depth());
3332 for(y=0; y < img.height(); ++y){
3333 srcData = (unsigned int *)img.scanLine(y);
3334 for(x=0; x < img.width(); ++x){
3335 destData = (unsigned int *)dest.scanLine(x);
3336 destData[img.height()-y-1] = srcData[x];
3337 }
3338 }
3339 break;
3340 case Rotate180:
3341 dest.create(img.width(), img.height(), img.depth());
3342 for(y=0; y < img.height(); ++y){
3343 srcData = (unsigned int *)img.scanLine(y);
3344 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
3345 for(x=0; x < img.width(); ++x)
3346 destData[img.width()-x-1] = srcData[x];
3347 }
3348 break;
3349 case Rotate270:
3350 dest.create(img.height(), img.width(), img.depth());
3351 for(y=0; y < img.height(); ++y){
3352 srcData = (unsigned int *)img.scanLine(y);
3353 for(x=0; x < img.width(); ++x){
3354 destData = (unsigned int *)dest.scanLine(img.width()-x-1);
3355 destData[y] = srcData[x];
3356 }
3357 }
3358 break;
3359 default:
3360 dest = img;
3361 break;
3362 }
3363 }
3364 else{
3365 unsigned char *srcData, *destData;
3366 unsigned int *srcTable, *destTable;
3367 switch(r){
3368 case Rotate90:
3369 dest.create(img.height(), img.width(), img.depth());
3370 dest.setNumColors(img.numColors());
3371 srcTable = (unsigned int *)img.colorTable();
3372 destTable = (unsigned int *)dest.colorTable();
3373 for(x=0; x < img.numColors(); ++x)
3374 destTable[x] = srcTable[x];
3375 for(y=0; y < img.height(); ++y){
3376 srcData = (unsigned char *)img.scanLine(y);
3377 for(x=0; x < img.width(); ++x){
3378 destData = (unsigned char *)dest.scanLine(x);
3379 destData[img.height()-y-1] = srcData[x];
3380 }
3381 }
3382 break;
3383 case Rotate180:
3384 dest.create(img.width(), img.height(), img.depth());
3385 dest.setNumColors(img.numColors());
3386 srcTable = (unsigned int *)img.colorTable();
3387 destTable = (unsigned int *)dest.colorTable();
3388 for(x=0; x < img.numColors(); ++x)
3389 destTable[x] = srcTable[x];
3390 for(y=0; y < img.height(); ++y){
3391 srcData = (unsigned char *)img.scanLine(y);
3392 destData = (unsigned char *)dest.scanLine(img.height()-y-1);
3393 for(x=0; x < img.width(); ++x)
3394 destData[img.width()-x-1] = srcData[x];
3395 }
3396 break;
3397 case Rotate270:
3398 dest.create(img.height(), img.width(), img.depth());
3399 dest.setNumColors(img.numColors());
3400 srcTable = (unsigned int *)img.colorTable();
3401 destTable = (unsigned int *)dest.colorTable();
3402 for(x=0; x < img.numColors(); ++x)
3403 destTable[x] = srcTable[x];
3404 for(y=0; y < img.height(); ++y){
3405 srcData = (unsigned char *)img.scanLine(y);
3406 for(x=0; x < img.width(); ++x){
3407 destData = (unsigned char *)dest.scanLine(img.width()-x-1);
3408 destData[y] = srcData[x];
3409 }
3410 }
3411 break;
3412 default:
3413 dest = img;
3414 break;
3415 }
3416
3417 }
3418 return(dest);
3419}
3420
3421void KImageEffect::solarize(TQImage &img, double factor)
3422{
3423 int i, count;
3424 int threshold;
3425 unsigned int *data;
3426
3427 threshold = (int)(factor*(MaxRGB+1)/100.0);
3428 if(img.depth() < 32){
3429 data = (unsigned int *)img.colorTable();
3430 count = img.numColors();
3431 }
3432 else{
3433 data = (unsigned int *)img.bits();
3434 count = img.width()*img.height();
3435 }
3436 for(i=0; i < count; ++i){
3437 data[i] = tqRgba(tqRed(data[i]) > threshold ? MaxRGB-tqRed(data[i]) : tqRed(data[i]),
3438 tqGreen(data[i]) > threshold ? MaxRGB-tqGreen(data[i]) : tqGreen(data[i]),
3439 tqBlue(data[i]) > threshold ? MaxRGB-tqBlue(data[i]) : tqBlue(data[i]),
3440 tqAlpha(data[i]));
3441 }
3442}
3443
3444TQImage KImageEffect::spread(TQImage &src, unsigned int amount)
3445{
3446 int quantum, x, y;
3447 int x_distance, y_distance;
3448 if(src.width() < 3 || src.height() < 3)
3449 return(src);
3450 TQImage dest(src);
3451 dest.detach();
3452 quantum=(amount+1) >> 1;
3453 if(src.depth() > 8){ // DirectClass source image
3454 unsigned int *p, *q;
3455 for(y=0; y < src.height(); y++){
3456 q = (unsigned int *)dest.scanLine(y);
3457 for(x=0; x < src.width(); x++){
3458 x_distance = x + ((rand() & (amount+1))-quantum);
3459 y_distance = y + ((rand() & (amount+1))-quantum);
3460 x_distance = TQMIN(x_distance, src.width()-1);
3461 y_distance = TQMIN(y_distance, src.height()-1);
3462 if(x_distance < 0)
3463 x_distance = 0;
3464 if(y_distance < 0)
3465 y_distance = 0;
3466 p = (unsigned int *)src.scanLine(y_distance);
3467 p += x_distance;
3468 *q++=(*p);
3469 }
3470 }
3471 }
3472 else{ // PsudeoClass source image
3473 // just do colortable values
3474 unsigned char *p, *q;
3475 for(y=0; y < src.height(); y++){
3476 q = (unsigned char *)dest.scanLine(y);
3477 for(x=0; x < src.width(); x++){
3478 x_distance = x + ((rand() & (amount+1))-quantum);
3479 y_distance = y + ((rand() & (amount+1))-quantum);
3480 x_distance = TQMIN(x_distance, src.width()-1);
3481 y_distance = TQMIN(y_distance, src.height()-1);
3482 if(x_distance < 0)
3483 x_distance = 0;
3484 if(y_distance < 0)
3485 y_distance = 0;
3486 p = (unsigned char *)src.scanLine(y_distance);
3487 p += x_distance;
3488 *q++=(*p);
3489 }
3490 }
3491 }
3492 return(dest);
3493}
3494
3495TQImage KImageEffect::swirl(TQImage &src, double degrees,
3496 unsigned int background)
3497{
3498 double cosine, distance, factor, radius, sine, x_center, x_distance,
3499 x_scale, y_center, y_distance, y_scale;
3500 int x, y;
3501 unsigned int *q;
3502 TQImage dest(src.width(), src.height(), 32);
3503
3504 // compute scaling factor
3505 x_center = src.width()/2.0;
3506 y_center = src.height()/2.0;
3507 radius = TQMAX(x_center,y_center);
3508 x_scale=1.0;
3509 y_scale=1.0;
3510 if(src.width() > src.height())
3511 y_scale=(double)src.width()/src.height();
3512 else if(src.width() < src.height())
3513 x_scale=(double)src.height()/src.width();
3514 degrees=DegreesToRadians(degrees);
3515 // swirl each row
3516 if(src.depth() > 8){ // DirectClass source image
3517 unsigned int *p;
3518 for(y=0; y < src.height(); y++){
3519 p = (unsigned int *)src.scanLine(y);
3520 q = (unsigned int *)dest.scanLine(y);
3521 y_distance = y_scale*(y-y_center);
3522 for(x=0; x < src.width(); x++){
3523 // determine if the pixel is within an ellipse
3524 *q=(*p);
3525 x_distance = x_scale*(x-x_center);
3526 distance = x_distance*x_distance+y_distance*y_distance;
3527 if (distance < (radius*radius)){
3528 // swirl
3529 factor = 1.0-sqrt(distance)/radius;
3530 sine = sin(degrees*factor*factor);
3531 cosine = cos(degrees*factor*factor);
3532 *q = interpolateColor(&src,
3533 (cosine*x_distance-sine*y_distance)/x_scale+x_center,
3534 (sine*x_distance+cosine*y_distance)/y_scale+y_center,
3535 background);
3536 }
3537 p++;
3538 q++;
3539 }
3540 }
3541 }
3542 else{ // PsudeoClass source image
3543 unsigned char *p;
3544 unsigned int *cTable = (unsigned int *)src.colorTable();
3545 for(y=0; y < src.height(); y++){
3546 p = (unsigned char *)src.scanLine(y);
3547 q = (unsigned int *)dest.scanLine(y);
3548 y_distance = y_scale*(y-y_center);
3549 for(x=0; x < src.width(); x++){
3550 // determine if the pixel is within an ellipse
3551 *q = *(cTable+(*p));
3552 x_distance = x_scale*(x-x_center);
3553 distance = x_distance*x_distance+y_distance*y_distance;
3554 if (distance < (radius*radius)){
3555 // swirl
3556 factor = 1.0-sqrt(distance)/radius;
3557 sine = sin(degrees*factor*factor);
3558 cosine = cos(degrees*factor*factor);
3559 *q = interpolateColor(&src,
3560 (cosine*x_distance-sine*y_distance)/x_scale+x_center,
3561 (sine*x_distance+cosine*y_distance)/y_scale+y_center,
3562 background);
3563 }
3564 p++;
3565 q++;
3566 }
3567 }
3568
3569 }
3570 return(dest);
3571}
3572
3573TQImage KImageEffect::wave(TQImage &src, double amplitude, double wavelength,
3574 unsigned int background)
3575{
3576 double *sine_map;
3577 int x, y;
3578 unsigned int *q;
3579
3580 TQImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
3581 // allocate sine map
3582 sine_map = (double *)malloc(dest.width()*sizeof(double));
3583 if(!sine_map)
3584 return(src);
3585 for(x=0; x < dest.width(); ++x)
3586 sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
3587 // wave image
3588 for(y=0; y < dest.height(); ++y){
3589 q = (unsigned int *)dest.scanLine(y);
3590 for (x=0; x < dest.width(); x++){
3591 *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
3592 ++q;
3593 }
3594 }
3595 free(sine_map);
3596 return(dest);
3597}
3598
3599//
3600// The following methods work by computing a value from neighboring pixels
3601// (mosfet 05/26/03)
3602//
3603
3604// New algorithms based on ImageMagick 5.5.6 (05/26/03)
3605
3606TQImage KImageEffect::oilPaint(TQImage &src, int /*radius*/)
3607{
3608 /* binary compat method - remove me when possible! */
3609 return(oilPaintConvolve(src, 0));
3610}
3611
3612TQImage KImageEffect::oilPaintConvolve(TQImage &src, double radius)
3613{
3614 unsigned long count /*,*histogram*/;
3615 unsigned long histogram[256];
3616 unsigned int k;
3617 int width;
3618 int x, y, mx, my, sx, sy;
3619 int mcx, mcy;
3620 unsigned int *s=0, *q;
3621
3622 if(src.depth() < 32)
3623 src.convertDepth(32);
3624 TQImage dest(src);
3625 dest.detach();
3626
3627 width = getOptimalKernelWidth(radius, 0.5);
3628 if(src.width() < width){
3629 tqWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!");
3630 return(dest);
3631 }
3632 /*
3633 histogram = (unsigned long *)malloc(256*sizeof(unsigned long));
3634 if(!histogram){
3635 tqWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!");
3636 return(dest);
3637 }
3638 */
3639 unsigned int **jumpTable = (unsigned int **)src.jumpTable();
3640 for(y=0; y < dest.height(); ++y){
3641 sy = y-(width/2);
3642 q = (unsigned int *)dest.scanLine(y);
3643 for(x=0; x < dest.width(); ++x){
3644 count = 0;
3645 memset(histogram, 0, 256*sizeof(unsigned long));
3646 //memset(histogram, 0, 256);
3647 sy = y-(width/2);
3648 for(mcy=0; mcy < width; ++mcy, ++sy){
3649 my = sy < 0 ? 0 : sy > src.height()-1 ?
3650 src.height()-1 : sy;
3651 sx = x+(-width/2);
3652 for(mcx=0; mcx < width; ++mcx, ++sx){
3653 mx = sx < 0 ? 0 : sx > src.width()-1 ?
3654 src.width()-1 : sx;
3655
3656 k = intensityValue(jumpTable[my][mx]);
3657 if(k > 255){
3658 tqWarning("KImageEffect::oilPaintConvolve(): k is %d",
3659 k);
3660 k = 255;
3661 }
3662 histogram[k]++;
3663 if(histogram[k] > count){
3664 count = histogram[k];
3665 s = jumpTable[my]+mx;
3666 }
3667 }
3668 }
3669 if (s)
3670 *q++ = (*s);
3671 }
3672 }
3673 /* liberateMemory((histogram); */
3674 return(dest);
3675}
3676
3677TQImage KImageEffect::charcoal(TQImage &src, double /*factor*/)
3678{
3679 /* binary compat method - remove me when possible! */
3680 return(charcoal(src, 0, 1));
3681}
3682
3683TQImage KImageEffect::charcoal(TQImage &src, double radius, double sigma)
3684{
3685 TQImage img(edge(src, radius));
3686 img = blur(img, radius, sigma);
3687 normalize(img);
3688 img.invertPixels(false);
3689 KImageEffect::toGray(img);
3690 return(img);
3691}
3692
3693void KImageEffect::normalize(TQImage &image)
3694{
3695 struct double_packet high, low, intensity, *histogram;
3696 struct short_packet *normalize_map;
3697 TQ_INT64 number_pixels;
3698 int x, y;
3699 unsigned int *p, *q;
3700 long i;
3701 unsigned long threshold_intensity;
3702 unsigned char r, g, b, a;
3703
3704 if(image.depth() < 32) // result will always be 32bpp
3705 image = image.convertDepth(32);
3706
3707 histogram = (struct double_packet *)
3708 malloc(256*sizeof(struct double_packet));
3709 normalize_map = (struct short_packet *)
3710 malloc(256*sizeof(struct short_packet));
3711
3712 if(!histogram || !normalize_map){
3713 if(histogram)
3714 liberateMemory(&histogram);
3715 if(normalize_map)
3716 liberateMemory(&normalize_map);
3717 tqWarning("KImageEffect::normalize(): Unable to allocate memory!");
3718 return;
3719 }
3720
3721 /*
3722 Form histogram.
3723 */
3724 memset(histogram, 0, 256*sizeof(struct double_packet));
3725 for(y=0; y < image.height(); ++y){
3726 p = (unsigned int *)image.scanLine(y);
3727 for(x=0; x < image.width(); ++x){
3728 histogram[(unsigned char)(tqRed(*p))].red++;
3729 histogram[(unsigned char)(tqGreen(*p))].green++;
3730 histogram[(unsigned char)(tqBlue(*p))].blue++;
3731 histogram[(unsigned char)(tqAlpha(*p))].alpha++;
3732 p++;
3733 }
3734 }
3735
3736 /*
3737 Find the histogram boundaries by locating the 0.1 percent levels.
3738 */
3739 number_pixels = (TQ_INT64)image.width()*image.height();
3740 threshold_intensity = number_pixels/1000;
3741
3742 /* red */
3743 memset(&intensity, 0, sizeof(struct double_packet));
3744 memset(&high, 0, sizeof(struct double_packet));
3745 memset(&low, 0, sizeof(struct double_packet));
3746 for(high.red=255; high.red != 0; high.red--){
3747 intensity.red+=histogram[(unsigned char)high.red].red;
3748 if(intensity.red > threshold_intensity)
3749 break;
3750 }
3751 if(low.red == high.red){
3752 threshold_intensity = 0;
3753 memset(&intensity, 0, sizeof(struct double_packet));
3754 for(low.red=0; low.red < 255; low.red++){
3755 intensity.red+=histogram[(unsigned char)low.red].red;
3756 if(intensity.red > threshold_intensity)
3757 break;
3758 }
3759 memset(&intensity, 0, sizeof(struct double_packet));
3760 for(high.red=255; high.red != 0; high.red--){
3761 intensity.red+=histogram[(unsigned char)high.red].red;
3762 if(intensity.red > threshold_intensity)
3763 break;
3764 }
3765 }
3766
3767 /* green */
3768 memset(&intensity, 0, sizeof(struct double_packet));
3769 for(high.green=255; high.green != 0; high.green--){
3770 intensity.green+=histogram[(unsigned char)high.green].green;
3771 if(intensity.green > threshold_intensity)
3772 break;
3773 }
3774 if(low.green == high.green){
3775 threshold_intensity = 0;
3776 memset(&intensity, 0, sizeof(struct double_packet));
3777 for(low.green=0; low.green < 255; low.green++){
3778 intensity.green+=histogram[(unsigned char)low.green].green;
3779 if(intensity.green > threshold_intensity)
3780 break;
3781 }
3782 memset(&intensity,0,sizeof(struct double_packet));
3783 for(high.green=255; high.green != 0; high.green--){
3784 intensity.green+=histogram[(unsigned char)high.green].green;
3785 if(intensity.green > threshold_intensity)
3786 break;
3787 }
3788 }
3789
3790 /* blue */
3791 memset(&intensity, 0, sizeof(struct double_packet));
3792 for(high.blue=255; high.blue != 0; high.blue--){
3793 intensity.blue+=histogram[(unsigned char)high.blue].blue;
3794 if(intensity.blue > threshold_intensity)
3795 break;
3796 }
3797 if(low.blue == high.blue){
3798 threshold_intensity = 0;
3799 memset(&intensity, 0, sizeof(struct double_packet));
3800 for(low.blue=0; low.blue < 255; low.blue++){
3801 intensity.blue+=histogram[(unsigned char)low.blue].blue;
3802 if(intensity.blue > threshold_intensity)
3803 break;
3804 }
3805 memset(&intensity,0,sizeof(struct double_packet));
3806 for(high.blue=255; high.blue != 0; high.blue--){
3807 intensity.blue+=histogram[(unsigned char)high.blue].blue;
3808 if(intensity.blue > threshold_intensity)
3809 break;
3810 }
3811 }
3812
3813 /* alpha */
3814 memset(&intensity, 0, sizeof(struct double_packet));
3815 for(high.alpha=255; high.alpha != 0; high.alpha--){
3816 intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
3817 if(intensity.alpha > threshold_intensity)
3818 break;
3819 }
3820 if(low.alpha == high.alpha){
3821 threshold_intensity = 0;
3822 memset(&intensity, 0, sizeof(struct double_packet));
3823 for(low.alpha=0; low.alpha < 255; low.alpha++){
3824 intensity.alpha+=histogram[(unsigned char)low.alpha].alpha;
3825 if(intensity.alpha > threshold_intensity)
3826 break;
3827 }
3828 memset(&intensity,0,sizeof(struct double_packet));
3829 for(high.alpha=255; high.alpha != 0; high.alpha--){
3830 intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
3831 if(intensity.alpha > threshold_intensity)
3832 break;
3833 }
3834 }
3835 liberateMemory(&histogram);
3836
3837 /*
3838 Stretch the histogram to create the normalized image mapping.
3839 */
3840
3841 // should the maxes be 65535?
3842 memset(normalize_map, 0 ,256*sizeof(struct short_packet));
3843 for(i=0; i <= (long) 255; i++){
3844 if(i < (long) low.red)
3845 normalize_map[i].red=0;
3846 else if (i > (long) high.red)
3847 normalize_map[i].red=65535;
3848 else if (low.red != high.red)
3849 normalize_map[i].red =
3850 (unsigned short)((65535*(i-low.red))/(high.red-low.red));
3851
3852 if(i < (long) low.green)
3853 normalize_map[i].green=0;
3854 else if (i > (long) high.green)
3855 normalize_map[i].green=65535;
3856 else if (low.green != high.green)
3857 normalize_map[i].green =
3858 (unsigned short)((65535*(i-low.green))/(high.green-low.green));
3859
3860 if(i < (long) low.blue)
3861 normalize_map[i].blue=0;
3862 else if (i > (long) high.blue)
3863 normalize_map[i].blue=65535;
3864 else if (low.blue != high.blue)
3865 normalize_map[i].blue =
3866 (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue));
3867
3868 if(i < (long) low.alpha)
3869 normalize_map[i].alpha=0;
3870 else if (i > (long) high.alpha)
3871 normalize_map[i].alpha=65535;
3872 else if (low.alpha != high.alpha)
3873 normalize_map[i].alpha =
3874 (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha));
3875
3876 }
3877
3878 for(y=0; y < image.height(); ++y){
3879 q = (unsigned int *)image.scanLine(y);
3880 for(x=0; x < image.width(); ++x){
3881 if(low.red != high.red)
3882 r = (normalize_map[(unsigned short)(tqRed(q[x]))].red)/257;
3883 else
3884 r = tqRed(q[x]);
3885 if(low.green != high.green)
3886 g = (normalize_map[(unsigned short)(tqGreen(q[x]))].green)/257;
3887 else
3888 g = tqGreen(q[x]);
3889 if(low.blue != high.blue)
3890 b = (normalize_map[(unsigned short)(tqBlue(q[x]))].blue)/257;
3891 else
3892 b = tqBlue(q[x]);
3893 if(low.alpha != high.alpha)
3894 a = (normalize_map[(unsigned short)(tqAlpha(q[x]))].alpha)/257;
3895 else
3896 a = tqAlpha(q[x]);
3897 q[x] = tqRgba(r, g, b, a);
3898 }
3899 }
3900 liberateMemory(&normalize_map);
3901}
3902
3903void KImageEffect::equalize(TQImage &image)
3904{
3905 struct double_packet high, low, intensity, *map, *histogram;
3906 struct short_packet *equalize_map;
3907 int x, y;
3908 unsigned int *p, *q;
3909 long i;
3910 unsigned char r, g, b, a;
3911
3912 if(image.depth() < 32) // result will always be 32bpp
3913 image = image.convertDepth(32);
3914
3915 histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet));
3916 map=(struct double_packet *) malloc(256*sizeof(struct double_packet));
3917 equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet));
3918 if(!histogram || !map || !equalize_map){
3919 if(histogram)
3920 liberateMemory(&histogram);
3921 if(map)
3922 liberateMemory(&map);
3923 if(equalize_map)
3924 liberateMemory(&equalize_map);
3925 tqWarning("KImageEffect::equalize(): Unable to allocate memory!");
3926 return;
3927 }
3928
3929 /*
3930 Form histogram.
3931 */
3932 memset(histogram, 0, 256*sizeof(struct double_packet));
3933 for(y=0; y < image.height(); ++y){
3934 p = (unsigned int *)image.scanLine(y);
3935 for(x=0; x < image.width(); ++x){
3936 histogram[(unsigned char)(tqRed(*p))].red++;
3937 histogram[(unsigned char)(tqGreen(*p))].green++;
3938 histogram[(unsigned char)(tqBlue(*p))].blue++;
3939 histogram[(unsigned char)(tqAlpha(*p))].alpha++;
3940 p++;
3941 }
3942 }
3943 /*
3944 Integrate the histogram to get the equalization map.
3945 */
3946 memset(&intensity, 0 ,sizeof(struct double_packet));
3947 for(i=0; i <= 255; ++i){
3948 intensity.red += histogram[i].red;
3949 intensity.green += histogram[i].green;
3950 intensity.blue += histogram[i].blue;
3951 intensity.alpha += histogram[i].alpha;
3952 map[i]=intensity;
3953 }
3954 low=map[0];
3955 high=map[255];
3956 memset(equalize_map, 0, 256*sizeof(short_packet));
3957 for(i=0; i <= 255; ++i){
3958 if(high.red != low.red)
3959 equalize_map[i].red=(unsigned short)
3960 ((65535*(map[i].red-low.red))/(high.red-low.red));
3961 if(high.green != low.green)
3962 equalize_map[i].green=(unsigned short)
3963 ((65535*(map[i].green-low.green))/(high.green-low.green));
3964 if(high.blue != low.blue)
3965 equalize_map[i].blue=(unsigned short)
3966 ((65535*(map[i].blue-low.blue))/(high.blue-low.blue));
3967 if(high.alpha != low.alpha)
3968 equalize_map[i].alpha=(unsigned short)
3969 ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha));
3970 }
3971 liberateMemory(&histogram);
3972 liberateMemory(&map);
3973
3974 /*
3975 Stretch the histogram.
3976 */
3977 for(y=0; y < image.height(); ++y){
3978 q = (unsigned int *)image.scanLine(y);
3979 for(x=0; x < image.width(); ++x){
3980 if(low.red != high.red)
3981 r = (equalize_map[(unsigned short)(tqRed(q[x]))].red/257);
3982 else
3983 r = tqRed(q[x]);
3984 if(low.green != high.green)
3985 g = (equalize_map[(unsigned short)(tqGreen(q[x]))].green/257);
3986 else
3987 g = tqGreen(q[x]);
3988 if(low.blue != high.blue)
3989 b = (equalize_map[(unsigned short)(tqBlue(q[x]))].blue/257);
3990 else
3991 b = tqBlue(q[x]);
3992 if(low.alpha != high.alpha)
3993 a = (equalize_map[(unsigned short)(tqAlpha(q[x]))].alpha/257);
3994 else
3995 a = tqAlpha(q[x]);
3996 q[x] = tqRgba(r, g, b, a);
3997 }
3998 }
3999 liberateMemory(&equalize_map);
4000
4001}
4002
4003TQImage KImageEffect::edge(TQImage &image, double radius)
4004{
4005 double *kernel;
4006 int width;
4007 long i;
4008 TQImage dest;
4009
4010 if(radius == 50.0){
4011 /* For binary compatability! Remove me when possible! This used to
4012 * take a different parameter, a factor, and this was the default
4013 * value */
4014 radius = 0.0;
4015 }
4016
4017 width = getOptimalKernelWidth(radius, 0.5);
4018 if(image.width() < width || image.height() < width){
4019 tqWarning("KImageEffect::edge(): Image is smaller than radius!");
4020 return(dest);
4021 }
4022 kernel= (double *)malloc(width*width*sizeof(double));
4023 if(!kernel){
4024 tqWarning("KImageEffect::edge(): Unable to allocate memory!");
4025 return(dest);
4026 }
4027 for(i=0; i < (width*width); i++)
4028 kernel[i]=(-1.0);
4029 kernel[i/2]=width*width-1.0;
4030 convolveImage(&image, &dest, width, kernel);
4031 free(kernel);
4032 return(dest);
4033}
4034
4035TQImage KImageEffect::emboss(TQImage &src)
4036{
4037 /* binary compat method - remove me when possible! */
4038 return(emboss(src, 0, 1));
4039}
4040
4041TQImage KImageEffect::emboss(TQImage &image, double radius, double sigma)
4042{
4043 double alpha, *kernel;
4044 int j, width;
4045 long i, u, v;
4046 TQImage dest;
4047
4048 if(sigma == 0.0){
4049 tqWarning("KImageEffect::emboss(): Zero sigma is not permitted!");
4050 return(dest);
4051 }
4052
4053 width = getOptimalKernelWidth(radius, sigma);
4054 if(image.width() < width || image.height() < width){
4055 tqWarning("KImageEffect::emboss(): Image is smaller than radius!");
4056 return(dest);
4057 }
4058 kernel= (double *)malloc(width*width*sizeof(double));
4059 if(!kernel){
4060 tqWarning("KImageEffect::emboss(): Unable to allocate memory!");
4061 return(dest);
4062 }
4063 if(image.depth() < 32)
4064 image = image.convertDepth(32);
4065
4066 i=0;
4067 j=width/2;
4068 for(v=(-width/2); v <= (width/2); v++){
4069 for(u=(-width/2); u <= (width/2); u++){
4070 alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
4071 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
4072 (2.0*MagickPI*sigma*sigma);
4073 if (u == j)
4074 kernel[i]=0.0;
4075 i++;
4076 }
4077 j--;
4078 }
4079 convolveImage(&image, &dest, width, kernel);
4080 liberateMemory(&kernel);
4081
4082 equalize(dest);
4083 return(dest);
4084}
4085
4086void KImageEffect::blurScanLine(double *kernel, int width,
4087 unsigned int *src, unsigned int *dest,
4088 int columns)
4089{
4090 double *p;
4091 unsigned int *q;
4092 int x;
4093 long i;
4094 double red, green, blue, alpha;
4095 double scale = 0.0;
4096
4097 if(width > columns){
4098 for(x=0; x < columns; ++x){
4099 scale = 0.0;
4100 red = blue = green = alpha = 0.0;
4101 p = kernel;
4102 q = src;
4103 for(i=0; i < columns; ++i){
4104 if((i >= (x-width/2)) && (i <= (x+width/2))){
4105 red += (*p)*(tqRed(*q)*257);
4106 green += (*p)*(tqGreen(*q)*257);
4107 blue += (*p)*(tqBlue(*q)*257);
4108 alpha += (*p)*(tqAlpha(*q)*257);
4109 }
4110 if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
4111 scale+=kernel[i+width/2-x];
4112 p++;
4113 q++;
4114 }
4115 scale = 1.0/scale;
4116 red = scale*(red+0.5);
4117 green = scale*(green+0.5);
4118 blue = scale*(blue+0.5);
4119 alpha = scale*(alpha+0.5);
4120
4121 red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4122 green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4123 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4124 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4125
4126 dest[x] = tqRgba((unsigned char)(red/257UL),
4127 (unsigned char)(green/257UL),
4128 (unsigned char)(blue/257UL),
4129 (unsigned char)(alpha/257UL));
4130 }
4131 return;
4132 }
4133
4134 for(x=0; x < width/2; ++x){
4135 scale = 0.0;
4136 red = blue = green = alpha = 0.0;
4137 p = kernel+width/2-x;
4138 q = src;
4139 for(i=width/2-x; i < width; ++i){
4140 red += (*p)*(tqRed(*q)*257);
4141 green += (*p)*(tqGreen(*q)*257);
4142 blue += (*p)*(tqBlue(*q)*257);
4143 alpha += (*p)*(tqAlpha(*q)*257);
4144 scale += (*p);
4145 p++;
4146 q++;
4147 }
4148 scale=1.0/scale;
4149
4150 red = scale*(red+0.5);
4151 green = scale*(green+0.5);
4152 blue = scale*(blue+0.5);
4153 alpha = scale*(alpha+0.5);
4154
4155 red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4156 green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4157 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4158 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4159
4160 dest[x] = tqRgba((unsigned char)(red/257UL),
4161 (unsigned char)(green/257UL),
4162 (unsigned char)(blue/257UL),
4163 (unsigned char)(alpha/257UL));
4164 }
4165
4166 for(; x < columns-width/2; ++x){
4167 red = blue = green = alpha = 0.0;
4168 p = kernel;
4169 q = src+(x-width/2);
4170 for (i=0; i < (long) width; ++i){
4171 red += (*p)*(tqRed(*q)*257);
4172 green += (*p)*(tqGreen(*q)*257);
4173 blue += (*p)*(tqBlue(*q)*257);
4174 alpha += (*p)*(tqAlpha(*q)*257);
4175 p++;
4176 q++;
4177 }
4178 red = scale*(red+0.5);
4179 green = scale*(green+0.5);
4180 blue = scale*(blue+0.5);
4181 alpha = scale*(alpha+0.5);
4182
4183 red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4184 green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4185 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4186 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4187
4188 dest[x] = tqRgba((unsigned char)(red/257UL),
4189 (unsigned char)(green/257UL),
4190 (unsigned char)(blue/257UL),
4191 (unsigned char)(alpha/257UL));
4192 }
4193
4194 for(; x < columns; ++x){
4195 red = blue = green = alpha = 0.0;
4196 scale=0;
4197 p = kernel;
4198 q = src+(x-width/2);
4199 for(i=0; i < columns-x+width/2; ++i){
4200 red += (*p)*(tqRed(*q)*257);
4201 green += (*p)*(tqGreen(*q)*257);
4202 blue += (*p)*(tqBlue(*q)*257);
4203 alpha += (*p)*(tqAlpha(*q)*257);
4204 scale += (*p);
4205 p++;
4206 q++;
4207 }
4208 scale=1.0/scale;
4209 red = scale*(red+0.5);
4210 green = scale*(green+0.5);
4211 blue = scale*(blue+0.5);
4212 alpha = scale*(alpha+0.5);
4213
4214 red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4215 green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4216 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4217 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4218
4219 dest[x] = tqRgba((unsigned char)(red/257UL),
4220 (unsigned char)(green/257UL),
4221 (unsigned char)(blue/257UL),
4222 (unsigned char)(alpha/257UL));
4223 }
4224}
4225
4226int KImageEffect::getBlurKernel(int width, double sigma, double **kernel)
4227{
4228#define KernelRank 3
4229 double alpha, normalize;
4230 long i;
4231 int bias;
4232
4233 assert(sigma != 0.0);
4234 if(width == 0)
4235 width = 3;
4236 *kernel=(double *)malloc(width*sizeof(double));
4237 if(*kernel == (double *)NULL)
4238 return(0);
4239 memset(*kernel, 0, width*sizeof(double));
4240 bias = KernelRank*width/2;
4241 for(i=(-bias); i <= bias; i++){
4242 alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
4243 (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma);
4244 }
4245 normalize=0;
4246 for(i=0; i < width; i++)
4247 normalize+=(*kernel)[i];
4248 for(i=0; i < width; i++)
4249 (*kernel)[i]/=normalize;
4250
4251 return(width);
4252}
4253
4254TQImage KImageEffect::blur(TQImage &src, double /*factor*/)
4255{
4256 /* binary compat method - remove me when possible! */
4257 return(blur(src, 0, 1));
4258}
4259
4260TQImage KImageEffect::blur(TQImage &src, double radius, double sigma)
4261{
4262 double *kernel;
4263 TQImage dest;
4264 int width;
4265 int x, y;
4266 unsigned int *scanline, *temp;
4267 unsigned int *p, *q;
4268
4269 if(sigma == 0.0){
4270 tqWarning("KImageEffect::blur(): Zero sigma is not permitted!");
4271 return(dest);
4272 }
4273 if(src.depth() < 32)
4274 src = src.convertDepth(32);
4275
4276 kernel=(double *) NULL;
4277 if(radius > 0)
4278 width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
4279 else{
4280 double *last_kernel;
4281 last_kernel=(double *) NULL;
4282 width=getBlurKernel(3,sigma,&kernel);
4283
4284 while ((long) (MaxRGB*kernel[0]) > 0){
4285 if(last_kernel != (double *)NULL){
4286 liberateMemory(&last_kernel);
4287 }
4288 last_kernel=kernel;
4289 kernel = (double *)NULL;
4290 width = getBlurKernel(width+2, sigma, &kernel);
4291 }
4292 if(last_kernel != (double *) NULL){
4293 liberateMemory(&kernel);
4294 width-=2;
4295 kernel = last_kernel;
4296 }
4297 }
4298
4299 if(width < 3){
4300 tqWarning("KImageEffect::blur(): Kernel radius is too small!");
4301 liberateMemory(&kernel);
4302 return(dest);
4303 }
4304
4305 dest.create(src.width(), src.height(), 32);
4306
4307 // Horizontal convolution
4308 scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
4309 temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
4310 for(y=0; y < src.height(); ++y){
4311 p = (unsigned int *)src.scanLine(y);
4312 q = (unsigned int *)dest.scanLine(y);
4313 blurScanLine(kernel, width, p, q, src.width());
4314 }
4315
4316 TQImage partial = dest;
4317
4318 // Vertical convolution
4319 unsigned int **srcTable = (unsigned int **)partial.jumpTable();
4320 unsigned int **destTable = (unsigned int **)dest.jumpTable();
4321 for(x=0; x < partial.width(); ++x){
4322 for(y=0; y < partial.height(); ++y){
4323 scanline[y] = srcTable[y][x];
4324 }
4325 blurScanLine(kernel, width, scanline, temp, partial.height());
4326 for(y=0; y < partial.height(); ++y){
4327 destTable[y][x] = temp[y];
4328 }
4329 }
4330 free(scanline);
4331 free(temp);
4332 free(kernel);
4333 return(dest);
4334}
4335
4336bool KImageEffect::convolveImage(TQImage *image, TQImage *dest,
4337 const unsigned int order,
4338 const double *kernel)
4339{
4340 long width;
4341 double red, green, blue, alpha;
4342 double normalize, *normal_kernel;
4343 const double *k;
4344 unsigned int *q;
4345 int x, y, mx, my, sx, sy;
4346 long i;
4347 int mcx, mcy;
4348
4349 width = order;
4350 if((width % 2) == 0){
4351 tqWarning("KImageEffect: Kernel width must be an odd number!");
4352 return(false);
4353 }
4354 normal_kernel = (double *)malloc(width*width*sizeof(double));
4355 if(!normal_kernel){
4356 tqWarning("KImageEffect: Unable to allocate memory!");
4357 return(false);
4358 }
4359 dest->reset();
4360 dest->create(image->width(), image->height(), 32);
4361 if(image->depth() < 32)
4362 *image = image->convertDepth(32);
4363
4364 normalize=0.0;
4365 for(i=0; i < (width*width); i++)
4366 normalize += kernel[i];
4367 if(fabs(normalize) <= MagickEpsilon)
4368 normalize=1.0;
4369 normalize=1.0/normalize;
4370 for(i=0; i < (width*width); i++)
4371 normal_kernel[i] = normalize*kernel[i];
4372
4373 unsigned int **jumpTable = (unsigned int **)image->jumpTable();
4374 for(y=0; y < dest->height(); ++y){
4375 sy = y-(width/2);
4376 q = (unsigned int *)dest->scanLine(y);
4377 for(x=0; x < dest->width(); ++x){
4378 k = normal_kernel;
4379 red = green = blue = alpha = 0;
4380 sy = y-(width/2);
4381 for(mcy=0; mcy < width; ++mcy, ++sy){
4382 my = sy < 0 ? 0 : sy > image->height()-1 ?
4383 image->height()-1 : sy;
4384 sx = x+(-width/2);
4385 for(mcx=0; mcx < width; ++mcx, ++sx){
4386 mx = sx < 0 ? 0 : sx > image->width()-1 ?
4387 image->width()-1 : sx;
4388 red += (*k)*(tqRed(jumpTable[my][mx])*257);
4389 green += (*k)*(tqGreen(jumpTable[my][mx])*257);
4390 blue += (*k)*(tqBlue(jumpTable[my][mx])*257);
4391 alpha += (*k)*(tqAlpha(jumpTable[my][mx])*257);
4392 ++k;
4393 }
4394 }
4395
4396 red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
4397 green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
4398 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
4399 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
4400
4401 *q++ = tqRgba((unsigned char)(red/257UL),
4402 (unsigned char)(green/257UL),
4403 (unsigned char)(blue/257UL),
4404 (unsigned char)(alpha/257UL));
4405 }
4406 }
4407 free(normal_kernel);
4408 return(true);
4409
4410}
4411
4412int KImageEffect::getOptimalKernelWidth(double radius, double sigma)
4413{
4414 double normalize, value;
4415 long width;
4416 long u;
4417
4418 assert(sigma != 0.0);
4419 if(radius > 0.0)
4420 return((int)(2.0*ceil(radius)+1.0));
4421 for(width=5; ;){
4422 normalize=0.0;
4423 for(u=(-width/2); u <= (width/2); u++)
4424 normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
4425 u=width/2;
4426 value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
4427 if((long)(65535*value) <= 0)
4428 break;
4429 width+=2;
4430 }
4431 return((int)width-2);
4432}
4433
4434TQImage KImageEffect::sharpen(TQImage &src, double /*factor*/)
4435{
4436 /* binary compat method - remove me when possible! */
4437 return(sharpen(src, 0, 1));
4438}
4439
4440TQImage KImageEffect::sharpen(TQImage &image, double radius, double sigma)
4441{
4442 double alpha, normalize, *kernel;
4443 int width;
4444 long i, u, v;
4445 TQImage dest;
4446
4447 if(sigma == 0.0){
4448 tqWarning("KImageEffect::sharpen(): Zero sigma is not permitted!");
4449 return(dest);
4450 }
4451 width = getOptimalKernelWidth(radius, sigma);
4452 if(image.width() < width){
4453 tqWarning("KImageEffect::sharpen(): Image is smaller than radius!");
4454 return(dest);
4455 }
4456 kernel = (double *)malloc(width*width*sizeof(double));
4457 if(!kernel){
4458 tqWarning("KImageEffect::sharpen(): Unable to allocate memory!");
4459 return(dest);
4460 }
4461
4462 i = 0;
4463 normalize=0.0;
4464 for(v=(-width/2); v <= (width/2); v++){
4465 for(u=(-width/2); u <= (width/2); u++){
4466 alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
4467 kernel[i]=alpha/(2.0*MagickPI*sigma*sigma);
4468 normalize+=kernel[i];
4469 i++;
4470 }
4471 }
4472 kernel[i/2]=(-2.0)*normalize;
4473 convolveImage(&image, &dest, width, kernel);
4474 free(kernel);
4475 return(dest);
4476}
4477
4478// End of new algorithms
4479
4480TQImage KImageEffect::shade(TQImage &src, bool color_shading, double azimuth,
4481 double elevation)
4482{
4483 struct PointInfo{
4484 double x, y, z;
4485 };
4486
4487 double distance, normal_distance, shade;
4488 int x, y;
4489
4490 struct PointInfo light, normal;
4491
4492 unsigned int *q;
4493
4494 TQImage dest(src.width(), src.height(), 32);
4495
4496 azimuth = DegreesToRadians(azimuth);
4497 elevation = DegreesToRadians(elevation);
4498 light.x = MaxRGB*cos(azimuth)*cos(elevation);
4499 light.y = MaxRGB*sin(azimuth)*cos(elevation);
4500 light.z = MaxRGB*sin(elevation);
4501 normal.z= 2*MaxRGB; // constant Z of surface normal
4502
4503 if(src.depth() > 8){ // DirectClass source image
4504 unsigned int *p, *s0, *s1, *s2;
4505 for(y=0; y < src.height(); ++y){
4506 p = (unsigned int *)src.scanLine(TQMIN(TQMAX(y-1,0),src.height()-3));
4507 q = (unsigned int *)dest.scanLine(y);
4508 // shade this row of pixels.
4509 *q++=(*(p+src.width()));
4510 p++;
4511 s0 = p;
4512 s1 = p + src.width();
4513 s2 = p + 2*src.width();
4514 for(x=1; x < src.width()-1; ++x){
4515 // determine the surface normal and compute shading.
4516 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
4517 (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
4518 (double) intensityValue(*(s2+1));
4519 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
4520 (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
4521 (double) intensityValue(*(s0+1));
4522 if((normal.x == 0) && (normal.y == 0))
4523 shade=light.z;
4524 else{
4525 shade=0.0;
4526 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4527 if (distance > 0.0){
4528 normal_distance=
4529 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4530 if(fabs(normal_distance) > 0.0000001)
4531 shade=distance/sqrt(normal_distance);
4532 }
4533 }
4534 if(!color_shading){
4535 *q = tqRgba((unsigned char)(shade),
4536 (unsigned char)(shade),
4537 (unsigned char)(shade),
4538 tqAlpha(*s1));
4539 }
4540 else{
4541 *q = tqRgba((unsigned char)((shade*tqRed(*s1))/(MaxRGB+1)),
4542 (unsigned char)((shade*tqGreen(*s1))/(MaxRGB+1)),
4543 (unsigned char)((shade*tqBlue(*s1))/(MaxRGB+1)),
4544 tqAlpha(*s1));
4545 }
4546 ++s0;
4547 ++s1;
4548 ++s2;
4549 q++;
4550 }
4551 *q++=(*s1);
4552 }
4553 }
4554 else{ // PsudeoClass source image
4555 unsigned char *p, *s0, *s1, *s2;
4556 int scanLineIdx;
4557 unsigned int *cTable = (unsigned int *)src.colorTable();
4558 for(y=0; y < src.height(); ++y){
4559 scanLineIdx = TQMIN(TQMAX(y-1,0),src.height()-3);
4560 p = (unsigned char *)src.scanLine(scanLineIdx);
4561 q = (unsigned int *)dest.scanLine(y);
4562 // shade this row of pixels.
4563 s0 = p;
4564 s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
4565 s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
4566 *q++=(*(cTable+(*s1)));
4567 ++p;
4568 ++s0;
4569 ++s1;
4570 ++s2;
4571 for(x=1; x < src.width()-1; ++x){
4572 // determine the surface normal and compute shading.
4573 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
4574 (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
4575 (double) intensityValue(*(cTable+(*(s2+1))));
4576 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
4577 (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
4578 (double) intensityValue(*(cTable+(*(s0+1))));
4579 if((normal.x == 0) && (normal.y == 0))
4580 shade=light.z;
4581 else{
4582 shade=0.0;
4583 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4584 if (distance > 0.0){
4585 normal_distance=
4586 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4587 if(fabs(normal_distance) > 0.0000001)
4588 shade=distance/sqrt(normal_distance);
4589 }
4590 }
4591 if(!color_shading){
4592 *q = tqRgba((unsigned char)(shade),
4593 (unsigned char)(shade),
4594 (unsigned char)(shade),
4595 tqAlpha(*(cTable+(*s1))));
4596 }
4597 else{
4598 *q = tqRgba((unsigned char)((shade*tqRed(*(cTable+(*s1))))/(MaxRGB+1)),
4599 (unsigned char)((shade*tqGreen(*(cTable+(*s1))))/(MaxRGB+1)),
4600 (unsigned char)((shade*tqBlue(*(cTable+(*s1))))/(MaxRGB+1)),
4601 tqAlpha(*s1));
4602 }
4603 ++s0;
4604 ++s1;
4605 ++s2;
4606 q++;
4607 }
4608 *q++=(*(cTable+(*s1)));
4609 }
4610 }
4611 return(dest);
4612}
4613
4614// High quality, expensive HSV contrast. You can do a faster one by just
4615// taking a grayscale threshold (ie: 128) and incrementing RGB color
4616// channels above it and decrementing those below it, but this gives much
4617// better results. (mosfet 12/28/01)
4618void KImageEffect::contrastHSV(TQImage &img, bool sharpen)
4619{
4620 int i, sign;
4621 unsigned int *data;
4622 int count;
4623 double brightness, scale, theta;
4624 TQColor c;
4625 int h, s, v;
4626
4627 sign = sharpen ? 1 : -1;
4628 scale=0.5000000000000001;
4629 if(img.depth() > 8){
4630 count = img.width()*img.height();
4631 data = (unsigned int *)img.bits();
4632 }
4633 else{
4634 count = img.numColors();
4635 data = (unsigned int *)img.colorTable();
4636 }
4637 for(i=0; i < count; ++i){
4638 c.setRgb(data[i]);
4639 c.hsv(&h, &s, &v);
4640 brightness = v/255.0;
4641 theta=(brightness-0.5)*M_PI;
4642 brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
4643 if (brightness > 1.0)
4644 brightness=1.0;
4645 else
4646 if (brightness < 0)
4647 brightness=0.0;
4648 v = (int)(brightness*255);
4649 c.setHsv(h, s, v);
4650 data[i] = tqRgba(c.red(), c.green(), c.blue(), tqAlpha(data[i]));
4651 }
4652}
4653
4654
4655struct BumpmapParams {
4656 BumpmapParams( double bm_azimuth, double bm_elevation,
4657 int bm_depth, KImageEffect::BumpmapType bm_type,
4658 bool invert ) {
4659 /* Convert to radians */
4660 double azimuth = DegreesToRadians( bm_azimuth );
4661 double elevation = DegreesToRadians( bm_elevation );
4662
4663 /* Calculate the light vector */
4664 lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
4665 ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
4666 int lz = (int)( sin(elevation) * 255.0 );
4667
4668 /* Calculate constant Z component of surface normal */
4669 int nz = (6 * 255) / bm_depth;
4670 nz2 = nz * nz;
4671 nzlz = nz * lz;
4672
4673 /* Optimize for vertical normals */
4674 background = lz;
4675
4676 /* Calculate darkness compensation factor */
4677 compensation = sin(elevation);
4678
4679 /* Create look-up table for map type */
4680 for (int i = 0; i < 256; i++)
4681 {
4682 double n = 0;
4683 switch (bm_type)
4684 {
4685 case KImageEffect::Spherical:
4686 n = i / 255.0 - 1.0;
4687 lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
4688 break;
4689
4690 case KImageEffect::Sinuosidal:
4691 n = i / 255.0;
4692 lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
4693 2.0 + 0.5);
4694 break;
4695
4696 case KImageEffect::Linear:
4697 default:
4698 lut[i] = i;
4699 }
4700
4701 if (invert)
4702 lut[i] = 255 - lut[i];
4703 }
4704 }
4705 int lx, ly;
4706 int nz2, nzlz;
4707 int background;
4708 double compensation;
4709 uchar lut[256];
4710};
4711
4712
4713static void bumpmap_convert_row( uint *row,
4714 int width,
4715 int bpp,
4716 int has_alpha,
4717 uchar *lut,
4718 int waterlevel )
4719{
4720 uint *p;
4721
4722 p = row;
4723
4724 has_alpha = has_alpha ? 1 : 0;
4725
4726 if (bpp >= 3)
4727 for (; width; width--)
4728 {
4729 if (has_alpha) {
4730 unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
4731 *p++ = lut[(unsigned int) ( waterlevel +
4732 ( ( idx -
4733 waterlevel) * tqBlue( *row )) / 255.0 )];
4734 } else {
4735 unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
4736 *p++ = lut[idx];
4737 }
4738
4739 ++row;
4740 }
4741}
4742
4743static void bumpmap_row( uint *src,
4744 uint *dest,
4745 int width,
4746 int bpp,
4747 int has_alpha,
4748 uint *bm_row1,
4749 uint *bm_row2,
4750 uint *bm_row3,
4751 int bm_width,
4752 int bm_xofs,
4753 bool tiled,
4754 bool row_in_bumpmap,
4755 int ambient,
4756 bool compensate,
4757 BumpmapParams *params )
4758{
4759 int xofs1, xofs2, xofs3;
4760 int shade;
4761 int ndotl;
4762 int nx, ny;
4763 int x;
4764 int tmp;
4765
4766 tmp = bm_xofs;
4767 xofs2 = MOD(tmp, bm_width);
4768
4769 for (x = 0; x < width; x++)
4770 {
4771 /* Calculate surface normal from bump map */
4772
4773 if (tiled || (row_in_bumpmap &&
4774 x >= - tmp && x < - tmp + bm_width)) {
4775 if (tiled) {
4776 xofs1 = MOD(xofs2 - 1, bm_width);
4777 xofs3 = MOD(xofs2 + 1, bm_width);
4778 } else {
4779 xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1);
4780 xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1);
4781 }
4782 nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
4783 bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
4784 ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
4785 bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
4786 } else {
4787 nx = ny = 0;
4788 }
4789
4790 /* Shade */
4791
4792 if ((nx == 0) && (ny == 0))
4793 shade = params->background;
4794 else {
4795 ndotl = nx * params->lx + ny * params->ly + params->nzlz;
4796
4797 if (ndotl < 0)
4798 shade = (int)( params->compensation * ambient );
4799 else {
4800 shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) );
4801
4802 shade = (int)( shade + TQMAX(0.0, (255 * params->compensation - shade)) *
4803 ambient / 255 );
4804 }
4805 }
4806
4807 /* Paint */
4808
4813 if (compensate) {
4814 int red = (int)((tqRed( *src ) * shade) / (params->compensation * 255));
4815 int green = (int)((tqGreen( *src ) * shade) / (params->compensation * 255));
4816 int blue = (int)((tqBlue( *src ) * shade) / (params->compensation * 255));
4817 int alpha = (int)((tqAlpha( *src ) * shade) / (params->compensation * 255));
4818 ++src;
4819 *dest++ = tqRgba( red, green, blue, alpha );
4820 } else {
4821 int red = tqRed( *src ) * shade / 255;
4822 int green = tqGreen( *src ) * shade / 255;
4823 int blue = tqBlue( *src ) * shade / 255;
4824 int alpha = tqAlpha( *src ) * shade / 255;
4825 ++src;
4826 *dest++ = tqRgba( red, green, blue, alpha );
4827 }
4828
4829 /* Next pixel */
4830
4831 if (++xofs2 == bm_width)
4832 xofs2 = 0;
4833 }
4834}
4835
4855TQImage KImageEffect::bumpmap(TQImage &img, TQImage &map, double azimuth, double elevation,
4856 int depth, int xofs, int yofs, int waterlevel,
4857 int ambient, bool compensate, bool invert,
4858 BumpmapType type, bool tiled)
4859{
4860 TQImage dst;
4861
4862 if ( img.depth() != 32 || img.depth() != 32 ) {
4863 tqWarning( "Bump-mapping effect works only with 32 bit images");
4864 return dst;
4865 }
4866
4867 dst.create( img.width(), img.height(), img.depth() );
4868 int bm_width = map.width();
4869 int bm_height = map.height();
4870 int bm_bpp = map.depth();
4871 int bm_has_alpha = map.hasAlphaBuffer();
4872
4873 int yofs1, yofs2, yofs3;
4874
4875 if ( tiled ) {
4876 yofs2 = MOD( yofs, bm_height );
4877 yofs1 = MOD( yofs2 - 1, bm_height);
4878 yofs3 = MOD( yofs2 + 1, bm_height);
4879 } else {
4880 yofs1 = 0;
4881 yofs2 = 0;
4882 yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 );
4883 }
4884
4885 BumpmapParams params( azimuth, elevation, depth, type, invert );
4886
4887 uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
4888 uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
4889 uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
4890
4891 bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
4892 bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
4893 bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
4894
4895 for (int y = 0; y < img.height(); ++y)
4896 {
4897 int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
4898
4899 uint* src_row = (unsigned int*)img.scanLine( y );
4900 uint* dest_row = (unsigned int*)dst.scanLine( y );
4901
4902 bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
4903 bm_row1, bm_row2, bm_row3, bm_width, xofs,
4904 tiled,
4905 row_in_bumpmap, ambient, compensate,
4906 &params );
4907
4908 /* Next line */
4909
4910 if (tiled || row_in_bumpmap)
4911 {
4912 uint* bm_tmprow = bm_row1;
4913 bm_row1 = bm_row2;
4914 bm_row2 = bm_row3;
4915 bm_row3 = bm_tmprow;
4916
4917 if (++yofs2 == bm_height)
4918 yofs2 = 0;
4919
4920 if (tiled)
4921 yofs3 = MOD(yofs2 + 1, bm_height);
4922 else
4923 yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1);
4924
4925 bm_row3 = (unsigned int*)map.scanLine( yofs3 );
4926 bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
4927 params.lut, waterlevel );
4928 }
4929 }
4930 return dst;
4931}
4932
4941TQImage KImageEffect::convertToPremultipliedAlpha(TQImage input) {
4942 TQImage alphaImage = input;
4943 if (!alphaImage.isNull()) alphaImage = alphaImage.convertDepth( 32 );
4944
4945 int w = alphaImage.width();
4946 int h = alphaImage.height();
4947
4948 int r;
4949 int g;
4950 int b;
4951 int a;
4952 float alpha_adjust;
4953 TQRgb l;
4954 TQRgb *ls;
4955 for (int y = 0; y < h; ++y) {
4956 ls = (TQRgb *)alphaImage.scanLine( y );
4957 for (int x = 0; x < w; ++x) {
4958 l = ls[x];
4959 alpha_adjust = (tqAlpha( l )/255.0);
4960 r = int( tqRed( l ) * alpha_adjust );
4961 g = int( tqGreen( l ) * alpha_adjust );
4962 b = int( tqBlue( l ) * alpha_adjust );
4963 a = int( tqAlpha( l ) * 1.0 );
4964 ls[x] = tqRgba( r, g, b, a );
4965 }
4966 }
4967 return alphaImage;
4968}
KCPUInfo::haveExtension
static bool haveExtension(unsigned int extension)
Returns true if the processor supports extension, and false otherwise.
Definition: kcpuinfo.h:62
KCPUInfo::IntelSSE2
@ IntelSSE2
Intel's SSE2 instructions.
Definition: kcpuinfo.h:49
KCPUInfo::IntelMMX
@ IntelMMX
Intel's MMX instructions.
Definition: kcpuinfo.h:47
KImageEffect::shade
static TQImage shade(TQImage &src, bool color_shading=true, double azimuth=30.0, double elevation=30.0)
Shades the image using a distance light source.
Definition: kimageeffect.cpp:4480
KImageEffect::NoiseType
NoiseType
This enum provides a noise type specification.
Definition: kimageeffect.h:108
KImageEffect::GaussianNoise
@ GaussianNoise
Gaussian distribution.
Definition: kimageeffect.h:109
KImageEffect::MultiplicativeGaussianNoise
@ MultiplicativeGaussianNoise
Multiplicative Gaussian distribution.
Definition: kimageeffect.h:110
KImageEffect::ImpulseNoise
@ ImpulseNoise
Impulse distribution.
Definition: kimageeffect.h:111
KImageEffect::PoissonNoise
@ PoissonNoise
Poisson distribution.
Definition: kimageeffect.h:113
KImageEffect::UniformNoise
@ UniformNoise
Uniform distribution.
Definition: kimageeffect.h:108
KImageEffect::LaplacianNoise
@ LaplacianNoise
Laplacian distribution.
Definition: kimageeffect.h:112
KImageEffect::edge
static TQImage edge(TQImage &src, double radius)
Detects edges in an image using pixel neighborhoods and an edge detection mask.
Definition: kimageeffect.cpp:4003
KImageEffect::equalize
static void equalize(TQImage &img)
Performs histogram equalisation on the reference image.
Definition: kimageeffect.cpp:3903
KImageEffect::threshold
static void threshold(TQImage &img, unsigned int value=128)
Thresholds the reference image.
Definition: kimageeffect.cpp:2810
KImageEffect::dither
static TQImage & dither(TQImage &image, const TQColor *palette, int size)
Dither an image using Floyd-Steinberg dithering for low-color situations.
Definition: kimageeffect.cpp:2261
KImageEffect::computeDestinationRect
static TQRect computeDestinationRect(const TQSize &lowerSize, Disposition disposition, TQImage &upper)
Compute the destination rectangle where to draw the upper image on top of another image using the giv...
Definition: kimageeffect.cpp:2628
KImageEffect::blend
static TQImage & blend(const TQColor &clr, TQImage &dst, float opacity)
Blends a color into the destination image, using an opacity value for blending one into another.
Definition: kimageeffect.cpp:1067
KImageEffect::emboss
static TQImage emboss(TQImage &src, double radius, double sigma)
Embosses the source image.
Definition: kimageeffect.cpp:4041
KImageEffect::channelIntensity
static TQImage & channelIntensity(TQImage &image, float percent, RGBComponent channel)
Modifies the intensity of a pixmap's RGB channel component.
Definition: kimageeffect.cpp:846
KImageEffect::swirl
static TQImage swirl(TQImage &src, double degrees=50.0, unsigned int background=0xFFFFFFFF)
Swirls the image by a specified amount.
Definition: kimageeffect.cpp:3495
KImageEffect::fade
static TQImage & fade(TQImage &image, float val, const TQColor &color)
Fade an image to a certain background color.
Definition: kimageeffect.cpp:2044
KImageEffect::charcoal
static TQImage charcoal(TQImage &src, double radius, double sigma)
Produces a neat little "charcoal" effect.
Definition: kimageeffect.cpp:3683
KImageEffect::ModulationType
ModulationType
This enum provides a modulation type specification.
Definition: kimageeffect.h:98
KImageEffect::Saturation
@ Saturation
Modulate image saturation.
Definition: kimageeffect.h:99
KImageEffect::Contrast
@ Contrast
Modulate image contrast.
Definition: kimageeffect.h:101
KImageEffect::HueShift
@ HueShift
Modulate image hue.
Definition: kimageeffect.h:100
KImageEffect::Intensity
@ Intensity
Modulate image intensity.
Definition: kimageeffect.h:98
KImageEffect::GradientType
GradientType
This enum provides a gradient type specification.
Definition: kimageeffect.h:58
KImageEffect::RotateDirection
RotateDirection
This enum provides a rotation specification.
Definition: kimageeffect.h:120
KImageEffect::Rotate270
@ Rotate270
Rotate 90 degrees to the left.
Definition: kimageeffect.h:122
KImageEffect::Rotate90
@ Rotate90
Rotate 90 degrees to the right.
Definition: kimageeffect.h:120
KImageEffect::Rotate180
@ Rotate180
Rotate 180 degrees.
Definition: kimageeffect.h:121
KImageEffect::gradient
static TQImage gradient(const TQSize &size, const TQColor &ca, const TQColor &cb, GradientType type, int ncols=3)
Create a gradient from color a to color b of the specified type.
Definition: kimageeffect.cpp:124
KImageEffect::oilPaintConvolve
static TQImage oilPaintConvolve(TQImage &src, double radius)
Produces an oil painting effect.
Definition: kimageeffect.cpp:3612
KImageEffect::sample
static TQImage sample(TQImage &src, int w, int h)
Scales an image using simple pixel sampling.
Definition: kimageeffect.cpp:2747
KImageEffect::implode
static TQImage implode(TQImage &src, double factor=30.0, unsigned int background=0xFFFFFFFF)
Implodes an image by a specified percent.
Definition: kimageeffect.cpp:3242
KImageEffect::Disposition
Disposition
Disposition of a source image on top of a destination image.
Definition: kimageeffect.h:326
KImageEffect::NoImage
@ NoImage
Don't overlay.
Definition: kimageeffect.h:326
KImageEffect::CenteredAutoFit
@ CenteredAutoFit
Center and scale or scale aspect.
Definition: kimageeffect.h:333
KImageEffect::CenterTiled
@ CenterTiled
Center and tile top image on bottom image.
Definition: kimageeffect.h:329
KImageEffect::CenteredMaxpect
@ CenteredMaxpect
Center and scale aspect.
Definition: kimageeffect.h:330
KImageEffect::Scaled
@ Scaled
Scale.
Definition: kimageeffect.h:332
KImageEffect::Centered
@ Centered
Center top image on botton image.
Definition: kimageeffect.h:327
KImageEffect::TiledMaxpect
@ TiledMaxpect
Tile and scale aspect.
Definition: kimageeffect.h:331
KImageEffect::Tiled
@ Tiled
Tile top image on bottom image.
Definition: kimageeffect.h:328
KImageEffect::bumpmap
static TQImage bumpmap(TQImage &img, TQImage &map, double azimuth, double elevation, int depth, int xofs, int yofs, int waterlevel, int ambient, bool compensate, bool invert, BumpmapType type, bool tiled)
A bumpmapping algorithm.
Definition: kimageeffect.cpp:4855
KImageEffect::modulate
static TQImage & modulate(TQImage &image, TQImage &modImage, bool reverse, ModulationType type, int factor, RGBComponent channel)
Modulate the image with a color channel of another image.
Definition: kimageeffect.cpp:937
KImageEffect::desaturate
static TQImage & desaturate(TQImage &image, float desat=0.3)
Desaturate an image evenly.
Definition: kimageeffect.cpp:2176
KImageEffect::flatten
static TQImage & flatten(TQImage &image, const TQColor &ca, const TQColor &cb, int ncols=0)
This recolors a pixmap.
Definition: kimageeffect.cpp:1947
KImageEffect::Lighting
Lighting
This enum provides a lighting direction specification.
Definition: kimageeffect.h:84
KImageEffect::NELite
@ NELite
Lighting from the top right of the image.
Definition: kimageeffect.h:91
KImageEffect::NorthLite
@ NorthLite
Lighting from the top of the image.
Definition: kimageeffect.h:84
KImageEffect::SWLite
@ SWLite
Lighting from the bottom left of the image.
Definition: kimageeffect.h:87
KImageEffect::NWLite
@ NWLite
Lighting from the top left of the image.
Definition: kimageeffect.h:85
KImageEffect::EastLite
@ EastLite
Lighting from the right of the image.
Definition: kimageeffect.h:90
KImageEffect::SELite
@ SELite
Lighting from the bottom right of the image.
Definition: kimageeffect.h:89
KImageEffect::WestLite
@ WestLite
Lighting from the left of the image.
Definition: kimageeffect.h:86
KImageEffect::SouthLite
@ SouthLite
Lighting from the bottom of the image.
Definition: kimageeffect.h:88
KImageEffect::toGray
static TQImage & toGray(TQImage &image, bool fast=false)
Convert an image to grayscale.
Definition: kimageeffect.cpp:2126
KImageEffect::rotate
static TQImage rotate(TQImage &src, RotateDirection r)
Rotates the image by the specified amount.
Definition: kimageeffect.cpp:3323
KImageEffect::spread
static TQImage spread(TQImage &src, unsigned int amount=3)
Randomly displaces pixels.
Definition: kimageeffect.cpp:3444
KImageEffect::solarize
static void solarize(TQImage &img, double factor=50.0)
Produces a 'solarization' effect seen when exposing a photographic film to light during the developme...
Definition: kimageeffect.cpp:3421
KImageEffect::sharpen
static TQImage sharpen(TQImage &src, double radius, double sigma)
Sharpens the pixels in the image using pixel neighborhoods.
Definition: kimageeffect.cpp:4440
KImageEffect::convertToPremultipliedAlpha
static TQImage convertToPremultipliedAlpha(TQImage input)
Convert an image with standard alpha to premultiplied alpha.
Definition: kimageeffect.cpp:4941
KImageEffect::wave
static TQImage wave(TQImage &src, double amplitude=25.0, double frequency=150.0, unsigned int background=0xFFFFFFFF)
Modifies the pixels along a sine wave.
Definition: kimageeffect.cpp:3573
KImageEffect::oilPaint
static TQImage oilPaint(TQImage &src, int radius=3)
This is provided for binary compatability only! Use the above method instead!
Definition: kimageeffect.cpp:3606
KImageEffect::contrast
static TQImage & contrast(TQImage &image, int c)
Fast, but low quality contrast of an image.
Definition: kimageeffect.cpp:2199
KImageEffect::addNoise
static TQImage addNoise(TQImage &src, NoiseType type=GaussianNoise)
Adds noise to an image.
Definition: kimageeffect.cpp:3112
KImageEffect::BumpmapType
BumpmapType
This enum lists possible bumpmapping implementations.
Definition: kimageeffect.h:129
KImageEffect::RGBComponent
RGBComponent
This enum provides a RGB channel specification.
Definition: kimageeffect.h:73
KImageEffect::Gray
@ Gray
Grey channel.
Definition: kimageeffect.h:76
KImageEffect::All
@ All
All channels.
Definition: kimageeffect.h:77
KImageEffect::Green
@ Green
Green channel.
Definition: kimageeffect.h:74
KImageEffect::Red
@ Red
Red channel.
Definition: kimageeffect.h:73
KImageEffect::Blue
@ Blue
Blue channel.
Definition: kimageeffect.h:75
KImageEffect::selectedImage
static TQImage & selectedImage(TQImage &img, const TQColor &col)
Calculate the image for a selected image, for instance a selected icon on the desktop.
Definition: kimageeffect.cpp:2705
KImageEffect::hash
static TQImage & hash(TQImage &image, Lighting lite=NorthLite, unsigned int spacing=0)
Build a hash on any given TQImage.
Definition: kimageeffect.cpp:1866
KImageEffect::normalize
static void normalize(TQImage &img)
Normalises the pixel values to span the full range of color values.
Definition: kimageeffect.cpp:3693
KImageEffect::blur
static TQImage blur(TQImage &src, double radius, double sigma)
Blurs an image by convolving pixel neighborhoods.
Definition: kimageeffect.cpp:4260
KImageEffect::intensity
static TQImage & intensity(TQImage &image, float percent)
Either brighten or dim the image by a specified percent.
Definition: kimageeffect.cpp:654
KImageEffect::unbalancedGradient
static TQImage unbalancedGradient(const TQSize &size, const TQColor &ca, const TQColor &cb, GradientType type, int xfactor=100, int yfactor=100, int ncols=3)
Create an unbalanced gradient.
Definition: kimageeffect.cpp:388
KImageEffect::contrastHSV
static void contrastHSV(TQImage &img, bool sharpen=true)
High quality, expensive HSV contrast.
Definition: kimageeffect.cpp:4618
KImageEffect::blendOnLower
static bool blendOnLower(int x, int y, const TQImage &upper, const TQImage &lower)
Blend an image into another one, using alpha in the expected way and over coordinates x and y with re...
Definition: kimageeffect.cpp:2514
KImageEffect::despeckle
static TQImage despeckle(TQImage &src)
Minimizes speckle noise in the source image using the 8 hull algorithm.
Definition: kimageeffect.cpp:2906
endl
kndbgstream & endl(kndbgstream &s)

tdefx

Skip menu "tdefx"
  • Main Page
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

tdefx

Skip menu "tdefx"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdefx by doxygen 1.9.4
This website is maintained by Timothy Pearson.