• 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 
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12 
13 1. Redistributions of source code must retain the above copyright
14  notice, this list of conditions and the following disclaimer.
15 2. 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 
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS 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 QImage
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)
77 template<class T>
78 inline 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 
85 static 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 
92 template<typename T>
93 static inline void liberateMemory(T **memory)
94 {
95  assert(memory != NULL);
96  if(*memory == NULL) return;
97  free((char*)*memory);
98  *memory=NULL;
99 }
100 
101 struct double_packet
102 {
103  double red;
104  double green;
105  double blue;
106  double alpha;
107 };
108 
109 struct 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 
124 TQImage 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 
388 TQImage 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 
627 namespace {
628 
629 struct KIE4Pack
630 {
631  TQ_UINT16 data[4];
632 };
633 
634 struct 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  */
654 TQImage& 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.tqcolorTable();
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 
846 TQImage& 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.tqcolorTable();
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 //
937 TQImage& 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.tqcolorTable():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
1067 TQImage& 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 #ifdef USE_QT4
1083  if (dst.format() != QImage::Format_ARGB32)
1084  dst = dst.convertToFormat(QImage::Format_ARGB32); // This is needed because Qt4 has multiple variants with a 32 bit depth, and the routines below expect one specific variant (ARGB)
1085 #endif
1086 
1087  int pixels = dst.width() * dst.height();
1088 
1089 #ifdef USE_SSE2_INLINE_ASM
1090  if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
1091  TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 );
1092 
1093  KIE8Pack packedalpha = { { alpha, alpha, alpha, 256,
1094  alpha, alpha, alpha, 256 } };
1095 
1096  TQ_UINT16 red = TQ_UINT16( clr.red() * 256 * opacity );
1097  TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity );
1098  TQ_UINT16 blue = TQ_UINT16( clr.blue() * 256 * opacity );
1099 
1100  KIE8Pack packedcolor = { { blue, green, red, 0,
1101  blue, green, red, 0 } };
1102 
1103  // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending
1104  __asm__ __volatile__(
1105  "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
1106  "movdqu (%0), %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6
1107  "movdqu (%1), %%xmm5\n\t" // Set up color * alpha * 256 in XMM5
1108  : : "r"(&packedalpha), "r"(&packedcolor),
1109  "m"(packedcolor), "m"(packedalpha) );
1110 
1111  TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1112 
1113  // Check how many pixels we need to process to achieve 16 byte alignment
1114  int offset = (16 - (TQ_UINT32( data ) & 0x0f)) / 4;
1115 
1116  // The main loop processes 8 pixels / iteration
1117  int remainder = (pixels - offset) % 8;
1118  pixels -= remainder;
1119 
1120  // Alignment loop
1121  for ( int i = 0; i < offset; i++ ) {
1122  __asm__ __volatile__(
1123  "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1
1124  "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1125  "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256
1126  "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
1127  "psrlw $8, %%xmm0\n\t" // Divide by 256
1128  "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1129  "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image
1130  : : "r"(data), "r"(i) );
1131  }
1132 
1133  // Main loop
1134  for ( int i = offset; i < pixels; i += 8 ) {
1135  __asm__ __volatile(
1136  // Load 8 pixels to XMM registers 1 - 4
1137  "movq (%0,%1,4), %%xmm0\n\t" // Load pixels 1 and 2 to XMM1
1138  "movq 8(%0,%1,4), %%xmm1\n\t" // Load pixels 3 and 4 to XMM2
1139  "movq 16(%0,%1,4), %%xmm2\n\t" // Load pixels 5 and 6 to XMM3
1140  "movq 24(%0,%1,4), %%xmm3\n\t" // Load pixels 7 and 8 to XMM4
1141 
1142  // Prefetch the pixels for next iteration
1143  "prefetchnta 32(%0,%1,4) \n\t"
1144 
1145  // Blend pixels 1 and 2
1146  "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixels
1147  "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixels with (1 - alpha) * 256
1148  "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
1149  "psrlw $8, %%xmm0\n\t" // Divide by 256
1150 
1151  // Blend pixels 3 and 4
1152  "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixels
1153  "pmullw %%xmm6, %%xmm1\n\t" // Multiply the pixels with (1 - alpha) * 256
1154  "paddw %%xmm5, %%xmm1\n\t" // Add color * alpha * 256 to the result
1155  "psrlw $8, %%xmm1\n\t" // Divide by 256
1156 
1157  // Blend pixels 5 and 6
1158  "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the pixels
1159  "pmullw %%xmm6, %%xmm2\n\t" // Multiply the pixels with (1 - alpha) * 256
1160  "paddw %%xmm5, %%xmm2\n\t" // Add color * alpha * 256 to the result
1161  "psrlw $8, %%xmm2\n\t" // Divide by 256
1162 
1163  // Blend pixels 7 and 8
1164  "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the pixels
1165  "pmullw %%xmm6, %%xmm3\n\t" // Multiply the pixels with (1 - alpha) * 256
1166  "paddw %%xmm5, %%xmm3\n\t" // Add color * alpha * 256 to the result
1167  "psrlw $8, %%xmm3\n\t" // Divide by 256
1168 
1169  // Pack the pixels into 2 double quadwords
1170  "packuswb %%xmm1, %%xmm0\n\t" // Pack pixels 1 - 4 to a double qword
1171  "packuswb %%xmm3, %%xmm2\n\t" // Pack pixles 5 - 8 to a double qword
1172 
1173  // Write the pixels back to the image
1174  "movdqa %%xmm0, (%0,%1,4)\n\t" // Store pixels 1 - 4
1175  "movdqa %%xmm2, 16(%0,%1,4)\n\t" // Store pixels 5 - 8
1176  : : "r"(data), "r"(i) );
1177  }
1178 
1179  // Cleanup loop
1180  for ( int i = pixels; i < pixels + remainder; i++ ) {
1181  __asm__ __volatile__(
1182  "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1
1183  "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1184  "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256
1185  "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result
1186  "psrlw $8, %%xmm0\n\t" // Divide by 256
1187  "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1188  "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image
1189  : : "r"(data), "r"(i) );
1190  }
1191  } else
1192 #endif
1193 
1194 #ifdef USE_MMX_INLINE_ASM
1195  if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
1196  TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 );
1197  KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } };
1198 
1199  TQ_UINT16 red = TQ_UINT16( clr.red() * 256 * opacity );
1200  TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity );
1201  TQ_UINT16 blue = TQ_UINT16( clr.blue() * 256 * opacity );
1202 
1203  KIE4Pack packedcolor = { { blue, green, red, 0 } };
1204 
1205  __asm__ __volatile__(
1206  "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking
1207  "movq (%0), %%mm6\n\t" // Set up (1 - alpha) * 256 in MM6
1208  "movq (%1), %%mm5\n\t" // Set up color * alpha * 256 in MM5
1209  : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) );
1210 
1211  TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1212 
1213  // The main loop processes 4 pixels / iteration
1214  int remainder = pixels % 4;
1215  pixels -= remainder;
1216 
1217  // Main loop
1218  for ( int i = 0; i < pixels; i += 4 ) {
1219  __asm__ __volatile__(
1220  // Load 4 pixels to MM registers 1 - 4
1221  "movd (%0,%1,4), %%mm0\n\t" // Load the 1st pixel to MM0
1222  "movd 4(%0,%1,4), %%mm1\n\t" // Load the 2nd pixel to MM1
1223  "movd 8(%0,%1,4), %%mm2\n\t" // Load the 3rd pixel to MM2
1224  "movd 12(%0,%1,4), %%mm3\n\t" // Load the 4th pixel to MM3
1225 
1226  // Blend the first pixel
1227  "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel
1228  "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with (1 - alpha) * 256
1229  "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result
1230  "psrlw $8, %%mm0\n\t" // Divide by 256
1231 
1232  // Blend the second pixel
1233  "punpcklbw %%mm7, %%mm1\n\t" // Unpack the pixel
1234  "pmullw %%mm6, %%mm1\n\t" // Multiply the pixel with (1 - alpha) * 256
1235  "paddw %%mm5, %%mm1\n\t" // Add color * alpha * 256 to the result
1236  "psrlw $8, %%mm1\n\t" // Divide by 256
1237 
1238  // Blend the third pixel
1239  "punpcklbw %%mm7, %%mm2\n\t" // Unpack the pixel
1240  "pmullw %%mm6, %%mm2\n\t" // Multiply the pixel with (1 - alpha) * 256
1241  "paddw %%mm5, %%mm2\n\t" // Add color * alpha * 256 to the result
1242  "psrlw $8, %%mm2\n\t" // Divide by 256
1243 
1244  // Blend the fourth pixel
1245  "punpcklbw %%mm7, %%mm3\n\t" // Unpack the pixel
1246  "pmullw %%mm6, %%mm3\n\t" // Multiply the pixel with (1 - alpha) * 256
1247  "paddw %%mm5, %%mm3\n\t" // Add color * alpha * 256 to the result
1248  "psrlw $8, %%mm3\n\t" // Divide by 256
1249 
1250  // Pack the pixels into 2 quadwords
1251  "packuswb %%mm1, %%mm0\n\t" // Pack pixels 1 and 2 to a qword
1252  "packuswb %%mm3, %%mm2\n\t" // Pack pixels 3 and 4 to a qword
1253 
1254  // Write the pixels back to the image
1255  "movq %%mm0, (%0,%1,4)\n\t" // Store pixels 1 and 2
1256  "movq %%mm2, 8(%0,%1,4)\n\t" // Store pixels 3 and 4
1257  : : "r"(data), "r"(i) );
1258  }
1259 
1260  // Cleanup loop
1261  for ( int i = pixels; i < pixels + remainder; i++ ) {
1262  __asm__ __volatile__(
1263  "movd (%0,%1,4), %%mm0\n\t" // Load one pixel to MM1
1264  "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel
1265  "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with 1 - alpha * 256
1266  "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result
1267  "psrlw $8, %%mm0\n\t" // Divide by 256
1268  "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword
1269  "movd %%mm0, (%0,%1,4)\n\t" // Write the pixel to the image
1270  : : "r"(data), "r"(i) );
1271  }
1272 
1273  // Empty the MMX state
1274  __asm__ __volatile__("emms");
1275  } else
1276 #endif // USE_MMX_INLINE_ASM
1277 
1278  {
1279  int rcol, gcol, bcol;
1280  clr.rgb(&rcol, &gcol, &bcol);
1281 
1282 #ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
1283  unsigned char *data = (unsigned char *)dst.bits() + 1;
1284 #else // BGRA
1285  unsigned char *data = (unsigned char *)dst.bits();
1286 #endif
1287 
1288  for (int i=0; i<pixels; i++)
1289  {
1290 #ifdef WORDS_BIGENDIAN
1291  *data += (unsigned char)((rcol - *data) * opacity);
1292  data++;
1293  *data += (unsigned char)((gcol - *data) * opacity);
1294  data++;
1295  *data += (unsigned char)((bcol - *data) * opacity);
1296  data++;
1297 #else
1298  *data += (unsigned char)((bcol - *data) * opacity);
1299  data++;
1300  *data += (unsigned char)((gcol - *data) * opacity);
1301  data++;
1302  *data += (unsigned char)((rcol - *data) * opacity);
1303  data++;
1304 #endif
1305  data++; // skip alpha
1306  }
1307  }
1308 
1309  return dst;
1310 }
1311 
1312 // Nice and fast direct pixel manipulation
1313 TQImage& KImageEffect::blend(TQImage& src, TQImage& dst, float opacity)
1314 {
1315  if (src.width() <= 0 || src.height() <= 0)
1316  return dst;
1317  if (dst.width() <= 0 || dst.height() <= 0)
1318  return dst;
1319 
1320  if (src.width() != dst.width() || src.height() != dst.height()) {
1321 #ifndef NDEBUG
1322  std::cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n";
1323 #endif
1324  return dst;
1325  }
1326 
1327  if (opacity < 0.0 || opacity > 1.0) {
1328 #ifndef NDEBUG
1329  std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
1330 #endif
1331  return dst;
1332  }
1333 
1334  if (src.depth() != 32) src = src.convertDepth(32);
1335  if (dst.depth() != 32) dst = dst.convertDepth(32);
1336 
1337 #ifdef USE_QT4
1338  if (src.format() != QImage::Format_ARGB32)
1339  src = dst.convertToFormat(QImage::Format_ARGB32); // This is needed because Qt4 has multiple variants with a 32 bit depth, and the routines below expect one specific variant (ARGB)
1340  if (dst.format() != QImage::Format_ARGB32)
1341  dst = dst.convertToFormat(QImage::Format_ARGB32); // This is needed because Qt4 has multiple variants with a 32 bit depth, and the routines below expect one specific variant (ARGB)
1342 #endif
1343 
1344  int pixels = src.width() * src.height();
1345 
1346 #ifdef USE_SSE2_INLINE_ASM
1347  if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
1348  TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 );
1349  KIE8Pack packedalpha = { { alpha, alpha, alpha, 0,
1350  alpha, alpha, alpha, 0 } };
1351 
1352  // Prepare the XMM6 and XMM7 registers for unpacking and blending
1353  __asm__ __volatile__(
1354  "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
1355  "movdqu (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6
1356  : : "r"(&packedalpha), "m"(packedalpha) );
1357 
1358  TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() );
1359  TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1360 
1361  // Check how many pixels we need to process to achieve 16 byte alignment
1362  int offset = (16 - (TQ_UINT32( data2 ) & 0x0f)) / 4;
1363 
1364  // The main loop processes 4 pixels / iteration
1365  int remainder = (pixels - offset) % 4;
1366  pixels -= remainder;
1367 
1368  // Alignment loop
1369  for ( int i = 0; i < offset; i++ ) {
1370  __asm__ __volatile__(
1371  "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1
1372  "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel
1373  "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0
1374  "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1375  "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
1376  "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
1377  "psllw $8, %%xmm1\n\t" // Multiply dst with 256
1378  "paddw %%xmm1, %%xmm0\n\t" // Add dst to result
1379  "psrlw $8, %%xmm0\n\t" // Divide by 256
1380  "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1381  "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image
1382  : : "r"(data1), "r"(data2), "r"(i) );
1383  }
1384 
1385  // Main loop
1386  for ( int i = offset; i < pixels; i += 4 ) {
1387  __asm__ __volatile__(
1388  // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3
1389  "movq (%0,%2,4), %%xmm0\n\t" // Load two src pixels to XMM0
1390  "movq (%1,%2,4), %%xmm1\n\t" // Load two dst pixels to XMM1
1391  "movq 8(%0,%2,4), %%xmm2\n\t" // Load two src pixels to XMM2
1392  "movq 8(%1,%2,4), %%xmm3\n\t" // Load two dst pixels to XMM3
1393 
1394  // Prefetch the pixels for the iteration after the next one
1395  "prefetchnta 32(%0,%2,4) \n\t"
1396  "prefetchnta 32(%1,%2,4) \n\t"
1397 
1398  // Blend the first two pixels
1399  "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the dst pixels
1400  "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the src pixels
1401  "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
1402  "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
1403  "psllw $8, %%xmm1\n\t" // Multiply dst with 256
1404  "paddw %%xmm1, %%xmm0\n\t" // Add dst to the result
1405  "psrlw $8, %%xmm0\n\t" // Divide by 256
1406 
1407  // Blend the next two pixels
1408  "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the dst pixels
1409  "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the src pixels
1410  "psubw %%xmm3, %%xmm2\n\t" // Subtract dst from src
1411  "pmullw %%xmm6, %%xmm2\n\t" // Multiply the result with alpha * 256
1412  "psllw $8, %%xmm3\n\t" // Multiply dst with 256
1413  "paddw %%xmm3, %%xmm2\n\t" // Add dst to the result
1414  "psrlw $8, %%xmm2\n\t" // Divide by 256
1415 
1416  // Write the pixels back to the image
1417  "packuswb %%xmm2, %%xmm0\n\t" // Pack the pixels to a double qword
1418  "movdqa %%xmm0, (%1,%2,4)\n\t" // Store the pixels
1419  : : "r"(data1), "r"(data2), "r"(i) );
1420  }
1421 
1422  // Cleanup loop
1423  for ( int i = pixels; i < pixels + remainder; i++ ) {
1424  __asm__ __volatile__(
1425  "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1
1426  "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel
1427  "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0
1428  "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel
1429  "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src
1430  "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256
1431  "psllw $8, %%xmm1\n\t" // Multiply dst with 256
1432  "paddw %%xmm1, %%xmm0\n\t" // Add dst to result
1433  "psrlw $8, %%xmm0\n\t" // Divide by 256
1434  "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword
1435  "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image
1436  : : "r"(data1), "r"(data2), "r"(i) );
1437  }
1438  } else
1439 #endif // USE_SSE2_INLINE_ASM
1440 
1441 #ifdef USE_MMX_INLINE_ASM
1442  if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
1443  TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 );
1444  KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } };
1445 
1446  // Prepare the MM6 and MM7 registers for blending and unpacking
1447  __asm__ __volatile__(
1448  "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking
1449  "movq (%0), %%mm6\n\t" // Set up alpha * 256 in MM6
1450  : : "r"(&packedalpha), "m"(packedalpha) );
1451 
1452  TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() );
1453  TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() );
1454 
1455  // The main loop processes 2 pixels / iteration
1456  int remainder = pixels % 2;
1457  pixels -= remainder;
1458 
1459  // Main loop
1460  for ( int i = 0; i < pixels; i += 2 ) {
1461  __asm__ __volatile__(
1462  // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3
1463  "movd (%0,%2,4), %%mm0\n\t" // Load the 1st src pixel to MM0
1464  "movd (%1,%2,4), %%mm1\n\t" // Load the 1st dst pixel to MM1
1465  "movd 4(%0,%2,4), %%mm2\n\t" // Load the 2nd src pixel to MM2
1466  "movd 4(%1,%2,4), %%mm3\n\t" // Load the 2nd dst pixel to MM3
1467 
1468  // Blend the first pixel
1469  "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel
1470  "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel
1471  "psubw %%mm1, %%mm0\n\t" // Subtract dst from src
1472  "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256
1473  "psllw $8, %%mm1\n\t" // Multiply dst with 256
1474  "paddw %%mm1, %%mm0\n\t" // Add dst to the result
1475  "psrlw $8, %%mm0\n\t" // Divide by 256
1476 
1477  // Blend the second pixel
1478  "punpcklbw %%mm7, %%mm2\n\t" // Unpack the src pixel
1479  "punpcklbw %%mm7, %%mm3\n\t" // Unpack the dst pixel
1480  "psubw %%mm3, %%mm2\n\t" // Subtract dst from src
1481  "pmullw %%mm6, %%mm2\n\t" // Multiply the result with alpha * 256
1482  "psllw $8, %%mm3\n\t" // Multiply dst with 256
1483  "paddw %%mm3, %%mm2\n\t" // Add dst to the result
1484  "psrlw $8, %%mm2\n\t" // Divide by 256
1485 
1486  // Write the pixels back to the image
1487  "packuswb %%mm2, %%mm0\n\t" // Pack the pixels to a qword
1488  "movq %%mm0, (%1,%2,4)\n\t" // Store the pixels
1489  : : "r"(data1), "r"(data2), "r"(i) );
1490  }
1491 
1492  // Blend the remaining pixel (if there is one)
1493  if ( remainder ) {
1494  __asm__ __volatile__(
1495  "movd (%0), %%mm0\n\t" // Load one src pixel to MM0
1496  "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel
1497  "movd (%1), %%mm1\n\t" // Load one dst pixel to MM1
1498  "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel
1499  "psubw %%mm1, %%mm0\n\t" // Subtract dst from src
1500  "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256
1501  "psllw $8, %%mm1\n\t" // Multiply dst with 256
1502  "paddw %%mm1, %%mm0\n\t" // Add dst to result
1503  "psrlw $8, %%mm0\n\t" // Divide by 256
1504  "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword
1505  "movd %%mm0, (%1)\n\t" // Write the pixel to the image
1506  : : "r"(data1 + pixels), "r"(data2 + pixels) );
1507  }
1508 
1509  // Empty the MMX state
1510  __asm__ __volatile__("emms");
1511  } else
1512 #endif // USE_MMX_INLINE_ASM
1513 
1514  {
1515 #ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
1516  unsigned char *data1 = (unsigned char *)dst.bits() + 1;
1517  unsigned char *data2 = (unsigned char *)src.bits() + 1;
1518 #else // BGRA
1519  unsigned char *data1 = (unsigned char *)dst.bits();
1520  unsigned char *data2 = (unsigned char *)src.bits();
1521 #endif
1522 
1523  for (int i=0; i<pixels; i++)
1524  {
1525 #ifdef WORDS_BIGENDIAN
1526  *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1527  data1++;
1528  *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1529  data1++;
1530  *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1531  data1++;
1532 #else
1533  *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1534  data1++;
1535  *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1536  data1++;
1537  *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
1538  data1++;
1539 #endif
1540  data1++; // skip alpha
1541  data2++;
1542  }
1543  }
1544 
1545  return dst;
1546 }
1547 
1548 
1549 TQImage& KImageEffect::blend(TQImage &image, float initial_intensity,
1550  const TQColor &bgnd, GradientType eff,
1551  bool anti_dir)
1552 {
1553  if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
1554 #ifndef NDEBUG
1555  std::cerr << "WARNING: KImageEffect::blend : invalid image\n";
1556 #endif
1557  return image;
1558  }
1559 
1560  int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
1561  int r, g, b;
1562  int ind;
1563 
1564  unsigned int xi, xf, yi, yf;
1565  unsigned int a;
1566 
1567  // check the boundaries of the initial intesity param
1568  float unaffected = 1;
1569  if (initial_intensity > 1) initial_intensity = 1;
1570  if (initial_intensity < -1) initial_intensity = -1;
1571  if (initial_intensity < 0) {
1572  unaffected = 1. + initial_intensity;
1573  initial_intensity = 0;
1574  }
1575 
1576 
1577  float intensity = initial_intensity;
1578  float var = 1. - initial_intensity;
1579 
1580  if (anti_dir) {
1581  initial_intensity = intensity = 1.;
1582  var = -var;
1583  }
1584 
1585  int x, y;
1586 
1587  unsigned int *data = (unsigned int *)image.bits();
1588 
1589  int image_width = image.width(); //Those can't change
1590  int image_height = image.height();
1591 
1592 
1593  if( eff == VerticalGradient || eff == HorizontalGradient ) {
1594 
1595  // set the image domain to apply the effect to
1596  xi = 0, xf = image_width;
1597  yi = 0, yf = image_height;
1598  if (eff == VerticalGradient) {
1599  if (anti_dir) yf = (int)(image_height * unaffected);
1600  else yi = (int)(image_height * (1 - unaffected));
1601  }
1602  else {
1603  if (anti_dir) xf = (int)(image_width * unaffected);
1604  else xi = (int)(image_height * (1 - unaffected));
1605  }
1606 
1607  var /= (eff == VerticalGradient?yf-yi:xf-xi);
1608 
1609  int ind_base;
1610  for (y = yi; y < (int)yf; y++) {
1611  intensity = eff == VerticalGradient? intensity + var :
1612  initial_intensity;
1613  ind_base = image_width * y ;
1614  for (x = xi; x < (int)xf ; x++) {
1615  if (eff == HorizontalGradient) intensity += var;
1616  ind = x + ind_base;
1617  r = tqRed (data[ind]) + (int)(intensity *
1618  (r_bgnd - tqRed (data[ind])));
1619  g = tqGreen(data[ind]) + (int)(intensity *
1620  (g_bgnd - tqGreen(data[ind])));
1621  b = tqBlue (data[ind]) + (int)(intensity *
1622  (b_bgnd - tqBlue (data[ind])));
1623  if (r > 255) r = 255; if (r < 0 ) r = 0;
1624  if (g > 255) g = 255; if (g < 0 ) g = 0;
1625  if (b > 255) b = 255; if (b < 0 ) b = 0;
1626  a = tqAlpha(data[ind]);
1627  data[ind] = tqRgba(r, g, b, a);
1628  }
1629  }
1630  }
1631  else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) {
1632  float xvar = var / 2 / image_width; // / unaffected;
1633  float yvar = var / 2 / image_height; // / unaffected;
1634  float tmp;
1635 
1636  for (x = 0; x < image_width ; x++) {
1637  tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1);
1638  ind = x;
1639  for (y = 0; y < image_height ; y++) {
1640  intensity = initial_intensity + tmp + yvar * y;
1641 
1642  r = tqRed (data[ind]) + (int)(intensity *
1643  (r_bgnd - tqRed (data[ind])));
1644  g = tqGreen(data[ind]) + (int)(intensity *
1645  (g_bgnd - tqGreen(data[ind])));
1646  b = tqBlue (data[ind]) + (int)(intensity *
1647  (b_bgnd - tqBlue (data[ind])));
1648  if (r > 255) r = 255; if (r < 0 ) r = 0;
1649  if (g > 255) g = 255; if (g < 0 ) g = 0;
1650  if (b > 255) b = 255; if (b < 0 ) b = 0;
1651  a = tqAlpha(data[ind]);
1652  data[ind] = tqRgba(r, g, b, a);
1653 
1654  ind += image_width;
1655  }
1656  }
1657  }
1658 
1659  else if (eff == RectangleGradient || eff == EllipticGradient) {
1660  float xvar;
1661  float yvar;
1662 
1663  for (x = 0; x < image_width / 2 + image_width % 2; x++) {
1664  xvar = var / image_width * (image_width - x*2/unaffected-1);
1665  for (y = 0; y < image_height / 2 + image_height % 2; y++) {
1666  yvar = var / image_height * (image_height - y*2/unaffected -1);
1667 
1668  if (eff == RectangleGradient)
1669  intensity = initial_intensity + TQMAX(xvar, yvar);
1670  else
1671  intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
1672  if (intensity > 1) intensity = 1;
1673  if (intensity < 0) intensity = 0;
1674 
1675  //NW
1676  ind = x + image_width * y ;
1677  r = tqRed (data[ind]) + (int)(intensity *
1678  (r_bgnd - tqRed (data[ind])));
1679  g = tqGreen(data[ind]) + (int)(intensity *
1680  (g_bgnd - tqGreen(data[ind])));
1681  b = tqBlue (data[ind]) + (int)(intensity *
1682  (b_bgnd - tqBlue (data[ind])));
1683  if (r > 255) r = 255; if (r < 0 ) r = 0;
1684  if (g > 255) g = 255; if (g < 0 ) g = 0;
1685  if (b > 255) b = 255; if (b < 0 ) b = 0;
1686  a = tqAlpha(data[ind]);
1687  data[ind] = tqRgba(r, g, b, a);
1688 
1689  //NE
1690  ind = image_width - x - 1 + image_width * y ;
1691  r = tqRed (data[ind]) + (int)(intensity *
1692  (r_bgnd - tqRed (data[ind])));
1693  g = tqGreen(data[ind]) + (int)(intensity *
1694  (g_bgnd - tqGreen(data[ind])));
1695  b = tqBlue (data[ind]) + (int)(intensity *
1696  (b_bgnd - tqBlue (data[ind])));
1697  if (r > 255) r = 255; if (r < 0 ) r = 0;
1698  if (g > 255) g = 255; if (g < 0 ) g = 0;
1699  if (b > 255) b = 255; if (b < 0 ) b = 0;
1700  a = tqAlpha(data[ind]);
1701  data[ind] = tqRgba(r, g, b, a);
1702  }
1703  }
1704 
1705  //CT loop is doubled because of stupid central row/column issue.
1706  // other solution?
1707  for (x = 0; x < image_width / 2; x++) {
1708  xvar = var / image_width * (image_width - x*2/unaffected-1);
1709  for (y = 0; y < image_height / 2; y++) {
1710  yvar = var / image_height * (image_height - y*2/unaffected -1);
1711 
1712  if (eff == RectangleGradient)
1713  intensity = initial_intensity + TQMAX(xvar, yvar);
1714  else
1715  intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
1716  if (intensity > 1) intensity = 1;
1717  if (intensity < 0) intensity = 0;
1718 
1719  //SW
1720  ind = x + image_width * (image_height - y -1) ;
1721  r = tqRed (data[ind]) + (int)(intensity *
1722  (r_bgnd - tqRed (data[ind])));
1723  g = tqGreen(data[ind]) + (int)(intensity *
1724  (g_bgnd - tqGreen(data[ind])));
1725  b = tqBlue (data[ind]) + (int)(intensity *
1726  (b_bgnd - tqBlue (data[ind])));
1727  if (r > 255) r = 255; if (r < 0 ) r = 0;
1728  if (g > 255) g = 255; if (g < 0 ) g = 0;
1729  if (b > 255) b = 255; if (b < 0 ) b = 0;
1730  a = tqAlpha(data[ind]);
1731  data[ind] = tqRgba(r, g, b, a);
1732 
1733  //SE
1734  ind = image_width-x-1 + image_width * (image_height - y - 1) ;
1735  r = tqRed (data[ind]) + (int)(intensity *
1736  (r_bgnd - tqRed (data[ind])));
1737  g = tqGreen(data[ind]) + (int)(intensity *
1738  (g_bgnd - tqGreen(data[ind])));
1739  b = tqBlue (data[ind]) + (int)(intensity *
1740  (b_bgnd - tqBlue (data[ind])));
1741  if (r > 255) r = 255; if (r < 0 ) r = 0;
1742  if (g > 255) g = 255; if (g < 0 ) g = 0;
1743  if (b > 255) b = 255; if (b < 0 ) b = 0;
1744  a = tqAlpha(data[ind]);
1745  data[ind] = tqRgba(r, g, b, a);
1746  }
1747  }
1748  }
1749 #ifndef NDEBUG
1750  else std::cerr << "KImageEffect::blend effect not implemented" << std::endl;
1751 #endif
1752  return image;
1753 }
1754 
1755 // Not very efficient as we create a third big image...
1756 //
1757 TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2,
1758  GradientType gt, int xf, int yf)
1759 {
1760  if (image1.width() == 0 || image1.height() == 0 ||
1761  image2.width() == 0 || image2.height() == 0)
1762  return image1;
1763 
1764  TQImage image3;
1765 
1766  image3 = KImageEffect::unbalancedGradient(image1.size(),
1767  TQColor(0,0,0), TQColor(255,255,255),
1768  gt, xf, yf, 0);
1769 
1770  return blend(image1,image2,image3, Red); // Channel to use is arbitrary
1771 }
1772 
1773 // Blend image2 into image1, using an RBG channel of blendImage
1774 //
1775 TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2,
1776  TQImage &blendImage, RGBComponent channel)
1777 {
1778  if (image1.width() == 0 || image1.height() == 0 ||
1779  image2.width() == 0 || image2.height() == 0 ||
1780  blendImage.width() == 0 || blendImage.height() == 0) {
1781 #ifndef NDEBUG
1782  std::cerr << "KImageEffect::blend effect invalid image" << std::endl;
1783 #endif
1784  return image1;
1785  }
1786 
1787  int r, g, b;
1788  int ind1, ind2, ind3;
1789 
1790  unsigned int x1, x2, x3, y1, y2, y3;
1791  unsigned int a;
1792 
1793  int x, y;
1794 
1795  // for image1 and image2, we only handle depth 32
1796  if (image1.depth()<32) image1 = image1.convertDepth(32);
1797  if (image2.depth()<32) image2 = image2.convertDepth(32);
1798 
1799  // for blendImage, we handle depth 8 and 32
1800  if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
1801 
1802  unsigned int *colorTable3 = (blendImage.depth()==8) ?
1803  blendImage.tqcolorTable():0;
1804 
1805  unsigned int *data1 = (unsigned int *)image1.bits();
1806  unsigned int *data2 = (unsigned int *)image2.bits();
1807  unsigned int *data3 = (unsigned int *)blendImage.bits();
1808  unsigned char *data3b = (unsigned char *)blendImage.bits();
1809  unsigned int color3;
1810 
1811  x1 = image1.width(); y1 = image1.height();
1812  x2 = image2.width(); y2 = image2.height();
1813  x3 = blendImage.width(); y3 = blendImage.height();
1814 
1815  for (y = 0; y < (int)y1; y++) {
1816  ind1 = x1*y;
1817  ind2 = x2*(y%y2);
1818  ind3 = x3*(y%y3);
1819 
1820  x=0;
1821  while(x < (int)x1) {
1822  color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
1823 
1824  a = (channel == Red) ? tqRed(color3) :
1825  (channel == Green) ? tqGreen(color3) :
1826  (channel == Blue) ? tqBlue(color3) : tqGray(color3);
1827 
1828  r = (a*tqRed(data1[ind1]) + (256-a)*tqRed(data2[ind2]))/256;
1829  g = (a*tqGreen(data1[ind1]) + (256-a)*tqGreen(data2[ind2]))/256;
1830  b = (a*tqBlue(data1[ind1]) + (256-a)*tqBlue(data2[ind2]))/256;
1831 
1832  a = tqAlpha(data1[ind1]);
1833  data1[ind1] = tqRgba(r, g, b, a);
1834 
1835  ind1++; ind2++; ind3++; x++;
1836  if ( (x%x2) ==0) ind2 -= x2;
1837  if ( (x%x3) ==0) ind3 -= x3;
1838  }
1839  }
1840  return image1;
1841 }
1842 
1843 
1844 //======================================================================
1845 //
1846 // Hash effects
1847 //
1848 //======================================================================
1849 
1850 unsigned int KImageEffect::lHash(unsigned int c)
1851 {
1852  unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c);
1853  unsigned char nr, ng, nb;
1854  nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
1855  ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
1856  nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
1857 
1858  return tqRgba(nr, ng, nb, a);
1859 }
1860 
1861 
1862 // -----------------------------------------------------------------------------
1863 
1864 unsigned int KImageEffect::uHash(unsigned int c)
1865 {
1866  unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c);
1867  unsigned char nr, ng, nb;
1868  nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
1869  ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
1870  nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
1871 
1872  return tqRgba(nr, ng, nb, a);
1873 }
1874 
1875 
1876 // -----------------------------------------------------------------------------
1877 
1878 TQImage& KImageEffect::hash(TQImage &image, Lighting lite, unsigned int spacing)
1879 {
1880  if (image.width() == 0 || image.height() == 0) {
1881 #ifndef NDEBUG
1882  std::cerr << "KImageEffect::hash effect invalid image" << std::endl;
1883 #endif
1884  return image;
1885  }
1886 
1887  int x, y;
1888  unsigned int *data = (unsigned int *)image.bits();
1889  unsigned int ind;
1890 
1891  //CT no need to do it if not enough space
1892  if ((lite == NorthLite ||
1893  lite == SouthLite)&&
1894  (unsigned)image.height() < 2+spacing) return image;
1895  if ((lite == EastLite ||
1896  lite == WestLite)&&
1897  (unsigned)image.height() < 2+spacing) return image;
1898 
1899  if (lite == NorthLite || lite == SouthLite) {
1900  for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
1901  for (x = 0; x < image.width(); x++) {
1902  ind = x + image.width() * y;
1903  data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
1904 
1905  ind = ind + image.width();
1906  data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
1907  }
1908  }
1909  }
1910 
1911  else if (lite == EastLite || lite == WestLite) {
1912  for (y = 0 ; y < image.height(); y++) {
1913  for (x = 0; x < image.width(); x = x + 2 + spacing) {
1914  ind = x + image.width() * y;
1915  data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
1916 
1917  ind++;
1918  data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
1919  }
1920  }
1921  }
1922 
1923  else if (lite == NWLite || lite == SELite) {
1924  for (y = 0 ; y < image.height(); y++) {
1925  for (x = 0;
1926  x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
1927  x = x + 2 + spacing) {
1928  ind = x + image.width() * y + ((y & 1)? 1 : 0);
1929  data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
1930 
1931  ind++;
1932  data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
1933  }
1934  }
1935  }
1936 
1937  else if (lite == SWLite || lite == NELite) {
1938  for (y = 0 ; y < image.height(); y++) {
1939  for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
1940  ind = x + image.width() * y - ((y & 1)? 1 : 0);
1941  data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
1942 
1943  ind++;
1944  data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
1945  }
1946  }
1947  }
1948 
1949  return image;
1950 }
1951 
1952 
1953 //======================================================================
1954 //
1955 // Flatten effects
1956 //
1957 //======================================================================
1958 
1959 TQImage& KImageEffect::flatten(TQImage &img, const TQColor &ca,
1960  const TQColor &cb, int ncols)
1961 {
1962  if (img.width() == 0 || img.height() == 0)
1963  return img;
1964 
1965  // a bitmap is easy...
1966  if (img.depth() == 1) {
1967  img.setColor(0, ca.rgb());
1968  img.setColor(1, cb.rgb());
1969  return img;
1970  }
1971 
1972  int r1 = ca.red(); int r2 = cb.red();
1973  int g1 = ca.green(); int g2 = cb.green();
1974  int b1 = ca.blue(); int b2 = cb.blue();
1975  int min = 0, max = 255;
1976 
1977  TQRgb col;
1978 
1979  // Get minimum and maximum greylevel.
1980  if (img.numColors()) {
1981  // pseudocolor
1982  for (int i = 0; i < img.numColors(); i++) {
1983  col = img.color(i);
1984  int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
1985  min = TQMIN(min, mean);
1986  max = TQMAX(max, mean);
1987  }
1988  } else {
1989  // truecolor
1990  for (int y=0; y < img.height(); y++)
1991  for (int x=0; x < img.width(); x++) {
1992  col = img.pixel(x, y);
1993  int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
1994  min = TQMIN(min, mean);
1995  max = TQMAX(max, mean);
1996  }
1997  }
1998 
1999  // Conversion factors
2000  float sr = ((float) r2 - r1) / (max - min);
2001  float sg = ((float) g2 - g1) / (max - min);
2002  float sb = ((float) b2 - b1) / (max - min);
2003 
2004 
2005  // Repaint the image
2006  if (img.numColors()) {
2007  for (int i=0; i < img.numColors(); i++) {
2008  col = img.color(i);
2009  int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
2010  int r = (int) (sr * (mean - min) + r1 + 0.5);
2011  int g = (int) (sg * (mean - min) + g1 + 0.5);
2012  int b = (int) (sb * (mean - min) + b1 + 0.5);
2013  img.setColor(i, tqRgba(r, g, b, tqAlpha(col)));
2014  }
2015  } else {
2016  for (int y=0; y < img.height(); y++)
2017  for (int x=0; x < img.width(); x++) {
2018  col = img.pixel(x, y);
2019  int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
2020  int r = (int) (sr * (mean - min) + r1 + 0.5);
2021  int g = (int) (sg * (mean - min) + g1 + 0.5);
2022  int b = (int) (sb * (mean - min) + b1 + 0.5);
2023  img.setPixel(x, y, tqRgba(r, g, b, tqAlpha(col)));
2024  }
2025  }
2026 
2027 
2028  // Dither if necessary
2029  if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
2030  return img;
2031 
2032  if (ncols == 1) ncols++;
2033  if (ncols > 256) ncols = 256;
2034 
2035  TQColor *pal = new TQColor[ncols];
2036  sr = ((float) r2 - r1) / (ncols - 1);
2037  sg = ((float) g2 - g1) / (ncols - 1);
2038  sb = ((float) b2 - b1) / (ncols - 1);
2039 
2040  for (int i=0; i<ncols; i++)
2041  pal[i] = TQColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
2042 
2043  dither(img, pal, ncols);
2044 
2045  delete[] pal;
2046  return img;
2047 }
2048 
2049 
2050 //======================================================================
2051 //
2052 // Fade effects
2053 //
2054 //======================================================================
2055 
2056 TQImage& KImageEffect::fade(TQImage &img, float val, const TQColor &color)
2057 {
2058  if (img.width() == 0 || img.height() == 0)
2059  return img;
2060 
2061  // We don't handle bitmaps
2062  if (img.depth() == 1)
2063  return img;
2064 
2065  unsigned char tbl[256];
2066  for (int i=0; i<256; i++)
2067  tbl[i] = (int) (val * i + 0.5);
2068 
2069  int red = color.red();
2070  int green = color.green();
2071  int blue = color.blue();
2072 
2073  TQRgb col;
2074  int r, g, b, cr, cg, cb;
2075 
2076  if (img.depth() <= 8) {
2077  // pseudo color
2078  for (int i=0; i<img.numColors(); i++) {
2079  col = img.color(i);
2080  cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col);
2081  if (cr > red)
2082  r = cr - tbl[cr - red];
2083  else
2084  r = cr + tbl[red - cr];
2085  if (cg > green)
2086  g = cg - tbl[cg - green];
2087  else
2088  g = cg + tbl[green - cg];
2089  if (cb > blue)
2090  b = cb - tbl[cb - blue];
2091  else
2092  b = cb + tbl[blue - cb];
2093  img.setColor(i, tqRgba(r, g, b, tqAlpha(col)));
2094  }
2095 
2096  } else {
2097  // truecolor
2098  for (int y=0; y<img.height(); y++) {
2099  TQRgb *data = (TQRgb *) img.scanLine(y);
2100  for (int x=0; x<img.width(); x++) {
2101  col = *data;
2102  cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col);
2103  if (cr > red)
2104  r = cr - tbl[cr - red];
2105  else
2106  r = cr + tbl[red - cr];
2107  if (cg > green)
2108  g = cg - tbl[cg - green];
2109  else
2110  g = cg + tbl[green - cg];
2111  if (cb > blue)
2112  b = cb - tbl[cb - blue];
2113  else
2114  b = cb + tbl[blue - cb];
2115  *data++ = tqRgba(r, g, b, tqAlpha(col));
2116  }
2117  }
2118  }
2119 
2120  return img;
2121 }
2122 
2123 //======================================================================
2124 //
2125 // Color effects
2126 //
2127 //======================================================================
2128 
2129 // This code is adapted from code (C) Rik Hemsley <rik@kde.org>
2130 //
2131 // The formula used (r + b + g) /3 is different from the tqGray formula
2132 // used by Qt. This is because our formula is much much faster. If,
2133 // however, it turns out that this is producing sub-optimal images,
2134 // then it will have to change (kurt)
2135 //
2136 // It does produce lower quality grayscale ;-) Use fast == true for the fast
2137 // algorithm, false for the higher quality one (mosfet).
2138 TQImage& KImageEffect::toGray(TQImage &img, bool fast)
2139 {
2140  if (img.width() == 0 || img.height() == 0)
2141  return img;
2142 
2143  if(fast){
2144  if (img.depth() == 32) {
2145  uchar * r(img.bits());
2146  uchar * g(img.bits() + 1);
2147  uchar * b(img.bits() + 2);
2148 
2149  uchar * end(img.bits() + img.numBytes());
2150 
2151  while (r != end) {
2152 
2153  *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
2154 
2155  r += 4;
2156  g += 4;
2157  b += 4;
2158  }
2159  }
2160  else
2161  {
2162  for (int i = 0; i < img.numColors(); i++)
2163  {
2164  uint r = tqRed(img.color(i));
2165  uint g = tqGreen(img.color(i));
2166  uint b = tqBlue(img.color(i));
2167 
2168  uint gray = (((r + g) >> 1) + b) >> 1;
2169  img.setColor(i, tqRgba(gray, gray, gray, tqAlpha(img.color(i))));
2170  }
2171  }
2172  }
2173  else{
2174  int pixels = img.depth() > 8 ? img.width()*img.height() :
2175  img.numColors();
2176  unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
2177  (unsigned int *)img.tqcolorTable();
2178  int val, i;
2179  for(i=0; i < pixels; ++i){
2180  val = tqGray(data[i]);
2181  data[i] = tqRgba(val, val, val, tqAlpha(data[i]));
2182  }
2183  }
2184  return img;
2185 }
2186 
2187 // CT 29Jan2000 - desaturation algorithms
2188 TQImage& KImageEffect::desaturate(TQImage &img, float desat)
2189 {
2190  if (img.width() == 0 || img.height() == 0)
2191  return img;
2192 
2193  if (desat < 0) desat = 0.;
2194  if (desat > 1) desat = 1.;
2195  int pixels = img.depth() > 8 ? img.width()*img.height() :
2196  img.numColors();
2197  unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
2198  (unsigned int *)img.tqcolorTable();
2199  int h, s, v, i;
2200  TQColor clr; // keep constructor out of loop (mosfet)
2201  for(i=0; i < pixels; ++i){
2202  clr.setRgb(data[i]);
2203  clr.hsv(&h, &s, &v);
2204  clr.setHsv(h, (int)(s * (1. - desat)), v);
2205  data[i] = clr.rgb();
2206  }
2207  return img;
2208 }
2209 
2210 // Contrast stuff (mosfet)
2211 TQImage& KImageEffect::contrast(TQImage &img, int c)
2212 {
2213  if (img.width() == 0 || img.height() == 0)
2214  return img;
2215 
2216  if(c > 255)
2217  c = 255;
2218  if(c < -255)
2219  c = -255;
2220  int pixels = img.depth() > 8 ? img.width()*img.height() :
2221  img.numColors();
2222  unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
2223  (unsigned int *)img.tqcolorTable();
2224  int i, r, g, b;
2225  for(i=0; i < pixels; ++i){
2226  r = tqRed(data[i]);
2227  g = tqGreen(data[i]);
2228  b = tqBlue(data[i]);
2229  if(tqGray(data[i]) <= 127){
2230  if(r - c > 0)
2231  r -= c;
2232  else
2233  r = 0;
2234  if(g - c > 0)
2235  g -= c;
2236  else
2237  g = 0;
2238  if(b - c > 0)
2239  b -= c;
2240  else
2241  b = 0;
2242  }
2243  else{
2244  if(r + c <= 255)
2245  r += c;
2246  else
2247  r = 255;
2248  if(g + c <= 255)
2249  g += c;
2250  else
2251  g = 255;
2252  if(b + c <= 255)
2253  b += c;
2254  else
2255  b = 255;
2256  }
2257  data[i] = tqRgba(r, g, b, tqAlpha(data[i]));
2258  }
2259  return(img);
2260 }
2261 
2262 //======================================================================
2263 //
2264 // Dithering effects
2265 //
2266 //======================================================================
2267 
2268 // adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
2269 //
2270 // Floyd-Steinberg dithering
2271 // Ref: Bitmapped Graphics Programming in C++
2272 // Marv Luse, Addison-Wesley Publishing, 1993.
2273 TQImage& KImageEffect::dither(TQImage &img, const TQColor *palette, int size)
2274 {
2275  if (img.width() == 0 || img.height() == 0 ||
2276  palette == 0 || img.depth() <= 8)
2277  return img;
2278 
2279  TQImage dImage( img.width(), img.height(), 8, size );
2280  int i;
2281 
2282  dImage.setNumColors( size );
2283  for ( i = 0; i < size; i++ )
2284  dImage.setColor( i, palette[ i ].rgb() );
2285 
2286  int *rerr1 = new int [ img.width() * 2 ];
2287  int *gerr1 = new int [ img.width() * 2 ];
2288  int *berr1 = new int [ img.width() * 2 ];
2289 
2290  memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
2291  memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
2292  memset( berr1, 0, sizeof( int ) * img.width() * 2 );
2293 
2294  int *rerr2 = rerr1 + img.width();
2295  int *gerr2 = gerr1 + img.width();
2296  int *berr2 = berr1 + img.width();
2297 
2298  for ( int j = 0; j < img.height(); j++ )
2299  {
2300  uint *ip = (uint * )img.scanLine( j );
2301  uchar *dp = dImage.scanLine( j );
2302 
2303  for ( i = 0; i < img.width(); i++ )
2304  {
2305  rerr1[i] = rerr2[i] + tqRed( *ip );
2306  rerr2[i] = 0;
2307  gerr1[i] = gerr2[i] + tqGreen( *ip );
2308  gerr2[i] = 0;
2309  berr1[i] = berr2[i] + tqBlue( *ip );
2310  berr2[i] = 0;
2311  ip++;
2312  }
2313 
2314  *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
2315 
2316  for ( i = 1; i < img.width()-1; i++ )
2317  {
2318  int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
2319  *dp = indx;
2320 
2321  int rerr = rerr1[i];
2322  rerr -= palette[indx].red();
2323  int gerr = gerr1[i];
2324  gerr -= palette[indx].green();
2325  int berr = berr1[i];
2326  berr -= palette[indx].blue();
2327 
2328  // diffuse red error
2329  rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
2330  rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
2331  rerr2[ i ] += ( rerr * 5 ) >> 4;
2332  rerr2[ i+1 ] += ( rerr ) >> 4;
2333 
2334  // diffuse green error
2335  gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
2336  gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
2337  gerr2[ i ] += ( gerr * 5 ) >> 4;
2338  gerr2[ i+1 ] += ( gerr ) >> 4;
2339 
2340  // diffuse red error
2341  berr1[ i+1 ] += ( berr * 7 ) >> 4;
2342  berr2[ i-1 ] += ( berr * 3 ) >> 4;
2343  berr2[ i ] += ( berr * 5 ) >> 4;
2344  berr2[ i+1 ] += ( berr ) >> 4;
2345 
2346  dp++;
2347  }
2348 
2349  *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
2350  }
2351 
2352  delete [] rerr1;
2353  delete [] gerr1;
2354  delete [] berr1;
2355 
2356  img = dImage;
2357  return img;
2358 }
2359 
2360 int KImageEffect::nearestColor( int r, int g, int b, const TQColor *palette, int size )
2361 {
2362  if (palette == 0)
2363  return 0;
2364 
2365  int dr = palette[0].red() - r;
2366  int dg = palette[0].green() - g;
2367  int db = palette[0].blue() - b;
2368 
2369  int minDist = dr*dr + dg*dg + db*db;
2370  int nearest = 0;
2371 
2372  for (int i = 1; i < size; i++ )
2373  {
2374  dr = palette[i].red() - r;
2375  dg = palette[i].green() - g;
2376  db = palette[i].blue() - b;
2377 
2378  int dist = dr*dr + dg*dg + db*db;
2379 
2380  if ( dist < minDist )
2381  {
2382  minDist = dist;
2383  nearest = i;
2384  }
2385  }
2386 
2387  return nearest;
2388 }
2389 
2390 bool KImageEffect::blend(
2391  const TQImage & upper,
2392  const TQImage & lower,
2393  TQImage & output
2394 )
2395 {
2396  if (
2397  upper.width() > lower.width() ||
2398  upper.height() > lower.height() ||
2399  upper.depth() != 32 ||
2400  lower.depth() != 32
2401  )
2402  {
2403 #ifndef NDEBUG
2404  std::cerr << "KImageEffect::blend : Sizes not correct\n" ;
2405 #endif
2406  return false;
2407  }
2408 
2409  output = lower.copy();
2410 
2411  uchar *i, *o;
2412  int a;
2413  int col;
2414  int w = upper.width();
2415  int row(upper.height() - 1);
2416 
2417  do {
2418 
2419  i = const_cast<TQImage&>(upper).scanLine(row);
2420  o = const_cast<TQImage&>(output).scanLine(row);
2421 
2422  col = w << 2;
2423  --col;
2424 
2425  do {
2426 
2427  while (!(a = i[col]) && (col != 3)) {
2428  --col; --col; --col; --col;
2429  }
2430 
2431  --col;
2432  o[col] += ((i[col] - o[col]) * a) >> 8;
2433 
2434  --col;
2435  o[col] += ((i[col] - o[col]) * a) >> 8;
2436 
2437  --col;
2438  o[col] += ((i[col] - o[col]) * a) >> 8;
2439 
2440  } while (col--);
2441 
2442  } while (row--);
2443 
2444  return true;
2445 }
2446 
2447 #if 0
2448 // Not yet...
2449 bool KImageEffect::blend(
2450  const TQImage & upper,
2451  const TQImage & lower,
2452  TQImage & output,
2453  const TQRect & destRect
2454 )
2455 {
2456  output = lower.copy();
2457  return output;
2458 }
2459 
2460 #endif
2461 
2462 bool KImageEffect::blend(
2463  int &x, int &y,
2464  const TQImage & upper,
2465  const TQImage & lower,
2466  TQImage & output
2467 )
2468 {
2469  int cx=0, cy=0, cw=upper.width(), ch=upper.height();
2470 
2471  if ( upper.width() + x > lower.width() ||
2472  upper.height() + y > lower.height() ||
2473  x < 0 || y < 0 ||
2474  upper.depth() != 32 || lower.depth() != 32 )
2475  {
2476  if ( x > lower.width() || y > lower.height() ) return false;
2477  if ( upper.width()<=0 || upper.height() <= 0 ) return false;
2478  if ( lower.width()<=0 || lower.height() <= 0 ) return false;
2479 
2480  if (x<0) {cx=-x; cw+=x; x=0; };
2481  if (cw + x > lower.width()) { cw=lower.width()-x; };
2482  if (y<0) {cy=-y; ch+=y; y=0; };
2483  if (ch + y > lower.height()) { ch=lower.height()-y; };
2484 
2485  if ( cx >= upper.width() || cy >= upper.height() ) return true;
2486  if ( cw <= 0 || ch <= 0 ) return true;
2487  }
2488 
2489  output.create(cw,ch,32);
2490 // output.setAlphaBuffer(true); // I should do some benchmarks to see if
2491  // this is worth the effort
2492 
2493  TQRgb *i, *o, *b;
2494 
2495  int a;
2496  int j,k;
2497  for (j=0; j<ch; j++)
2498  {
2499  b=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ]);
2500  i=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ]);
2501  o=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(output).scanLine(j) [ cw << 2 ]);
2502 
2503  k=cw-1;
2504  --b; --i; --o;
2505  do
2506  {
2507  while ( !(a=tqAlpha(*i)) && k>0 )
2508  {
2509  i--;
2510 // *o=0;
2511  *o=*b;
2512  --o; --b;
2513  k--;
2514  };
2515 // *o=0xFF;
2516  *o = tqRgb(tqRed(*b) + (((tqRed(*i) - tqRed(*b)) * a) >> 8),
2517  tqGreen(*b) + (((tqGreen(*i) - tqGreen(*b)) * a) >> 8),
2518  tqBlue(*b) + (((tqBlue(*i) - tqBlue(*b)) * a) >> 8));
2519  --i; --o; --b;
2520  } while (k--);
2521  }
2522 
2523  return true;
2524 }
2525 
2526 bool KImageEffect::blendOnLower(
2527  int x, int y,
2528  const TQImage & upper,
2529  const TQImage & lower
2530 )
2531 {
2532  int cx=0, cy=0, cw=upper.width(), ch=upper.height();
2533 
2534  if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
2535  if ( x + cw > lower.width() ||
2536  y + ch > lower.height() ||
2537  x < 0 || y < 0 )
2538  {
2539  if ( x > lower.width() || y > lower.height() ) return true;
2540  if ( upper.width()<=0 || upper.height() <= 0 ) return true;
2541  if ( lower.width()<=0 || lower.height() <= 0 ) return true;
2542 
2543  if (x<0) {cx=-x; cw+=x; x=0; };
2544  if (cw + x > lower.width()) { cw=lower.width()-x; };
2545  if (y<0) {cy=-y; ch+=y; y=0; };
2546  if (ch + y > lower.height()) { ch=lower.height()-y; };
2547 
2548  if ( cx >= upper.width() || cy >= upper.height() ) return true;
2549  if ( cw <= 0 || ch <= 0 ) return true;
2550  }
2551 
2552  uchar *i, *b;
2553  int a;
2554  int k;
2555 
2556  for (int j=0; j<ch; j++)
2557  {
2558  b=&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ];
2559  i=&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ];
2560 
2561  k=cw-1;
2562  --b; --i;
2563  do
2564  {
2565 #ifndef WORDS_BIGENDIAN
2566  while ( !(a=*i) && k>0 )
2567 #else
2568  while ( !(a=*(i-3)) && k>0 )
2569 #endif
2570  {
2571  i-=4; b-=4; k--;
2572  };
2573 
2574 #ifndef WORDS_BIGENDIAN
2575  --i; --b;
2576  *b += ( ((*i - *b) * a) >> 8 );
2577  --i; --b;
2578  *b += ( ((*i - *b) * a) >> 8 );
2579  --i; --b;
2580  *b += ( ((*i - *b) * a) >> 8 );
2581  --i; --b;
2582 #else
2583  *b += ( ((*i - *b) * a) >> 8 );
2584  --i; --b;
2585  *b += ( ((*i - *b) * a) >> 8 );
2586  --i; --b;
2587  *b += ( ((*i - *b) * a) >> 8 );
2588  i -= 2; b -= 2;
2589 #endif
2590  } while (k--);
2591  }
2592 
2593  return true;
2594 }
2595 
2596 void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset,
2597  TQImage &lower, const TQRect &lowerRect)
2598 {
2599  // clip rect
2600  TQRect lr = lowerRect & lower.rect();
2601  lr.setWidth( TQMIN(lr.width(), upper.width()-upperOffset.x()) );
2602  lr.setHeight( TQMIN(lr.height(), upper.height()-upperOffset.y()) );
2603  if ( !lr.isValid() ) return;
2604 
2605  // blend
2606  for (int y = 0; y < lr.height(); y++) {
2607  for (int x = 0; x < lr.width(); x++) {
2608  TQRgb *b = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(TQRgb));
2609  TQRgb *d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(TQRgb));
2610  int a = tqAlpha(*d);
2611  *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
2612  tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
2613  tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
2614  }
2615  }
2616 }
2617 
2618 void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset,
2619  TQImage &lower, const TQRect &lowerRect, float opacity)
2620 {
2621  // clip rect
2622  TQRect lr = lowerRect & lower.rect();
2623  lr.setWidth( TQMIN(lr.width(), upper.width()-upperOffset.x()) );
2624  lr.setHeight( TQMIN(lr.height(), upper.height()-upperOffset.y()) );
2625  if ( !lr.isValid() ) return;
2626 
2627  // blend
2628  for (int y = 0; y < lr.height(); y++) {
2629  for (int x = 0; x < lr.width(); x++) {
2630  TQRgb *b = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(TQRgb));
2631  TQRgb *d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(TQRgb));
2632  int a = tqRound(opacity * tqAlpha(*d));
2633  *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
2634  tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
2635  tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
2636  }
2637  }
2638 }
2639 
2640 TQRect KImageEffect::computeDestinationRect(const TQSize &lowerSize,
2641  Disposition disposition, TQImage &upper)
2642 {
2643  int w = lowerSize.width();
2644  int h = lowerSize.height();
2645  int ww = upper.width();
2646  int wh = upper.height();
2647  TQRect d;
2648 
2649  switch (disposition) {
2650  case NoImage:
2651  break;
2652  case Centered:
2653  d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
2654  break;
2655  case Tiled:
2656  d.setRect(0, 0, w, h);
2657  break;
2658  case CenterTiled:
2659  d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh,
2660  w-1, h-1);
2661  break;
2662  case Scaled:
2663  upper = upper.smoothScale(w, h);
2664  d.setRect(0, 0, w, h);
2665  break;
2666  case CenteredAutoFit:
2667  if( ww <= w && wh <= h ) {
2668  d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered
2669  break;
2670  }
2671  // fall through
2672  case CenteredMaxpect: {
2673  double sx = (double) w / ww;
2674  double sy = (double) h / wh;
2675  if (sx > sy) {
2676  ww = (int)(sy * ww);
2677  wh = h;
2678  } else {
2679  wh = (int)(sx * wh);
2680  ww = w;
2681  }
2682  upper = upper.smoothScale(ww, wh);
2683  d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
2684  break;
2685  }
2686  case TiledMaxpect: {
2687  double sx = (double) w / ww;
2688  double sy = (double) h / wh;
2689  if (sx > sy) {
2690  ww = (int)(sy * ww);
2691  wh = h;
2692  } else {
2693  wh = (int)(sx * wh);
2694  ww = w;
2695  }
2696  upper = upper.smoothScale(ww, wh);
2697  d.setRect(0, 0, w, h);
2698  break;
2699  }
2700  }
2701 
2702  return d;
2703 }
2704 
2705 void KImageEffect::blendOnLower(TQImage &upper, TQImage &lower,
2706  Disposition disposition, float opacity)
2707 {
2708  TQRect r = computeDestinationRect(lower.size(), disposition, upper);
2709  for (int y = r.top(); y<r.bottom(); y += upper.height())
2710  for (int x = r.left(); x<r.right(); x += upper.width())
2711  blendOnLower(upper, TQPoint(-TQMIN(x, 0), -TQMIN(y, 0)),
2712  lower, TQRect(x, y, upper.width(), upper.height()), opacity);
2713 }
2714 
2715 
2716 // For selected icons
2717 TQImage& KImageEffect::selectedImage( TQImage &img, const TQColor &col )
2718 {
2719  return blend( col, img, 0.5);
2720 }
2721 
2722 //
2723 // ===================================================================
2724 // Effects originally ported from ImageMagick for PixiePlus, plus a few
2725 // new ones. (mosfet 05/26/2003)
2726 // ===================================================================
2727 //
2728 /*
2729  Portions of this software are based on ImageMagick. Such portions are clearly
2730 marked as being ported from ImageMagick. ImageMagick is copyrighted under the
2731 following conditions:
2732 
2733 Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to
2734 making software imaging solutions freely available.
2735 
2736 Permission is hereby granted, free of charge, to any person obtaining a copy
2737 of this software and associated documentation files ("ImageMagick"), to deal
2738 in ImageMagick without restriction, including without limitation the rights
2739 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
2740 copies of ImageMagick, and to permit persons to whom the ImageMagick is
2741 furnished to do so, subject to the following conditions:
2742 
2743 The above copyright notice and this permission notice shall be included in all
2744 copies or substantial portions of ImageMagick.
2745 
2746 The software is provided "as is", without warranty of any kind, express or
2747 implied, including but not limited to the warranties of merchantability,
2748 fitness for a particular purpose and noninfringement. In no event shall
2749 ImageMagick Studio be liable for any claim, damages or other liability,
2750 whether in an action of contract, tort or otherwise, arising from, out of or
2751 in connection with ImageMagick or the use or other dealings in ImageMagick.
2752 
2753 Except as contained in this notice, the name of the ImageMagick Studio shall
2754 not be used in advertising or otherwise to promote the sale, use or other
2755 dealings in ImageMagick without prior written authorization from the
2756 ImageMagick Studio.
2757 */
2758 
2759 TQImage KImageEffect::sample(TQImage &src, int w, int h)
2760 {
2761  if(w == src.width() && h == src.height())
2762  return(src);
2763 
2764  int depth = src.depth();
2765  TQImage dest(w, h, depth, depth <= 8 ? src.numColors() : 0,
2766  depth == 1 ? TQImage::LittleEndian : TQImage::IgnoreEndian);
2767  int *x_offset = (int *)malloc(w*sizeof(int));
2768  int *y_offset = (int *)malloc(h*sizeof(int));
2769  if(!x_offset || !y_offset){
2770 #ifndef NDEBUG
2771  tqWarning("KImageEffect::sample(): Unable to allocate pixel buffer");
2772 #endif
2773  free(x_offset);
2774  free(y_offset);
2775  return(src);
2776  }
2777 
2778  // init pixel offsets
2779  for(int x=0; x < w; ++x)
2780  x_offset[x] = (int)(x*src.width()/((double)w));
2781  for(int y=0; y < h; ++y)
2782  y_offset[y] = (int)(y*src.height()/((double)h));
2783 
2784  if(depth > 8){ // DirectClass source image
2785  for(int y=0; y < h; ++y){
2786  unsigned int *destData = (unsigned int *)dest.scanLine(y);
2787  unsigned int *srcData = (unsigned int *)src.scanLine(y_offset[y]);
2788  for(int x=0; x < w; ++x)
2789  destData[x] = srcData[x_offset[x]];
2790  }
2791  }
2792  else if(depth == 1) {
2793  int r = src.bitOrder() == TQImage::LittleEndian;
2794  memcpy(dest.tqcolorTable(), src.tqcolorTable(), src.numColors()*sizeof(TQRgb));
2795  for(int y=0; y < h; ++y){
2796  unsigned char *destData = dest.scanLine(y);
2797  unsigned char *srcData = src.scanLine(y_offset[y]);
2798  for(int x=0; x < w; ++x){
2799  int k = x_offset[x];
2800  int l = r ? (k & 7) : (7 - (k&7));
2801  if(srcData[k >> 3] & (1 << l))
2802  destData[x >> 3] |= 1 << (x & 7);
2803  else
2804  destData[x >> 3] &= ~(1 << (x & 7));
2805  }
2806  }
2807  }
2808  else{ // PseudoClass source image
2809  memcpy(dest.tqcolorTable(), src.tqcolorTable(), src.numColors()*sizeof(TQRgb));
2810  for(int y=0; y < h; ++y){
2811  unsigned char *destData = dest.scanLine(y);
2812  unsigned char *srcData = src.scanLine(y_offset[y]);
2813  for(int x=0; x < w; ++x)
2814  destData[x] = srcData[x_offset[x]];
2815  }
2816  }
2817  free(x_offset);
2818  free(y_offset);
2819  return(dest);
2820 }
2821 
2822 void KImageEffect::threshold(TQImage &img, unsigned int threshold)
2823 {
2824  int i, count;
2825  unsigned int *data;
2826  if(img.depth() > 8){ // DirectClass
2827  count = img.width()*img.height();
2828  data = (unsigned int *)img.bits();
2829  }
2830  else{ // PsudeoClass
2831  count = img.numColors();
2832  data = (unsigned int *)img.tqcolorTable();
2833  }
2834  for(i=0; i < count; ++i)
2835  data[i] = intensityValue(data[i]) < threshold ? QColor(Qt::black).rgb() : QColor(Qt::white).rgb();
2836 }
2837 
2838 void KImageEffect::hull(const int x_offset, const int y_offset,
2839  const int polarity, const int columns,
2840  const int rows,
2841  unsigned int *f, unsigned int *g)
2842 {
2843  int x, y;
2844 
2845  unsigned int *p, *q, *r, *s;
2846  unsigned int v;
2847  if(f == NULL || g == NULL)
2848  return;
2849  p=f+(columns+2);
2850  q=g+(columns+2);
2851  r=p+(y_offset*(columns+2)+x_offset);
2852  for (y=0; y < rows; y++){
2853  p++;
2854  q++;
2855  r++;
2856  if(polarity > 0)
2857  for (x=0; x < columns; x++){
2858  v=(*p);
2859  if (*r > v)
2860  v++;
2861  *q=v;
2862  p++;
2863  q++;
2864  r++;
2865  }
2866  else
2867  for(x=0; x < columns; x++){
2868  v=(*p);
2869  if (v > (unsigned int) (*r+1))
2870  v--;
2871  *q=v;
2872  p++;
2873  q++;
2874  r++;
2875  }
2876  p++;
2877  q++;
2878  r++;
2879  }
2880  p=f+(columns+2);
2881  q=g+(columns+2);
2882  r=q+(y_offset*(columns+2)+x_offset);
2883  s=q-(y_offset*(columns+2)+x_offset);
2884  for(y=0; y < rows; y++){
2885  p++;
2886  q++;
2887  r++;
2888  s++;
2889  if(polarity > 0)
2890  for(x=0; x < (int) columns; x++){
2891  v=(*q);
2892  if (((unsigned int) (*s+1) > v) && (*r > v))
2893  v++;
2894  *p=v;
2895  p++;
2896  q++;
2897  r++;
2898  s++;
2899  }
2900  else
2901  for (x=0; x < columns; x++){
2902  v=(*q);
2903  if (((unsigned int) (*s+1) < v) && (*r < v))
2904  v--;
2905  *p=v;
2906  p++;
2907  q++;
2908  r++;
2909  s++;
2910  }
2911  p++;
2912  q++;
2913  r++;
2914  s++;
2915  }
2916 }
2917 
2918 TQImage KImageEffect::despeckle(TQImage &src)
2919 {
2920  int i, j, x, y;
2921  unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
2922  *alpha_channel;
2923  int packets;
2924  static const int
2925  X[4]= {0, 1, 1,-1},
2926  Y[4]= {1, 0, 1, 1};
2927 
2928  unsigned int *destData;
2929  TQImage dest(src.width(), src.height(), 32);
2930 
2931  packets = (src.width()+2)*(src.height()+2);
2932  red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2933  green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2934  blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2935  alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2936  buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
2937  if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
2938  !buffer){
2939  free(red_channel);
2940  free(green_channel);
2941  free(blue_channel);
2942  free(alpha_channel);
2943  free(buffer);
2944  return(src);
2945  }
2946 
2947  // copy image pixels to color component buffers
2948  j = src.width()+2;
2949  if(src.depth() > 8){ // DirectClass source image
2950  unsigned int *srcData;
2951  for(y=0; y < src.height(); ++y){
2952  srcData = (unsigned int *)src.scanLine(y);
2953  ++j;
2954  for(x=0; x < src.width(); ++x){
2955  red_channel[j] = tqRed(srcData[x]);
2956  green_channel[j] = tqGreen(srcData[x]);
2957  blue_channel[j] = tqBlue(srcData[x]);
2958  alpha_channel[j] = tqAlpha(srcData[x]);
2959  ++j;
2960  }
2961  ++j;
2962  }
2963  }
2964  else{ // PsudeoClass source image
2965  unsigned char *srcData;
2966  unsigned int *cTable = src.tqcolorTable();
2967  unsigned int pixel;
2968  for(y=0; y < src.height(); ++y){
2969  srcData = (unsigned char *)src.scanLine(y);
2970  ++j;
2971  for(x=0; x < src.width(); ++x){
2972  pixel = *(cTable+srcData[x]);
2973  red_channel[j] = tqRed(pixel);
2974  green_channel[j] = tqGreen(pixel);
2975  blue_channel[j] = tqBlue(pixel);
2976  alpha_channel[j] = tqAlpha(pixel);
2977  ++j;
2978  }
2979  ++j;
2980  }
2981  }
2982  // reduce speckle in red channel
2983  for(i=0; i < 4; i++){
2984  hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
2985  hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
2986  hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
2987  hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
2988  }
2989  // reduce speckle in green channel
2990  for (i=0; i < packets; i++)
2991  buffer[i]=0;
2992  for (i=0; i < 4; i++){
2993  hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
2994  hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
2995  hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
2996  hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
2997  }
2998  // reduce speckle in blue channel
2999  for (i=0; i < packets; i++)
3000  buffer[i]=0;
3001  for (i=0; i < 4; i++){
3002  hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
3003  hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
3004  hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
3005  hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
3006  }
3007  // copy color component buffers to despeckled image
3008  j = dest.width()+2;
3009  for(y=0; y < dest.height(); ++y)
3010  {
3011  destData = (unsigned int *)dest.scanLine(y);
3012  ++j;
3013  for (x=0; x < dest.width(); ++x)
3014  {
3015  destData[x] = tqRgba(red_channel[j], green_channel[j],
3016  blue_channel[j], alpha_channel[j]);
3017  ++j;
3018  }
3019  ++j;
3020  }
3021  free(buffer);
3022  free(red_channel);
3023  free(green_channel);
3024  free(blue_channel);
3025  free(alpha_channel);
3026  return(dest);
3027 }
3028 
3029 unsigned int KImageEffect::generateNoise(unsigned int pixel,
3030  NoiseType noise_type)
3031 {
3032 #define NoiseEpsilon 1.0e-5
3033 #define NoiseMask 0x7fff
3034 #define SigmaUniform 4.0
3035 #define SigmaGaussian 4.0
3036 #define SigmaImpulse 0.10
3037 #define SigmaLaplacian 10.0
3038 #define SigmaMultiplicativeGaussian 0.5
3039 #define SigmaPoisson 0.05
3040 #define TauGaussian 20.0
3041 
3042  double alpha, beta, sigma, value;
3043  alpha=(double) (rand() & NoiseMask)/NoiseMask;
3044  if (alpha == 0.0)
3045  alpha=1.0;
3046  switch(noise_type){
3047  case UniformNoise:
3048  default:
3049  {
3050  value=(double) pixel+SigmaUniform*(alpha-0.5);
3051  break;
3052  }
3053  case GaussianNoise:
3054  {
3055  double tau;
3056 
3057  beta=(double) (rand() & NoiseMask)/NoiseMask;
3058  sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
3059  tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
3060  value=(double) pixel+
3061  (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
3062  break;
3063  }
3064  case MultiplicativeGaussianNoise:
3065  {
3066  if (alpha <= NoiseEpsilon)
3067  sigma=MaxRGB;
3068  else
3069  sigma=sqrt(-2.0*log(alpha));
3070  beta=(rand() & NoiseMask)/NoiseMask;
3071  value=(double) pixel+
3072  pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
3073  break;
3074  }
3075  case ImpulseNoise:
3076  {
3077  if (alpha < (SigmaImpulse/2.0))
3078  value=0;
3079  else
3080  if (alpha >= (1.0-(SigmaImpulse/2.0)))
3081  value=MaxRGB;
3082  else
3083  value=pixel;
3084  break;
3085  }
3086  case LaplacianNoise:
3087  {
3088  if (alpha <= 0.5)
3089  {
3090  if (alpha <= NoiseEpsilon)
3091  value=(double) pixel-MaxRGB;
3092  else
3093  value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
3094  break;
3095  }
3096  beta=1.0-alpha;
3097  if (beta <= (0.5*NoiseEpsilon))
3098  value=(double) pixel+MaxRGB;
3099  else
3100  value=(double) pixel-SigmaLaplacian*log(2.0*beta);
3101  break;
3102  }
3103  case PoissonNoise:
3104  {
3105  int
3106  i;
3107 
3108  for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
3109  {
3110  beta=(double) (rand() & NoiseMask)/NoiseMask;
3111  alpha=alpha*beta;
3112  }
3113  value=i/SigmaPoisson;
3114  break;
3115  }
3116  }
3117  if(value < 0.0)
3118  return(0);
3119  if(value > MaxRGB)
3120  return(MaxRGB);
3121  return((unsigned int) (value+0.5));
3122 }
3123 
3124 TQImage KImageEffect::addNoise(TQImage &src, NoiseType noise_type)
3125 {
3126  int x, y;
3127  TQImage dest(src.width(), src.height(), 32);
3128  unsigned int *destData;
3129 
3130  if(src.depth() > 8){ // DirectClass source image
3131  unsigned int *srcData;
3132  for(y=0; y < src.height(); ++y){
3133  srcData = (unsigned int *)src.scanLine(y);
3134  destData = (unsigned int *)dest.scanLine(y);
3135  for(x=0; x < src.width(); ++x){
3136  destData[x] = tqRgba(generateNoise(tqRed(srcData[x]), noise_type),
3137  generateNoise(tqGreen(srcData[x]), noise_type),
3138  generateNoise(tqBlue(srcData[x]), noise_type),
3139  tqAlpha(srcData[x]));
3140  }
3141  }
3142  }
3143  else{ // PsudeoClass source image
3144  unsigned char *srcData;
3145  unsigned int *cTable = src.tqcolorTable();
3146  unsigned int pixel;
3147  for(y=0; y < src.height(); ++y){
3148  srcData = (unsigned char *)src.scanLine(y);
3149  destData = (unsigned int *)dest.scanLine(y);
3150  for(x=0; x < src.width(); ++x){
3151  pixel = *(cTable+srcData[x]);
3152  destData[x] = tqRgba(generateNoise(tqRed(pixel), noise_type),
3153  generateNoise(tqGreen(pixel), noise_type),
3154  generateNoise(tqBlue(pixel), noise_type),
3155  tqAlpha(pixel));
3156  }
3157  }
3158 
3159  }
3160  return(dest);
3161 }
3162 
3163 unsigned int KImageEffect::interpolateColor(TQImage *image, double x_offset,
3164  double y_offset,
3165  unsigned int background)
3166 {
3167  double alpha, beta;
3168  unsigned int p, q, r, s;
3169  int x, y;
3170 
3171  x = (int)x_offset;
3172  y = (int)y_offset;
3173  if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
3174  return(background);
3175  if(image->depth() > 8){
3176  if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
3177  unsigned int *t = (unsigned int *)image->scanLine(y);
3178  p = t[x];
3179  q = t[x+1];
3180  r = t[x+image->width()];
3181  s = t[x+image->width()+1];
3182  }
3183  else{
3184  unsigned int *t = (unsigned int *)image->scanLine(y);
3185  p = background;
3186  if((x >= 0) && (y >= 0)){
3187  p = t[x];
3188  }
3189  q = background;
3190  if(((x+1) < image->width()) && (y >= 0)){
3191  q = t[x+1];
3192  }
3193  r = background;
3194  if((x >= 0) && ((y+1) < image->height())){
3195  t = (unsigned int *)image->scanLine(y+1);
3196  r = t[x+image->width()];
3197  }
3198  s = background;
3199  if(((x+1) < image->width()) && ((y+1) < image->height())){
3200  t = (unsigned int *)image->scanLine(y+1);
3201  s = t[x+image->width()+1];
3202  }
3203 
3204  }
3205  }
3206  else{
3207  unsigned int *colorTable = (unsigned int *)image->tqcolorTable();
3208  if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
3209  unsigned char *t;
3210  t = (unsigned char *)image->scanLine(y);
3211  p = *(colorTable+t[x]);
3212  q = *(colorTable+t[x+1]);
3213  t = (unsigned char *)image->scanLine(y+1);
3214  r = *(colorTable+t[x]);
3215  s = *(colorTable+t[x+1]);
3216  }
3217  else{
3218  unsigned char *t;
3219  p = background;
3220  if((x >= 0) && (y >= 0)){
3221  t = (unsigned char *)image->scanLine(y);
3222  p = *(colorTable+t[x]);
3223  }
3224  q = background;
3225  if(((x+1) < image->width()) && (y >= 0)){
3226  t = (unsigned char *)image->scanLine(y);
3227  q = *(colorTable+t[x+1]);
3228  }
3229  r = background;
3230  if((x >= 0) && ((y+1) < image->height())){
3231  t = (unsigned char *)image->scanLine(y+1);
3232  r = *(colorTable+t[x]);
3233  }
3234  s = background;
3235  if(((x+1) < image->width()) && ((y+1) < image->height())){
3236  t = (unsigned char *)image->scanLine(y+1);
3237  s = *(colorTable+t[x+1]);
3238  }
3239 
3240  }
3241 
3242  }
3243  x_offset -= floor(x_offset);
3244  y_offset -= floor(y_offset);
3245  alpha = 1.0-x_offset;
3246  beta = 1.0-y_offset;
3247 
3248  return(tqRgba((unsigned char)(beta*(alpha*tqRed(p)+x_offset*tqRed(q))+y_offset*(alpha*tqRed(r)+x_offset*tqRed(s))),
3249  (unsigned char)(beta*(alpha*tqGreen(p)+x_offset*tqGreen(q))+y_offset*(alpha*tqGreen(r)+x_offset*tqGreen(s))),
3250  (unsigned char)(beta*(alpha*tqBlue(p)+x_offset*tqBlue(q))+y_offset*(alpha*tqBlue(r)+x_offset*tqBlue(s))),
3251  (unsigned char)(beta*(alpha*tqAlpha(p)+x_offset*tqAlpha(q))+y_offset*(alpha*tqAlpha(r)+x_offset*tqAlpha(s)))));
3252 }
3253 
3254 TQImage KImageEffect::implode(TQImage &src, double factor,
3255  unsigned int background)
3256 {
3257  double amount, distance, radius;
3258  double x_center, x_distance, x_scale;
3259  double y_center, y_distance, y_scale;
3260  unsigned int *destData;
3261  int x, y;
3262 
3263  TQImage dest(src.width(), src.height(), 32);
3264 
3265  // compute scaling factor
3266  x_scale = 1.0;
3267  y_scale = 1.0;
3268  x_center = (double)0.5*src.width();
3269  y_center = (double)0.5*src.height();
3270  radius=x_center;
3271  if(src.width() > src.height())
3272  y_scale = (double)src.width()/src.height();
3273  else if(src.width() < src.height()){
3274  x_scale = (double) src.height()/src.width();
3275  radius = y_center;
3276  }
3277  amount=factor/10.0;
3278  if(amount >= 0)
3279  amount/=10.0;
3280  if(src.depth() > 8){ // DirectClass source image
3281  unsigned int *srcData;
3282  for(y=0; y < src.height(); ++y){
3283  srcData = (unsigned int *)src.scanLine(y);
3284  destData = (unsigned int *)dest.scanLine(y);
3285  y_distance=y_scale*(y-y_center);
3286  for(x=0; x < src.width(); ++x){
3287  destData[x] = srcData[x];
3288  x_distance = x_scale*(x-x_center);
3289  distance= x_distance*x_distance+y_distance*y_distance;
3290  if(distance < (radius*radius)){
3291  double factor;
3292  // Implode the pixel.
3293  factor=1.0;
3294  if(distance > 0.0)
3295  factor=
3296  pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
3297  destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
3298  factor*y_distance/y_scale+y_center,
3299  background);
3300  }
3301  }
3302  }
3303  }
3304  else{ // PsudeoClass source image
3305  unsigned char *srcData;
3306  unsigned char idx;
3307  unsigned int *cTable = src.tqcolorTable();
3308  for(y=0; y < src.height(); ++y){
3309  srcData = (unsigned char *)src.scanLine(y);
3310  destData = (unsigned int *)dest.scanLine(y);
3311  y_distance=y_scale*(y-y_center);
3312  for(x=0; x < src.width(); ++x){
3313  idx = srcData[x];
3314  destData[x] = cTable[idx];
3315  x_distance = x_scale*(x-x_center);
3316  distance= x_distance*x_distance+y_distance*y_distance;
3317  if(distance < (radius*radius)){
3318  double factor;
3319  // Implode the pixel.
3320  factor=1.0;
3321  if(distance > 0.0)
3322  factor=
3323  pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
3324  destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
3325  factor*y_distance/y_scale+y_center,
3326  background);
3327  }
3328  }
3329  }
3330 
3331  }
3332  return(dest);
3333 }
3334 
3335 TQImage KImageEffect::rotate(TQImage &img, RotateDirection r)
3336 {
3337  TQImage dest;
3338  int x, y;
3339  if(img.depth() > 8){
3340  unsigned int *srcData, *destData;
3341  switch(r){
3342  case Rotate90:
3343  dest.create(img.height(), img.width(), img.depth());
3344  for(y=0; y < img.height(); ++y){
3345  srcData = (unsigned int *)img.scanLine(y);
3346  for(x=0; x < img.width(); ++x){
3347  destData = (unsigned int *)dest.scanLine(x);
3348  destData[img.height()-y-1] = srcData[x];
3349  }
3350  }
3351  break;
3352  case Rotate180:
3353  dest.create(img.width(), img.height(), img.depth());
3354  for(y=0; y < img.height(); ++y){
3355  srcData = (unsigned int *)img.scanLine(y);
3356  destData = (unsigned int *)dest.scanLine(img.height()-y-1);
3357  for(x=0; x < img.width(); ++x)
3358  destData[img.width()-x-1] = srcData[x];
3359  }
3360  break;
3361  case Rotate270:
3362  dest.create(img.height(), img.width(), img.depth());
3363  for(y=0; y < img.height(); ++y){
3364  srcData = (unsigned int *)img.scanLine(y);
3365  for(x=0; x < img.width(); ++x){
3366  destData = (unsigned int *)dest.scanLine(img.width()-x-1);
3367  destData[y] = srcData[x];
3368  }
3369  }
3370  break;
3371  default:
3372  dest = img;
3373  break;
3374  }
3375  }
3376  else{
3377  unsigned char *srcData, *destData;
3378  unsigned int *srcTable, *destTable;
3379  switch(r){
3380  case Rotate90:
3381  dest.create(img.height(), img.width(), img.depth());
3382  dest.setNumColors(img.numColors());
3383  srcTable = (unsigned int *)img.tqcolorTable();
3384  destTable = (unsigned int *)dest.tqcolorTable();
3385  for(x=0; x < img.numColors(); ++x)
3386  destTable[x] = srcTable[x];
3387  for(y=0; y < img.height(); ++y){
3388  srcData = (unsigned char *)img.scanLine(y);
3389  for(x=0; x < img.width(); ++x){
3390  destData = (unsigned char *)dest.scanLine(x);
3391  destData[img.height()-y-1] = srcData[x];
3392  }
3393  }
3394  break;
3395  case Rotate180:
3396  dest.create(img.width(), img.height(), img.depth());
3397  dest.setNumColors(img.numColors());
3398  srcTable = (unsigned int *)img.tqcolorTable();
3399  destTable = (unsigned int *)dest.tqcolorTable();
3400  for(x=0; x < img.numColors(); ++x)
3401  destTable[x] = srcTable[x];
3402  for(y=0; y < img.height(); ++y){
3403  srcData = (unsigned char *)img.scanLine(y);
3404  destData = (unsigned char *)dest.scanLine(img.height()-y-1);
3405  for(x=0; x < img.width(); ++x)
3406  destData[img.width()-x-1] = srcData[x];
3407  }
3408  break;
3409  case Rotate270:
3410  dest.create(img.height(), img.width(), img.depth());
3411  dest.setNumColors(img.numColors());
3412  srcTable = (unsigned int *)img.tqcolorTable();
3413  destTable = (unsigned int *)dest.tqcolorTable();
3414  for(x=0; x < img.numColors(); ++x)
3415  destTable[x] = srcTable[x];
3416  for(y=0; y < img.height(); ++y){
3417  srcData = (unsigned char *)img.scanLine(y);
3418  for(x=0; x < img.width(); ++x){
3419  destData = (unsigned char *)dest.scanLine(img.width()-x-1);
3420  destData[y] = srcData[x];
3421  }
3422  }
3423  break;
3424  default:
3425  dest = img;
3426  break;
3427  }
3428 
3429  }
3430  return(dest);
3431 }
3432 
3433 void KImageEffect::solarize(TQImage &img, double factor)
3434 {
3435  int i, count;
3436  int threshold;
3437  unsigned int *data;
3438 
3439  threshold = (int)(factor*(MaxRGB+1)/100.0);
3440  if(img.depth() < 32){
3441  data = (unsigned int *)img.tqcolorTable();
3442  count = img.numColors();
3443  }
3444  else{
3445  data = (unsigned int *)img.bits();
3446  count = img.width()*img.height();
3447  }
3448  for(i=0; i < count; ++i){
3449  data[i] = tqRgba(tqRed(data[i]) > threshold ? MaxRGB-tqRed(data[i]) : tqRed(data[i]),
3450  tqGreen(data[i]) > threshold ? MaxRGB-tqGreen(data[i]) : tqGreen(data[i]),
3451  tqBlue(data[i]) > threshold ? MaxRGB-tqBlue(data[i]) : tqBlue(data[i]),
3452  tqAlpha(data[i]));
3453  }
3454 }
3455 
3456 TQImage KImageEffect::spread(TQImage &src, unsigned int amount)
3457 {
3458  int quantum, x, y;
3459  int x_distance, y_distance;
3460  if(src.width() < 3 || src.height() < 3)
3461  return(src);
3462  TQImage dest(src);
3463  dest.detach();
3464  quantum=(amount+1) >> 1;
3465  if(src.depth() > 8){ // DirectClass source image
3466  unsigned int *p, *q;
3467  for(y=0; y < src.height(); y++){
3468  q = (unsigned int *)dest.scanLine(y);
3469  for(x=0; x < src.width(); x++){
3470  x_distance = x + ((rand() & (amount+1))-quantum);
3471  y_distance = y + ((rand() & (amount+1))-quantum);
3472  x_distance = TQMIN(x_distance, src.width()-1);
3473  y_distance = TQMIN(y_distance, src.height()-1);
3474  if(x_distance < 0)
3475  x_distance = 0;
3476  if(y_distance < 0)
3477  y_distance = 0;
3478  p = (unsigned int *)src.scanLine(y_distance);
3479  p += x_distance;
3480  *q++=(*p);
3481  }
3482  }
3483  }
3484  else{ // PsudeoClass source image
3485  // just do colortable values
3486  unsigned char *p, *q;
3487  for(y=0; y < src.height(); y++){
3488  q = (unsigned char *)dest.scanLine(y);
3489  for(x=0; x < src.width(); x++){
3490  x_distance = x + ((rand() & (amount+1))-quantum);
3491  y_distance = y + ((rand() & (amount+1))-quantum);
3492  x_distance = TQMIN(x_distance, src.width()-1);
3493  y_distance = TQMIN(y_distance, src.height()-1);
3494  if(x_distance < 0)
3495  x_distance = 0;
3496  if(y_distance < 0)
3497  y_distance = 0;
3498  p = (unsigned char *)src.scanLine(y_distance);
3499  p += x_distance;
3500  *q++=(*p);
3501  }
3502  }
3503  }
3504  return(dest);
3505 }
3506 
3507 TQImage KImageEffect::swirl(TQImage &src, double degrees,
3508  unsigned int background)
3509 {
3510  double cosine, distance, factor, radius, sine, x_center, x_distance,
3511  x_scale, y_center, y_distance, y_scale;
3512  int x, y;
3513  unsigned int *q;
3514  TQImage dest(src.width(), src.height(), 32);
3515 
3516  // compute scaling factor
3517  x_center = src.width()/2.0;
3518  y_center = src.height()/2.0;
3519  radius = TQMAX(x_center,y_center);
3520  x_scale=1.0;
3521  y_scale=1.0;
3522  if(src.width() > src.height())
3523  y_scale=(double)src.width()/src.height();
3524  else if(src.width() < src.height())
3525  x_scale=(double)src.height()/src.width();
3526  degrees=DegreesToRadians(degrees);
3527  // swirl each row
3528  if(src.depth() > 8){ // DirectClass source image
3529  unsigned int *p;
3530  for(y=0; y < src.height(); y++){
3531  p = (unsigned int *)src.scanLine(y);
3532  q = (unsigned int *)dest.scanLine(y);
3533  y_distance = y_scale*(y-y_center);
3534  for(x=0; x < src.width(); x++){
3535  // determine if the pixel is within an ellipse
3536  *q=(*p);
3537  x_distance = x_scale*(x-x_center);
3538  distance = x_distance*x_distance+y_distance*y_distance;
3539  if (distance < (radius*radius)){
3540  // swirl
3541  factor = 1.0-sqrt(distance)/radius;
3542  sine = sin(degrees*factor*factor);
3543  cosine = cos(degrees*factor*factor);
3544  *q = interpolateColor(&src,
3545  (cosine*x_distance-sine*y_distance)/x_scale+x_center,
3546  (sine*x_distance+cosine*y_distance)/y_scale+y_center,
3547  background);
3548  }
3549  p++;
3550  q++;
3551  }
3552  }
3553  }
3554  else{ // PsudeoClass source image
3555  unsigned char *p;
3556  unsigned int *cTable = (unsigned int *)src.tqcolorTable();
3557  for(y=0; y < src.height(); y++){
3558  p = (unsigned char *)src.scanLine(y);
3559  q = (unsigned int *)dest.scanLine(y);
3560  y_distance = y_scale*(y-y_center);
3561  for(x=0; x < src.width(); x++){
3562  // determine if the pixel is within an ellipse
3563  *q = *(cTable+(*p));
3564  x_distance = x_scale*(x-x_center);
3565  distance = x_distance*x_distance+y_distance*y_distance;
3566  if (distance < (radius*radius)){
3567  // swirl
3568  factor = 1.0-sqrt(distance)/radius;
3569  sine = sin(degrees*factor*factor);
3570  cosine = cos(degrees*factor*factor);
3571  *q = interpolateColor(&src,
3572  (cosine*x_distance-sine*y_distance)/x_scale+x_center,
3573  (sine*x_distance+cosine*y_distance)/y_scale+y_center,
3574  background);
3575  }
3576  p++;
3577  q++;
3578  }
3579  }
3580 
3581  }
3582  return(dest);
3583 }
3584 
3585 TQImage KImageEffect::wave(TQImage &src, double amplitude, double wavelength,
3586  unsigned int background)
3587 {
3588  double *sine_map;
3589  int x, y;
3590  unsigned int *q;
3591 
3592  TQImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
3593  // allocate sine map
3594  sine_map = (double *)malloc(dest.width()*sizeof(double));
3595  if(!sine_map)
3596  return(src);
3597  for(x=0; x < dest.width(); ++x)
3598  sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
3599  // wave image
3600  for(y=0; y < dest.height(); ++y){
3601  q = (unsigned int *)dest.scanLine(y);
3602  for (x=0; x < dest.width(); x++){
3603  *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
3604  ++q;
3605  }
3606  }
3607  free(sine_map);
3608  return(dest);
3609 }
3610 
3611 //
3612 // The following methods work by computing a value from neighboring pixels
3613 // (mosfet 05/26/03)
3614 //
3615 
3616 // New algorithms based on ImageMagick 5.5.6 (05/26/03)
3617 
3618 TQImage KImageEffect::oilPaint(TQImage &src, int /*radius*/)
3619 {
3620  /* binary compat method - remove me when possible! */
3621  return(oilPaintConvolve(src, 0));
3622 }
3623 
3624 TQImage KImageEffect::oilPaintConvolve(TQImage &src, double radius)
3625 {
3626  unsigned long count /*,*histogram*/;
3627  unsigned long histogram[256];
3628  unsigned int k;
3629  int width;
3630  int x, y, mx, my, sx, sy;
3631  int mcx, mcy;
3632  unsigned int *s=0, *q;
3633 
3634  if(src.depth() < 32)
3635  src.convertDepth(32);
3636  TQImage dest(src);
3637  dest.detach();
3638 
3639  width = getOptimalKernelWidth(radius, 0.5);
3640  if(src.width() < width){
3641  tqWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!");
3642  return(dest);
3643  }
3644  /*
3645  histogram = (unsigned long *)malloc(256*sizeof(unsigned long));
3646  if(!histogram){
3647  tqWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!");
3648  return(dest);
3649  }
3650  */
3651  unsigned int **jumpTable = (unsigned int **)src.jumpTable();
3652  for(y=0; y < dest.height(); ++y){
3653  sy = y-(width/2);
3654  q = (unsigned int *)dest.scanLine(y);
3655  for(x=0; x < dest.width(); ++x){
3656  count = 0;
3657  memset(histogram, 0, 256*sizeof(unsigned long));
3658  //memset(histogram, 0, 256);
3659  sy = y-(width/2);
3660  for(mcy=0; mcy < width; ++mcy, ++sy){
3661  my = sy < 0 ? 0 : sy > src.height()-1 ?
3662  src.height()-1 : sy;
3663  sx = x+(-width/2);
3664  for(mcx=0; mcx < width; ++mcx, ++sx){
3665  mx = sx < 0 ? 0 : sx > src.width()-1 ?
3666  src.width()-1 : sx;
3667 
3668  k = intensityValue(jumpTable[my][mx]);
3669  if(k > 255){
3670  tqWarning("KImageEffect::oilPaintConvolve(): k is %d",
3671  k);
3672  k = 255;
3673  }
3674  histogram[k]++;
3675  if(histogram[k] > count){
3676  count = histogram[k];
3677  s = jumpTable[my]+mx;
3678  }
3679  }
3680  }
3681  if (s)
3682  *q++ = (*s);
3683  }
3684  }
3685  /* liberateMemory((histogram); */
3686  return(dest);
3687 }
3688 
3689 TQImage KImageEffect::charcoal(TQImage &src, double /*factor*/)
3690 {
3691  /* binary compat method - remove me when possible! */
3692  return(charcoal(src, 0, 1));
3693 }
3694 
3695 TQImage KImageEffect::charcoal(TQImage &src, double radius, double sigma)
3696 {
3697  TQImage img(edge(src, radius));
3698  img = blur(img, radius, sigma);
3699  normalize(img);
3700  img.invertPixels(false);
3701  KImageEffect::toGray(img);
3702  return(img);
3703 }
3704 
3705 void KImageEffect::normalize(TQImage &image)
3706 {
3707  struct double_packet high, low, intensity, *histogram;
3708  struct short_packet *normalize_map;
3709  TQ_INT64 number_pixels;
3710  int x, y;
3711  unsigned int *p, *q;
3712  long i;
3713  unsigned long threshold_intensity;
3714  unsigned char r, g, b, a;
3715 
3716  if(image.depth() < 32) // result will always be 32bpp
3717  image = image.convertDepth(32);
3718 
3719  histogram = (struct double_packet *)
3720  malloc(256*sizeof(struct double_packet));
3721  normalize_map = (struct short_packet *)
3722  malloc(256*sizeof(struct short_packet));
3723 
3724  if(!histogram || !normalize_map){
3725  if(histogram)
3726  liberateMemory(&histogram);
3727  if(normalize_map)
3728  liberateMemory(&normalize_map);
3729  tqWarning("KImageEffect::normalize(): Unable to allocate memory!");
3730  return;
3731  }
3732 
3733  /*
3734  Form histogram.
3735  */
3736  memset(histogram, 0, 256*sizeof(struct double_packet));
3737  for(y=0; y < image.height(); ++y){
3738  p = (unsigned int *)image.scanLine(y);
3739  for(x=0; x < image.width(); ++x){
3740  histogram[(unsigned char)(tqRed(*p))].red++;
3741  histogram[(unsigned char)(tqGreen(*p))].green++;
3742  histogram[(unsigned char)(tqBlue(*p))].blue++;
3743  histogram[(unsigned char)(tqAlpha(*p))].alpha++;
3744  p++;
3745  }
3746  }
3747 
3748  /*
3749  Find the histogram boundaries by locating the 0.1 percent levels.
3750  */
3751  number_pixels = (TQ_INT64)image.width()*image.height();
3752  threshold_intensity = number_pixels/1000;
3753 
3754  /* red */
3755  memset(&intensity, 0, sizeof(struct double_packet));
3756  memset(&high, 0, sizeof(struct double_packet));
3757  memset(&low, 0, sizeof(struct double_packet));
3758  for(high.red=255; high.red != 0; high.red--){
3759  intensity.red+=histogram[(unsigned char)high.red].red;
3760  if(intensity.red > threshold_intensity)
3761  break;
3762  }
3763  if(low.red == high.red){
3764  threshold_intensity = 0;
3765  memset(&intensity, 0, sizeof(struct double_packet));
3766  for(low.red=0; low.red < 255; low.red++){
3767  intensity.red+=histogram[(unsigned char)low.red].red;
3768  if(intensity.red > threshold_intensity)
3769  break;
3770  }
3771  memset(&intensity, 0, sizeof(struct double_packet));
3772  for(high.red=255; high.red != 0; high.red--){
3773  intensity.red+=histogram[(unsigned char)high.red].red;
3774  if(intensity.red > threshold_intensity)
3775  break;
3776  }
3777  }
3778 
3779  /* green */
3780  memset(&intensity, 0, sizeof(struct double_packet));
3781  for(high.green=255; high.green != 0; high.green--){
3782  intensity.green+=histogram[(unsigned char)high.green].green;
3783  if(intensity.green > threshold_intensity)
3784  break;
3785  }
3786  if(low.green == high.green){
3787  threshold_intensity = 0;
3788  memset(&intensity, 0, sizeof(struct double_packet));
3789  for(low.green=0; low.green < 255; low.green++){
3790  intensity.green+=histogram[(unsigned char)low.green].green;
3791  if(intensity.green > threshold_intensity)
3792  break;
3793  }
3794  memset(&intensity,0,sizeof(struct double_packet));
3795  for(high.green=255; high.green != 0; high.green--){
3796  intensity.green+=histogram[(unsigned char)high.green].green;
3797  if(intensity.green > threshold_intensity)
3798  break;
3799  }
3800  }
3801 
3802  /* blue */
3803  memset(&intensity, 0, sizeof(struct double_packet));
3804  for(high.blue=255; high.blue != 0; high.blue--){
3805  intensity.blue+=histogram[(unsigned char)high.blue].blue;
3806  if(intensity.blue > threshold_intensity)
3807  break;
3808  }
3809  if(low.blue == high.blue){
3810  threshold_intensity = 0;
3811  memset(&intensity, 0, sizeof(struct double_packet));
3812  for(low.blue=0; low.blue < 255; low.blue++){
3813  intensity.blue+=histogram[(unsigned char)low.blue].blue;
3814  if(intensity.blue > threshold_intensity)
3815  break;
3816  }
3817  memset(&intensity,0,sizeof(struct double_packet));
3818  for(high.blue=255; high.blue != 0; high.blue--){
3819  intensity.blue+=histogram[(unsigned char)high.blue].blue;
3820  if(intensity.blue > threshold_intensity)
3821  break;
3822  }
3823  }
3824 
3825  /* alpha */
3826  memset(&intensity, 0, sizeof(struct double_packet));
3827  for(high.alpha=255; high.alpha != 0; high.alpha--){
3828  intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
3829  if(intensity.alpha > threshold_intensity)
3830  break;
3831  }
3832  if(low.alpha == high.alpha){
3833  threshold_intensity = 0;
3834  memset(&intensity, 0, sizeof(struct double_packet));
3835  for(low.alpha=0; low.alpha < 255; low.alpha++){
3836  intensity.alpha+=histogram[(unsigned char)low.alpha].alpha;
3837  if(intensity.alpha > threshold_intensity)
3838  break;
3839  }
3840  memset(&intensity,0,sizeof(struct double_packet));
3841  for(high.alpha=255; high.alpha != 0; high.alpha--){
3842  intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
3843  if(intensity.alpha > threshold_intensity)
3844  break;
3845  }
3846  }
3847  liberateMemory(&histogram);
3848 
3849  /*
3850  Stretch the histogram to create the normalized image mapping.
3851  */
3852 
3853  // should the maxes be 65535?
3854  memset(normalize_map, 0 ,256*sizeof(struct short_packet));
3855  for(i=0; i <= (long) 255; i++){
3856  if(i < (long) low.red)
3857  normalize_map[i].red=0;
3858  else if (i > (long) high.red)
3859  normalize_map[i].red=65535;
3860  else if (low.red != high.red)
3861  normalize_map[i].red =
3862  (unsigned short)((65535*(i-low.red))/(high.red-low.red));
3863 
3864  if(i < (long) low.green)
3865  normalize_map[i].green=0;
3866  else if (i > (long) high.green)
3867  normalize_map[i].green=65535;
3868  else if (low.green != high.green)
3869  normalize_map[i].green =
3870  (unsigned short)((65535*(i-low.green))/(high.green-low.green));
3871 
3872  if(i < (long) low.blue)
3873  normalize_map[i].blue=0;
3874  else if (i > (long) high.blue)
3875  normalize_map[i].blue=65535;
3876  else if (low.blue != high.blue)
3877  normalize_map[i].blue =
3878  (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue));
3879 
3880  if(i < (long) low.alpha)
3881  normalize_map[i].alpha=0;
3882  else if (i > (long) high.alpha)
3883  normalize_map[i].alpha=65535;
3884  else if (low.alpha != high.alpha)
3885  normalize_map[i].alpha =
3886  (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha));
3887 
3888  }
3889 
3890  for(y=0; y < image.height(); ++y){
3891  q = (unsigned int *)image.scanLine(y);
3892  for(x=0; x < image.width(); ++x){
3893  if(low.red != high.red)
3894  r = (normalize_map[(unsigned short)(tqRed(q[x]))].red)/257;
3895  else
3896  r = tqRed(q[x]);
3897  if(low.green != high.green)
3898  g = (normalize_map[(unsigned short)(tqGreen(q[x]))].green)/257;
3899  else
3900  g = tqGreen(q[x]);
3901  if(low.blue != high.blue)
3902  b = (normalize_map[(unsigned short)(tqBlue(q[x]))].blue)/257;
3903  else
3904  b = tqBlue(q[x]);
3905  if(low.alpha != high.alpha)
3906  a = (normalize_map[(unsigned short)(tqAlpha(q[x]))].alpha)/257;
3907  else
3908  a = tqAlpha(q[x]);
3909  q[x] = tqRgba(r, g, b, a);
3910  }
3911  }
3912  liberateMemory(&normalize_map);
3913 }
3914 
3915 void KImageEffect::equalize(TQImage &image)
3916 {
3917  struct double_packet high, low, intensity, *map, *histogram;
3918  struct short_packet *equalize_map;
3919  int x, y;
3920  unsigned int *p, *q;
3921  long i;
3922  unsigned char r, g, b, a;
3923 
3924  if(image.depth() < 32) // result will always be 32bpp
3925  image = image.convertDepth(32);
3926 
3927  histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet));
3928  map=(struct double_packet *) malloc(256*sizeof(struct double_packet));
3929  equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet));
3930  if(!histogram || !map || !equalize_map){
3931  if(histogram)
3932  liberateMemory(&histogram);
3933  if(map)
3934  liberateMemory(&map);
3935  if(equalize_map)
3936  liberateMemory(&equalize_map);
3937  tqWarning("KImageEffect::equalize(): Unable to allocate memory!");
3938  return;
3939  }
3940 
3941  /*
3942  Form histogram.
3943  */
3944  memset(histogram, 0, 256*sizeof(struct double_packet));
3945  for(y=0; y < image.height(); ++y){
3946  p = (unsigned int *)image.scanLine(y);
3947  for(x=0; x < image.width(); ++x){
3948  histogram[(unsigned char)(tqRed(*p))].red++;
3949  histogram[(unsigned char)(tqGreen(*p))].green++;
3950  histogram[(unsigned char)(tqBlue(*p))].blue++;
3951  histogram[(unsigned char)(tqAlpha(*p))].alpha++;
3952  p++;
3953  }
3954  }
3955  /*
3956  Integrate the histogram to get the equalization map.
3957  */
3958  memset(&intensity, 0 ,sizeof(struct double_packet));
3959  for(i=0; i <= 255; ++i){
3960  intensity.red += histogram[i].red;
3961  intensity.green += histogram[i].green;
3962  intensity.blue += histogram[i].blue;
3963  intensity.alpha += histogram[i].alpha;
3964  map[i]=intensity;
3965  }
3966  low=map[0];
3967  high=map[255];
3968  memset(equalize_map, 0, 256*sizeof(short_packet));
3969  for(i=0; i <= 255; ++i){
3970  if(high.red != low.red)
3971  equalize_map[i].red=(unsigned short)
3972  ((65535*(map[i].red-low.red))/(high.red-low.red));
3973  if(high.green != low.green)
3974  equalize_map[i].green=(unsigned short)
3975  ((65535*(map[i].green-low.green))/(high.green-low.green));
3976  if(high.blue != low.blue)
3977  equalize_map[i].blue=(unsigned short)
3978  ((65535*(map[i].blue-low.blue))/(high.blue-low.blue));
3979  if(high.alpha != low.alpha)
3980  equalize_map[i].alpha=(unsigned short)
3981  ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha));
3982  }
3983  liberateMemory(&histogram);
3984  liberateMemory(&map);
3985 
3986  /*
3987  Stretch the histogram.
3988  */
3989  for(y=0; y < image.height(); ++y){
3990  q = (unsigned int *)image.scanLine(y);
3991  for(x=0; x < image.width(); ++x){
3992  if(low.red != high.red)
3993  r = (equalize_map[(unsigned short)(tqRed(q[x]))].red/257);
3994  else
3995  r = tqRed(q[x]);
3996  if(low.green != high.green)
3997  g = (equalize_map[(unsigned short)(tqGreen(q[x]))].green/257);
3998  else
3999  g = tqGreen(q[x]);
4000  if(low.blue != high.blue)
4001  b = (equalize_map[(unsigned short)(tqBlue(q[x]))].blue/257);
4002  else
4003  b = tqBlue(q[x]);
4004  if(low.alpha != high.alpha)
4005  a = (equalize_map[(unsigned short)(tqAlpha(q[x]))].alpha/257);
4006  else
4007  a = tqAlpha(q[x]);
4008  q[x] = tqRgba(r, g, b, a);
4009  }
4010  }
4011  liberateMemory(&equalize_map);
4012 
4013 }
4014 
4015 TQImage KImageEffect::edge(TQImage &image, double radius)
4016 {
4017  double *kernel;
4018  int width;
4019  long i;
4020  TQImage dest;
4021 
4022  if(radius == 50.0){
4023  /* For binary compatability! Remove me when possible! This used to
4024  * take a different parameter, a factor, and this was the default
4025  * value */
4026  radius = 0.0;
4027  }
4028 
4029  width = getOptimalKernelWidth(radius, 0.5);
4030  if(image.width() < width || image.height() < width){
4031  tqWarning("KImageEffect::edge(): Image is smaller than radius!");
4032  return(dest);
4033  }
4034  kernel= (double *)malloc(width*width*sizeof(double));
4035  if(!kernel){
4036  tqWarning("KImageEffect::edge(): Unable to allocate memory!");
4037  return(dest);
4038  }
4039  for(i=0; i < (width*width); i++)
4040  kernel[i]=(-1.0);
4041  kernel[i/2]=width*width-1.0;
4042  convolveImage(&image, &dest, width, kernel);
4043  free(kernel);
4044  return(dest);
4045 }
4046 
4047 TQImage KImageEffect::emboss(TQImage &src)
4048 {
4049  /* binary compat method - remove me when possible! */
4050  return(emboss(src, 0, 1));
4051 }
4052 
4053 TQImage KImageEffect::emboss(TQImage &image, double radius, double sigma)
4054 {
4055  double alpha, *kernel;
4056  int j, width;
4057  long i, u, v;
4058  TQImage dest;
4059 
4060  if(sigma == 0.0){
4061  tqWarning("KImageEffect::emboss(): Zero sigma is not permitted!");
4062  return(dest);
4063  }
4064 
4065  width = getOptimalKernelWidth(radius, sigma);
4066  if(image.width() < width || image.height() < width){
4067  tqWarning("KImageEffect::emboss(): Image is smaller than radius!");
4068  return(dest);
4069  }
4070  kernel= (double *)malloc(width*width*sizeof(double));
4071  if(!kernel){
4072  tqWarning("KImageEffect::emboss(): Unable to allocate memory!");
4073  return(dest);
4074  }
4075  if(image.depth() < 32)
4076  image = image.convertDepth(32);
4077 
4078  i=0;
4079  j=width/2;
4080  for(v=(-width/2); v <= (width/2); v++){
4081  for(u=(-width/2); u <= (width/2); u++){
4082  alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
4083  kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
4084  (2.0*MagickPI*sigma*sigma);
4085  if (u == j)
4086  kernel[i]=0.0;
4087  i++;
4088  }
4089  j--;
4090  }
4091  convolveImage(&image, &dest, width, kernel);
4092  liberateMemory(&kernel);
4093 
4094  equalize(dest);
4095  return(dest);
4096 }
4097 
4098 void KImageEffect::blurScanLine(double *kernel, int width,
4099  unsigned int *src, unsigned int *dest,
4100  int columns)
4101 {
4102  double *p;
4103  unsigned int *q;
4104  int x;
4105  long i;
4106  double red, green, blue, alpha;
4107  double scale = 0.0;
4108 
4109  if(width > columns){
4110  for(x=0; x < columns; ++x){
4111  scale = 0.0;
4112  red = blue = green = alpha = 0.0;
4113  p = kernel;
4114  q = src;
4115  for(i=0; i < columns; ++i){
4116  if((i >= (x-width/2)) && (i <= (x+width/2))){
4117  red += (*p)*(tqRed(*q)*257);
4118  green += (*p)*(tqGreen(*q)*257);
4119  blue += (*p)*(tqBlue(*q)*257);
4120  alpha += (*p)*(tqAlpha(*q)*257);
4121  }
4122  if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
4123  scale+=kernel[i+width/2-x];
4124  p++;
4125  q++;
4126  }
4127  scale = 1.0/scale;
4128  red = scale*(red+0.5);
4129  green = scale*(green+0.5);
4130  blue = scale*(blue+0.5);
4131  alpha = scale*(alpha+0.5);
4132 
4133  red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4134  green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4135  blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4136  alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4137 
4138  dest[x] = tqRgba((unsigned char)(red/257UL),
4139  (unsigned char)(green/257UL),
4140  (unsigned char)(blue/257UL),
4141  (unsigned char)(alpha/257UL));
4142  }
4143  return;
4144  }
4145 
4146  for(x=0; x < width/2; ++x){
4147  scale = 0.0;
4148  red = blue = green = alpha = 0.0;
4149  p = kernel+width/2-x;
4150  q = src;
4151  for(i=width/2-x; i < width; ++i){
4152  red += (*p)*(tqRed(*q)*257);
4153  green += (*p)*(tqGreen(*q)*257);
4154  blue += (*p)*(tqBlue(*q)*257);
4155  alpha += (*p)*(tqAlpha(*q)*257);
4156  scale += (*p);
4157  p++;
4158  q++;
4159  }
4160  scale=1.0/scale;
4161 
4162  red = scale*(red+0.5);
4163  green = scale*(green+0.5);
4164  blue = scale*(blue+0.5);
4165  alpha = scale*(alpha+0.5);
4166 
4167  red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4168  green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4169  blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4170  alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4171 
4172  dest[x] = tqRgba((unsigned char)(red/257UL),
4173  (unsigned char)(green/257UL),
4174  (unsigned char)(blue/257UL),
4175  (unsigned char)(alpha/257UL));
4176  }
4177 
4178  for(; x < columns-width/2; ++x){
4179  red = blue = green = alpha = 0.0;
4180  p = kernel;
4181  q = src+(x-width/2);
4182  for (i=0; i < (long) width; ++i){
4183  red += (*p)*(tqRed(*q)*257);
4184  green += (*p)*(tqGreen(*q)*257);
4185  blue += (*p)*(tqBlue(*q)*257);
4186  alpha += (*p)*(tqAlpha(*q)*257);
4187  p++;
4188  q++;
4189  }
4190  red = scale*(red+0.5);
4191  green = scale*(green+0.5);
4192  blue = scale*(blue+0.5);
4193  alpha = scale*(alpha+0.5);
4194 
4195  red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4196  green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4197  blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4198  alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4199 
4200  dest[x] = tqRgba((unsigned char)(red/257UL),
4201  (unsigned char)(green/257UL),
4202  (unsigned char)(blue/257UL),
4203  (unsigned char)(alpha/257UL));
4204  }
4205 
4206  for(; x < columns; ++x){
4207  red = blue = green = alpha = 0.0;
4208  scale=0;
4209  p = kernel;
4210  q = src+(x-width/2);
4211  for(i=0; i < columns-x+width/2; ++i){
4212  red += (*p)*(tqRed(*q)*257);
4213  green += (*p)*(tqGreen(*q)*257);
4214  blue += (*p)*(tqBlue(*q)*257);
4215  alpha += (*p)*(tqAlpha(*q)*257);
4216  scale += (*p);
4217  p++;
4218  q++;
4219  }
4220  scale=1.0/scale;
4221  red = scale*(red+0.5);
4222  green = scale*(green+0.5);
4223  blue = scale*(blue+0.5);
4224  alpha = scale*(alpha+0.5);
4225 
4226  red = red < 0 ? 0 : red > 65535 ? 65535 : red;
4227  green = green < 0 ? 0 : green > 65535 ? 65535 : green;
4228  blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
4229  alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
4230 
4231  dest[x] = tqRgba((unsigned char)(red/257UL),
4232  (unsigned char)(green/257UL),
4233  (unsigned char)(blue/257UL),
4234  (unsigned char)(alpha/257UL));
4235  }
4236 }
4237 
4238 int KImageEffect::getBlurKernel(int width, double sigma, double **kernel)
4239 {
4240 #define KernelRank 3
4241  double alpha, normalize;
4242  long i;
4243  int bias;
4244 
4245  assert(sigma != 0.0);
4246  if(width == 0)
4247  width = 3;
4248  *kernel=(double *)malloc(width*sizeof(double));
4249  if(*kernel == (double *)NULL)
4250  return(0);
4251  memset(*kernel, 0, width*sizeof(double));
4252  bias = KernelRank*width/2;
4253  for(i=(-bias); i <= bias; i++){
4254  alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
4255  (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma);
4256  }
4257  normalize=0;
4258  for(i=0; i < width; i++)
4259  normalize+=(*kernel)[i];
4260  for(i=0; i < width; i++)
4261  (*kernel)[i]/=normalize;
4262 
4263  return(width);
4264 }
4265 
4266 TQImage KImageEffect::blur(TQImage &src, double /*factor*/)
4267 {
4268  /* binary compat method - remove me when possible! */
4269  return(blur(src, 0, 1));
4270 }
4271 
4272 TQImage KImageEffect::blur(TQImage &src, double radius, double sigma)
4273 {
4274  double *kernel;
4275  TQImage dest;
4276  int width;
4277  int x, y;
4278  unsigned int *scanline, *temp;
4279  unsigned int *p, *q;
4280 
4281  if(sigma == 0.0){
4282  tqWarning("KImageEffect::blur(): Zero sigma is not permitted!");
4283  return(dest);
4284  }
4285  if(src.depth() < 32)
4286  src = src.convertDepth(32);
4287 
4288  kernel=(double *) NULL;
4289  if(radius > 0)
4290  width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
4291  else{
4292  double *last_kernel;
4293  last_kernel=(double *) NULL;
4294  width=getBlurKernel(3,sigma,&kernel);
4295 
4296  while ((long) (MaxRGB*kernel[0]) > 0){
4297  if(last_kernel != (double *)NULL){
4298  liberateMemory(&last_kernel);
4299  }
4300  last_kernel=kernel;
4301  kernel = (double *)NULL;
4302  width = getBlurKernel(width+2, sigma, &kernel);
4303  }
4304  if(last_kernel != (double *) NULL){
4305  liberateMemory(&kernel);
4306  width-=2;
4307  kernel = last_kernel;
4308  }
4309  }
4310 
4311  if(width < 3){
4312  tqWarning("KImageEffect::blur(): Kernel radius is too small!");
4313  liberateMemory(&kernel);
4314  return(dest);
4315  }
4316 
4317  dest.create(src.width(), src.height(), 32);
4318 
4319  // Horizontal convolution
4320  scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
4321  temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
4322  for(y=0; y < src.height(); ++y){
4323  p = (unsigned int *)src.scanLine(y);
4324  q = (unsigned int *)dest.scanLine(y);
4325  blurScanLine(kernel, width, p, q, src.width());
4326  }
4327 
4328  TQImage partial = dest;
4329 
4330  // Vertical convolution
4331  unsigned int **srcTable = (unsigned int **)partial.jumpTable();
4332  unsigned int **destTable = (unsigned int **)dest.jumpTable();
4333  for(x=0; x < partial.width(); ++x){
4334  for(y=0; y < partial.height(); ++y){
4335  scanline[y] = srcTable[y][x];
4336  }
4337  blurScanLine(kernel, width, scanline, temp, partial.height());
4338  for(y=0; y < partial.height(); ++y){
4339  destTable[y][x] = temp[y];
4340  }
4341  }
4342  free(scanline);
4343  free(temp);
4344  free(kernel);
4345  return(dest);
4346 }
4347 
4348 bool KImageEffect::convolveImage(TQImage *image, TQImage *dest,
4349  const unsigned int order,
4350  const double *kernel)
4351 {
4352  long width;
4353  double red, green, blue, alpha;
4354  double normalize, *normal_kernel;
4355  const double *k;
4356  unsigned int *q;
4357  int x, y, mx, my, sx, sy;
4358  long i;
4359  int mcx, mcy;
4360 
4361  width = order;
4362  if((width % 2) == 0){
4363  tqWarning("KImageEffect: Kernel width must be an odd number!");
4364  return(false);
4365  }
4366  normal_kernel = (double *)malloc(width*width*sizeof(double));
4367  if(!normal_kernel){
4368  tqWarning("KImageEffect: Unable to allocate memory!");
4369  return(false);
4370  }
4371  dest->reset();
4372  dest->create(image->width(), image->height(), 32);
4373  if(image->depth() < 32)
4374  *image = image->convertDepth(32);
4375 
4376  normalize=0.0;
4377  for(i=0; i < (width*width); i++)
4378  normalize += kernel[i];
4379  if(fabs(normalize) <= MagickEpsilon)
4380  normalize=1.0;
4381  normalize=1.0/normalize;
4382  for(i=0; i < (width*width); i++)
4383  normal_kernel[i] = normalize*kernel[i];
4384 
4385  unsigned int **jumpTable = (unsigned int **)image->jumpTable();
4386  for(y=0; y < dest->height(); ++y){
4387  sy = y-(width/2);
4388  q = (unsigned int *)dest->scanLine(y);
4389  for(x=0; x < dest->width(); ++x){
4390  k = normal_kernel;
4391  red = green = blue = alpha = 0;
4392  sy = y-(width/2);
4393  for(mcy=0; mcy < width; ++mcy, ++sy){
4394  my = sy < 0 ? 0 : sy > image->height()-1 ?
4395  image->height()-1 : sy;
4396  sx = x+(-width/2);
4397  for(mcx=0; mcx < width; ++mcx, ++sx){
4398  mx = sx < 0 ? 0 : sx > image->width()-1 ?
4399  image->width()-1 : sx;
4400  red += (*k)*(tqRed(jumpTable[my][mx])*257);
4401  green += (*k)*(tqGreen(jumpTable[my][mx])*257);
4402  blue += (*k)*(tqBlue(jumpTable[my][mx])*257);
4403  alpha += (*k)*(tqAlpha(jumpTable[my][mx])*257);
4404  ++k;
4405  }
4406  }
4407 
4408  red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
4409  green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
4410  blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
4411  alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
4412 
4413  *q++ = tqRgba((unsigned char)(red/257UL),
4414  (unsigned char)(green/257UL),
4415  (unsigned char)(blue/257UL),
4416  (unsigned char)(alpha/257UL));
4417  }
4418  }
4419  free(normal_kernel);
4420  return(true);
4421 
4422 }
4423 
4424 int KImageEffect::getOptimalKernelWidth(double radius, double sigma)
4425 {
4426  double normalize, value;
4427  long width;
4428  long u;
4429 
4430  assert(sigma != 0.0);
4431  if(radius > 0.0)
4432  return((int)(2.0*ceil(radius)+1.0));
4433  for(width=5; ;){
4434  normalize=0.0;
4435  for(u=(-width/2); u <= (width/2); u++)
4436  normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
4437  u=width/2;
4438  value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
4439  if((long)(65535*value) <= 0)
4440  break;
4441  width+=2;
4442  }
4443  return((int)width-2);
4444 }
4445 
4446 TQImage KImageEffect::sharpen(TQImage &src, double /*factor*/)
4447 {
4448  /* binary compat method - remove me when possible! */
4449  return(sharpen(src, 0, 1));
4450 }
4451 
4452 TQImage KImageEffect::sharpen(TQImage &image, double radius, double sigma)
4453 {
4454  double alpha, normalize, *kernel;
4455  int width;
4456  long i, u, v;
4457  TQImage dest;
4458 
4459  if(sigma == 0.0){
4460  tqWarning("KImageEffect::sharpen(): Zero sigma is not permitted!");
4461  return(dest);
4462  }
4463  width = getOptimalKernelWidth(radius, sigma);
4464  if(image.width() < width){
4465  tqWarning("KImageEffect::sharpen(): Image is smaller than radius!");
4466  return(dest);
4467  }
4468  kernel = (double *)malloc(width*width*sizeof(double));
4469  if(!kernel){
4470  tqWarning("KImageEffect::sharpen(): Unable to allocate memory!");
4471  return(dest);
4472  }
4473 
4474  i = 0;
4475  normalize=0.0;
4476  for(v=(-width/2); v <= (width/2); v++){
4477  for(u=(-width/2); u <= (width/2); u++){
4478  alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
4479  kernel[i]=alpha/(2.0*MagickPI*sigma*sigma);
4480  normalize+=kernel[i];
4481  i++;
4482  }
4483  }
4484  kernel[i/2]=(-2.0)*normalize;
4485  convolveImage(&image, &dest, width, kernel);
4486  free(kernel);
4487  return(dest);
4488 }
4489 
4490 // End of new algorithms
4491 
4492 TQImage KImageEffect::shade(TQImage &src, bool color_shading, double azimuth,
4493  double elevation)
4494 {
4495  struct PointInfo{
4496  double x, y, z;
4497  };
4498 
4499  double distance, normal_distance, shade;
4500  int x, y;
4501 
4502  struct PointInfo light, normal;
4503 
4504  unsigned int *q;
4505 
4506  TQImage dest(src.width(), src.height(), 32);
4507 
4508  azimuth = DegreesToRadians(azimuth);
4509  elevation = DegreesToRadians(elevation);
4510  light.x = MaxRGB*cos(azimuth)*cos(elevation);
4511  light.y = MaxRGB*sin(azimuth)*cos(elevation);
4512  light.z = MaxRGB*sin(elevation);
4513  normal.z= 2*MaxRGB; // constant Z of surface normal
4514 
4515  if(src.depth() > 8){ // DirectClass source image
4516  unsigned int *p, *s0, *s1, *s2;
4517  for(y=0; y < src.height(); ++y){
4518  p = (unsigned int *)src.scanLine(TQMIN(TQMAX(y-1,0),src.height()-3));
4519  q = (unsigned int *)dest.scanLine(y);
4520  // shade this row of pixels.
4521  *q++=(*(p+src.width()));
4522  p++;
4523  s0 = p;
4524  s1 = p + src.width();
4525  s2 = p + 2*src.width();
4526  for(x=1; x < src.width()-1; ++x){
4527  // determine the surface normal and compute shading.
4528  normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
4529  (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
4530  (double) intensityValue(*(s2+1));
4531  normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
4532  (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
4533  (double) intensityValue(*(s0+1));
4534  if((normal.x == 0) && (normal.y == 0))
4535  shade=light.z;
4536  else{
4537  shade=0.0;
4538  distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4539  if (distance > 0.0){
4540  normal_distance=
4541  normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4542  if(fabs(normal_distance) > 0.0000001)
4543  shade=distance/sqrt(normal_distance);
4544  }
4545  }
4546  if(!color_shading){
4547  *q = tqRgba((unsigned char)(shade),
4548  (unsigned char)(shade),
4549  (unsigned char)(shade),
4550  tqAlpha(*s1));
4551  }
4552  else{
4553  *q = tqRgba((unsigned char)((shade*tqRed(*s1))/(MaxRGB+1)),
4554  (unsigned char)((shade*tqGreen(*s1))/(MaxRGB+1)),
4555  (unsigned char)((shade*tqBlue(*s1))/(MaxRGB+1)),
4556  tqAlpha(*s1));
4557  }
4558  ++s0;
4559  ++s1;
4560  ++s2;
4561  q++;
4562  }
4563  *q++=(*s1);
4564  }
4565  }
4566  else{ // PsudeoClass source image
4567  unsigned char *p, *s0, *s1, *s2;
4568  int scanLineIdx;
4569  unsigned int *cTable = (unsigned int *)src.tqcolorTable();
4570  for(y=0; y < src.height(); ++y){
4571  scanLineIdx = TQMIN(TQMAX(y-1,0),src.height()-3);
4572  p = (unsigned char *)src.scanLine(scanLineIdx);
4573  q = (unsigned int *)dest.scanLine(y);
4574  // shade this row of pixels.
4575  s0 = p;
4576  s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
4577  s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
4578  *q++=(*(cTable+(*s1)));
4579  ++p;
4580  ++s0;
4581  ++s1;
4582  ++s2;
4583  for(x=1; x < src.width()-1; ++x){
4584  // determine the surface normal and compute shading.
4585  normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
4586  (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
4587  (double) intensityValue(*(cTable+(*(s2+1))));
4588  normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
4589  (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
4590  (double) intensityValue(*(cTable+(*(s0+1))));
4591  if((normal.x == 0) && (normal.y == 0))
4592  shade=light.z;
4593  else{
4594  shade=0.0;
4595  distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
4596  if (distance > 0.0){
4597  normal_distance=
4598  normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
4599  if(fabs(normal_distance) > 0.0000001)
4600  shade=distance/sqrt(normal_distance);
4601  }
4602  }
4603  if(!color_shading){
4604  *q = tqRgba((unsigned char)(shade),
4605  (unsigned char)(shade),
4606  (unsigned char)(shade),
4607  tqAlpha(*(cTable+(*s1))));
4608  }
4609  else{
4610  *q = tqRgba((unsigned char)((shade*tqRed(*(cTable+(*s1))))/(MaxRGB+1)),
4611  (unsigned char)((shade*tqGreen(*(cTable+(*s1))))/(MaxRGB+1)),
4612  (unsigned char)((shade*tqBlue(*(cTable+(*s1))))/(MaxRGB+1)),
4613  tqAlpha(*s1));
4614  }
4615  ++s0;
4616  ++s1;
4617  ++s2;
4618  q++;
4619  }
4620  *q++=(*(cTable+(*s1)));
4621  }
4622  }
4623  return(dest);
4624 }
4625 
4626 // High quality, expensive HSV contrast. You can do a faster one by just
4627 // taking a grayscale threshold (ie: 128) and incrementing RGB color
4628 // channels above it and decrementing those below it, but this gives much
4629 // better results. (mosfet 12/28/01)
4630 void KImageEffect::contrastHSV(TQImage &img, bool sharpen)
4631 {
4632  int i, sign;
4633  unsigned int *data;
4634  int count;
4635  double brightness, scale, theta;
4636  TQColor c;
4637  int h, s, v;
4638 
4639  sign = sharpen ? 1 : -1;
4640  scale=0.5000000000000001;
4641  if(img.depth() > 8){
4642  count = img.width()*img.height();
4643  data = (unsigned int *)img.bits();
4644  }
4645  else{
4646  count = img.numColors();
4647  data = (unsigned int *)img.tqcolorTable();
4648  }
4649  for(i=0; i < count; ++i){
4650  c.setRgb(data[i]);
4651  c.hsv(&h, &s, &v);
4652  brightness = v/255.0;
4653  theta=(brightness-0.5)*M_PI;
4654  brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
4655  if (brightness > 1.0)
4656  brightness=1.0;
4657  else
4658  if (brightness < 0)
4659  brightness=0.0;
4660  v = (int)(brightness*255);
4661  c.setHsv(h, s, v);
4662  data[i] = tqRgba(c.red(), c.green(), c.blue(), tqAlpha(data[i]));
4663  }
4664 }
4665 
4666 
4667 struct BumpmapParams {
4668  BumpmapParams( double bm_azimuth, double bm_elevation,
4669  int bm_depth, KImageEffect::BumpmapType bm_type,
4670  bool invert ) {
4671  /* Convert to radians */
4672  double azimuth = DegreesToRadians( bm_azimuth );
4673  double elevation = DegreesToRadians( bm_elevation );
4674 
4675  /* Calculate the light vector */
4676  lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
4677  ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
4678  int lz = (int)( sin(elevation) * 255.0 );
4679 
4680  /* Calculate constant Z component of surface normal */
4681  int nz = (6 * 255) / bm_depth;
4682  nz2 = nz * nz;
4683  nzlz = nz * lz;
4684 
4685  /* Optimize for vertical normals */
4686  background = lz;
4687 
4688  /* Calculate darkness compensation factor */
4689  compensation = sin(elevation);
4690 
4691  /* Create look-up table for map type */
4692  for (int i = 0; i < 256; i++)
4693  {
4694  double n = 0;
4695  switch (bm_type)
4696  {
4697  case KImageEffect::Spherical:
4698  n = i / 255.0 - 1.0;
4699  lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
4700  break;
4701 
4702  case KImageEffect::Sinuosidal:
4703  n = i / 255.0;
4704  lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
4705  2.0 + 0.5);
4706  break;
4707 
4708  case KImageEffect::Linear:
4709  default:
4710  lut[i] = i;
4711  }
4712 
4713  if (invert)
4714  lut[i] = 255 - lut[i];
4715  }
4716  }
4717  int lx, ly;
4718  int nz2, nzlz;
4719  int background;
4720  double compensation;
4721  uchar lut[256];
4722 };
4723 
4724 
4725 static void bumpmap_convert_row( uint *row,
4726  int width,
4727  int bpp,
4728  int has_alpha,
4729  uchar *lut,
4730  int waterlevel )
4731 {
4732  uint *p;
4733 
4734  p = row;
4735 
4736  has_alpha = has_alpha ? 1 : 0;
4737 
4738  if (bpp >= 3)
4739  for (; width; width--)
4740  {
4741  if (has_alpha) {
4742  unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
4743  *p++ = lut[(unsigned int) ( waterlevel +
4744  ( ( idx -
4745  waterlevel) * tqBlue( *row )) / 255.0 )];
4746  } else {
4747  unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
4748  *p++ = lut[idx];
4749  }
4750 
4751  ++row;
4752  }
4753 }
4754 
4755 static void bumpmap_row( uint *src,
4756  uint *dest,
4757  int width,
4758  int bpp,
4759  int has_alpha,
4760  uint *bm_row1,
4761  uint *bm_row2,
4762  uint *bm_row3,
4763  int bm_width,
4764  int bm_xofs,
4765  bool tiled,
4766  bool row_in_bumpmap,
4767  int ambient,
4768  bool compensate,
4769  BumpmapParams *params )
4770 {
4771  int xofs1, xofs2, xofs3;
4772  int shade;
4773  int ndotl;
4774  int nx, ny;
4775  int x;
4776  int tmp;
4777 
4778  tmp = bm_xofs;
4779  xofs2 = MOD(tmp, bm_width);
4780 
4781  for (x = 0; x < width; x++)
4782  {
4783  /* Calculate surface normal from bump map */
4784 
4785  if (tiled || (row_in_bumpmap &&
4786  x >= - tmp && x < - tmp + bm_width)) {
4787  if (tiled) {
4788  xofs1 = MOD(xofs2 - 1, bm_width);
4789  xofs3 = MOD(xofs2 + 1, bm_width);
4790  } else {
4791  xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1);
4792  xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1);
4793  }
4794  nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
4795  bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
4796  ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
4797  bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
4798  } else {
4799  nx = ny = 0;
4800  }
4801 
4802  /* Shade */
4803 
4804  if ((nx == 0) && (ny == 0))
4805  shade = params->background;
4806  else {
4807  ndotl = nx * params->lx + ny * params->ly + params->nzlz;
4808 
4809  if (ndotl < 0)
4810  shade = (int)( params->compensation * ambient );
4811  else {
4812  shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) );
4813 
4814  shade = (int)( shade + TQMAX(0.0, (255 * params->compensation - shade)) *
4815  ambient / 255 );
4816  }
4817  }
4818 
4819  /* Paint */
4820 
4825  if (compensate) {
4826  int red = (int)((tqRed( *src ) * shade) / (params->compensation * 255));
4827  int green = (int)((tqGreen( *src ) * shade) / (params->compensation * 255));
4828  int blue = (int)((tqBlue( *src ) * shade) / (params->compensation * 255));
4829  int alpha = (int)((tqAlpha( *src ) * shade) / (params->compensation * 255));
4830  ++src;
4831  *dest++ = tqRgba( red, green, blue, alpha );
4832  } else {
4833  int red = tqRed( *src ) * shade / 255;
4834  int green = tqGreen( *src ) * shade / 255;
4835  int blue = tqBlue( *src ) * shade / 255;
4836  int alpha = tqAlpha( *src ) * shade / 255;
4837  ++src;
4838  *dest++ = tqRgba( red, green, blue, alpha );
4839  }
4840 
4841  /* Next pixel */
4842 
4843  if (++xofs2 == bm_width)
4844  xofs2 = 0;
4845  }
4846 }
4847 
4867 TQImage KImageEffect::bumpmap(TQImage &img, TQImage &map, double azimuth, double elevation,
4868  int depth, int xofs, int yofs, int waterlevel,
4869  int ambient, bool compensate, bool invert,
4870  BumpmapType type, bool tiled)
4871 {
4872  TQImage dst;
4873 
4874  if ( img.depth() != 32 || img.depth() != 32 ) {
4875  tqWarning( "Bump-mapping effect works only with 32 bit images");
4876  return dst;
4877  }
4878 
4879  dst.create( img.width(), img.height(), img.depth() );
4880  int bm_width = map.width();
4881  int bm_height = map.height();
4882  int bm_bpp = map.depth();
4883  int bm_has_alpha = map.hasAlphaBuffer();
4884 
4885  int yofs1, yofs2, yofs3;
4886 
4887  if ( tiled ) {
4888  yofs2 = MOD( yofs, bm_height );
4889  yofs1 = MOD( yofs2 - 1, bm_height);
4890  yofs3 = MOD( yofs2 + 1, bm_height);
4891  } else {
4892  yofs1 = 0;
4893  yofs2 = 0;
4894  yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 );
4895  }
4896 
4897  BumpmapParams params( azimuth, elevation, depth, type, invert );
4898 
4899  uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
4900  uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
4901  uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
4902 
4903  bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
4904  bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
4905  bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
4906 
4907  for (int y = 0; y < img.height(); ++y)
4908  {
4909  int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
4910 
4911  uint* src_row = (unsigned int*)img.scanLine( y );
4912  uint* dest_row = (unsigned int*)dst.scanLine( y );
4913 
4914  bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
4915  bm_row1, bm_row2, bm_row3, bm_width, xofs,
4916  tiled,
4917  row_in_bumpmap, ambient, compensate,
4918  &params );
4919 
4920  /* Next line */
4921 
4922  if (tiled || row_in_bumpmap)
4923  {
4924  uint* bm_tmprow = bm_row1;
4925  bm_row1 = bm_row2;
4926  bm_row2 = bm_row3;
4927  bm_row3 = bm_tmprow;
4928 
4929  if (++yofs2 == bm_height)
4930  yofs2 = 0;
4931 
4932  if (tiled)
4933  yofs3 = MOD(yofs2 + 1, bm_height);
4934  else
4935  yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1);
4936 
4937  bm_row3 = (unsigned int*)map.scanLine( yofs3 );
4938  bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
4939  params.lut, waterlevel );
4940  }
4941  }
4942  return dst;
4943 }
4944 
4953 TQImage KImageEffect::convertToPremultipliedAlpha(TQImage input) {
4954  TQImage alphaImage = input;
4955  if (!alphaImage.isNull()) alphaImage = alphaImage.convertDepth( 32 );
4956 
4957  int w = alphaImage.width();
4958  int h = alphaImage.height();
4959 
4960  int r;
4961  int g;
4962  int b;
4963  int a;
4964  float alpha_adjust;
4965  TQRgb l;
4966  TQRgb *ls;
4967  for (int y = 0; y < h; ++y) {
4968  ls = (TQRgb *)alphaImage.scanLine( y );
4969  for (int x = 0; x < w; ++x) {
4970  l = ls[x];
4971  alpha_adjust = (tqAlpha( l )/255.0);
4972  r = int( tqRed( l ) * alpha_adjust );
4973  g = int( tqGreen( l ) * alpha_adjust );
4974  b = int( tqBlue( l ) * alpha_adjust );
4975  a = int( tqAlpha( l ) * 1.0 );
4976  ls[x] = tqRgba( r, g, b, a );
4977  }
4978  }
4979  return alphaImage;
4980 }
KImageEffect::Red
Red channel.
Definition: kimageeffect.h:73
KImageEffect::spread
static TQImage spread(TQImage &src, unsigned int amount=3)
Randomly displaces pixels.
Definition: kimageeffect.cpp:3456
KImageEffect::blur
static TQImage blur(TQImage &src, double radius, double sigma)
Blurs an image by convolving pixel neighborhoods.
Definition: kimageeffect.cpp:4272
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:2273
KImageEffect::NWLite
Lighting from the top left of the image.
Definition: kimageeffect.h:85
KCPUInfo::haveExtension
static bool haveExtension(unsigned int extension)
Returns true if the processor supports extension, and false otherwise.
Definition: kcpuinfo.h:62
KImageEffect::fade
static TQImage & fade(TQImage &image, float val, const TQColor &color)
Fade an image to a certain background color.
Definition: kimageeffect.cpp:2056
KImageEffect::GaussianNoise
Gaussian distribution.
Definition: kimageeffect.h:109
KImageEffect::Lighting
Lighting
This enum provides a lighting direction specification.
Definition: kimageeffect.h:84
KImageEffect::oilPaintConvolve
static TQImage oilPaintConvolve(TQImage &src, double radius)
Produces an oil painting effect.
Definition: kimageeffect.cpp:3624
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:4492
KImageEffect::Gray
Grey channel.
Definition: kimageeffect.h:76
KImageEffect::ModulationType
ModulationType
This enum provides a modulation type specification.
Definition: kimageeffect.h:98
KImageEffect::BumpmapType
BumpmapType
This enum lists possible bumpmapping implementations.
Definition: kimageeffect.h:129
KImageEffect::rotate
static TQImage rotate(TQImage &src, RotateDirection r)
Rotates the image by the specified amount.
Definition: kimageeffect.cpp:3335
KImageEffect::flatten
static TQImage & flatten(TQImage &image, const TQColor &ca, const TQColor &cb, int ncols=0)
This recolors a pixmap.
Definition: kimageeffect.cpp:1959
KImageEffect::ImpulseNoise
Impulse distribution.
Definition: kimageeffect.h:111
KImageEffect::SELite
Lighting from the bottom right of the image.
Definition: kimageeffect.h:89
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::implode
static TQImage implode(TQImage &src, double factor=30.0, unsigned int background=0xFFFFFFFF)
Implodes an image by a specified percent.
Definition: kimageeffect.cpp:3254
KImageEffect::CenteredMaxpect
Center and scale aspect.
Definition: kimageeffect.h:330
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:4867
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::MultiplicativeGaussianNoise
Multiplicative Gaussian distribution.
Definition: kimageeffect.h:110
KImageEffect::GradientType
GradientType
This enum provides a gradient type specification.
Definition: kimageeffect.h:58
KImageEffect::RGBComponent
RGBComponent
This enum provides a RGB channel specification.
Definition: kimageeffect.h:73
KImageEffect::Tiled
Tile top image on bottom image.
Definition: kimageeffect.h:328
KImageEffect::threshold
static void threshold(TQImage &img, unsigned int value=128)
Thresholds the reference image.
Definition: kimageeffect.cpp:2822
KImageEffect::convertToPremultipliedAlpha
static TQImage convertToPremultipliedAlpha(TQImage input)
Convert an image with standard alpha to premultiplied alpha.
Definition: kimageeffect.cpp:4953
KImageEffect::despeckle
static TQImage despeckle(TQImage &src)
Minimizes speckle noise in the source image using the 8 hull algorithm.
Definition: kimageeffect.cpp:2918
KImageEffect::WestLite
Lighting from the left of the image.
Definition: kimageeffect.h:86
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:3433
KImageEffect::NorthLite
Lighting from the top of the image.
Definition: kimageeffect.h:84
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::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:2717
KImageEffect::RotateDirection
RotateDirection
This enum provides a rotation specification.
Definition: kimageeffect.h:120
KImageEffect::Green
Green channel.
Definition: kimageeffect.h:74
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:3585
KImageEffect::normalize
static void normalize(TQImage &img)
Normalises the pixel values to span the full range of color values.
Definition: kimageeffect.cpp:3705
KImageEffect::Intensity
Modulate image intensity.
Definition: kimageeffect.h:98
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::NoiseType
NoiseType
This enum provides a noise type specification.
Definition: kimageeffect.h:108
KImageEffect::Centered
Center top image on botton image.
Definition: kimageeffect.h:327
KImageEffect::HueShift
Modulate image hue.
Definition: kimageeffect.h:100
KCPUInfo::IntelMMX
Intel's MMX instructions.
Definition: kcpuinfo.h:47
KImageEffect::desaturate
static TQImage & desaturate(TQImage &image, float desat=0.3)
Desaturate an image evenly.
Definition: kimageeffect.cpp:2188
KImageEffect::Rotate180
Rotate 180 degrees.
Definition: kimageeffect.h:121
KImageEffect::Contrast
Modulate image contrast.
Definition: kimageeffect.h:101
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:2640
KImageEffect::intensity
static TQImage & intensity(TQImage &image, float percent)
Either brighten or dim the image by a specified percent.
Definition: kimageeffect.cpp:654
KImageEffect::Scaled
Scale.
Definition: kimageeffect.h:332
KImageEffect::CenteredAutoFit
Center and scale or scale aspect.
Definition: kimageeffect.h:333
KImageEffect::addNoise
static TQImage addNoise(TQImage &src, NoiseType type=GaussianNoise)
Adds noise to an image.
Definition: kimageeffect.cpp:3124
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:4015
KCPUInfo::IntelSSE2
Intel's SSE2 instructions.
Definition: kcpuinfo.h:49
KImageEffect::PoissonNoise
Poisson distribution.
Definition: kimageeffect.h:113
KImageEffect::contrastHSV
static void contrastHSV(TQImage &img, bool sharpen=true)
High quality, expensive HSV contrast.
Definition: kimageeffect.cpp:4630
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:2526
KImageEffect::TiledMaxpect
Tile and scale aspect.
Definition: kimageeffect.h:331
KImageEffect::Disposition
Disposition
Disposition of a source image on top of a destination image.
Definition: kimageeffect.h:326
KImageEffect::All
All channels.
Definition: kimageeffect.h:77
KImageEffect::Rotate90
Rotate 90 degrees to the right.
Definition: kimageeffect.h:120
KImageEffect::sharpen
static TQImage sharpen(TQImage &src, double radius, double sigma)
Sharpens the pixels in the image using pixel neighborhoods.
Definition: kimageeffect.cpp:4452
KImageEffect::hash
static TQImage & hash(TQImage &image, Lighting lite=NorthLite, unsigned int spacing=0)
Build a hash on any given QImage.
Definition: kimageeffect.cpp:1878
KImageEffect::charcoal
static TQImage charcoal(TQImage &src, double radius, double sigma)
Produces a neat little "charcoal" effect.
Definition: kimageeffect.cpp:3695
KImageEffect::Saturation
Modulate image saturation.
Definition: kimageeffect.h:99
KImageEffect::equalize
static void equalize(TQImage &img)
Performs histogram equalisation on the reference image.
Definition: kimageeffect.cpp:3915
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:3507
KImageEffect::LaplacianNoise
Laplacian distribution.
Definition: kimageeffect.h:112
KImageEffect::EastLite
Lighting from the right of the image.
Definition: kimageeffect.h:90
KImageEffect::UniformNoise
Uniform distribution.
Definition: kimageeffect.h:108
KImageEffect::CenterTiled
Center and tile top image on bottom image.
Definition: kimageeffect.h:329
KImageEffect::sample
static TQImage sample(TQImage &src, int w, int h)
Scales an image using simple pixel sampling.
Definition: kimageeffect.cpp:2759
KImageEffect::toGray
static TQImage & toGray(TQImage &image, bool fast=false)
Convert an image to grayscale.
Definition: kimageeffect.cpp:2138
KImageEffect::contrast
static TQImage & contrast(TQImage &image, int c)
Fast, but low quality contrast of an image.
Definition: kimageeffect.cpp:2211
endl
kndbgstream & endl(kndbgstream &s)
KImageEffect::NoImage
Don't overlay.
Definition: kimageeffect.h:326
KImageEffect::Rotate270
Rotate 90 degrees to the left.
Definition: kimageeffect.h:122
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::SouthLite
Lighting from the bottom of the image.
Definition: kimageeffect.h:88
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:3618
KImageEffect::SWLite
Lighting from the bottom left of the image.
Definition: kimageeffect.h:87
KImageEffect::Blue
Blue channel.
Definition: kimageeffect.h:75
KImageEffect::NELite
Lighting from the top right of the image.
Definition: kimageeffect.h:91
KImageEffect::emboss
static TQImage emboss(TQImage &src, double radius, double sigma)
Embosses the source image.
Definition: kimageeffect.cpp:4053

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
  •     tdecore
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  • tdeioslave
  •   http
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdefx by doxygen 1.8.8
This website is maintained by Timothy Pearson.