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

tdefx

kimageeffect.cpp
00001 /* This file is part of the KDE libraries
00002     Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley <mosfet@kde.org>
00003     (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
00004     (C) 1998, 1999 Dirk Mueller <mueller@kde.org>
00005     (C) 1999 Geert Jansen <g.t.jansen@stud.tue.nl>
00006     (C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
00007     (C) 2004 Zack Rusin <zack@kde.org>
00008 
00009 Redistribution and use in source and binary forms, with or without
00010 modification, are permitted provided that the following conditions
00011 are met:
00012 
00013 1. Redistributions of source code must retain the above copyright
00014    notice, this list of conditions and the following disclaimer.
00015 2. Redistributions in binary form must reproduce the above copyright
00016    notice, this list of conditions and the following disclaimer in the
00017    documentation and/or other materials provided with the distribution.
00018 
00019 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
00020 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
00021 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00022 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
00023 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
00024 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00025 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
00026 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00027 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
00028 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029 
00030 */
00031 
00032 // $Id$
00033 
00034 #include <math.h>
00035 #include <assert.h>
00036 
00037 #include <tqimage.h>
00038 #include <stdlib.h>
00039 #include <iostream>
00040 
00041 #include "kimageeffect.h"
00042 #include "kcpuinfo.h"
00043 
00044 #include <config.h>
00045 
00046 #if 0
00047 //disabled until #74478 fixed.
00048 
00049 #if defined(__i386__) && ( defined(__GNUC__) || defined(__INTEL_COMPILER) )
00050 #  if defined( HAVE_X86_MMX )
00051 #    define USE_MMX_INLINE_ASM
00052 #  endif
00053 #  if defined( HAVE_X86_SSE2 )
00054 #    define USE_SSE2_INLINE_ASM
00055 #  endif
00056 #endif
00057 
00058 #endif
00059 //======================================================================
00060 //
00061 // Utility stuff for effects ported from ImageMagick to QImage
00062 //
00063 //======================================================================
00064 #define MaxRGB 255L
00065 #define DegreesToRadians(x) ((x)*M_PI/180.0)
00066 #define MagickSQ2PI 2.50662827463100024161235523934010416269302368164062
00067 #define MagickEpsilon  1.0e-12
00068 #define MagickPI  3.14159265358979323846264338327950288419716939937510
00069 #define MOD(x, y) ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y))
00070 
00076 #define FXCLAMP(x,low,high) fxClamp(x,low,high)
00077 template<class T>
00078 inline const T& fxClamp( const T& x, const T& low, const T& high )
00079 {
00080     if ( x < low )       return low;
00081     else if ( x > high ) return high;
00082     else                 return x;
00083 }
00084 
00085 static inline unsigned int intensityValue(unsigned int color)
00086 {
00087     return((unsigned int)((0.299*tqRed(color) +
00088                            0.587*tqGreen(color) +
00089                            0.1140000000000001*tqBlue(color))));
00090 }
00091 
00092 template<typename T>
00093 static inline void liberateMemory(T **memory)
00094 {
00095     assert(memory != NULL);
00096     if(*memory == NULL) return;
00097     free((char*)*memory);
00098     *memory=NULL;
00099 }
00100 
00101 struct double_packet
00102 {
00103     double red;
00104     double green;
00105     double blue;
00106     double alpha;
00107 };
00108 
00109 struct short_packet
00110 {
00111     unsigned short int red;
00112     unsigned short int green;
00113     unsigned short int blue;
00114     unsigned short int alpha;
00115 };
00116 
00117 
00118 //======================================================================
00119 //
00120 // Gradient effects
00121 //
00122 //======================================================================
00123 
00124 TQImage KImageEffect::gradient(const TQSize &size, const TQColor &ca,
00125     const TQColor &cb, GradientType eff, int ncols)
00126 {
00127     int rDiff, gDiff, bDiff;
00128     int rca, gca, bca, rcb, gcb, bcb;
00129 
00130     TQImage image(size, 32);
00131 
00132     if (size.width() == 0 || size.height() == 0) {
00133 #ifndef NDEBUG
00134       std::cerr << "WARNING: KImageEffect::gradient: invalid image" << std::endl;
00135 #endif
00136       return image;
00137     }
00138 
00139     int x, y;
00140 
00141     rDiff = (rcb = cb.red())   - (rca = ca.red());
00142     gDiff = (gcb = cb.green()) - (gca = ca.green());
00143     bDiff = (bcb = cb.blue())  - (bca = ca.blue());
00144 
00145     if( eff == VerticalGradient || eff == HorizontalGradient ){
00146 
00147         uint *p;
00148         uint rgb;
00149 
00150         int rl = rca << 16;
00151         int gl = gca << 16;
00152         int bl = bca << 16;
00153 
00154         if( eff == VerticalGradient ) {
00155 
00156             int rcdelta = ((1<<16) / size.height()) * rDiff;
00157             int gcdelta = ((1<<16) / size.height()) * gDiff;
00158             int bcdelta = ((1<<16) / size.height()) * bDiff;
00159 
00160             for ( y = 0; y < size.height(); y++ ) {
00161                 p = (uint *) image.scanLine(y);
00162 
00163                 rl += rcdelta;
00164                 gl += gcdelta;
00165                 bl += bcdelta;
00166 
00167                 rgb = tqRgb( (rl>>16), (gl>>16), (bl>>16) );
00168 
00169                 for( x = 0; x < size.width(); x++ ) {
00170                     *p = rgb;
00171                     p++;
00172                 }
00173             }
00174 
00175         }
00176         else {                  // must be HorizontalGradient
00177 
00178             unsigned int *o_src = (unsigned int *)image.scanLine(0);
00179             unsigned int *src = o_src;
00180 
00181             int rcdelta = ((1<<16) / size.width()) * rDiff;
00182             int gcdelta = ((1<<16) / size.width()) * gDiff;
00183             int bcdelta = ((1<<16) / size.width()) * bDiff;
00184 
00185             for( x = 0; x < size.width(); x++) {
00186 
00187                 rl += rcdelta;
00188                 gl += gcdelta;
00189                 bl += bcdelta;
00190 
00191                 *src++ = tqRgb( (rl>>16), (gl>>16), (bl>>16));
00192             }
00193 
00194             src = o_src;
00195 
00196             // Believe it or not, manually copying in a for loop is faster
00197             // than calling memcpy for each scanline (on the order of ms...).
00198             // I think this is due to the function call overhead (mosfet).
00199 
00200             for (y = 1; y < size.height(); ++y) {
00201 
00202                 p = (unsigned int *)image.scanLine(y);
00203                 src = o_src;
00204                 for(x=0; x < size.width(); ++x)
00205                     *p++ = *src++;
00206             }
00207         }
00208     }
00209 
00210     else {
00211 
00212         float rfd, gfd, bfd;
00213         float rd = rca, gd = gca, bd = bca;
00214 
00215         unsigned char *xtable[3];
00216         unsigned char *ytable[3];
00217 
00218         unsigned int w = size.width(), h = size.height();
00219         xtable[0] = new unsigned char[w];
00220         xtable[1] = new unsigned char[w];
00221         xtable[2] = new unsigned char[w];
00222         ytable[0] = new unsigned char[h];
00223         ytable[1] = new unsigned char[h];
00224         ytable[2] = new unsigned char[h];
00225         w*=2, h*=2;
00226 
00227         if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) {
00228             // Diagonal dgradient code inspired by BlackBox (mosfet)
00229             // BlackBox dgradient is (C) Brad Hughes, <bhughes@tcac.net> and
00230             // Mike Cole <mike@mydot.com>.
00231 
00232             rfd = (float)rDiff/w;
00233             gfd = (float)gDiff/w;
00234             bfd = (float)bDiff/w;
00235 
00236             int dir;
00237             for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) {
00238                 dir = eff == DiagonalGradient? x : size.width() - x - 1;
00239                 xtable[0][dir] = (unsigned char) rd;
00240                 xtable[1][dir] = (unsigned char) gd;
00241                 xtable[2][dir] = (unsigned char) bd;
00242             }
00243             rfd = (float)rDiff/h;
00244             gfd = (float)gDiff/h;
00245             bfd = (float)bDiff/h;
00246             rd = gd = bd = 0;
00247             for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) {
00248                 ytable[0][y] = (unsigned char) rd;
00249                 ytable[1][y] = (unsigned char) gd;
00250                 ytable[2][y] = (unsigned char) bd;
00251             }
00252 
00253             for (y = 0; y < size.height(); y++) {
00254                 unsigned int *scanline = (unsigned int *)image.scanLine(y);
00255                 for (x = 0; x < size.width(); x++) {
00256                     scanline[x] = tqRgb(xtable[0][x] + ytable[0][y],
00257                                        xtable[1][x] + ytable[1][y],
00258                                        xtable[2][x] + ytable[2][y]);
00259                 }
00260             }
00261         }
00262 
00263         else if (eff == RectangleGradient ||
00264                  eff == PyramidGradient ||
00265                  eff == PipeCrossGradient ||
00266                  eff == EllipticGradient)
00267         {
00268             int rSign = rDiff>0? 1: -1;
00269             int gSign = gDiff>0? 1: -1;
00270             int bSign = bDiff>0? 1: -1;
00271 
00272             rfd = (float)rDiff / size.width();
00273             gfd = (float)gDiff / size.width();
00274             bfd = (float)bDiff / size.width();
00275 
00276             rd = (float)rDiff/2;
00277             gd = (float)gDiff/2;
00278             bd = (float)bDiff/2;
00279 
00280             for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd)
00281             {
00282                 xtable[0][x] = (unsigned char) abs((int)rd);
00283                 xtable[1][x] = (unsigned char) abs((int)gd);
00284                 xtable[2][x] = (unsigned char) abs((int)bd);
00285             }
00286 
00287             rfd = (float)rDiff/size.height();
00288             gfd = (float)gDiff/size.height();
00289             bfd = (float)bDiff/size.height();
00290 
00291             rd = (float)rDiff/2;
00292             gd = (float)gDiff/2;
00293             bd = (float)bDiff/2;
00294 
00295             for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd)
00296             {
00297                 ytable[0][y] = (unsigned char) abs((int)rd);
00298                 ytable[1][y] = (unsigned char) abs((int)gd);
00299                 ytable[2][y] = (unsigned char) abs((int)bd);
00300             }
00301 
00302             int h = (size.height()+1)>>1;
00303             for (y = 0; y < h; y++) {
00304                 unsigned int *sl1 = (unsigned int *)image.scanLine(y);
00305                 unsigned int *sl2 = (unsigned int *)image.scanLine(TQMAX(size.height()-y-1, y));
00306 
00307                 int w = (size.width()+1)>>1;
00308                 int x2 = size.width()-1;
00309 
00310                 for (x = 0; x < w; x++, x2--) {
00311             unsigned int rgb = 0;
00312                     if (eff == PyramidGradient) {
00313                         rgb = tqRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
00314                                    gcb-gSign*(xtable[1][x]+ytable[1][y]),
00315                                    bcb-bSign*(xtable[2][x]+ytable[2][y]));
00316                     }
00317                     if (eff == RectangleGradient) {
00318                         rgb = tqRgb(rcb - rSign *
00319                                    TQMAX(xtable[0][x], ytable[0][y]) * 2,
00320                                    gcb - gSign *
00321                                    TQMAX(xtable[1][x], ytable[1][y]) * 2,
00322                                    bcb - bSign *
00323                                    TQMAX(xtable[2][x], ytable[2][y]) * 2);
00324                     }
00325                     if (eff == PipeCrossGradient) {
00326                         rgb = tqRgb(rcb - rSign *
00327                                    TQMIN(xtable[0][x], ytable[0][y]) * 2,
00328                                    gcb - gSign *
00329                                    TQMIN(xtable[1][x], ytable[1][y]) * 2,
00330                                    bcb - bSign *
00331                                    TQMIN(xtable[2][x], ytable[2][y]) * 2);
00332                     }
00333                     if (eff == EllipticGradient) {
00334                         rgb = tqRgb(rcb - rSign *
00335                                    (int)sqrt((xtable[0][x]*xtable[0][x] +
00336                                               ytable[0][y]*ytable[0][y])*2.0),
00337                                    gcb - gSign *
00338                                    (int)sqrt((xtable[1][x]*xtable[1][x] +
00339                                               ytable[1][y]*ytable[1][y])*2.0),
00340                                    bcb - bSign *
00341                                    (int)sqrt((xtable[2][x]*xtable[2][x] +
00342                                               ytable[2][y]*ytable[2][y])*2.0));
00343                     }
00344 
00345                     sl1[x] = sl2[x] = rgb;
00346                     sl1[x2] = sl2[x2] = rgb;
00347                 }
00348             }
00349         }
00350 
00351         delete [] xtable[0];
00352         delete [] xtable[1];
00353         delete [] xtable[2];
00354         delete [] ytable[0];
00355         delete [] ytable[1];
00356         delete [] ytable[2];
00357     }
00358 
00359     // dither if necessary
00360     if (ncols && (TQPixmap::defaultDepth() < 15 )) {
00361     if ( ncols < 2 || ncols > 256 )
00362         ncols = 3;
00363     TQColor *dPal = new TQColor[ncols];
00364     for (int i=0; i<ncols; i++) {
00365         dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
00366                  gca + gDiff * i / ( ncols - 1 ),
00367                  bca + bDiff * i / ( ncols - 1 ) );
00368     }
00369         dither(image, dPal, ncols);
00370         delete [] dPal;
00371     }
00372 
00373     return image;
00374 }
00375 
00376 
00377 // -----------------------------------------------------------------------------
00378 
00379 //CT this was (before Dirk A. Mueller's speedup changes)
00380 //   merely the same code as in the above method, but it's supposedly
00381 //   way less performant since it introduces a lot of supplementary tests
00382 //   and simple math operations for the calculus of the balance.
00383 //      (surprizingly, it isn't less performant, in the contrary :-)
00384 //   Yes, I could have merged them, but then the excellent performance of
00385 //   the balanced code would suffer with no other gain than a mere
00386 //   source code and byte code size economy.
00387 
00388 TQImage KImageEffect::unbalancedGradient(const TQSize &size, const TQColor &ca,
00389     const TQColor &cb, GradientType eff, int xfactor, int yfactor,
00390     int ncols)
00391 {
00392     int dir; // general parameter used for direction switches
00393 
00394     bool _xanti = false , _yanti = false;
00395 
00396     if (xfactor < 0) _xanti = true; // negative on X direction
00397     if (yfactor < 0) _yanti = true; // negative on Y direction
00398 
00399     xfactor = abs(xfactor);
00400     yfactor = abs(yfactor);
00401 
00402     if (!xfactor) xfactor = 1;
00403     if (!yfactor) yfactor = 1;
00404 
00405     if (xfactor > 200 ) xfactor = 200;
00406     if (yfactor > 200 ) yfactor = 200;
00407 
00408 
00409     //    float xbal = xfactor/5000.;
00410     //    float ybal = yfactor/5000.;
00411     float xbal = xfactor/30./size.width();
00412     float ybal = yfactor/30./size.height();
00413     float rat;
00414 
00415     int rDiff, gDiff, bDiff;
00416     int rca, gca, bca, rcb, gcb, bcb;
00417 
00418     TQImage image(size, 32);
00419 
00420     if (size.width() == 0 || size.height() == 0) {
00421 #ifndef NDEBUG
00422       std::cerr << "WARNING: KImageEffect::unbalancedGradient : invalid image\n";
00423 #endif
00424       return image;
00425     }
00426 
00427     int x, y;
00428     unsigned int *scanline;
00429 
00430     rDiff = (rcb = cb.red())   - (rca = ca.red());
00431     gDiff = (gcb = cb.green()) - (gca = ca.green());
00432     bDiff = (bcb = cb.blue())  - (bca = ca.blue());
00433 
00434     if( eff == VerticalGradient || eff == HorizontalGradient){
00435         TQColor cRow;
00436 
00437         uint *p;
00438         uint rgbRow;
00439 
00440     if( eff == VerticalGradient) {
00441       for ( y = 0; y < size.height(); y++ ) {
00442         dir = _yanti ? y : size.height() - 1 - y;
00443             p = (uint *) image.scanLine(dir);
00444             rat =  1 - exp( - (float)y  * ybal );
00445 
00446             cRow.setRgb( rcb - (int) ( rDiff * rat ),
00447                          gcb - (int) ( gDiff * rat ),
00448                          bcb - (int) ( bDiff * rat ) );
00449 
00450             rgbRow = cRow.rgb();
00451 
00452             for( x = 0; x < size.width(); x++ ) {
00453           *p = rgbRow;
00454           p++;
00455             }
00456       }
00457     }
00458     else {
00459 
00460       unsigned int *src = (unsigned int *)image.scanLine(0);
00461       for(x = 0; x < size.width(); x++ )
00462           {
00463           dir = _xanti ? x : size.width() - 1 - x;
00464           rat = 1 - exp( - (float)x  * xbal );
00465 
00466               src[dir] = tqRgb(rcb - (int) ( rDiff * rat ),
00467                               gcb - (int) ( gDiff * rat ),
00468                               bcb - (int) ( bDiff * rat ));
00469           }
00470 
00471       // Believe it or not, manually copying in a for loop is faster
00472       // than calling memcpy for each scanline (on the order of ms...).
00473       // I think this is due to the function call overhead (mosfet).
00474 
00475       for(y = 1; y < size.height(); ++y)
00476           {
00477           scanline = (unsigned int *)image.scanLine(y);
00478           for(x=0; x < size.width(); ++x)
00479                   scanline[x] = src[x];
00480           }
00481     }
00482     }
00483 
00484     else {
00485       int w=size.width(), h=size.height();
00486 
00487       unsigned char *xtable[3];
00488       unsigned char *ytable[3];
00489       xtable[0] = new unsigned char[w];
00490       xtable[1] = new unsigned char[w];
00491       xtable[2] = new unsigned char[w];
00492       ytable[0] = new unsigned char[h];
00493       ytable[1] = new unsigned char[h];
00494       ytable[2] = new unsigned char[h];
00495 
00496       if ( eff == DiagonalGradient || eff == CrossDiagonalGradient)
00497       {
00498       for (x = 0; x < w; x++) {
00499               dir = _xanti ? x : w - 1 - x;
00500               rat = 1 - exp( - (float)x * xbal );
00501 
00502               xtable[0][dir] = (unsigned char) ( rDiff/2 * rat );
00503               xtable[1][dir] = (unsigned char) ( gDiff/2 * rat );
00504               xtable[2][dir] = (unsigned char) ( bDiff/2 * rat );
00505           }
00506 
00507       for (y = 0; y < h; y++) {
00508               dir = _yanti ? y : h - 1 - y;
00509               rat =  1 - exp( - (float)y  * ybal );
00510 
00511               ytable[0][dir] = (unsigned char) ( rDiff/2 * rat );
00512               ytable[1][dir] = (unsigned char) ( gDiff/2 * rat );
00513               ytable[2][dir] = (unsigned char) ( bDiff/2 * rat );
00514           }
00515 
00516       for (y = 0; y < h; y++) {
00517               unsigned int *scanline = (unsigned int *)image.scanLine(y);
00518               for (x = 0; x < w; x++) {
00519                   scanline[x] = tqRgb(rcb - (xtable[0][x] + ytable[0][y]),
00520                                      gcb - (xtable[1][x] + ytable[1][y]),
00521                                      bcb - (xtable[2][x] + ytable[2][y]));
00522               }
00523           }
00524       }
00525 
00526       else if (eff == RectangleGradient ||
00527                eff == PyramidGradient ||
00528                eff == PipeCrossGradient ||
00529                eff == EllipticGradient)
00530       {
00531           int rSign = rDiff>0? 1: -1;
00532           int gSign = gDiff>0? 1: -1;
00533           int bSign = bDiff>0? 1: -1;
00534 
00535           for (x = 0; x < w; x++)
00536           {
00537               dir = _xanti ? x : w - 1 - x;
00538               rat =  1 - exp( - (float)x * xbal );
00539 
00540               xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
00541               xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
00542               xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
00543           }
00544 
00545           for (y = 0; y < h; y++)
00546           {
00547               dir = _yanti ? y : h - 1 - y;
00548 
00549               rat =  1 - exp( - (float)y * ybal );
00550 
00551               ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
00552               ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
00553               ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
00554           }
00555 
00556           for (y = 0; y < h; y++) {
00557               unsigned int *scanline = (unsigned int *)image.scanLine(y);
00558               for (x = 0; x < w; x++) {
00559                   if (eff == PyramidGradient)
00560                   {
00561                       scanline[x] = tqRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
00562                                          gcb-gSign*(xtable[1][x]+ytable[1][y]),
00563                                          bcb-bSign*(xtable[2][x]+ytable[2][y]));
00564                   }
00565                   else if (eff == RectangleGradient)
00566                   {
00567                       scanline[x] = tqRgb(rcb - rSign *
00568                                          TQMAX(xtable[0][x], ytable[0][y]) * 2,
00569                                          gcb - gSign *
00570                                          TQMAX(xtable[1][x], ytable[1][y]) * 2,
00571                                          bcb - bSign *
00572                                          TQMAX(xtable[2][x], ytable[2][y]) * 2);
00573                   }
00574                   else if (eff == PipeCrossGradient)
00575                   {
00576                       scanline[x] = tqRgb(rcb - rSign *
00577                                          TQMIN(xtable[0][x], ytable[0][y]) * 2,
00578                                          gcb - gSign *
00579                                          TQMIN(xtable[1][x], ytable[1][y]) * 2,
00580                                          bcb - bSign *
00581                                          TQMIN(xtable[2][x], ytable[2][y]) * 2);
00582                   }
00583                   else if (eff == EllipticGradient)
00584                   {
00585                       scanline[x] = tqRgb(rcb - rSign *
00586                                          (int)sqrt((xtable[0][x]*xtable[0][x] +
00587                                                     ytable[0][y]*ytable[0][y])*2.0),
00588                                          gcb - gSign *
00589                                          (int)sqrt((xtable[1][x]*xtable[1][x] +
00590                                                     ytable[1][y]*ytable[1][y])*2.0),
00591                                          bcb - bSign *
00592                                          (int)sqrt((xtable[2][x]*xtable[2][x] +
00593                                                     ytable[2][y]*ytable[2][y])*2.0));
00594                   }
00595               }
00596           }
00597       }
00598 
00599       if (ncols && (TQPixmap::defaultDepth() < 15 )) {
00600           if ( ncols < 2 || ncols > 256 )
00601               ncols = 3;
00602           TQColor *dPal = new TQColor[ncols];
00603           for (int i=0; i<ncols; i++) {
00604               dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
00605                                gca + gDiff * i / ( ncols - 1 ),
00606                                bca + bDiff * i / ( ncols - 1 ) );
00607           }
00608           dither(image, dPal, ncols);
00609           delete [] dPal;
00610       }
00611 
00612       delete [] xtable[0];
00613       delete [] xtable[1];
00614       delete [] xtable[2];
00615       delete [] ytable[0];
00616       delete [] ytable[1];
00617       delete [] ytable[2];
00618 
00619     }
00620 
00621     return image;
00622 }
00623 
00627 namespace {
00628 
00629 struct KIE4Pack
00630 {
00631     TQ_UINT16 data[4];
00632 };
00633 
00634 struct KIE8Pack
00635 {
00636     TQ_UINT16 data[8];
00637 };
00638 
00639 }
00640 
00641 //======================================================================
00642 //
00643 // Intensity effects
00644 //
00645 //======================================================================
00646 
00647 
00648 /* This builds a 256 byte unsigned char lookup table with all
00649  * the possible percent values prior to applying the effect, then uses
00650  * integer math for the pixels. For any image larger than 9x9 this will be
00651  * less expensive than doing a float operation on the 3 color components of
00652  * each pixel. (mosfet)
00653  */
00654 TQImage& KImageEffect::intensity(TQImage &image, float percent)
00655 {
00656     if (image.width() == 0 || image.height() == 0) {
00657 #ifndef NDEBUG
00658       std::cerr << "WARNING: KImageEffect::intensity : invalid image\n";
00659 #endif
00660       return image;
00661     }
00662 
00663     int segColors = image.depth() > 8 ? 256 : image.numColors();
00664     int pixels = image.depth() > 8 ? image.width()*image.height() :
00665                  image.numColors();
00666     unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
00667                          (unsigned int *)image.tqcolorTable();
00668 
00669     bool brighten = (percent >= 0);
00670     if(percent < 0)
00671         percent = -percent;
00672 
00673 #ifdef USE_MMX_INLINE_ASM
00674     bool haveMMX = KCPUInfo::haveExtension( KCPUInfo::IntelMMX );
00675 
00676     if(haveMMX)
00677     {
00678         TQ_UINT16 p = TQ_UINT16(256.0f*(percent));
00679         KIE4Pack mult = {{p,p,p,0}};
00680 
00681         __asm__ __volatile__(
00682         "pxor %%mm7, %%mm7\n\t"                // zero mm7 for unpacking
00683         "movq  (%0), %%mm6\n\t"                // copy intensity change to mm6
00684         : : "r"(&mult), "m"(mult));
00685 
00686         unsigned int rem = pixels % 4;
00687         pixels -= rem;
00688         TQ_UINT32 *end = ( data + pixels );
00689 
00690         if (brighten)
00691         {
00692             while ( data != end ) {
00693                 __asm__ __volatile__(
00694                 "movq       (%0), %%mm0\n\t"
00695                 "movq      8(%0), %%mm4\n\t"   // copy 4 pixels of data to mm0 and mm4
00696                 "movq      %%mm0, %%mm1\n\t"
00697                 "movq      %%mm0, %%mm3\n\t"
00698                 "movq      %%mm4, %%mm5\n\t"   // copy to registers for unpacking
00699                 "punpcklbw %%mm7, %%mm0\n\t"
00700                 "punpckhbw %%mm7, %%mm1\n\t"   // unpack the two pixels from mm0
00701                 "pmullw    %%mm6, %%mm0\n\t"
00702                 "punpcklbw %%mm7, %%mm4\n\t"
00703                 "pmullw    %%mm6, %%mm1\n\t"   // multiply by intensity*256
00704                 "psrlw        $8, %%mm0\n\t"   // divide by 256
00705                 "pmullw    %%mm6, %%mm4\n\t"
00706                 "psrlw        $8, %%mm1\n\t"
00707                 "psrlw        $8, %%mm4\n\t"
00708                 "packuswb  %%mm1, %%mm0\n\t"   // pack solution into mm0. saturates at 255
00709                 "movq      %%mm5, %%mm1\n\t"
00710 
00711                 "punpckhbw %%mm7, %%mm1\n\t"   // unpack 4th pixel in mm1
00712 
00713                 "pmullw    %%mm6, %%mm1\n\t"
00714                 "paddusb   %%mm3, %%mm0\n\t"   // add intesity result to original of mm0
00715                 "psrlw        $8, %%mm1\n\t"
00716                 "packuswb  %%mm1, %%mm4\n\t"   // pack upper two pixels into mm4
00717 
00718                 "movq      %%mm0, (%0)\n\t"    // rewrite to memory lower two pixels
00719                 "paddusb   %%mm5, %%mm4\n\t"
00720                 "movq      %%mm4, 8(%0)\n\t"   // rewrite upper two pixels
00721                 : : "r"(data) );
00722                 data += 4;
00723             }
00724 
00725             end += rem;
00726             while ( data != end ) {
00727                 __asm__ __volatile__(
00728                 "movd       (%0), %%mm0\n\t"   // repeat above but for
00729                 "punpcklbw %%mm7, %%mm0\n\t"   // one pixel at a time
00730                 "movq      %%mm0, %%mm3\n\t"
00731                 "pmullw    %%mm6, %%mm0\n\t"
00732                 "psrlw        $8, %%mm0\n\t"
00733                 "paddw     %%mm3, %%mm0\n\t"
00734                 "packuswb  %%mm0, %%mm0\n\t"
00735                 "movd      %%mm0, (%0)\n\t"
00736                 : : "r"(data) );
00737         data++;
00738             }
00739         }
00740         else
00741         {
00742             while ( data != end ) {
00743                 __asm__ __volatile__(
00744                 "movq       (%0), %%mm0\n\t"
00745                 "movq      8(%0), %%mm4\n\t"
00746                 "movq      %%mm0, %%mm1\n\t"
00747                 "movq      %%mm0, %%mm3\n\t"
00748 
00749                 "movq      %%mm4, %%mm5\n\t"
00750 
00751                 "punpcklbw %%mm7, %%mm0\n\t"
00752                 "punpckhbw %%mm7, %%mm1\n\t"
00753                 "pmullw    %%mm6, %%mm0\n\t"
00754                 "punpcklbw %%mm7, %%mm4\n\t"
00755                 "pmullw    %%mm6, %%mm1\n\t"
00756                 "psrlw        $8, %%mm0\n\t"
00757                 "pmullw    %%mm6, %%mm4\n\t"
00758                 "psrlw        $8, %%mm1\n\t"
00759                 "psrlw        $8, %%mm4\n\t"
00760                 "packuswb  %%mm1, %%mm0\n\t"
00761                 "movq      %%mm5, %%mm1\n\t"
00762 
00763                 "punpckhbw %%mm7, %%mm1\n\t"
00764 
00765                 "pmullw    %%mm6, %%mm1\n\t"
00766                 "psubusb   %%mm0, %%mm3\n\t"   // subtract darkening amount
00767                 "psrlw        $8, %%mm1\n\t"
00768                 "packuswb  %%mm1, %%mm4\n\t"
00769 
00770                 "movq      %%mm3, (%0)\n\t"
00771                 "psubusb   %%mm4, %%mm5\n\t"   // only change for this version is
00772                 "movq      %%mm5, 8(%0)\n\t"   // subtraction here as we are darkening image
00773                 : : "r"(data) );
00774                 data += 4;
00775             }
00776 
00777             end += rem;
00778             while ( data != end ) {
00779                 __asm__ __volatile__(
00780                 "movd       (%0), %%mm0\n\t"
00781                 "punpcklbw %%mm7, %%mm0\n\t"
00782                 "movq      %%mm0, %%mm3\n\t"
00783                 "pmullw    %%mm6, %%mm0\n\t"
00784                 "psrlw        $8, %%mm0\n\t"
00785                 "psubusw   %%mm0, %%mm3\n\t"
00786                 "packuswb  %%mm3, %%mm3\n\t"
00787                 "movd      %%mm3, (%0)\n\t"
00788                 : : "r"(data) );
00789                 data++;
00790             }
00791         }
00792         __asm__ __volatile__("emms");          // clear mmx state
00793     }
00794     else
00795 #endif // USE_MMX_INLINE_ASM
00796     {
00797         unsigned char *segTbl = new unsigned char[segColors];
00798         int tmp;
00799         if(brighten){ // keep overflow check out of loops
00800             for(int i=0; i < segColors; ++i){
00801                 tmp = (int)(i*percent);
00802                 if(tmp > 255)
00803                     tmp = 255;
00804                 segTbl[i] = tmp;
00805             }
00806         }
00807         else{
00808             for(int i=0; i < segColors; ++i){
00809                 tmp = (int)(i*percent);
00810                 if(tmp < 0)
00811                     tmp = 0;
00812                  segTbl[i] = tmp;
00813             }
00814         }
00815 
00816         if(brighten){ // same here
00817             for(int i=0; i < pixels; ++i){
00818                 int r = tqRed(data[i]);
00819                 int g = tqGreen(data[i]);
00820                 int b = tqBlue(data[i]);
00821                 int a = tqAlpha(data[i]);
00822                 r = r + segTbl[r] > 255 ? 255 : r + segTbl[r];
00823                 g = g + segTbl[g] > 255 ? 255 : g + segTbl[g];
00824                 b = b + segTbl[b] > 255 ? 255 : b + segTbl[b];
00825                 data[i] = tqRgba(r, g, b,a);
00826             }
00827         }
00828         else{
00829             for(int i=0; i < pixels; ++i){
00830                 int r = tqRed(data[i]);
00831                 int g = tqGreen(data[i]);
00832                 int b = tqBlue(data[i]);
00833                 int a = tqAlpha(data[i]);
00834                 r = r - segTbl[r] < 0 ? 0 : r - segTbl[r];
00835                 g = g - segTbl[g] < 0 ? 0 : g - segTbl[g];
00836                 b = b - segTbl[b] < 0 ? 0 : b - segTbl[b];
00837                 data[i] = tqRgba(r, g, b, a);
00838             }
00839         }
00840         delete [] segTbl;
00841     }
00842 
00843     return image;
00844 }
00845 
00846 TQImage& KImageEffect::channelIntensity(TQImage &image, float percent,
00847                                        RGBComponent channel)
00848 {
00849     if (image.width() == 0 || image.height() == 0) {
00850 #ifndef NDEBUG
00851       std::cerr << "WARNING: KImageEffect::channelIntensity : invalid image\n";
00852 #endif
00853       return image;
00854     }
00855 
00856     int segColors = image.depth() > 8 ? 256 : image.numColors();
00857     unsigned char *segTbl = new unsigned char[segColors];
00858     int pixels = image.depth() > 8 ? image.width()*image.height() :
00859         image.numColors();
00860     unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
00861         (unsigned int *)image.tqcolorTable();
00862     bool brighten = (percent >= 0);
00863     if(percent < 0)
00864         percent = -percent;
00865 
00866     if(brighten){ // keep overflow check out of loops
00867         for(int i=0; i < segColors; ++i){
00868             int tmp = (int)(i*percent);
00869             if(tmp > 255)
00870                 tmp = 255;
00871             segTbl[i] = tmp;
00872         }
00873     }
00874     else{
00875         for(int i=0; i < segColors; ++i){
00876             int tmp = (int)(i*percent);
00877             if(tmp < 0)
00878                 tmp = 0;
00879             segTbl[i] = tmp;
00880         }
00881     }
00882 
00883     if(brighten){ // same here
00884         if(channel == Red){ // and here ;-)
00885             for(int i=0; i < pixels; ++i){
00886                 int c = tqRed(data[i]);
00887                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
00888                 data[i] = tqRgba(c, tqGreen(data[i]), tqBlue(data[i]), tqAlpha(data[i]));
00889             }
00890         }
00891         else if(channel == Green){
00892             for(int i=0; i < pixels; ++i){
00893                 int c = tqGreen(data[i]);
00894                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
00895                 data[i] = tqRgba(tqRed(data[i]), c, tqBlue(data[i]), tqAlpha(data[i]));
00896             }
00897         }
00898         else{
00899             for(int i=0; i < pixels; ++i){
00900                 int c = tqBlue(data[i]);
00901                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
00902                 data[i] = tqRgba(tqRed(data[i]), tqGreen(data[i]), c, tqAlpha(data[i]));
00903             }
00904         }
00905 
00906     }
00907     else{
00908         if(channel == Red){
00909             for(int i=0; i < pixels; ++i){
00910                 int c = tqRed(data[i]);
00911                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
00912                 data[i] = tqRgba(c, tqGreen(data[i]), tqBlue(data[i]), tqAlpha(data[i]));
00913             }
00914         }
00915         else if(channel == Green){
00916             for(int i=0; i < pixels; ++i){
00917                 int c = tqGreen(data[i]);
00918                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
00919                 data[i] = tqRgba(tqRed(data[i]), c, tqBlue(data[i]), tqAlpha(data[i]));
00920             }
00921         }
00922         else{
00923             for(int i=0; i < pixels; ++i){
00924                 int c = tqBlue(data[i]);
00925                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
00926                 data[i] = tqRgba(tqRed(data[i]), tqGreen(data[i]), c, tqAlpha(data[i]));
00927             }
00928         }
00929     }
00930     delete [] segTbl;
00931 
00932     return image;
00933 }
00934 
00935 // Modulate an image with an RBG channel of another image
00936 //
00937 TQImage& KImageEffect::modulate(TQImage &image, TQImage &modImage, bool reverse,
00938     ModulationType type, int factor, RGBComponent channel)
00939 {
00940     if (image.width() == 0 || image.height() == 0 ||
00941         modImage.width() == 0 || modImage.height() == 0) {
00942 #ifndef NDEBUG
00943       std::cerr << "WARNING: KImageEffect::modulate : invalid image\n";
00944 #endif
00945       return image;
00946     }
00947 
00948     int r, g, b, h, s, v, a;
00949     TQColor clr;
00950     int mod=0;
00951     unsigned int x1, x2, y1, y2;
00952     int x, y;
00953 
00954     // for image, we handle only depth 32
00955     if (image.depth()<32) image = image.convertDepth(32);
00956 
00957     // for modImage, we handle depth 8 and 32
00958     if (modImage.depth()<8) modImage = modImage.convertDepth(8);
00959 
00960     unsigned int *colorTable2 = (modImage.depth()==8) ?
00961                  modImage.tqcolorTable():0;
00962     unsigned int *data1, *data2;
00963     unsigned char *data2b;
00964     unsigned int color1, color2;
00965 
00966     x1 = image.width();    y1 = image.height();
00967     x2 = modImage.width(); y2 = modImage.height();
00968 
00969     for (y = 0; y < (int)y1; y++) {
00970         data1 =  (unsigned int *) image.scanLine(y);
00971     data2 =  (unsigned int *) modImage.scanLine( y%y2 );
00972     data2b = (unsigned char *) modImage.scanLine( y%y2 );
00973 
00974     x=0;
00975     while(x < (int)x1) {
00976       color2 = (colorTable2) ? colorTable2[*data2b] : *data2;
00977       if (reverse) {
00978           color1 = color2;
00979           color2 = *data1;
00980       }
00981       else
00982           color1 = *data1;
00983 
00984       if (type == Intensity || type == Contrast) {
00985               r = tqRed(color1);
00986           g = tqGreen(color1);
00987           b = tqBlue(color1);
00988           if (channel != All) {
00989                 mod = (channel == Red) ? tqRed(color2) :
00990             (channel == Green) ? tqGreen(color2) :
00991                 (channel == Blue) ? tqBlue(color2) :
00992             (channel == Gray) ? tqGray(color2) : 0;
00993             mod = mod*factor/50;
00994           }
00995 
00996           if (type == Intensity) {
00997             if (channel == All) {
00998               r += r * factor/50 * tqRed(color2)/256;
00999               g += g * factor/50 * tqGreen(color2)/256;
01000               b += b * factor/50 * tqBlue(color2)/256;
01001             }
01002             else {
01003               r += r * mod/256;
01004               g += g * mod/256;
01005               b += b * mod/256;
01006             }
01007           }
01008           else { // Contrast
01009             if (channel == All) {
01010           r += (r-128) * factor/50 * tqRed(color2)/128;
01011               g += (g-128) * factor/50 * tqGreen(color2)/128;
01012               b += (b-128) * factor/50 * tqBlue(color2)/128;
01013             }
01014             else {
01015               r += (r-128) * mod/128;
01016               g += (g-128) * mod/128;
01017               b += (b-128) * mod/128;
01018             }
01019           }
01020 
01021           if (r<0) r=0; if (r>255) r=255;
01022           if (g<0) g=0; if (g>255) g=255;
01023           if (b<0) b=0; if (b>255) b=255;
01024           a = tqAlpha(*data1);
01025           *data1 = tqRgba(r, g, b, a);
01026       }
01027       else if (type == Saturation || type == HueShift) {
01028           clr.setRgb(color1);
01029           clr.hsv(&h, &s, &v);
01030               mod = (channel == Red) ? tqRed(color2) :
01031             (channel == Green) ? tqGreen(color2) :
01032                 (channel == Blue) ? tqBlue(color2) :
01033             (channel == Gray) ? tqGray(color2) : 0;
01034           mod = mod*factor/50;
01035 
01036           if (type == Saturation) {
01037           s -= s * mod/256;
01038           if (s<0) s=0; if (s>255) s=255;
01039           }
01040           else { // HueShift
01041             h += mod;
01042         while(h<0) h+=360;
01043         h %= 360;
01044           }
01045 
01046           clr.setHsv(h, s, v);
01047           a = tqAlpha(*data1);
01048           *data1 = clr.rgb() | ((uint)(a & 0xff) << 24);
01049       }
01050       data1++; data2++; data2b++; x++;
01051       if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; }
01052         }
01053     }
01054     return image;
01055 }
01056 
01057 
01058 
01059 //======================================================================
01060 //
01061 // Blend effects
01062 //
01063 //======================================================================
01064 
01065 
01066 // Nice and fast direct pixel manipulation
01067 TQImage& KImageEffect::blend(const TQColor& clr, TQImage& dst, float opacity)
01068 {
01069     if (dst.width() <= 0 || dst.height() <= 0)
01070         return dst;
01071 
01072     if (opacity < 0.0 || opacity > 1.0) {
01073 #ifndef NDEBUG
01074         std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
01075 #endif
01076         return dst;
01077     }
01078 
01079     if (dst.depth() != 32)
01080         dst = dst.convertDepth(32);
01081 
01082 #ifdef USE_QT4
01083     if (dst.format() != QImage::Format_ARGB32)
01084         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)
01085 #endif
01086 
01087     int pixels = dst.width() * dst.height();
01088 
01089 #ifdef USE_SSE2_INLINE_ASM
01090     if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
01091         TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 );
01092 
01093         KIE8Pack packedalpha = { { alpha, alpha, alpha, 256,
01094                                    alpha, alpha, alpha, 256 } };
01095 
01096         TQ_UINT16 red   = TQ_UINT16( clr.red()   * 256 * opacity );
01097         TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity );
01098         TQ_UINT16 blue  = TQ_UINT16( clr.blue()  * 256 * opacity );
01099 
01100         KIE8Pack packedcolor = { { blue, green, red, 0,
01101                                    blue, green, red, 0 } };
01102 
01103         // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending
01104         __asm__ __volatile__(
01105         "pxor        %%xmm7,  %%xmm7\n\t" // Zero out XMM7 for unpacking
01106         "movdqu        (%0),  %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6
01107         "movdqu        (%1),  %%xmm5\n\t" // Set up color * alpha * 256 in XMM5
01108         : : "r"(&packedalpha), "r"(&packedcolor),
01109             "m"(packedcolor),  "m"(packedalpha) );
01110 
01111         TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() );
01112 
01113         // Check how many pixels we need to process to achieve 16 byte alignment
01114         int offset = (16 - (TQ_UINT32( data ) & 0x0f)) / 4;
01115 
01116         // The main loop processes 8 pixels / iteration
01117         int remainder = (pixels - offset) % 8;
01118         pixels -= remainder;
01119 
01120         // Alignment loop
01121         for ( int i = 0; i < offset; i++ ) {
01122             __asm__ __volatile__(
01123             "movd         (%0,%1,4),      %%xmm0\n\t"  // Load one pixel to XMM1
01124             "punpcklbw       %%xmm7,      %%xmm0\n\t"  // Unpack the pixel
01125             "pmullw          %%xmm6,      %%xmm0\n\t"  // Multiply the pixel with (1 - alpha) * 256
01126             "paddw           %%xmm5,      %%xmm0\n\t"  // Add color * alpha * 256 to the result
01127             "psrlw               $8,      %%xmm0\n\t"  // Divide by 256
01128             "packuswb        %%xmm1,      %%xmm0\n\t"  // Pack the pixel to a dword
01129             "movd            %%xmm0,   (%0,%1,4)\n\t"  // Write the pixel to the image
01130             : : "r"(data), "r"(i) );
01131         }
01132 
01133         // Main loop
01134         for ( int i = offset; i < pixels; i += 8 ) {
01135             __asm__ __volatile(
01136             // Load 8 pixels to XMM registers 1 - 4
01137             "movq         (%0,%1,4),      %%xmm0\n\t"  // Load pixels 1 and 2 to XMM1
01138             "movq        8(%0,%1,4),      %%xmm1\n\t"  // Load pixels 3 and 4 to XMM2
01139             "movq       16(%0,%1,4),      %%xmm2\n\t"  // Load pixels 5 and 6 to XMM3
01140             "movq       24(%0,%1,4),      %%xmm3\n\t"  // Load pixels 7 and 8 to XMM4
01141 
01142             // Prefetch the pixels for next iteration
01143             "prefetchnta 32(%0,%1,4)            \n\t"
01144 
01145             // Blend pixels 1 and 2
01146             "punpcklbw       %%xmm7,      %%xmm0\n\t"  // Unpack the pixels
01147             "pmullw          %%xmm6,      %%xmm0\n\t"  // Multiply the pixels with (1 - alpha) * 256
01148             "paddw           %%xmm5,      %%xmm0\n\t"  // Add color * alpha * 256 to the result
01149             "psrlw               $8,      %%xmm0\n\t"  // Divide by 256
01150 
01151             // Blend pixels 3 and 4
01152             "punpcklbw       %%xmm7,      %%xmm1\n\t"  // Unpack the pixels
01153             "pmullw          %%xmm6,      %%xmm1\n\t"  // Multiply the pixels with (1 - alpha) * 256
01154             "paddw           %%xmm5,      %%xmm1\n\t"  // Add color * alpha * 256 to the result
01155             "psrlw               $8,      %%xmm1\n\t"  // Divide by 256
01156 
01157             // Blend pixels 5 and 6
01158             "punpcklbw       %%xmm7,      %%xmm2\n\t"  // Unpack the pixels
01159             "pmullw          %%xmm6,      %%xmm2\n\t"  // Multiply the pixels with (1 - alpha) * 256
01160             "paddw           %%xmm5,      %%xmm2\n\t"  // Add color * alpha * 256 to the result
01161             "psrlw               $8,      %%xmm2\n\t"  // Divide by 256
01162 
01163             // Blend pixels 7 and 8
01164             "punpcklbw       %%xmm7,      %%xmm3\n\t"  // Unpack the pixels
01165             "pmullw          %%xmm6,      %%xmm3\n\t"  // Multiply the pixels with (1 - alpha) * 256
01166             "paddw           %%xmm5,      %%xmm3\n\t"  // Add color * alpha * 256 to the result
01167             "psrlw               $8,      %%xmm3\n\t"  // Divide by 256
01168 
01169             // Pack the pixels into 2 double quadwords
01170             "packuswb        %%xmm1,      %%xmm0\n\t"  // Pack pixels 1 - 4 to a double qword
01171             "packuswb        %%xmm3,      %%xmm2\n\t"  // Pack pixles 5 - 8 to a double qword
01172 
01173             // Write the pixels back to the image
01174             "movdqa          %%xmm0,   (%0,%1,4)\n\t"  // Store pixels 1 - 4
01175             "movdqa          %%xmm2, 16(%0,%1,4)\n\t"  // Store pixels 5 - 8
01176             : : "r"(data), "r"(i) );
01177         }
01178 
01179         // Cleanup loop
01180         for ( int i = pixels; i < pixels + remainder; i++ ) {
01181             __asm__ __volatile__(
01182             "movd         (%0,%1,4),      %%xmm0\n\t"  // Load one pixel to XMM1
01183             "punpcklbw       %%xmm7,      %%xmm0\n\t"  // Unpack the pixel
01184             "pmullw          %%xmm6,      %%xmm0\n\t"  // Multiply the pixel with (1 - alpha) * 256
01185             "paddw           %%xmm5,      %%xmm0\n\t"  // Add color * alpha * 256 to the result
01186             "psrlw               $8,      %%xmm0\n\t"  // Divide by 256
01187             "packuswb        %%xmm1,      %%xmm0\n\t"  // Pack the pixel to a dword
01188             "movd            %%xmm0,   (%0,%1,4)\n\t"  // Write the pixel to the image
01189             : : "r"(data), "r"(i) );
01190         }
01191     } else
01192 #endif
01193 
01194 #ifdef USE_MMX_INLINE_ASM
01195     if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
01196         TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 );
01197         KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } };
01198 
01199         TQ_UINT16 red   = TQ_UINT16( clr.red()   * 256 * opacity );
01200         TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity );
01201         TQ_UINT16 blue  = TQ_UINT16( clr.blue()  * 256 * opacity );
01202 
01203         KIE4Pack packedcolor = { { blue, green, red, 0 } };
01204 
01205         __asm__ __volatile__(
01206         "pxor        %%mm7,    %%mm7\n\t"       // Zero out MM7 for unpacking
01207         "movq         (%0),    %%mm6\n\t"       // Set up (1 - alpha) * 256 in MM6
01208         "movq         (%1),    %%mm5\n\t"       // Set up color * alpha * 256 in MM5
01209         : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) );
01210 
01211         TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() );
01212 
01213         // The main loop processes 4 pixels / iteration
01214         int remainder = pixels % 4;
01215         pixels -= remainder;
01216 
01217         // Main loop
01218         for ( int i = 0; i < pixels; i += 4 ) {
01219             __asm__ __volatile__(
01220             // Load 4 pixels to MM registers 1 - 4
01221             "movd         (%0,%1,4),      %%mm0\n\t"  // Load the 1st pixel to MM0
01222             "movd        4(%0,%1,4),      %%mm1\n\t"  // Load the 2nd pixel to MM1
01223             "movd        8(%0,%1,4),      %%mm2\n\t"  // Load the 3rd pixel to MM2
01224             "movd       12(%0,%1,4),      %%mm3\n\t"  // Load the 4th pixel to MM3
01225 
01226             // Blend the first pixel
01227             "punpcklbw        %%mm7,      %%mm0\n\t"  // Unpack the pixel
01228             "pmullw           %%mm6,      %%mm0\n\t"  // Multiply the pixel with (1 - alpha) * 256
01229             "paddw            %%mm5,      %%mm0\n\t"  // Add color * alpha * 256 to the result
01230             "psrlw               $8,      %%mm0\n\t"  // Divide by 256
01231 
01232             // Blend the second pixel
01233             "punpcklbw        %%mm7,      %%mm1\n\t"  // Unpack the pixel
01234             "pmullw           %%mm6,      %%mm1\n\t"  // Multiply the pixel with (1 - alpha) * 256
01235             "paddw            %%mm5,      %%mm1\n\t"  // Add color * alpha * 256 to the result
01236             "psrlw               $8,      %%mm1\n\t"  // Divide by 256
01237 
01238             // Blend the third pixel
01239             "punpcklbw        %%mm7,      %%mm2\n\t"  // Unpack the pixel
01240             "pmullw           %%mm6,      %%mm2\n\t"  // Multiply the pixel with (1 - alpha) * 256
01241             "paddw            %%mm5,      %%mm2\n\t"  // Add color * alpha * 256 to the result
01242             "psrlw               $8,      %%mm2\n\t"  // Divide by 256
01243 
01244             // Blend the fourth pixel
01245             "punpcklbw        %%mm7,      %%mm3\n\t"  // Unpack the pixel
01246             "pmullw           %%mm6,      %%mm3\n\t"  // Multiply the pixel with (1 - alpha) * 256
01247             "paddw            %%mm5,      %%mm3\n\t"  // Add color * alpha * 256 to the result
01248             "psrlw               $8,      %%mm3\n\t"  // Divide by 256
01249 
01250             // Pack the pixels into 2 quadwords
01251             "packuswb         %%mm1,      %%mm0\n\t"  // Pack pixels 1 and 2 to a qword
01252             "packuswb         %%mm3,      %%mm2\n\t"  // Pack pixels 3 and 4 to a qword
01253 
01254             // Write the pixels back to the image
01255             "movq             %%mm0,  (%0,%1,4)\n\t"  // Store pixels 1 and 2
01256             "movq             %%mm2, 8(%0,%1,4)\n\t"  // Store pixels 3 and 4
01257             : : "r"(data), "r"(i) );
01258         }
01259 
01260         // Cleanup loop
01261         for ( int i = pixels; i < pixels + remainder; i++ ) {
01262             __asm__ __volatile__(
01263             "movd         (%0,%1,4),      %%mm0\n\t"  // Load one pixel to MM1
01264             "punpcklbw        %%mm7,      %%mm0\n\t"  // Unpack the pixel
01265             "pmullw           %%mm6,      %%mm0\n\t"  // Multiply the pixel with 1 - alpha * 256
01266             "paddw            %%mm5,      %%mm0\n\t"  // Add color * alpha * 256 to the result
01267             "psrlw               $8,      %%mm0\n\t"  // Divide by 256
01268             "packuswb         %%mm0,      %%mm0\n\t"  // Pack the pixel to a dword
01269             "movd             %%mm0,  (%0,%1,4)\n\t"  // Write the pixel to the image
01270             : : "r"(data), "r"(i) );
01271         }
01272 
01273         // Empty the MMX state
01274         __asm__ __volatile__("emms");
01275     } else
01276 #endif // USE_MMX_INLINE_ASM
01277 
01278     {
01279         int rcol, gcol, bcol;
01280         clr.rgb(&rcol, &gcol, &bcol);
01281 
01282 #ifdef WORDS_BIGENDIAN   // ARGB (skip alpha)
01283         unsigned char *data = (unsigned char *)dst.bits() + 1;
01284 #else                    // BGRA
01285         unsigned char *data = (unsigned char *)dst.bits();
01286 #endif
01287 
01288         for (int i=0; i<pixels; i++)
01289         {
01290 #ifdef WORDS_BIGENDIAN
01291             *data += (unsigned char)((rcol - *data) * opacity);
01292             data++;
01293             *data += (unsigned char)((gcol - *data) * opacity);
01294             data++;
01295             *data += (unsigned char)((bcol - *data) * opacity);
01296             data++;
01297 #else
01298             *data += (unsigned char)((bcol - *data) * opacity);
01299             data++;
01300             *data += (unsigned char)((gcol - *data) * opacity);
01301             data++;
01302             *data += (unsigned char)((rcol - *data) * opacity);
01303             data++;
01304 #endif
01305             data++; // skip alpha
01306         }
01307     }
01308 
01309     return dst;
01310 }
01311 
01312 // Nice and fast direct pixel manipulation
01313 TQImage& KImageEffect::blend(TQImage& src, TQImage& dst, float opacity)
01314 {
01315     if (src.width() <= 0 || src.height() <= 0)
01316         return dst;
01317     if (dst.width() <= 0 || dst.height() <= 0)
01318         return dst;
01319 
01320     if (src.width() != dst.width() || src.height() != dst.height()) {
01321 #ifndef NDEBUG
01322         std::cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n";
01323 #endif
01324         return dst;
01325     }
01326 
01327     if (opacity < 0.0 || opacity > 1.0) {
01328 #ifndef NDEBUG
01329         std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
01330 #endif
01331         return dst;
01332     }
01333 
01334     if (src.depth() != 32) src = src.convertDepth(32);
01335     if (dst.depth() != 32) dst = dst.convertDepth(32);
01336 
01337 #ifdef USE_QT4
01338     if (src.format() != QImage::Format_ARGB32)
01339         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)
01340     if (dst.format() != QImage::Format_ARGB32)
01341         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)
01342 #endif
01343 
01344     int pixels = src.width() * src.height();
01345 
01346 #ifdef USE_SSE2_INLINE_ASM
01347     if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
01348         TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 );
01349         KIE8Pack packedalpha = { { alpha, alpha, alpha, 0,
01350                                    alpha, alpha, alpha, 0 } };
01351 
01352         // Prepare the XMM6 and XMM7 registers for unpacking and blending
01353         __asm__ __volatile__(
01354         "pxor      %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
01355         "movdqu      (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6
01356         : : "r"(&packedalpha), "m"(packedalpha) );
01357 
01358         TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() );
01359         TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() );
01360 
01361         // Check how many pixels we need to process to achieve 16 byte alignment
01362         int offset = (16 - (TQ_UINT32( data2 ) & 0x0f)) / 4;
01363 
01364         // The main loop processes 4 pixels / iteration
01365         int remainder = (pixels - offset) % 4;
01366         pixels -= remainder;
01367 
01368         // Alignment loop
01369         for ( int i = 0; i < offset; i++ ) {
01370             __asm__ __volatile__(
01371             "movd       (%1,%2,4),    %%xmm1\n\t"  // Load one dst pixel to XMM1
01372             "punpcklbw     %%xmm7,    %%xmm1\n\t"  // Unpack the pixel
01373             "movd       (%0,%2,4),    %%xmm0\n\t"  // Load one src pixel to XMM0
01374             "punpcklbw     %%xmm7,    %%xmm0\n\t"  // Unpack the pixel
01375             "psubw         %%xmm1,    %%xmm0\n\t"  // Subtract dst from src
01376             "pmullw        %%xmm6,    %%xmm0\n\t"  // Multiply the result with alpha * 256
01377             "psllw             $8,    %%xmm1\n\t"  // Multiply dst with 256
01378             "paddw         %%xmm1,    %%xmm0\n\t"  // Add dst to result
01379             "psrlw             $8,    %%xmm0\n\t"  // Divide by 256
01380             "packuswb      %%xmm1,    %%xmm0\n\t"  // Pack the pixel to a dword
01381             "movd          %%xmm0, (%1,%2,4)\n\t"  // Write the pixel to the image
01382             : : "r"(data1), "r"(data2), "r"(i) );
01383         }
01384 
01385         // Main loop
01386         for ( int i = offset; i < pixels; i += 4 ) {
01387             __asm__ __volatile__(
01388             // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3
01389             "movq       (%0,%2,4),    %%xmm0\n\t"  // Load two src pixels to XMM0
01390             "movq       (%1,%2,4),    %%xmm1\n\t"  // Load two dst pixels to XMM1
01391             "movq      8(%0,%2,4),    %%xmm2\n\t"  // Load two src pixels to XMM2
01392             "movq      8(%1,%2,4),    %%xmm3\n\t"  // Load two dst pixels to XMM3
01393 
01394             // Prefetch the pixels for the iteration after the next one
01395             "prefetchnta 32(%0,%2,4)        \n\t"
01396             "prefetchnta 32(%1,%2,4)        \n\t"
01397 
01398             // Blend the first two pixels
01399             "punpcklbw     %%xmm7,    %%xmm1\n\t"  // Unpack the dst pixels
01400             "punpcklbw     %%xmm7,    %%xmm0\n\t"  // Unpack the src pixels
01401             "psubw         %%xmm1,    %%xmm0\n\t"  // Subtract dst from src
01402             "pmullw        %%xmm6,    %%xmm0\n\t"  // Multiply the result with alpha * 256
01403             "psllw             $8,    %%xmm1\n\t"  // Multiply dst with 256
01404             "paddw         %%xmm1,    %%xmm0\n\t"  // Add dst to the result
01405             "psrlw             $8,    %%xmm0\n\t"  // Divide by 256
01406 
01407             // Blend the next two pixels
01408             "punpcklbw     %%xmm7,    %%xmm3\n\t"  // Unpack the dst pixels
01409             "punpcklbw     %%xmm7,    %%xmm2\n\t"  // Unpack the src pixels
01410             "psubw         %%xmm3,    %%xmm2\n\t"  // Subtract dst from src
01411             "pmullw        %%xmm6,    %%xmm2\n\t"  // Multiply the result with alpha * 256
01412             "psllw             $8,    %%xmm3\n\t"  // Multiply dst with 256
01413             "paddw         %%xmm3,    %%xmm2\n\t"  // Add dst to the result
01414             "psrlw             $8,    %%xmm2\n\t"  // Divide by 256
01415 
01416             // Write the pixels back to the image
01417             "packuswb      %%xmm2,    %%xmm0\n\t"  // Pack the pixels to a double qword
01418             "movdqa        %%xmm0, (%1,%2,4)\n\t"  // Store the pixels
01419             : : "r"(data1), "r"(data2), "r"(i) );
01420         }
01421 
01422         // Cleanup loop
01423         for ( int i = pixels; i < pixels + remainder; i++ ) {
01424             __asm__ __volatile__(
01425             "movd       (%1,%2,4),    %%xmm1\n\t"  // Load one dst pixel to XMM1
01426             "punpcklbw     %%xmm7,    %%xmm1\n\t"  // Unpack the pixel
01427             "movd       (%0,%2,4),    %%xmm0\n\t"  // Load one src pixel to XMM0
01428             "punpcklbw     %%xmm7,    %%xmm0\n\t"  // Unpack the pixel
01429             "psubw         %%xmm1,    %%xmm0\n\t"  // Subtract dst from src
01430             "pmullw        %%xmm6,    %%xmm0\n\t"  // Multiply the result with alpha * 256
01431             "psllw             $8,    %%xmm1\n\t"  // Multiply dst with 256
01432             "paddw         %%xmm1,    %%xmm0\n\t"  // Add dst to result
01433             "psrlw             $8,    %%xmm0\n\t"  // Divide by 256
01434             "packuswb      %%xmm1,    %%xmm0\n\t"  // Pack the pixel to a dword
01435             "movd          %%xmm0, (%1,%2,4)\n\t"  // Write the pixel to the image
01436             : : "r"(data1), "r"(data2), "r"(i) );
01437         }
01438     } else
01439 #endif // USE_SSE2_INLINE_ASM
01440 
01441 #ifdef USE_MMX_INLINE_ASM
01442     if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
01443         TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 );
01444         KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } };
01445 
01446         // Prepare the MM6 and MM7 registers for blending and unpacking
01447         __asm__ __volatile__(
01448         "pxor       %%mm7,   %%mm7\n\t"      // Zero out MM7 for unpacking
01449         "movq        (%0),   %%mm6\n\t"      // Set up alpha * 256 in MM6
01450         : : "r"(&packedalpha), "m"(packedalpha) );
01451 
01452         TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() );
01453         TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() );
01454 
01455         // The main loop processes 2 pixels / iteration
01456         int remainder = pixels % 2;
01457         pixels -= remainder;
01458 
01459         // Main loop
01460         for ( int i = 0; i < pixels; i += 2 ) {
01461             __asm__ __volatile__(
01462             // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3
01463             "movd        (%0,%2,4),     %%mm0\n\t"  // Load the 1st src pixel to MM0
01464             "movd        (%1,%2,4),     %%mm1\n\t"  // Load the 1st dst pixel to MM1
01465             "movd       4(%0,%2,4),     %%mm2\n\t"  // Load the 2nd src pixel to MM2
01466             "movd       4(%1,%2,4),     %%mm3\n\t"  // Load the 2nd dst pixel to MM3
01467 
01468             // Blend the first pixel
01469             "punpcklbw       %%mm7,     %%mm0\n\t"  // Unpack the src pixel
01470             "punpcklbw       %%mm7,     %%mm1\n\t"  // Unpack the dst pixel
01471             "psubw           %%mm1,     %%mm0\n\t"  // Subtract dst from src
01472             "pmullw          %%mm6,     %%mm0\n\t"  // Multiply the result with alpha * 256
01473             "psllw              $8,     %%mm1\n\t"  // Multiply dst with 256
01474             "paddw           %%mm1,     %%mm0\n\t"  // Add dst to the result
01475             "psrlw              $8,     %%mm0\n\t"  // Divide by 256
01476 
01477             // Blend the second pixel
01478             "punpcklbw       %%mm7,     %%mm2\n\t"  // Unpack the src pixel
01479             "punpcklbw       %%mm7,     %%mm3\n\t"  // Unpack the dst pixel
01480             "psubw           %%mm3,     %%mm2\n\t"  // Subtract dst from src
01481             "pmullw          %%mm6,     %%mm2\n\t"  // Multiply the result with alpha * 256
01482             "psllw              $8,     %%mm3\n\t"  // Multiply dst with 256
01483             "paddw           %%mm3,     %%mm2\n\t"  // Add dst to the result
01484             "psrlw              $8,     %%mm2\n\t"  // Divide by 256
01485 
01486             // Write the pixels back to the image
01487             "packuswb        %%mm2,     %%mm0\n\t"  // Pack the pixels to a qword
01488             "movq            %%mm0, (%1,%2,4)\n\t"  // Store the pixels
01489             : : "r"(data1), "r"(data2), "r"(i) );
01490         }
01491 
01492         // Blend the remaining pixel (if there is one)
01493         if ( remainder ) {
01494              __asm__ __volatile__(
01495             "movd             (%0),     %%mm0\n\t"  // Load one src pixel to MM0
01496             "punpcklbw       %%mm7,     %%mm0\n\t"  // Unpack the src pixel
01497             "movd             (%1),     %%mm1\n\t"  // Load one dst pixel to MM1
01498             "punpcklbw       %%mm7,     %%mm1\n\t"  // Unpack the dst pixel
01499             "psubw           %%mm1,     %%mm0\n\t"  // Subtract dst from src
01500             "pmullw          %%mm6,     %%mm0\n\t"  // Multiply the result with alpha * 256
01501             "psllw              $8,     %%mm1\n\t"  // Multiply dst with 256
01502             "paddw           %%mm1,     %%mm0\n\t"  // Add dst to result
01503             "psrlw              $8,     %%mm0\n\t"  // Divide by 256
01504             "packuswb        %%mm0,     %%mm0\n\t"  // Pack the pixel to a dword
01505             "movd            %%mm0,      (%1)\n\t"  // Write the pixel to the image
01506             : : "r"(data1 + pixels), "r"(data2 + pixels) );
01507         }
01508 
01509         // Empty the MMX state
01510         __asm__ __volatile__("emms");
01511     } else
01512 #endif // USE_MMX_INLINE_ASM
01513 
01514     {
01515 #ifdef WORDS_BIGENDIAN   // ARGB (skip alpha)
01516         unsigned char *data1 = (unsigned char *)dst.bits() + 1;
01517         unsigned char *data2 = (unsigned char *)src.bits() + 1;
01518 #else                    // BGRA
01519         unsigned char *data1 = (unsigned char *)dst.bits();
01520         unsigned char *data2 = (unsigned char *)src.bits();
01521 #endif
01522 
01523         for (int i=0; i<pixels; i++)
01524         {
01525 #ifdef WORDS_BIGENDIAN
01526             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01527             data1++;
01528             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01529             data1++;
01530             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01531             data1++;
01532 #else
01533             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01534             data1++;
01535             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01536             data1++;
01537             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01538             data1++;
01539 #endif
01540             data1++; // skip alpha
01541             data2++;
01542         }
01543     }
01544 
01545     return dst;
01546 }
01547 
01548 
01549 TQImage& KImageEffect::blend(TQImage &image, float initial_intensity,
01550                             const TQColor &bgnd, GradientType eff,
01551                             bool anti_dir)
01552 {
01553     if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
01554 #ifndef NDEBUG
01555       std::cerr << "WARNING: KImageEffect::blend : invalid image\n";
01556 #endif
01557       return image;
01558     }
01559 
01560     int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
01561     int r, g, b;
01562     int ind;
01563 
01564     unsigned int xi, xf, yi, yf;
01565     unsigned int a;
01566 
01567     // check the boundaries of the initial intesity param
01568     float unaffected = 1;
01569     if (initial_intensity >  1) initial_intensity =  1;
01570     if (initial_intensity < -1) initial_intensity = -1;
01571     if (initial_intensity < 0) {
01572         unaffected = 1. + initial_intensity;
01573         initial_intensity = 0;
01574     }
01575 
01576 
01577     float intensity = initial_intensity;
01578     float var = 1. - initial_intensity;
01579 
01580     if (anti_dir) {
01581         initial_intensity = intensity = 1.;
01582         var = -var;
01583     }
01584 
01585     int x, y;
01586 
01587     unsigned int *data =  (unsigned int *)image.bits();
01588 
01589     int image_width = image.width(); //Those can't change
01590     int image_height = image.height();
01591 
01592 
01593     if( eff == VerticalGradient || eff == HorizontalGradient ) {
01594 
01595         // set the image domain to apply the effect to
01596         xi = 0, xf = image_width;
01597         yi = 0, yf = image_height;
01598         if (eff == VerticalGradient) {
01599             if (anti_dir) yf = (int)(image_height * unaffected);
01600             else yi = (int)(image_height * (1 - unaffected));
01601         }
01602         else {
01603             if (anti_dir) xf = (int)(image_width * unaffected);
01604             else xi = (int)(image_height * (1 - unaffected));
01605         }
01606 
01607         var /= (eff == VerticalGradient?yf-yi:xf-xi);
01608 
01609         int ind_base;
01610         for (y = yi; y < (int)yf; y++) {
01611             intensity = eff == VerticalGradient? intensity + var :
01612                 initial_intensity;
01613             ind_base = image_width  * y ;
01614             for (x = xi; x < (int)xf ; x++) {
01615                 if (eff == HorizontalGradient) intensity += var;
01616                 ind = x + ind_base;
01617                 r = tqRed  (data[ind]) + (int)(intensity *
01618                                               (r_bgnd - tqRed  (data[ind])));
01619                 g = tqGreen(data[ind]) + (int)(intensity *
01620                                               (g_bgnd - tqGreen(data[ind])));
01621                 b = tqBlue (data[ind]) + (int)(intensity *
01622                                               (b_bgnd - tqBlue (data[ind])));
01623                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01624                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01625                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01626                 a = tqAlpha(data[ind]);
01627                 data[ind] = tqRgba(r, g, b, a);
01628             }
01629         }
01630     }
01631     else if (eff == DiagonalGradient  || eff == CrossDiagonalGradient) {
01632         float xvar = var / 2 / image_width;  // / unaffected;
01633         float yvar = var / 2 / image_height; // / unaffected;
01634         float tmp;
01635 
01636         for (x = 0; x < image_width ; x++) {
01637             tmp =  xvar * (eff == DiagonalGradient? x : image.width()-x-1);
01638             ind = x;
01639             for (y = 0; y < image_height ; y++) {
01640                 intensity = initial_intensity + tmp + yvar * y;
01641 
01642                 r = tqRed  (data[ind]) + (int)(intensity *
01643                                               (r_bgnd - tqRed  (data[ind])));
01644                 g = tqGreen(data[ind]) + (int)(intensity *
01645                                               (g_bgnd - tqGreen(data[ind])));
01646                 b = tqBlue (data[ind]) + (int)(intensity *
01647                                               (b_bgnd - tqBlue (data[ind])));
01648                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01649                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01650                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01651                 a = tqAlpha(data[ind]);
01652                 data[ind] = tqRgba(r, g, b, a);
01653 
01654                 ind += image_width;
01655             }
01656         }
01657     }
01658 
01659     else if (eff == RectangleGradient || eff == EllipticGradient) {
01660         float xvar;
01661         float yvar;
01662 
01663         for (x = 0; x < image_width / 2 + image_width % 2; x++) {
01664             xvar = var / image_width  * (image_width - x*2/unaffected-1);
01665             for (y = 0; y < image_height / 2 + image_height % 2; y++) {
01666                 yvar = var / image_height   * (image_height - y*2/unaffected -1);
01667 
01668                 if (eff == RectangleGradient)
01669                     intensity = initial_intensity + TQMAX(xvar, yvar);
01670                 else
01671                     intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
01672                 if (intensity > 1) intensity = 1;
01673                 if (intensity < 0) intensity = 0;
01674 
01675                 //NW
01676                 ind = x + image_width  * y ;
01677                 r = tqRed  (data[ind]) + (int)(intensity *
01678                                               (r_bgnd - tqRed  (data[ind])));
01679                 g = tqGreen(data[ind]) + (int)(intensity *
01680                                               (g_bgnd - tqGreen(data[ind])));
01681                 b = tqBlue (data[ind]) + (int)(intensity *
01682                                               (b_bgnd - tqBlue (data[ind])));
01683                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01684                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01685                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01686                 a = tqAlpha(data[ind]);
01687                 data[ind] = tqRgba(r, g, b, a);
01688 
01689                 //NE
01690                 ind = image_width - x - 1 + image_width  * y ;
01691                 r = tqRed  (data[ind]) + (int)(intensity *
01692                                               (r_bgnd - tqRed  (data[ind])));
01693                 g = tqGreen(data[ind]) + (int)(intensity *
01694                                               (g_bgnd - tqGreen(data[ind])));
01695                 b = tqBlue (data[ind]) + (int)(intensity *
01696                                               (b_bgnd - tqBlue (data[ind])));
01697                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01698                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01699                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01700                 a = tqAlpha(data[ind]);
01701                 data[ind] = tqRgba(r, g, b, a);
01702             }
01703         }
01704 
01705         //CT  loop is doubled because of stupid central row/column issue.
01706         //    other solution?
01707         for (x = 0; x < image_width / 2; x++) {
01708             xvar = var / image_width  * (image_width - x*2/unaffected-1);
01709             for (y = 0; y < image_height / 2; y++) {
01710                 yvar = var / image_height   * (image_height - y*2/unaffected -1);
01711 
01712                 if (eff == RectangleGradient)
01713                     intensity = initial_intensity + TQMAX(xvar, yvar);
01714                 else
01715                     intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
01716                 if (intensity > 1) intensity = 1;
01717                 if (intensity < 0) intensity = 0;
01718 
01719                 //SW
01720                 ind = x + image_width  * (image_height - y -1) ;
01721                 r = tqRed  (data[ind]) + (int)(intensity *
01722                                               (r_bgnd - tqRed  (data[ind])));
01723                 g = tqGreen(data[ind]) + (int)(intensity *
01724                                               (g_bgnd - tqGreen(data[ind])));
01725                 b = tqBlue (data[ind]) + (int)(intensity *
01726                                               (b_bgnd - tqBlue (data[ind])));
01727                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01728                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01729                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01730                 a = tqAlpha(data[ind]);
01731                 data[ind] = tqRgba(r, g, b, a);
01732 
01733                 //SE
01734                 ind = image_width-x-1 + image_width * (image_height - y - 1) ;
01735                 r = tqRed  (data[ind]) + (int)(intensity *
01736                                               (r_bgnd - tqRed  (data[ind])));
01737                 g = tqGreen(data[ind]) + (int)(intensity *
01738                                               (g_bgnd - tqGreen(data[ind])));
01739                 b = tqBlue (data[ind]) + (int)(intensity *
01740                                               (b_bgnd - tqBlue (data[ind])));
01741                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01742                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01743                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01744                 a = tqAlpha(data[ind]);
01745                 data[ind] = tqRgba(r, g, b, a);
01746             }
01747         }
01748     }
01749 #ifndef NDEBUG
01750     else std::cerr << "KImageEffect::blend effect not implemented" << std::endl;
01751 #endif
01752     return image;
01753 }
01754 
01755 // Not very efficient as we create a third big image...
01756 //
01757 TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2,
01758                 GradientType gt, int xf, int yf)
01759 {
01760   if (image1.width() == 0 || image1.height() == 0 ||
01761       image2.width() == 0 || image2.height() == 0)
01762     return image1;
01763 
01764   TQImage image3;
01765 
01766   image3 = KImageEffect::unbalancedGradient(image1.size(),
01767                     TQColor(0,0,0), TQColor(255,255,255),
01768                     gt, xf, yf, 0);
01769 
01770   return blend(image1,image2,image3, Red); // Channel to use is arbitrary
01771 }
01772 
01773 // Blend image2 into image1, using an RBG channel of blendImage
01774 //
01775 TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2,
01776                 TQImage &blendImage, RGBComponent channel)
01777 {
01778     if (image1.width() == 0 || image1.height() == 0 ||
01779         image2.width() == 0 || image2.height() == 0 ||
01780         blendImage.width() == 0 || blendImage.height() == 0) {
01781 #ifndef NDEBUG
01782         std::cerr << "KImageEffect::blend effect invalid image" << std::endl;
01783 #endif
01784       return image1;
01785     }
01786 
01787     int r, g, b;
01788     int ind1, ind2, ind3;
01789 
01790     unsigned int x1, x2, x3, y1, y2, y3;
01791     unsigned int a;
01792 
01793     int x, y;
01794 
01795     // for image1 and image2, we only handle depth 32
01796     if (image1.depth()<32) image1 = image1.convertDepth(32);
01797     if (image2.depth()<32) image2 = image2.convertDepth(32);
01798 
01799     // for blendImage, we handle depth 8 and 32
01800     if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
01801 
01802     unsigned int *colorTable3 = (blendImage.depth()==8) ?
01803                  blendImage.tqcolorTable():0;
01804 
01805     unsigned int *data1 =  (unsigned int *)image1.bits();
01806     unsigned int *data2 =  (unsigned int *)image2.bits();
01807     unsigned int *data3   =  (unsigned int *)blendImage.bits();
01808     unsigned char *data3b =  (unsigned char *)blendImage.bits();
01809     unsigned int color3;
01810 
01811     x1 = image1.width();     y1 = image1.height();
01812     x2 = image2.width();     y2 = image2.height();
01813     x3 = blendImage.width(); y3 = blendImage.height();
01814 
01815     for (y = 0; y < (int)y1; y++) {
01816     ind1 = x1*y;
01817     ind2 = x2*(y%y2);
01818     ind3 = x3*(y%y3);
01819 
01820     x=0;
01821     while(x < (int)x1) {
01822       color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
01823 
01824           a = (channel == Red) ? tqRed(color3) :
01825               (channel == Green) ? tqGreen(color3) :
01826           (channel == Blue) ? tqBlue(color3) : tqGray(color3);
01827 
01828       r = (a*tqRed(data1[ind1]) + (256-a)*tqRed(data2[ind2]))/256;
01829       g = (a*tqGreen(data1[ind1]) + (256-a)*tqGreen(data2[ind2]))/256;
01830       b = (a*tqBlue(data1[ind1]) + (256-a)*tqBlue(data2[ind2]))/256;
01831 
01832       a = tqAlpha(data1[ind1]);
01833       data1[ind1] = tqRgba(r, g, b, a);
01834 
01835       ind1++; ind2++; ind3++; x++;
01836       if ( (x%x2) ==0) ind2 -= x2;
01837       if ( (x%x3) ==0) ind3 -= x3;
01838         }
01839     }
01840     return image1;
01841 }
01842 
01843 
01844 //======================================================================
01845 //
01846 // Hash effects
01847 //
01848 //======================================================================
01849 
01850 unsigned int KImageEffect::lHash(unsigned int c)
01851 {
01852     unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c);
01853     unsigned char nr, ng, nb;
01854     nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
01855     ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
01856     nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
01857 
01858     return tqRgba(nr, ng, nb, a);
01859 }
01860 
01861 
01862 // -----------------------------------------------------------------------------
01863 
01864 unsigned int KImageEffect::uHash(unsigned int c)
01865 {
01866     unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c);
01867     unsigned char nr, ng, nb;
01868     nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
01869     ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
01870     nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
01871 
01872     return tqRgba(nr, ng, nb, a);
01873 }
01874 
01875 
01876 // -----------------------------------------------------------------------------
01877 
01878 TQImage& KImageEffect::hash(TQImage &image, Lighting lite, unsigned int spacing)
01879 {
01880     if (image.width() == 0 || image.height() == 0) {
01881 #ifndef NDEBUG
01882         std::cerr << "KImageEffect::hash effect invalid image" << std::endl;
01883 #endif
01884       return image;
01885     }
01886 
01887     int x, y;
01888     unsigned int *data =  (unsigned int *)image.bits();
01889     unsigned int ind;
01890 
01891     //CT no need to do it if not enough space
01892     if ((lite == NorthLite ||
01893          lite == SouthLite)&&
01894         (unsigned)image.height() < 2+spacing) return image;
01895     if ((lite == EastLite ||
01896          lite == WestLite)&&
01897         (unsigned)image.height() < 2+spacing) return image;
01898 
01899     if (lite == NorthLite || lite == SouthLite) {
01900         for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
01901             for (x = 0; x < image.width(); x++) {
01902                 ind = x + image.width() * y;
01903                 data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
01904 
01905                 ind = ind + image.width();
01906                 data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
01907             }
01908         }
01909     }
01910 
01911     else if (lite == EastLite || lite == WestLite) {
01912         for (y = 0 ; y < image.height(); y++) {
01913             for (x = 0; x < image.width(); x = x + 2 + spacing) {
01914                 ind = x + image.width() * y;
01915                 data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
01916 
01917                 ind++;
01918                 data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
01919             }
01920         }
01921     }
01922 
01923     else if (lite == NWLite || lite == SELite) {
01924         for (y = 0 ; y < image.height(); y++) {
01925             for (x = 0;
01926                  x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
01927                  x = x + 2 + spacing) {
01928                 ind = x + image.width() * y + ((y & 1)? 1 : 0);
01929                 data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
01930 
01931                 ind++;
01932                 data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
01933             }
01934         }
01935     }
01936 
01937     else if (lite == SWLite || lite == NELite) {
01938         for (y = 0 ; y < image.height(); y++) {
01939             for (x = 0  + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
01940                 ind = x + image.width() * y - ((y & 1)? 1 : 0);
01941                 data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
01942 
01943                 ind++;
01944                 data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
01945             }
01946         }
01947     }
01948 
01949     return image;
01950 }
01951 
01952 
01953 //======================================================================
01954 //
01955 // Flatten effects
01956 //
01957 //======================================================================
01958 
01959 TQImage& KImageEffect::flatten(TQImage &img, const TQColor &ca,
01960                             const TQColor &cb, int ncols)
01961 {
01962     if (img.width() == 0 || img.height() == 0)
01963       return img;
01964 
01965     // a bitmap is easy...
01966     if (img.depth() == 1) {
01967     img.setColor(0, ca.rgb());
01968     img.setColor(1, cb.rgb());
01969     return img;
01970     }
01971 
01972     int r1 = ca.red(); int r2 = cb.red();
01973     int g1 = ca.green(); int g2 = cb.green();
01974     int b1 = ca.blue(); int b2 = cb.blue();
01975     int min = 0, max = 255;
01976 
01977     TQRgb col;
01978 
01979     // Get minimum and maximum greylevel.
01980     if (img.numColors()) {
01981     // pseudocolor
01982     for (int i = 0; i < img.numColors(); i++) {
01983         col = img.color(i);
01984         int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
01985         min = TQMIN(min, mean);
01986         max = TQMAX(max, mean);
01987     }
01988     } else {
01989     // truecolor
01990     for (int y=0; y < img.height(); y++)
01991         for (int x=0; x < img.width(); x++) {
01992         col = img.pixel(x, y);
01993         int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
01994         min = TQMIN(min, mean);
01995         max = TQMAX(max, mean);
01996         }
01997     }
01998 
01999     // Conversion factors
02000     float sr = ((float) r2 - r1) / (max - min);
02001     float sg = ((float) g2 - g1) / (max - min);
02002     float sb = ((float) b2 - b1) / (max - min);
02003 
02004 
02005     // Repaint the image
02006     if (img.numColors()) {
02007     for (int i=0; i < img.numColors(); i++) {
02008         col = img.color(i);
02009         int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
02010         int r = (int) (sr * (mean - min) + r1 + 0.5);
02011         int g = (int) (sg * (mean - min) + g1 + 0.5);
02012         int b = (int) (sb * (mean - min) + b1 + 0.5);
02013         img.setColor(i, tqRgba(r, g, b, tqAlpha(col)));
02014     }
02015     } else {
02016     for (int y=0; y < img.height(); y++)
02017         for (int x=0; x < img.width(); x++) {
02018         col = img.pixel(x, y);
02019         int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3;
02020         int r = (int) (sr * (mean - min) + r1 + 0.5);
02021         int g = (int) (sg * (mean - min) + g1 + 0.5);
02022         int b = (int) (sb * (mean - min) + b1 + 0.5);
02023         img.setPixel(x, y, tqRgba(r, g, b, tqAlpha(col)));
02024         }
02025     }
02026 
02027 
02028     // Dither if necessary
02029     if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
02030     return img;
02031 
02032     if (ncols == 1) ncols++;
02033     if (ncols > 256) ncols = 256;
02034 
02035     TQColor *pal = new TQColor[ncols];
02036     sr = ((float) r2 - r1) / (ncols - 1);
02037     sg = ((float) g2 - g1) / (ncols - 1);
02038     sb = ((float) b2 - b1) / (ncols - 1);
02039 
02040     for (int i=0; i<ncols; i++)
02041     pal[i] = TQColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
02042 
02043     dither(img, pal, ncols);
02044 
02045     delete[] pal;
02046     return img;
02047 }
02048 
02049 
02050 //======================================================================
02051 //
02052 // Fade effects
02053 //
02054 //======================================================================
02055 
02056 TQImage& KImageEffect::fade(TQImage &img, float val, const TQColor &color)
02057 {
02058     if (img.width() == 0 || img.height() == 0)
02059       return img;
02060 
02061     // We don't handle bitmaps
02062     if (img.depth() == 1)
02063     return img;
02064 
02065     unsigned char tbl[256];
02066     for (int i=0; i<256; i++)
02067     tbl[i] = (int) (val * i + 0.5);
02068 
02069     int red = color.red();
02070     int green = color.green();
02071     int blue = color.blue();
02072 
02073     TQRgb col;
02074     int r, g, b, cr, cg, cb;
02075 
02076     if (img.depth() <= 8) {
02077     // pseudo color
02078     for (int i=0; i<img.numColors(); i++) {
02079         col = img.color(i);
02080         cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col);
02081         if (cr > red)
02082         r = cr - tbl[cr - red];
02083         else
02084         r = cr + tbl[red - cr];
02085         if (cg > green)
02086         g = cg - tbl[cg - green];
02087         else
02088         g = cg + tbl[green - cg];
02089         if (cb > blue)
02090         b = cb - tbl[cb - blue];
02091         else
02092         b = cb + tbl[blue - cb];
02093         img.setColor(i, tqRgba(r, g, b, tqAlpha(col)));
02094     }
02095 
02096     } else {
02097     // truecolor
02098         for (int y=0; y<img.height(); y++) {
02099             TQRgb *data = (TQRgb *) img.scanLine(y);
02100             for (int x=0; x<img.width(); x++) {
02101                 col = *data;
02102                 cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col);
02103                 if (cr > red)
02104                     r = cr - tbl[cr - red];
02105                 else
02106                     r = cr + tbl[red - cr];
02107                 if (cg > green)
02108                     g = cg - tbl[cg - green];
02109                 else
02110                     g = cg + tbl[green - cg];
02111                 if (cb > blue)
02112                     b = cb - tbl[cb - blue];
02113                 else
02114                     b = cb + tbl[blue - cb];
02115                 *data++ = tqRgba(r, g, b, tqAlpha(col));
02116             }
02117         }
02118     }
02119 
02120     return img;
02121 }
02122 
02123 //======================================================================
02124 //
02125 // Color effects
02126 //
02127 //======================================================================
02128 
02129 // This code is adapted from code (C) Rik Hemsley <rik@kde.org>
02130 //
02131 // The formula used (r + b + g) /3 is different from the tqGray formula
02132 // used by Qt.  This is because our formula is much much faster.  If,
02133 // however, it turns out that this is producing sub-optimal images,
02134 // then it will have to change (kurt)
02135 //
02136 // It does produce lower quality grayscale ;-) Use fast == true for the fast
02137 // algorithm, false for the higher quality one (mosfet).
02138 TQImage& KImageEffect::toGray(TQImage &img, bool fast)
02139 {
02140     if (img.width() == 0 || img.height() == 0)
02141       return img;
02142 
02143     if(fast){
02144         if (img.depth() == 32) {
02145             uchar * r(img.bits());
02146             uchar * g(img.bits() + 1);
02147             uchar * b(img.bits() + 2);
02148 
02149             uchar * end(img.bits() + img.numBytes());
02150 
02151             while (r != end) {
02152 
02153                 *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
02154 
02155                 r += 4;
02156                 g += 4;
02157                 b += 4;
02158             }
02159         }
02160         else
02161         {
02162             for (int i = 0; i < img.numColors(); i++)
02163             {
02164                 uint r = tqRed(img.color(i));
02165                 uint g = tqGreen(img.color(i));
02166                 uint b = tqBlue(img.color(i));
02167 
02168                 uint gray = (((r + g) >> 1) + b) >> 1;
02169                 img.setColor(i, tqRgba(gray, gray, gray, tqAlpha(img.color(i))));
02170             }
02171         }
02172     }
02173     else{
02174         int pixels = img.depth() > 8 ? img.width()*img.height() :
02175             img.numColors();
02176         unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
02177             (unsigned int *)img.tqcolorTable();
02178         int val, i;
02179         for(i=0; i < pixels; ++i){
02180             val = tqGray(data[i]);
02181             data[i] = tqRgba(val, val, val, tqAlpha(data[i]));
02182         }
02183     }
02184     return img;
02185 }
02186 
02187 // CT 29Jan2000 - desaturation algorithms
02188 TQImage& KImageEffect::desaturate(TQImage &img, float desat)
02189 {
02190     if (img.width() == 0 || img.height() == 0)
02191       return img;
02192 
02193     if (desat < 0) desat = 0.;
02194     if (desat > 1) desat = 1.;
02195     int pixels = img.depth() > 8 ? img.width()*img.height() :
02196         img.numColors();
02197     unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
02198         (unsigned int *)img.tqcolorTable();
02199     int h, s, v, i;
02200     TQColor clr; // keep constructor out of loop (mosfet)
02201     for(i=0; i < pixels; ++i){
02202         clr.setRgb(data[i]);
02203     clr.hsv(&h, &s, &v);
02204     clr.setHsv(h, (int)(s * (1. - desat)), v);
02205     data[i] = clr.rgb();
02206     }
02207     return img;
02208 }
02209 
02210 // Contrast stuff (mosfet)
02211 TQImage& KImageEffect::contrast(TQImage &img, int c)
02212 {
02213     if (img.width() == 0 || img.height() == 0)
02214       return img;
02215 
02216     if(c > 255)
02217         c = 255;
02218     if(c < -255)
02219         c =  -255;
02220     int pixels = img.depth() > 8 ? img.width()*img.height() :
02221         img.numColors();
02222     unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
02223         (unsigned int *)img.tqcolorTable();
02224     int i, r, g, b;
02225     for(i=0; i < pixels; ++i){
02226         r = tqRed(data[i]);
02227         g = tqGreen(data[i]);
02228         b = tqBlue(data[i]);
02229         if(tqGray(data[i]) <= 127){
02230             if(r - c > 0)
02231                 r -= c;
02232             else
02233                 r = 0;
02234             if(g - c > 0)
02235                 g -= c;
02236             else
02237                 g = 0;
02238             if(b - c > 0)
02239                 b -= c;
02240             else
02241                 b = 0;
02242         }
02243         else{
02244             if(r + c <= 255)
02245                 r += c;
02246             else
02247                 r = 255;
02248             if(g + c <= 255)
02249                 g += c;
02250             else
02251                 g = 255;
02252             if(b + c <= 255)
02253                 b += c;
02254             else
02255                 b = 255;
02256         }
02257         data[i] = tqRgba(r, g, b, tqAlpha(data[i]));
02258     }
02259     return(img);
02260 }
02261 
02262 //======================================================================
02263 //
02264 // Dithering effects
02265 //
02266 //======================================================================
02267 
02268 // adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
02269 //
02270 // Floyd-Steinberg dithering
02271 // Ref: Bitmapped Graphics Programming in C++
02272 //      Marv Luse, Addison-Wesley Publishing, 1993.
02273 TQImage& KImageEffect::dither(TQImage &img, const TQColor *palette, int size)
02274 {
02275     if (img.width() == 0 || img.height() == 0 ||
02276         palette == 0 || img.depth() <= 8)
02277       return img;
02278 
02279     TQImage dImage( img.width(), img.height(), 8, size );
02280     int i;
02281 
02282     dImage.setNumColors( size );
02283     for ( i = 0; i < size; i++ )
02284         dImage.setColor( i, palette[ i ].rgb() );
02285 
02286     int *rerr1 = new int [ img.width() * 2 ];
02287     int *gerr1 = new int [ img.width() * 2 ];
02288     int *berr1 = new int [ img.width() * 2 ];
02289 
02290     memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
02291     memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
02292     memset( berr1, 0, sizeof( int ) * img.width() * 2 );
02293 
02294     int *rerr2 = rerr1 + img.width();
02295     int *gerr2 = gerr1 + img.width();
02296     int *berr2 = berr1 + img.width();
02297 
02298     for ( int j = 0; j < img.height(); j++ )
02299     {
02300         uint *ip = (uint * )img.scanLine( j );
02301         uchar *dp = dImage.scanLine( j );
02302 
02303         for ( i = 0; i < img.width(); i++ )
02304         {
02305             rerr1[i] = rerr2[i] + tqRed( *ip );
02306             rerr2[i] = 0;
02307             gerr1[i] = gerr2[i] + tqGreen( *ip );
02308             gerr2[i] = 0;
02309             berr1[i] = berr2[i] + tqBlue( *ip );
02310             berr2[i] = 0;
02311             ip++;
02312         }
02313 
02314         *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
02315 
02316         for ( i = 1; i < img.width()-1; i++ )
02317         {
02318             int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
02319             *dp = indx;
02320 
02321             int rerr = rerr1[i];
02322             rerr -= palette[indx].red();
02323             int gerr = gerr1[i];
02324             gerr -= palette[indx].green();
02325             int berr = berr1[i];
02326             berr -= palette[indx].blue();
02327 
02328             // diffuse red error
02329             rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
02330             rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
02331             rerr2[  i  ] += ( rerr * 5 ) >> 4;
02332             rerr2[ i+1 ] += ( rerr ) >> 4;
02333 
02334             // diffuse green error
02335             gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
02336             gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
02337             gerr2[  i  ] += ( gerr * 5 ) >> 4;
02338             gerr2[ i+1 ] += ( gerr ) >> 4;
02339 
02340             // diffuse red error
02341             berr1[ i+1 ] += ( berr * 7 ) >> 4;
02342             berr2[ i-1 ] += ( berr * 3 ) >> 4;
02343             berr2[  i  ] += ( berr * 5 ) >> 4;
02344             berr2[ i+1 ] += ( berr ) >> 4;
02345 
02346             dp++;
02347         }
02348 
02349         *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
02350     }
02351 
02352     delete [] rerr1;
02353     delete [] gerr1;
02354     delete [] berr1;
02355 
02356     img = dImage;
02357     return img;
02358 }
02359 
02360 int KImageEffect::nearestColor( int r, int g, int b, const TQColor *palette, int size )
02361 {
02362     if (palette == 0)
02363       return 0;
02364 
02365     int dr = palette[0].red() - r;
02366     int dg = palette[0].green() - g;
02367     int db = palette[0].blue() - b;
02368 
02369     int minDist =  dr*dr + dg*dg + db*db;
02370     int nearest = 0;
02371 
02372     for (int i = 1; i < size; i++ )
02373     {
02374         dr = palette[i].red() - r;
02375         dg = palette[i].green() - g;
02376         db = palette[i].blue() - b;
02377 
02378         int dist = dr*dr + dg*dg + db*db;
02379 
02380         if ( dist < minDist )
02381         {
02382             minDist = dist;
02383             nearest = i;
02384         }
02385     }
02386 
02387     return nearest;
02388 }
02389 
02390 bool KImageEffect::blend(
02391     const TQImage & upper,
02392     const TQImage & lower,
02393     TQImage & output
02394 )
02395 {
02396   if (
02397       upper.width()  > lower.width()  ||
02398       upper.height() > lower.height() ||
02399       upper.depth() != 32             ||
02400       lower.depth() != 32
02401   )
02402   {
02403 #ifndef NDEBUG
02404     std::cerr << "KImageEffect::blend : Sizes not correct\n" ;
02405 #endif
02406     return false;
02407   }
02408 
02409   output = lower.copy();
02410 
02411   uchar *i, *o;
02412   int a;
02413   int col;
02414   int w = upper.width();
02415   int row(upper.height() - 1);
02416 
02417   do {
02418 
02419     i = const_cast<TQImage&>(upper).scanLine(row);
02420     o = const_cast<TQImage&>(output).scanLine(row);
02421 
02422     col = w << 2;
02423     --col;
02424 
02425     do {
02426 
02427       while (!(a = i[col]) && (col != 3)) {
02428         --col; --col; --col; --col;
02429       }
02430 
02431       --col;
02432       o[col] += ((i[col] - o[col]) * a) >> 8;
02433 
02434       --col;
02435       o[col] += ((i[col] - o[col]) * a) >> 8;
02436 
02437       --col;
02438       o[col] += ((i[col] - o[col]) * a) >> 8;
02439 
02440     } while (col--);
02441 
02442   } while (row--);
02443 
02444   return true;
02445 }
02446 
02447 #if 0
02448 // Not yet...
02449 bool KImageEffect::blend(
02450     const TQImage & upper,
02451     const TQImage & lower,
02452     TQImage & output,
02453     const TQRect & destRect
02454 )
02455 {
02456   output = lower.copy();
02457   return output;
02458 }
02459 
02460 #endif
02461 
02462 bool KImageEffect::blend(
02463     int &x, int &y,
02464     const TQImage & upper,
02465     const TQImage & lower,
02466     TQImage & output
02467 )
02468 {
02469   int cx=0, cy=0, cw=upper.width(), ch=upper.height();
02470 
02471   if ( upper.width() + x > lower.width()  ||
02472       upper.height() + y > lower.height() ||
02473       x < 0 || y < 0 ||
02474       upper.depth() != 32 || lower.depth() != 32 )
02475   {
02476     if ( x > lower.width() || y > lower.height() ) return false;
02477     if ( upper.width()<=0 || upper.height() <= 0 ) return false;
02478     if ( lower.width()<=0 || lower.height() <= 0 ) return false;
02479 
02480     if (x<0) {cx=-x; cw+=x; x=0; };
02481     if (cw + x > lower.width()) { cw=lower.width()-x; };
02482     if (y<0) {cy=-y; ch+=y; y=0; };
02483     if (ch + y > lower.height()) { ch=lower.height()-y; };
02484 
02485     if ( cx >= upper.width() || cy >= upper.height() ) return true;
02486     if ( cw <= 0 || ch <= 0 ) return true;
02487   }
02488 
02489   output.create(cw,ch,32);
02490 //  output.setAlphaBuffer(true); // I should do some benchmarks to see if
02491     // this is worth the effort
02492 
02493   TQRgb *i, *o, *b;
02494 
02495   int a;
02496   int j,k;
02497   for (j=0; j<ch; j++)
02498   {
02499     b=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ]);
02500     i=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ]);
02501     o=reinterpret_cast<TQRgb *>(&const_cast<TQImage&>(output).scanLine(j)  [ cw << 2 ]);
02502 
02503     k=cw-1;
02504     --b; --i; --o;
02505     do
02506     {
02507       while ( !(a=tqAlpha(*i)) && k>0 )
02508       {
02509         i--;
02510 //  *o=0;
02511     *o=*b;
02512     --o; --b;
02513     k--;
02514       };
02515 //      *o=0xFF;
02516       *o = tqRgb(tqRed(*b) + (((tqRed(*i) - tqRed(*b)) * a) >> 8),
02517                 tqGreen(*b) + (((tqGreen(*i) - tqGreen(*b)) * a) >> 8),
02518                 tqBlue(*b) + (((tqBlue(*i) - tqBlue(*b)) * a) >> 8));
02519       --i; --o; --b;
02520     } while (k--);
02521   }
02522 
02523   return true;
02524 }
02525 
02526 bool KImageEffect::blendOnLower(
02527     int x, int y,
02528     const TQImage & upper,
02529     const TQImage & lower
02530 )
02531 {
02532   int cx=0, cy=0, cw=upper.width(), ch=upper.height();
02533 
02534   if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
02535   if ( x + cw > lower.width()  ||
02536       y + ch > lower.height() ||
02537       x < 0 || y < 0 )
02538   {
02539     if ( x > lower.width() || y > lower.height() ) return true;
02540     if ( upper.width()<=0 || upper.height() <= 0 ) return true;
02541     if ( lower.width()<=0 || lower.height() <= 0 ) return true;
02542 
02543     if (x<0) {cx=-x; cw+=x; x=0; };
02544     if (cw + x > lower.width()) { cw=lower.width()-x; };
02545     if (y<0) {cy=-y; ch+=y; y=0; };
02546     if (ch + y > lower.height()) { ch=lower.height()-y; };
02547 
02548     if ( cx >= upper.width() || cy >= upper.height() ) return true;
02549     if ( cw <= 0 || ch <= 0 ) return true;
02550   }
02551 
02552   uchar *i, *b;
02553   int a;
02554   int k;
02555 
02556   for (int j=0; j<ch; j++)
02557   {
02558     b=&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ];
02559     i=&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ];
02560 
02561     k=cw-1;
02562     --b; --i;
02563     do
02564     {
02565 #ifndef WORDS_BIGENDIAN
02566       while ( !(a=*i) && k>0 )
02567 #else
02568       while ( !(a=*(i-3)) && k>0 )
02569 #endif
02570       {
02571         i-=4; b-=4; k--;
02572       };
02573 
02574 #ifndef WORDS_BIGENDIAN
02575       --i; --b;
02576       *b += ( ((*i - *b) * a) >> 8 );
02577       --i; --b;
02578       *b += ( ((*i - *b) * a) >> 8 );
02579       --i; --b;
02580       *b += ( ((*i - *b) * a) >> 8 );
02581       --i; --b;
02582 #else
02583       *b += ( ((*i - *b) * a) >> 8 );
02584       --i; --b;
02585       *b += ( ((*i - *b) * a) >> 8 );
02586       --i; --b;
02587       *b += ( ((*i - *b) * a) >> 8 );
02588       i -= 2; b -= 2;
02589 #endif
02590     } while (k--);
02591   }
02592 
02593   return true;
02594 }
02595 
02596 void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset,
02597                                 TQImage &lower, const TQRect &lowerRect)
02598 {
02599     // clip rect
02600     TQRect lr =  lowerRect & lower.rect();
02601     lr.setWidth( TQMIN(lr.width(), upper.width()-upperOffset.x()) );
02602     lr.setHeight( TQMIN(lr.height(), upper.height()-upperOffset.y()) );
02603     if ( !lr.isValid() ) return;
02604 
02605     // blend
02606     for (int y = 0; y < lr.height(); y++) {
02607         for (int x = 0; x < lr.width(); x++) {
02608             TQRgb *b = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(TQRgb));
02609             TQRgb *d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(TQRgb));
02610             int a = tqAlpha(*d);
02611             *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
02612                       tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
02613                       tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
02614         }
02615     }
02616 }
02617 
02618 void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset,
02619                           TQImage &lower, const TQRect &lowerRect, float opacity)
02620 {
02621     // clip rect
02622     TQRect lr =  lowerRect & lower.rect();
02623     lr.setWidth( TQMIN(lr.width(), upper.width()-upperOffset.x()) );
02624     lr.setHeight( TQMIN(lr.height(), upper.height()-upperOffset.y()) );
02625     if ( !lr.isValid() ) return;
02626 
02627     // blend
02628     for (int y = 0; y < lr.height(); y++) {
02629         for (int x = 0; x < lr.width(); x++) {
02630             TQRgb *b = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(TQRgb));
02631             TQRgb *d = reinterpret_cast<TQRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(TQRgb));
02632             int a = tqRound(opacity * tqAlpha(*d));
02633             *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8),
02634                       tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8),
02635                       tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8));
02636         }
02637     }
02638 }
02639 
02640 TQRect KImageEffect::computeDestinationRect(const TQSize &lowerSize,
02641                                        Disposition disposition, TQImage &upper)
02642 {
02643     int w = lowerSize.width();
02644     int h = lowerSize.height();
02645     int ww = upper.width();
02646     int wh = upper.height();
02647     TQRect d;
02648 
02649     switch (disposition) {
02650     case NoImage:
02651         break;
02652     case Centered:
02653         d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
02654         break;
02655     case Tiled:
02656         d.setRect(0, 0, w, h);
02657         break;
02658     case CenterTiled:
02659         d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh,
02660                     w-1, h-1);
02661         break;
02662     case Scaled:
02663         upper = upper.smoothScale(w, h);
02664         d.setRect(0, 0, w, h);
02665         break;
02666     case CenteredAutoFit:
02667         if( ww <= w && wh <= h ) {
02668             d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered
02669             break;
02670         }
02671         // fall through
02672     case CenteredMaxpect: {
02673         double sx = (double) w / ww;
02674         double sy = (double) h / wh;
02675         if (sx > sy) {
02676             ww = (int)(sy * ww);
02677             wh = h;
02678         } else {
02679             wh = (int)(sx * wh);
02680             ww = w;
02681         }
02682         upper = upper.smoothScale(ww, wh);
02683         d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
02684         break;
02685     }
02686     case TiledMaxpect: {
02687         double sx = (double) w / ww;
02688         double sy = (double) h / wh;
02689         if (sx > sy) {
02690             ww = (int)(sy * ww);
02691             wh = h;
02692         } else {
02693             wh = (int)(sx * wh);
02694             ww = w;
02695         }
02696         upper = upper.smoothScale(ww, wh);
02697         d.setRect(0, 0, w, h);
02698         break;
02699     }
02700     }
02701 
02702     return d;
02703 }
02704 
02705 void KImageEffect::blendOnLower(TQImage &upper, TQImage &lower,
02706                                 Disposition disposition, float opacity)
02707 {
02708     TQRect r = computeDestinationRect(lower.size(), disposition, upper);
02709     for (int y = r.top(); y<r.bottom(); y += upper.height())
02710         for (int x = r.left(); x<r.right(); x += upper.width())
02711             blendOnLower(upper, TQPoint(-TQMIN(x, 0), -TQMIN(y, 0)),
02712                    lower, TQRect(x, y, upper.width(), upper.height()), opacity);
02713 }
02714 
02715 
02716 // For selected icons
02717 TQImage& KImageEffect::selectedImage( TQImage &img, const TQColor &col )
02718 {
02719     return blend( col, img, 0.5);
02720 }
02721 
02722 //
02723 // ===================================================================
02724 // Effects originally ported from ImageMagick for PixiePlus, plus a few
02725 // new ones. (mosfet 05/26/2003)
02726 // ===================================================================
02727 //
02728 /*
02729  Portions of this software are based on ImageMagick. Such portions are clearly
02730 marked as being ported from ImageMagick. ImageMagick is copyrighted under the
02731 following conditions:
02732 
02733 Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to
02734 making software imaging solutions freely available.
02735 
02736 Permission is hereby granted, free of charge, to any person obtaining a copy
02737 of this software and associated documentation files ("ImageMagick"), to deal
02738 in ImageMagick without restriction, including without limitation the rights
02739 to use, copy, modify, merge, publish, distribute, sublicense,  and/or sell
02740 copies of ImageMagick, and to permit persons to whom the ImageMagick is
02741 furnished to do so, subject to the following conditions:
02742 
02743 The above copyright notice and this permission notice shall be included in all
02744 copies or substantial portions of ImageMagick.
02745 
02746 The software is provided "as is", without warranty of any kind, express or
02747 implied, including but not limited to the warranties of merchantability,
02748 fitness for a particular purpose and noninfringement.  In no event shall
02749 ImageMagick Studio be liable for any claim, damages or other liability,
02750 whether in an action of contract, tort or otherwise, arising from, out of or
02751 in connection with ImageMagick or the use or other dealings in ImageMagick.
02752 
02753 Except as contained in this notice, the name of the ImageMagick Studio shall
02754 not be used in advertising or otherwise to promote the sale, use or other
02755 dealings in ImageMagick without prior written authorization from the
02756 ImageMagick Studio.
02757 */
02758 
02759 TQImage KImageEffect::sample(TQImage &src, int w, int h)
02760 {
02761     if(w == src.width() && h == src.height())
02762         return(src);
02763 
02764     int depth = src.depth();
02765     TQImage dest(w, h, depth, depth <= 8 ? src.numColors() : 0,
02766     depth == 1 ? TQImage::LittleEndian : TQImage::IgnoreEndian);
02767     int *x_offset = (int *)malloc(w*sizeof(int));
02768     int *y_offset = (int *)malloc(h*sizeof(int));
02769     if(!x_offset || !y_offset){
02770 #ifndef NDEBUG
02771         tqWarning("KImageEffect::sample(): Unable to allocate pixel buffer");
02772 #endif
02773         free(x_offset);
02774         free(y_offset);
02775         return(src);
02776     }
02777 
02778     // init pixel offsets
02779     for(int x=0; x < w; ++x)
02780         x_offset[x] = (int)(x*src.width()/((double)w));
02781     for(int y=0; y < h; ++y)
02782         y_offset[y] = (int)(y*src.height()/((double)h));
02783 
02784     if(depth > 8){ // DirectClass source image
02785         for(int y=0; y < h; ++y){
02786             unsigned int *destData = (unsigned int *)dest.scanLine(y);
02787             unsigned int *srcData = (unsigned int *)src.scanLine(y_offset[y]);
02788             for(int x=0; x < w; ++x)
02789                 destData[x] = srcData[x_offset[x]];
02790         }
02791     }
02792     else if(depth == 1) {
02793         int r = src.bitOrder() == TQImage::LittleEndian;
02794         memcpy(dest.tqcolorTable(), src.tqcolorTable(), src.numColors()*sizeof(TQRgb));
02795         for(int y=0; y < h; ++y){
02796             unsigned char *destData = dest.scanLine(y);
02797             unsigned char *srcData = src.scanLine(y_offset[y]);
02798             for(int x=0; x < w; ++x){
02799                 int k = x_offset[x];
02800                 int l = r ? (k & 7) : (7 - (k&7));
02801                 if(srcData[k >> 3] & (1 << l))
02802                     destData[x >> 3] |= 1 << (x & 7);
02803                 else
02804                     destData[x >> 3] &= ~(1 << (x & 7));
02805             }
02806         }
02807     }
02808     else{ // PseudoClass source image
02809         memcpy(dest.tqcolorTable(), src.tqcolorTable(), src.numColors()*sizeof(TQRgb));
02810         for(int y=0; y < h; ++y){
02811             unsigned char *destData = dest.scanLine(y);
02812             unsigned char *srcData = src.scanLine(y_offset[y]);
02813             for(int x=0; x < w; ++x)
02814                 destData[x] = srcData[x_offset[x]];
02815         }
02816     }
02817     free(x_offset);
02818     free(y_offset);
02819     return(dest);
02820 }
02821 
02822 void KImageEffect::threshold(TQImage &img, unsigned int threshold)
02823 {
02824     int i, count;
02825     unsigned int *data;
02826     if(img.depth() > 8){ // DirectClass
02827         count = img.width()*img.height();
02828         data = (unsigned int *)img.bits();
02829     }
02830     else{ // PsudeoClass
02831         count = img.numColors();
02832         data = (unsigned int *)img.tqcolorTable();
02833     }
02834     for(i=0; i < count; ++i)
02835         data[i] = intensityValue(data[i]) < threshold ? QColor(Qt::black).rgb() : QColor(Qt::white).rgb();
02836 }
02837 
02838 void KImageEffect::hull(const int x_offset, const int y_offset,
02839                         const int polarity, const int columns,
02840                         const int rows,
02841                         unsigned int *f, unsigned int *g)
02842 {
02843     int x, y;
02844 
02845     unsigned int *p, *q, *r, *s;
02846     unsigned int v;
02847     if(f == NULL || g == NULL)
02848         return;
02849     p=f+(columns+2);
02850     q=g+(columns+2);
02851     r=p+(y_offset*(columns+2)+x_offset);
02852     for (y=0; y < rows; y++){
02853         p++;
02854         q++;
02855         r++;
02856         if(polarity > 0)
02857             for (x=0; x < columns; x++){
02858                 v=(*p);
02859                 if (*r > v)
02860                     v++;
02861                 *q=v;
02862                 p++;
02863                 q++;
02864                 r++;
02865             }
02866         else
02867             for(x=0; x < columns; x++){
02868                 v=(*p);
02869                 if (v > (unsigned int) (*r+1))
02870                     v--;
02871                 *q=v;
02872                 p++;
02873                 q++;
02874                 r++;
02875             }
02876         p++;
02877         q++;
02878         r++;
02879     }
02880     p=f+(columns+2);
02881     q=g+(columns+2);
02882     r=q+(y_offset*(columns+2)+x_offset);
02883     s=q-(y_offset*(columns+2)+x_offset);
02884     for(y=0; y < rows; y++){
02885         p++;
02886         q++;
02887         r++;
02888         s++;
02889         if(polarity > 0)
02890             for(x=0; x < (int) columns; x++){
02891                 v=(*q);
02892                 if (((unsigned int) (*s+1) > v) && (*r > v))
02893                     v++;
02894                 *p=v;
02895                 p++;
02896                 q++;
02897                 r++;
02898                 s++;
02899             }
02900         else
02901             for (x=0; x < columns; x++){
02902                 v=(*q);
02903                 if (((unsigned int) (*s+1) < v) && (*r < v))
02904                     v--;
02905                 *p=v;
02906                 p++;
02907                 q++;
02908                 r++;
02909                 s++;
02910             }
02911         p++;
02912         q++;
02913         r++;
02914         s++;
02915     }
02916 }
02917 
02918 TQImage KImageEffect::despeckle(TQImage &src)
02919 {
02920     int i, j, x, y;
02921     unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
02922         *alpha_channel;
02923     int packets;
02924     static const int
02925     X[4]= {0, 1, 1,-1},
02926     Y[4]= {1, 0, 1, 1};
02927 
02928     unsigned int *destData;
02929     TQImage dest(src.width(), src.height(), 32);
02930 
02931     packets = (src.width()+2)*(src.height()+2);
02932     red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02933     green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02934     blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02935     alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02936     buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
02937     if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
02938        !buffer){
02939         free(red_channel);
02940         free(green_channel);
02941         free(blue_channel);
02942         free(alpha_channel);
02943         free(buffer);
02944         return(src);
02945     }
02946 
02947     // copy image pixels to color component buffers
02948     j = src.width()+2;
02949     if(src.depth() > 8){ // DirectClass source image
02950         unsigned int *srcData;
02951         for(y=0; y < src.height(); ++y){
02952             srcData = (unsigned int *)src.scanLine(y);
02953             ++j;
02954             for(x=0; x < src.width(); ++x){
02955                 red_channel[j] = tqRed(srcData[x]);
02956                 green_channel[j] = tqGreen(srcData[x]);
02957                 blue_channel[j] = tqBlue(srcData[x]);
02958                 alpha_channel[j] = tqAlpha(srcData[x]);
02959                 ++j;
02960             }
02961             ++j;
02962         }
02963     }
02964     else{ // PsudeoClass source image
02965         unsigned char *srcData;
02966         unsigned int *cTable = src.tqcolorTable();
02967         unsigned int pixel;
02968         for(y=0; y < src.height(); ++y){
02969             srcData = (unsigned char *)src.scanLine(y);
02970             ++j;
02971             for(x=0; x < src.width(); ++x){
02972                 pixel = *(cTable+srcData[x]);
02973                 red_channel[j] = tqRed(pixel);
02974                 green_channel[j] = tqGreen(pixel);
02975                 blue_channel[j] = tqBlue(pixel);
02976                 alpha_channel[j] = tqAlpha(pixel);
02977                 ++j;
02978             }
02979             ++j;
02980         }
02981     }
02982     // reduce speckle in red channel
02983     for(i=0; i < 4; i++){
02984         hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
02985         hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
02986         hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
02987         hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
02988     }
02989     // reduce speckle in green channel
02990     for (i=0; i < packets; i++)
02991         buffer[i]=0;
02992     for (i=0; i < 4; i++){
02993         hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
02994         hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
02995         hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
02996         hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
02997     }
02998     // reduce speckle in blue channel
02999     for (i=0; i < packets; i++)
03000         buffer[i]=0;
03001     for (i=0; i < 4; i++){
03002         hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
03003         hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
03004         hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
03005         hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
03006     }
03007     // copy color component buffers to despeckled image
03008     j = dest.width()+2;
03009     for(y=0; y < dest.height(); ++y)
03010     {
03011         destData = (unsigned int *)dest.scanLine(y);
03012         ++j;
03013         for (x=0; x < dest.width(); ++x)
03014         {
03015             destData[x] = tqRgba(red_channel[j], green_channel[j],
03016                                 blue_channel[j], alpha_channel[j]);
03017             ++j;
03018         }
03019         ++j;
03020     }
03021     free(buffer);
03022     free(red_channel);
03023     free(green_channel);
03024     free(blue_channel);
03025     free(alpha_channel);
03026     return(dest);
03027 }
03028 
03029 unsigned int KImageEffect::generateNoise(unsigned int pixel,
03030                                          NoiseType noise_type)
03031 {
03032 #define NoiseEpsilon  1.0e-5
03033 #define NoiseMask  0x7fff
03034 #define SigmaUniform  4.0
03035 #define SigmaGaussian  4.0
03036 #define SigmaImpulse  0.10
03037 #define SigmaLaplacian 10.0
03038 #define SigmaMultiplicativeGaussian  0.5
03039 #define SigmaPoisson  0.05
03040 #define TauGaussian  20.0
03041 
03042     double alpha, beta, sigma, value;
03043     alpha=(double) (rand() & NoiseMask)/NoiseMask;
03044     if (alpha == 0.0)
03045         alpha=1.0;
03046     switch(noise_type){
03047     case UniformNoise:
03048     default:
03049         {
03050             value=(double) pixel+SigmaUniform*(alpha-0.5);
03051             break;
03052         }
03053     case GaussianNoise:
03054         {
03055             double tau;
03056 
03057             beta=(double) (rand() & NoiseMask)/NoiseMask;
03058             sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
03059             tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
03060             value=(double) pixel+
03061                 (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
03062             break;
03063         }
03064     case MultiplicativeGaussianNoise:
03065         {
03066             if (alpha <= NoiseEpsilon)
03067                 sigma=MaxRGB;
03068             else
03069                 sigma=sqrt(-2.0*log(alpha));
03070             beta=(rand() & NoiseMask)/NoiseMask;
03071             value=(double) pixel+
03072                 pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
03073             break;
03074         }
03075     case ImpulseNoise:
03076         {
03077             if (alpha < (SigmaImpulse/2.0))
03078                 value=0;
03079             else
03080                 if (alpha >= (1.0-(SigmaImpulse/2.0)))
03081                     value=MaxRGB;
03082                 else
03083                     value=pixel;
03084             break;
03085         }
03086     case LaplacianNoise:
03087         {
03088             if (alpha <= 0.5)
03089             {
03090                 if (alpha <= NoiseEpsilon)
03091                     value=(double) pixel-MaxRGB;
03092                 else
03093                     value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
03094                 break;
03095             }
03096             beta=1.0-alpha;
03097             if (beta <= (0.5*NoiseEpsilon))
03098                 value=(double) pixel+MaxRGB;
03099             else
03100                 value=(double) pixel-SigmaLaplacian*log(2.0*beta);
03101             break;
03102         }
03103     case PoissonNoise:
03104         {
03105             int
03106                 i;
03107 
03108             for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
03109             {
03110                 beta=(double) (rand() & NoiseMask)/NoiseMask;
03111                 alpha=alpha*beta;
03112             }
03113             value=i/SigmaPoisson;
03114             break;
03115         }
03116     }
03117     if(value < 0.0)
03118         return(0);
03119     if(value > MaxRGB)
03120         return(MaxRGB);
03121     return((unsigned int) (value+0.5));
03122 }
03123 
03124 TQImage KImageEffect::addNoise(TQImage &src, NoiseType noise_type)
03125 {
03126     int x, y;
03127     TQImage dest(src.width(), src.height(), 32);
03128     unsigned int *destData;
03129 
03130     if(src.depth() > 8){ // DirectClass source image
03131         unsigned int *srcData;
03132         for(y=0; y < src.height(); ++y){
03133             srcData = (unsigned int *)src.scanLine(y);
03134             destData = (unsigned int *)dest.scanLine(y);
03135             for(x=0; x < src.width(); ++x){
03136                 destData[x] = tqRgba(generateNoise(tqRed(srcData[x]), noise_type),
03137                                     generateNoise(tqGreen(srcData[x]), noise_type),
03138                                     generateNoise(tqBlue(srcData[x]), noise_type),
03139                                     tqAlpha(srcData[x]));
03140             }
03141         }
03142     }
03143     else{ // PsudeoClass source image
03144         unsigned char *srcData;
03145         unsigned int *cTable = src.tqcolorTable();
03146         unsigned int pixel;
03147         for(y=0; y < src.height(); ++y){
03148             srcData = (unsigned char *)src.scanLine(y);
03149             destData = (unsigned int *)dest.scanLine(y);
03150             for(x=0; x < src.width(); ++x){
03151                 pixel = *(cTable+srcData[x]);
03152                 destData[x] = tqRgba(generateNoise(tqRed(pixel), noise_type),
03153                                     generateNoise(tqGreen(pixel), noise_type),
03154                                     generateNoise(tqBlue(pixel), noise_type),
03155                                     tqAlpha(pixel));
03156             }
03157         }
03158 
03159     }
03160     return(dest);
03161 }
03162 
03163 unsigned int KImageEffect::interpolateColor(TQImage *image, double x_offset,
03164                                             double y_offset,
03165                                             unsigned int background)
03166 {
03167     double alpha, beta;
03168     unsigned int p, q, r, s;
03169     int x, y;
03170 
03171     x = (int)x_offset;
03172     y = (int)y_offset;
03173     if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
03174         return(background);
03175     if(image->depth() > 8){
03176         if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1)))    {
03177             unsigned int *t = (unsigned int *)image->scanLine(y);
03178             p = t[x];
03179             q = t[x+1];
03180             r = t[x+image->width()];
03181             s = t[x+image->width()+1];
03182         }
03183         else{
03184             unsigned int *t = (unsigned int *)image->scanLine(y);
03185             p = background;
03186             if((x >= 0) && (y >= 0)){
03187                 p = t[x];
03188             }
03189             q = background;
03190             if(((x+1) < image->width()) && (y >= 0)){
03191                 q = t[x+1];
03192             }
03193             r = background;
03194             if((x >= 0) && ((y+1) < image->height())){
03195                 t = (unsigned int *)image->scanLine(y+1);
03196                 r = t[x+image->width()];
03197             }
03198             s = background;
03199             if(((x+1) < image->width()) && ((y+1) < image->height())){
03200                 t = (unsigned int *)image->scanLine(y+1);
03201                 s = t[x+image->width()+1];
03202             }
03203 
03204         }
03205     }
03206     else{
03207         unsigned int *colorTable = (unsigned int *)image->tqcolorTable();
03208         if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1)))    {
03209             unsigned char *t;
03210             t = (unsigned char *)image->scanLine(y);
03211             p = *(colorTable+t[x]);
03212             q = *(colorTable+t[x+1]);
03213             t = (unsigned char *)image->scanLine(y+1);
03214             r = *(colorTable+t[x]);
03215             s = *(colorTable+t[x+1]);
03216         }
03217         else{
03218             unsigned char *t;
03219             p = background;
03220             if((x >= 0) && (y >= 0)){
03221                 t = (unsigned char *)image->scanLine(y);
03222                 p = *(colorTable+t[x]);
03223             }
03224             q = background;
03225             if(((x+1) < image->width()) && (y >= 0)){
03226                 t = (unsigned char *)image->scanLine(y);
03227                 q = *(colorTable+t[x+1]);
03228             }
03229             r = background;
03230             if((x >= 0) && ((y+1) < image->height())){
03231                 t = (unsigned char *)image->scanLine(y+1);
03232                 r = *(colorTable+t[x]);
03233             }
03234             s = background;
03235             if(((x+1) < image->width()) && ((y+1) < image->height())){
03236                 t = (unsigned char *)image->scanLine(y+1);
03237                 s = *(colorTable+t[x+1]);
03238             }
03239 
03240         }
03241 
03242     }
03243     x_offset -= floor(x_offset);
03244     y_offset -= floor(y_offset);
03245     alpha = 1.0-x_offset;
03246     beta = 1.0-y_offset;
03247 
03248     return(tqRgba((unsigned char)(beta*(alpha*tqRed(p)+x_offset*tqRed(q))+y_offset*(alpha*tqRed(r)+x_offset*tqRed(s))),
03249                  (unsigned char)(beta*(alpha*tqGreen(p)+x_offset*tqGreen(q))+y_offset*(alpha*tqGreen(r)+x_offset*tqGreen(s))),
03250                  (unsigned char)(beta*(alpha*tqBlue(p)+x_offset*tqBlue(q))+y_offset*(alpha*tqBlue(r)+x_offset*tqBlue(s))),
03251                  (unsigned char)(beta*(alpha*tqAlpha(p)+x_offset*tqAlpha(q))+y_offset*(alpha*tqAlpha(r)+x_offset*tqAlpha(s)))));
03252 }
03253 
03254 TQImage KImageEffect::implode(TQImage &src, double factor,
03255                              unsigned int background)
03256 {
03257     double amount, distance, radius;
03258     double x_center, x_distance, x_scale;
03259     double y_center, y_distance, y_scale;
03260     unsigned int *destData;
03261     int x, y;
03262 
03263     TQImage dest(src.width(), src.height(), 32);
03264 
03265     // compute scaling factor
03266     x_scale = 1.0;
03267     y_scale = 1.0;
03268     x_center = (double)0.5*src.width();
03269     y_center = (double)0.5*src.height();
03270     radius=x_center;
03271     if(src.width() > src.height())
03272         y_scale = (double)src.width()/src.height();
03273     else if(src.width() < src.height()){
03274         x_scale = (double) src.height()/src.width();
03275         radius = y_center;
03276     }
03277     amount=factor/10.0;
03278     if(amount >= 0)
03279         amount/=10.0;
03280     if(src.depth() > 8){ // DirectClass source image
03281         unsigned int *srcData;
03282         for(y=0; y < src.height(); ++y){
03283             srcData = (unsigned int *)src.scanLine(y);
03284             destData = (unsigned int *)dest.scanLine(y);
03285             y_distance=y_scale*(y-y_center);
03286             for(x=0; x < src.width(); ++x){
03287                 destData[x] = srcData[x];
03288                 x_distance = x_scale*(x-x_center);
03289                 distance= x_distance*x_distance+y_distance*y_distance;
03290                 if(distance < (radius*radius)){
03291                     double factor;
03292                     // Implode the pixel.
03293                     factor=1.0;
03294                     if(distance > 0.0)
03295                         factor=
03296                             pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
03297                     destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
03298                                                    factor*y_distance/y_scale+y_center,
03299                                                    background);
03300                 }
03301             }
03302         }
03303     }
03304     else{ // PsudeoClass source image
03305         unsigned char *srcData;
03306         unsigned char idx;
03307         unsigned int *cTable = src.tqcolorTable();
03308         for(y=0; y < src.height(); ++y){
03309             srcData = (unsigned char *)src.scanLine(y);
03310             destData = (unsigned int *)dest.scanLine(y);
03311             y_distance=y_scale*(y-y_center);
03312             for(x=0; x < src.width(); ++x){
03313                 idx = srcData[x];
03314                 destData[x] = cTable[idx];
03315                 x_distance = x_scale*(x-x_center);
03316                 distance= x_distance*x_distance+y_distance*y_distance;
03317                 if(distance < (radius*radius)){
03318                     double factor;
03319                     // Implode the pixel.
03320                     factor=1.0;
03321                     if(distance > 0.0)
03322                         factor=
03323                             pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
03324                     destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
03325                                                    factor*y_distance/y_scale+y_center,
03326                                                    background);
03327                 }
03328             }
03329         }
03330 
03331     }
03332     return(dest);
03333 }
03334 
03335 TQImage KImageEffect::rotate(TQImage &img, RotateDirection r)
03336 {
03337     TQImage dest;
03338     int x, y;
03339     if(img.depth() > 8){
03340         unsigned int *srcData, *destData;
03341         switch(r){
03342         case Rotate90:
03343             dest.create(img.height(), img.width(), img.depth());
03344             for(y=0; y < img.height(); ++y){
03345                 srcData = (unsigned int *)img.scanLine(y);
03346                 for(x=0; x < img.width(); ++x){
03347                     destData = (unsigned int *)dest.scanLine(x);
03348                     destData[img.height()-y-1] = srcData[x];
03349                 }
03350             }
03351             break;
03352         case Rotate180:
03353             dest.create(img.width(), img.height(), img.depth());
03354             for(y=0; y < img.height(); ++y){
03355                 srcData = (unsigned int *)img.scanLine(y);
03356                 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
03357                 for(x=0; x < img.width(); ++x)
03358                     destData[img.width()-x-1] = srcData[x];
03359             }
03360             break;
03361         case Rotate270:
03362             dest.create(img.height(), img.width(), img.depth());
03363             for(y=0; y < img.height(); ++y){
03364                 srcData = (unsigned int *)img.scanLine(y);
03365                 for(x=0; x < img.width(); ++x){
03366                     destData = (unsigned int *)dest.scanLine(img.width()-x-1);
03367                     destData[y] = srcData[x];
03368                 }
03369             }
03370             break;
03371         default:
03372             dest = img;
03373             break;
03374         }
03375     }
03376     else{
03377         unsigned char *srcData, *destData;
03378         unsigned int *srcTable, *destTable;
03379         switch(r){
03380         case Rotate90:
03381             dest.create(img.height(), img.width(), img.depth());
03382             dest.setNumColors(img.numColors());
03383             srcTable = (unsigned int *)img.tqcolorTable();
03384             destTable = (unsigned int *)dest.tqcolorTable();
03385             for(x=0; x < img.numColors(); ++x)
03386                 destTable[x] = srcTable[x];
03387             for(y=0; y < img.height(); ++y){
03388                 srcData = (unsigned char *)img.scanLine(y);
03389                 for(x=0; x < img.width(); ++x){
03390                     destData = (unsigned char *)dest.scanLine(x);
03391                     destData[img.height()-y-1] = srcData[x];
03392                 }
03393             }
03394             break;
03395         case Rotate180:
03396             dest.create(img.width(), img.height(), img.depth());
03397             dest.setNumColors(img.numColors());
03398             srcTable = (unsigned int *)img.tqcolorTable();
03399             destTable = (unsigned int *)dest.tqcolorTable();
03400             for(x=0; x < img.numColors(); ++x)
03401                 destTable[x] = srcTable[x];
03402             for(y=0; y < img.height(); ++y){
03403                 srcData = (unsigned char *)img.scanLine(y);
03404                 destData = (unsigned char *)dest.scanLine(img.height()-y-1);
03405                 for(x=0; x < img.width(); ++x)
03406                     destData[img.width()-x-1] = srcData[x];
03407             }
03408             break;
03409         case Rotate270:
03410             dest.create(img.height(), img.width(), img.depth());
03411             dest.setNumColors(img.numColors());
03412             srcTable = (unsigned int *)img.tqcolorTable();
03413             destTable = (unsigned int *)dest.tqcolorTable();
03414             for(x=0; x < img.numColors(); ++x)
03415                 destTable[x] = srcTable[x];
03416             for(y=0; y < img.height(); ++y){
03417                 srcData = (unsigned char *)img.scanLine(y);
03418                 for(x=0; x < img.width(); ++x){
03419                     destData = (unsigned char *)dest.scanLine(img.width()-x-1);
03420                     destData[y] = srcData[x];
03421                 }
03422             }
03423             break;
03424         default:
03425             dest = img;
03426             break;
03427         }
03428 
03429     }
03430     return(dest);
03431 }
03432 
03433 void KImageEffect::solarize(TQImage &img, double factor)
03434 {
03435     int i, count;
03436     int threshold;
03437     unsigned int *data;
03438 
03439     threshold = (int)(factor*(MaxRGB+1)/100.0);
03440     if(img.depth() < 32){
03441         data = (unsigned int *)img.tqcolorTable();
03442         count = img.numColors();
03443     }
03444     else{
03445         data = (unsigned int *)img.bits();
03446         count = img.width()*img.height();
03447     }
03448     for(i=0; i < count; ++i){
03449         data[i] = tqRgba(tqRed(data[i]) > threshold ? MaxRGB-tqRed(data[i]) : tqRed(data[i]),
03450                         tqGreen(data[i]) > threshold ? MaxRGB-tqGreen(data[i]) : tqGreen(data[i]),
03451                         tqBlue(data[i]) > threshold ? MaxRGB-tqBlue(data[i]) : tqBlue(data[i]),
03452                         tqAlpha(data[i]));
03453     }
03454 }
03455 
03456 TQImage KImageEffect::spread(TQImage &src, unsigned int amount)
03457 {
03458     int quantum, x, y;
03459     int x_distance, y_distance;
03460     if(src.width() < 3 || src.height() < 3)
03461         return(src);
03462     TQImage dest(src);
03463     dest.detach();
03464     quantum=(amount+1) >> 1;
03465     if(src.depth() > 8){ // DirectClass source image
03466         unsigned int *p, *q;
03467         for(y=0; y < src.height(); y++){
03468             q = (unsigned int *)dest.scanLine(y);
03469             for(x=0; x < src.width(); x++){
03470                 x_distance = x + ((rand() & (amount+1))-quantum);
03471                 y_distance = y + ((rand() & (amount+1))-quantum);
03472                 x_distance = TQMIN(x_distance, src.width()-1);
03473                 y_distance = TQMIN(y_distance, src.height()-1);
03474                 if(x_distance < 0)
03475                     x_distance = 0;
03476                 if(y_distance < 0)
03477                     y_distance = 0;
03478                 p = (unsigned int *)src.scanLine(y_distance);
03479                 p += x_distance;
03480                 *q++=(*p);
03481             }
03482         }
03483     }
03484     else{ // PsudeoClass source image
03485         // just do colortable values
03486         unsigned char *p, *q;
03487         for(y=0; y < src.height(); y++){
03488             q = (unsigned char *)dest.scanLine(y);
03489             for(x=0; x < src.width(); x++){
03490                 x_distance = x + ((rand() & (amount+1))-quantum);
03491                 y_distance = y + ((rand() & (amount+1))-quantum);
03492                 x_distance = TQMIN(x_distance, src.width()-1);
03493                 y_distance = TQMIN(y_distance, src.height()-1);
03494                 if(x_distance < 0)
03495                     x_distance = 0;
03496                 if(y_distance < 0)
03497                     y_distance = 0;
03498                 p = (unsigned char *)src.scanLine(y_distance);
03499                 p += x_distance;
03500                 *q++=(*p);
03501             }
03502         }
03503     }
03504     return(dest);
03505 }
03506 
03507 TQImage KImageEffect::swirl(TQImage &src, double degrees,
03508                            unsigned int background)
03509 {
03510     double cosine, distance, factor, radius, sine, x_center, x_distance,
03511         x_scale, y_center, y_distance, y_scale;
03512     int x, y;
03513     unsigned int *q;
03514     TQImage dest(src.width(), src.height(), 32);
03515 
03516     // compute scaling factor
03517     x_center = src.width()/2.0;
03518     y_center = src.height()/2.0;
03519     radius = TQMAX(x_center,y_center);
03520     x_scale=1.0;
03521     y_scale=1.0;
03522     if(src.width() > src.height())
03523         y_scale=(double)src.width()/src.height();
03524     else if(src.width() < src.height())
03525         x_scale=(double)src.height()/src.width();
03526     degrees=DegreesToRadians(degrees);
03527     // swirl each row
03528     if(src.depth() > 8){ // DirectClass source image
03529         unsigned int *p;
03530         for(y=0; y < src.height(); y++){
03531             p = (unsigned int *)src.scanLine(y);
03532             q = (unsigned int *)dest.scanLine(y);
03533             y_distance = y_scale*(y-y_center);
03534             for(x=0; x < src.width(); x++){
03535                 // determine if the pixel is within an ellipse
03536                 *q=(*p);
03537                 x_distance = x_scale*(x-x_center);
03538                 distance = x_distance*x_distance+y_distance*y_distance;
03539                 if (distance < (radius*radius)){
03540                     // swirl
03541                     factor = 1.0-sqrt(distance)/radius;
03542                     sine = sin(degrees*factor*factor);
03543                     cosine = cos(degrees*factor*factor);
03544                     *q = interpolateColor(&src,
03545                                           (cosine*x_distance-sine*y_distance)/x_scale+x_center,
03546                                           (sine*x_distance+cosine*y_distance)/y_scale+y_center,
03547                                           background);
03548                 }
03549                 p++;
03550                 q++;
03551             }
03552         }
03553     }
03554     else{ // PsudeoClass source image
03555         unsigned char *p;
03556         unsigned int *cTable = (unsigned int *)src.tqcolorTable();
03557         for(y=0; y < src.height(); y++){
03558             p = (unsigned char *)src.scanLine(y);
03559             q = (unsigned int *)dest.scanLine(y);
03560             y_distance = y_scale*(y-y_center);
03561             for(x=0; x < src.width(); x++){
03562                 // determine if the pixel is within an ellipse
03563                 *q = *(cTable+(*p));
03564                 x_distance = x_scale*(x-x_center);
03565                 distance = x_distance*x_distance+y_distance*y_distance;
03566                 if (distance < (radius*radius)){
03567                     // swirl
03568                     factor = 1.0-sqrt(distance)/radius;
03569                     sine = sin(degrees*factor*factor);
03570                     cosine = cos(degrees*factor*factor);
03571                     *q = interpolateColor(&src,
03572                                           (cosine*x_distance-sine*y_distance)/x_scale+x_center,
03573                                           (sine*x_distance+cosine*y_distance)/y_scale+y_center,
03574                                           background);
03575                 }
03576                 p++;
03577                 q++;
03578             }
03579         }
03580 
03581     }
03582     return(dest);
03583 }
03584 
03585 TQImage KImageEffect::wave(TQImage &src, double amplitude, double wavelength,
03586                           unsigned int background)
03587 {
03588     double *sine_map;
03589     int x, y;
03590     unsigned int *q;
03591 
03592     TQImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
03593     // allocate sine map
03594     sine_map = (double *)malloc(dest.width()*sizeof(double));
03595     if(!sine_map)
03596         return(src);
03597     for(x=0; x < dest.width(); ++x)
03598         sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
03599     // wave image
03600     for(y=0; y < dest.height(); ++y){
03601         q = (unsigned int *)dest.scanLine(y);
03602         for (x=0; x < dest.width(); x++){
03603             *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
03604             ++q;
03605         }
03606     }
03607     free(sine_map);
03608     return(dest);
03609 }
03610 
03611 //
03612 // The following methods work by computing a value from neighboring pixels
03613 // (mosfet 05/26/03)
03614 //
03615 
03616 // New algorithms based on ImageMagick 5.5.6 (05/26/03)
03617 
03618 TQImage KImageEffect::oilPaint(TQImage &src, int /*radius*/)
03619 {
03620     /* binary compat method - remove me when possible! */
03621     return(oilPaintConvolve(src, 0));
03622 }
03623 
03624 TQImage KImageEffect::oilPaintConvolve(TQImage &src, double radius)
03625 {
03626     unsigned long count /*,*histogram*/;
03627     unsigned long histogram[256];
03628     unsigned int k;
03629     int width;
03630     int x, y, mx, my, sx, sy;
03631     int mcx, mcy;
03632     unsigned int *s=0, *q;
03633 
03634     if(src.depth() < 32)
03635         src.convertDepth(32);
03636     TQImage dest(src);
03637     dest.detach();
03638 
03639     width = getOptimalKernelWidth(radius, 0.5);
03640     if(src.width() < width){
03641         tqWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!");
03642         return(dest);
03643     }
03644     /*
03645     histogram = (unsigned long *)malloc(256*sizeof(unsigned long));
03646     if(!histogram){
03647         tqWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!");
03648         return(dest);
03649     }
03650     */
03651     unsigned int **jumpTable = (unsigned int **)src.jumpTable();
03652     for(y=0; y < dest.height(); ++y){
03653         sy = y-(width/2);
03654         q = (unsigned int *)dest.scanLine(y);
03655         for(x=0; x < dest.width(); ++x){
03656             count = 0;
03657             memset(histogram, 0, 256*sizeof(unsigned long));
03658             //memset(histogram, 0, 256);
03659             sy = y-(width/2);
03660             for(mcy=0; mcy < width; ++mcy, ++sy){
03661                 my = sy < 0 ? 0 : sy > src.height()-1 ?
03662                     src.height()-1 : sy;
03663                 sx = x+(-width/2);
03664                 for(mcx=0; mcx < width; ++mcx, ++sx){
03665                     mx = sx < 0 ? 0 : sx > src.width()-1 ?
03666                         src.width()-1 : sx;
03667 
03668                     k = intensityValue(jumpTable[my][mx]);
03669                     if(k > 255){
03670                         tqWarning("KImageEffect::oilPaintConvolve(): k is %d",
03671                                  k);
03672                         k = 255;
03673                     }
03674                     histogram[k]++;
03675                     if(histogram[k] > count){
03676                         count = histogram[k];
03677                         s = jumpTable[my]+mx;
03678                     }
03679                 }
03680             }
03681             if (s)
03682                 *q++ = (*s);
03683         }
03684     }
03685     /* liberateMemory((histogram); */
03686     return(dest);
03687 }
03688 
03689 TQImage KImageEffect::charcoal(TQImage &src, double /*factor*/)
03690 {
03691     /* binary compat method - remove me when possible! */
03692     return(charcoal(src, 0, 1));
03693 }
03694 
03695 TQImage KImageEffect::charcoal(TQImage &src, double radius, double sigma)
03696 {
03697     TQImage img(edge(src, radius));
03698     img = blur(img, radius, sigma);
03699     normalize(img);
03700     img.invertPixels(false);
03701     KImageEffect::toGray(img);
03702     return(img);
03703 }
03704 
03705 void KImageEffect::normalize(TQImage &image)
03706 {
03707     struct double_packet high, low, intensity, *histogram;
03708     struct short_packet *normalize_map;
03709     TQ_INT64 number_pixels;
03710     int x, y;
03711     unsigned int *p, *q;
03712     long i;
03713     unsigned long threshold_intensity;
03714     unsigned char r, g, b, a;
03715 
03716     if(image.depth() < 32) // result will always be 32bpp
03717         image = image.convertDepth(32);
03718 
03719     histogram = (struct double_packet *)
03720         malloc(256*sizeof(struct double_packet));
03721     normalize_map = (struct short_packet *)
03722         malloc(256*sizeof(struct short_packet));
03723 
03724     if(!histogram || !normalize_map){
03725         if(histogram)
03726             liberateMemory(&histogram);
03727         if(normalize_map)
03728             liberateMemory(&normalize_map);
03729         tqWarning("KImageEffect::normalize(): Unable to allocate memory!");
03730         return;
03731     }
03732 
03733     /*
03734     Form histogram.
03735     */
03736     memset(histogram, 0, 256*sizeof(struct double_packet));
03737     for(y=0; y < image.height(); ++y){
03738         p = (unsigned int *)image.scanLine(y);
03739         for(x=0; x < image.width(); ++x){
03740             histogram[(unsigned char)(tqRed(*p))].red++;
03741             histogram[(unsigned char)(tqGreen(*p))].green++;
03742             histogram[(unsigned char)(tqBlue(*p))].blue++;
03743             histogram[(unsigned char)(tqAlpha(*p))].alpha++;
03744             p++;
03745         }
03746     }
03747 
03748     /*
03749     Find the histogram boundaries by locating the 0.1 percent levels.
03750     */
03751     number_pixels = (TQ_INT64)image.width()*image.height();
03752     threshold_intensity = number_pixels/1000;
03753 
03754     /* red */
03755     memset(&intensity, 0, sizeof(struct double_packet));
03756     memset(&high, 0, sizeof(struct double_packet));
03757     memset(&low, 0, sizeof(struct double_packet));
03758     for(high.red=255; high.red != 0; high.red--){
03759         intensity.red+=histogram[(unsigned char)high.red].red;
03760         if(intensity.red > threshold_intensity)
03761             break;
03762     }
03763     if(low.red == high.red){
03764         threshold_intensity = 0;
03765         memset(&intensity, 0, sizeof(struct double_packet));
03766         for(low.red=0; low.red < 255; low.red++){
03767             intensity.red+=histogram[(unsigned char)low.red].red;
03768             if(intensity.red > threshold_intensity)
03769                 break;
03770         }
03771         memset(&intensity, 0, sizeof(struct double_packet));
03772         for(high.red=255; high.red != 0; high.red--){
03773             intensity.red+=histogram[(unsigned char)high.red].red;
03774             if(intensity.red > threshold_intensity)
03775                 break;
03776         }
03777     }
03778 
03779     /* green */
03780     memset(&intensity, 0, sizeof(struct double_packet));
03781     for(high.green=255; high.green != 0; high.green--){
03782         intensity.green+=histogram[(unsigned char)high.green].green;
03783         if(intensity.green > threshold_intensity)
03784             break;
03785     }
03786     if(low.green == high.green){
03787         threshold_intensity = 0;
03788         memset(&intensity, 0, sizeof(struct double_packet));
03789         for(low.green=0; low.green < 255; low.green++){
03790             intensity.green+=histogram[(unsigned char)low.green].green;
03791             if(intensity.green > threshold_intensity)
03792                 break;
03793         }
03794         memset(&intensity,0,sizeof(struct double_packet));
03795         for(high.green=255; high.green != 0; high.green--){
03796             intensity.green+=histogram[(unsigned char)high.green].green;
03797             if(intensity.green > threshold_intensity)
03798                 break;
03799         }
03800     }
03801 
03802     /* blue */
03803     memset(&intensity, 0, sizeof(struct double_packet));
03804     for(high.blue=255; high.blue != 0; high.blue--){
03805         intensity.blue+=histogram[(unsigned char)high.blue].blue;
03806         if(intensity.blue > threshold_intensity)
03807             break;
03808     }
03809     if(low.blue == high.blue){
03810         threshold_intensity = 0;
03811         memset(&intensity, 0, sizeof(struct double_packet));
03812         for(low.blue=0; low.blue < 255; low.blue++){
03813             intensity.blue+=histogram[(unsigned char)low.blue].blue;
03814             if(intensity.blue > threshold_intensity)
03815                 break;
03816         }
03817         memset(&intensity,0,sizeof(struct double_packet));
03818         for(high.blue=255; high.blue != 0; high.blue--){
03819             intensity.blue+=histogram[(unsigned char)high.blue].blue;
03820             if(intensity.blue > threshold_intensity)
03821                 break;
03822         }
03823     }
03824 
03825     /* alpha */
03826     memset(&intensity, 0, sizeof(struct double_packet));
03827     for(high.alpha=255; high.alpha != 0; high.alpha--){
03828         intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
03829         if(intensity.alpha > threshold_intensity)
03830             break;
03831     }
03832     if(low.alpha == high.alpha){
03833         threshold_intensity = 0;
03834         memset(&intensity, 0, sizeof(struct double_packet));
03835         for(low.alpha=0; low.alpha < 255; low.alpha++){
03836             intensity.alpha+=histogram[(unsigned char)low.alpha].alpha;
03837             if(intensity.alpha > threshold_intensity)
03838                 break;
03839         }
03840         memset(&intensity,0,sizeof(struct double_packet));
03841         for(high.alpha=255; high.alpha != 0; high.alpha--){
03842             intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
03843             if(intensity.alpha > threshold_intensity)
03844                 break;
03845         }
03846     }
03847     liberateMemory(&histogram);
03848 
03849     /*
03850      Stretch the histogram to create the normalized image mapping.
03851      */
03852 
03853     // should the maxes be 65535?
03854     memset(normalize_map, 0 ,256*sizeof(struct short_packet));
03855     for(i=0; i <= (long) 255; i++){
03856         if(i < (long) low.red)
03857             normalize_map[i].red=0;
03858         else if (i > (long) high.red)
03859             normalize_map[i].red=65535;
03860         else if (low.red != high.red)
03861             normalize_map[i].red =
03862                 (unsigned short)((65535*(i-low.red))/(high.red-low.red));
03863 
03864         if(i < (long) low.green)
03865             normalize_map[i].green=0;
03866         else if (i > (long) high.green)
03867             normalize_map[i].green=65535;
03868         else if (low.green != high.green)
03869             normalize_map[i].green =
03870                 (unsigned short)((65535*(i-low.green))/(high.green-low.green));
03871 
03872         if(i < (long) low.blue)
03873             normalize_map[i].blue=0;
03874         else if (i > (long) high.blue)
03875             normalize_map[i].blue=65535;
03876         else if (low.blue != high.blue)
03877             normalize_map[i].blue =
03878                 (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue));
03879 
03880         if(i < (long) low.alpha)
03881             normalize_map[i].alpha=0;
03882         else if (i > (long) high.alpha)
03883             normalize_map[i].alpha=65535;
03884         else if (low.alpha != high.alpha)
03885             normalize_map[i].alpha =
03886                 (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha));
03887 
03888     }
03889 
03890     for(y=0; y < image.height(); ++y){
03891         q = (unsigned int *)image.scanLine(y);
03892         for(x=0; x < image.width(); ++x){
03893             if(low.red != high.red)
03894                 r = (normalize_map[(unsigned short)(tqRed(q[x]))].red)/257;
03895             else
03896                 r = tqRed(q[x]);
03897             if(low.green != high.green)
03898                 g = (normalize_map[(unsigned short)(tqGreen(q[x]))].green)/257;
03899             else
03900                 g = tqGreen(q[x]);
03901             if(low.blue != high.blue)
03902                 b = (normalize_map[(unsigned short)(tqBlue(q[x]))].blue)/257;
03903             else
03904                 b = tqBlue(q[x]);
03905             if(low.alpha != high.alpha)
03906                 a = (normalize_map[(unsigned short)(tqAlpha(q[x]))].alpha)/257;
03907             else
03908                 a = tqAlpha(q[x]);
03909             q[x] = tqRgba(r, g, b, a);
03910         }
03911     }
03912     liberateMemory(&normalize_map);
03913 }
03914 
03915 void KImageEffect::equalize(TQImage &image)
03916 {
03917     struct double_packet high, low, intensity, *map, *histogram;
03918     struct short_packet *equalize_map;
03919     int x, y;
03920     unsigned int *p, *q;
03921     long i;
03922     unsigned char r, g, b, a;
03923 
03924     if(image.depth() < 32) // result will always be 32bpp
03925         image = image.convertDepth(32);
03926 
03927     histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet));
03928     map=(struct double_packet *) malloc(256*sizeof(struct double_packet));
03929     equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet));
03930     if(!histogram || !map || !equalize_map){
03931         if(histogram)
03932             liberateMemory(&histogram);
03933         if(map)
03934             liberateMemory(&map);
03935         if(equalize_map)
03936             liberateMemory(&equalize_map);
03937         tqWarning("KImageEffect::equalize(): Unable to allocate memory!");
03938         return;
03939     }
03940 
03941     /*
03942     Form histogram.
03943     */
03944     memset(histogram, 0, 256*sizeof(struct double_packet));
03945     for(y=0; y < image.height(); ++y){
03946         p = (unsigned int *)image.scanLine(y);
03947         for(x=0; x < image.width(); ++x){
03948             histogram[(unsigned char)(tqRed(*p))].red++;
03949             histogram[(unsigned char)(tqGreen(*p))].green++;
03950             histogram[(unsigned char)(tqBlue(*p))].blue++;
03951             histogram[(unsigned char)(tqAlpha(*p))].alpha++;
03952             p++;
03953         }
03954     }
03955     /*
03956      Integrate the histogram to get the equalization map.
03957      */
03958     memset(&intensity, 0 ,sizeof(struct double_packet));
03959     for(i=0; i <= 255; ++i){
03960         intensity.red += histogram[i].red;
03961         intensity.green += histogram[i].green;
03962         intensity.blue += histogram[i].blue;
03963         intensity.alpha += histogram[i].alpha;
03964         map[i]=intensity;
03965     }
03966     low=map[0];
03967     high=map[255];
03968     memset(equalize_map, 0, 256*sizeof(short_packet));
03969     for(i=0; i <= 255; ++i){
03970         if(high.red != low.red)
03971             equalize_map[i].red=(unsigned short)
03972                 ((65535*(map[i].red-low.red))/(high.red-low.red));
03973         if(high.green != low.green)
03974             equalize_map[i].green=(unsigned short)
03975                 ((65535*(map[i].green-low.green))/(high.green-low.green));
03976         if(high.blue != low.blue)
03977             equalize_map[i].blue=(unsigned short)
03978                 ((65535*(map[i].blue-low.blue))/(high.blue-low.blue));
03979         if(high.alpha != low.alpha)
03980             equalize_map[i].alpha=(unsigned short)
03981                 ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha));
03982     }
03983     liberateMemory(&histogram);
03984     liberateMemory(&map);
03985 
03986     /*
03987      Stretch the histogram.
03988      */
03989     for(y=0; y < image.height(); ++y){
03990         q = (unsigned int *)image.scanLine(y);
03991         for(x=0; x < image.width(); ++x){
03992             if(low.red != high.red)
03993                 r = (equalize_map[(unsigned short)(tqRed(q[x]))].red/257);
03994             else
03995                 r = tqRed(q[x]);
03996             if(low.green != high.green)
03997                 g = (equalize_map[(unsigned short)(tqGreen(q[x]))].green/257);
03998             else
03999                 g = tqGreen(q[x]);
04000             if(low.blue != high.blue)
04001                 b = (equalize_map[(unsigned short)(tqBlue(q[x]))].blue/257);
04002             else
04003                 b = tqBlue(q[x]);
04004             if(low.alpha != high.alpha)
04005                 a = (equalize_map[(unsigned short)(tqAlpha(q[x]))].alpha/257);
04006             else
04007                 a = tqAlpha(q[x]);
04008             q[x] = tqRgba(r, g, b, a);
04009         }
04010     }
04011     liberateMemory(&equalize_map);
04012 
04013 }
04014 
04015 TQImage KImageEffect::edge(TQImage &image, double radius)
04016 {
04017     double *kernel;
04018     int width;
04019     long i;
04020     TQImage dest;
04021 
04022     if(radius == 50.0){
04023         /* For binary compatability! Remove me when possible! This used to
04024          * take a different parameter, a factor, and this was the default
04025          * value */
04026         radius = 0.0;
04027     }
04028 
04029     width = getOptimalKernelWidth(radius, 0.5);
04030     if(image.width() < width || image.height() < width){
04031         tqWarning("KImageEffect::edge(): Image is smaller than radius!");
04032         return(dest);
04033     }
04034     kernel= (double *)malloc(width*width*sizeof(double));
04035     if(!kernel){
04036         tqWarning("KImageEffect::edge(): Unable to allocate memory!");
04037         return(dest);
04038     }
04039     for(i=0; i < (width*width); i++)
04040         kernel[i]=(-1.0);
04041     kernel[i/2]=width*width-1.0;
04042     convolveImage(&image, &dest, width, kernel);
04043     free(kernel);
04044     return(dest);
04045 }
04046 
04047 TQImage KImageEffect::emboss(TQImage &src)
04048 {
04049     /* binary compat method - remove me when possible! */
04050     return(emboss(src, 0, 1));
04051 }
04052 
04053 TQImage KImageEffect::emboss(TQImage &image, double radius, double sigma)
04054 {
04055     double alpha, *kernel;
04056     int j, width;
04057     long i, u, v;
04058     TQImage dest;
04059 
04060     if(sigma == 0.0){
04061         tqWarning("KImageEffect::emboss(): Zero sigma is not permitted!");
04062         return(dest);
04063     }
04064 
04065     width = getOptimalKernelWidth(radius, sigma);
04066     if(image.width() < width || image.height() < width){
04067         tqWarning("KImageEffect::emboss(): Image is smaller than radius!");
04068         return(dest);
04069     }
04070     kernel= (double *)malloc(width*width*sizeof(double));
04071     if(!kernel){
04072         tqWarning("KImageEffect::emboss(): Unable to allocate memory!");
04073         return(dest);
04074     }
04075     if(image.depth() < 32)
04076         image = image.convertDepth(32);
04077 
04078     i=0;
04079     j=width/2;
04080     for(v=(-width/2); v <= (width/2); v++){
04081         for(u=(-width/2); u <= (width/2); u++){
04082             alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
04083             kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
04084                 (2.0*MagickPI*sigma*sigma);
04085             if (u == j)
04086                 kernel[i]=0.0;
04087             i++;
04088         }
04089         j--;
04090     }
04091     convolveImage(&image, &dest, width, kernel);
04092     liberateMemory(&kernel);
04093 
04094     equalize(dest);
04095     return(dest);
04096 }
04097 
04098 void KImageEffect::blurScanLine(double *kernel, int width,
04099                                 unsigned int *src, unsigned int *dest,
04100                                 int columns)
04101 {
04102     double *p;
04103     unsigned int *q;
04104     int x;
04105     long i;
04106     double red, green, blue, alpha;
04107     double scale = 0.0;
04108 
04109     if(width > columns){
04110         for(x=0; x < columns; ++x){
04111             scale = 0.0;
04112             red = blue = green = alpha = 0.0;
04113             p = kernel;
04114             q = src;
04115             for(i=0; i < columns; ++i){
04116                 if((i >= (x-width/2)) && (i <= (x+width/2))){
04117                     red += (*p)*(tqRed(*q)*257);
04118                     green += (*p)*(tqGreen(*q)*257);
04119                     blue += (*p)*(tqBlue(*q)*257);
04120                     alpha += (*p)*(tqAlpha(*q)*257);
04121                 }
04122                 if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
04123                     scale+=kernel[i+width/2-x];
04124                 p++;
04125                 q++;
04126             }
04127             scale = 1.0/scale;
04128             red = scale*(red+0.5);
04129             green = scale*(green+0.5);
04130             blue = scale*(blue+0.5);
04131             alpha = scale*(alpha+0.5);
04132 
04133             red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04134             green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04135             blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04136             alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04137 
04138             dest[x] = tqRgba((unsigned char)(red/257UL),
04139                             (unsigned char)(green/257UL),
04140                             (unsigned char)(blue/257UL),
04141                             (unsigned char)(alpha/257UL));
04142         }
04143         return;
04144     }
04145 
04146     for(x=0; x < width/2; ++x){
04147         scale = 0.0;
04148         red = blue = green = alpha = 0.0;
04149         p = kernel+width/2-x;
04150         q = src;
04151         for(i=width/2-x; i < width; ++i){
04152             red += (*p)*(tqRed(*q)*257);
04153             green += (*p)*(tqGreen(*q)*257);
04154             blue += (*p)*(tqBlue(*q)*257);
04155             alpha += (*p)*(tqAlpha(*q)*257);
04156             scale += (*p);
04157             p++;
04158             q++;
04159         }
04160         scale=1.0/scale;
04161 
04162         red = scale*(red+0.5);
04163         green = scale*(green+0.5);
04164         blue = scale*(blue+0.5);
04165         alpha = scale*(alpha+0.5);
04166 
04167         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04168         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04169         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04170         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04171 
04172         dest[x] = tqRgba((unsigned char)(red/257UL),
04173                         (unsigned char)(green/257UL),
04174                         (unsigned char)(blue/257UL),
04175                         (unsigned char)(alpha/257UL));
04176     }
04177 
04178     for(; x < columns-width/2; ++x){
04179         red = blue = green = alpha = 0.0;
04180         p = kernel;
04181         q = src+(x-width/2);
04182         for (i=0; i < (long) width; ++i){
04183             red += (*p)*(tqRed(*q)*257);
04184             green += (*p)*(tqGreen(*q)*257);
04185             blue += (*p)*(tqBlue(*q)*257);
04186             alpha += (*p)*(tqAlpha(*q)*257);
04187             p++;
04188             q++;
04189         }
04190         red = scale*(red+0.5);
04191         green = scale*(green+0.5);
04192         blue = scale*(blue+0.5);
04193         alpha = scale*(alpha+0.5);
04194 
04195         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04196         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04197         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04198         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04199 
04200         dest[x] = tqRgba((unsigned char)(red/257UL),
04201                         (unsigned char)(green/257UL),
04202                         (unsigned char)(blue/257UL),
04203                         (unsigned char)(alpha/257UL));
04204     }
04205 
04206     for(; x < columns; ++x){
04207         red = blue = green = alpha = 0.0;
04208         scale=0;
04209         p = kernel;
04210         q = src+(x-width/2);
04211         for(i=0; i < columns-x+width/2; ++i){
04212             red += (*p)*(tqRed(*q)*257);
04213             green += (*p)*(tqGreen(*q)*257);
04214             blue += (*p)*(tqBlue(*q)*257);
04215             alpha += (*p)*(tqAlpha(*q)*257);
04216             scale += (*p);
04217             p++;
04218             q++;
04219         }
04220         scale=1.0/scale;
04221         red = scale*(red+0.5);
04222         green = scale*(green+0.5);
04223         blue = scale*(blue+0.5);
04224         alpha = scale*(alpha+0.5);
04225 
04226         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04227         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04228         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04229         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04230 
04231         dest[x] = tqRgba((unsigned char)(red/257UL),
04232                         (unsigned char)(green/257UL),
04233                         (unsigned char)(blue/257UL),
04234                         (unsigned char)(alpha/257UL));
04235     }
04236 }
04237 
04238 int KImageEffect::getBlurKernel(int width, double sigma, double **kernel)
04239 {
04240 #define KernelRank 3
04241     double alpha, normalize;
04242     long i;
04243     int bias;
04244 
04245     assert(sigma != 0.0);
04246     if(width == 0)
04247         width = 3;
04248     *kernel=(double *)malloc(width*sizeof(double));
04249     if(*kernel == (double *)NULL)
04250         return(0);
04251     memset(*kernel, 0, width*sizeof(double));
04252     bias = KernelRank*width/2;
04253     for(i=(-bias); i <= bias; i++){
04254         alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
04255         (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma);
04256     }
04257     normalize=0;
04258     for(i=0; i < width; i++)
04259         normalize+=(*kernel)[i];
04260     for(i=0; i < width; i++)
04261         (*kernel)[i]/=normalize;
04262 
04263     return(width);
04264 }
04265 
04266 TQImage KImageEffect::blur(TQImage &src, double /*factor*/)
04267 {
04268     /* binary compat method - remove me when possible! */
04269     return(blur(src, 0, 1));
04270 }
04271 
04272 TQImage KImageEffect::blur(TQImage &src, double radius, double sigma)
04273 {
04274     double *kernel;
04275     TQImage dest;
04276     int width;
04277     int x, y;
04278     unsigned int *scanline, *temp;
04279     unsigned int *p, *q;
04280 
04281     if(sigma == 0.0){
04282         tqWarning("KImageEffect::blur(): Zero sigma is not permitted!");
04283         return(dest);
04284     }
04285     if(src.depth() < 32)
04286         src = src.convertDepth(32);
04287 
04288     kernel=(double *) NULL;
04289     if(radius > 0)
04290         width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
04291     else{
04292         double *last_kernel;
04293         last_kernel=(double *) NULL;
04294         width=getBlurKernel(3,sigma,&kernel);
04295 
04296         while ((long) (MaxRGB*kernel[0]) > 0){
04297             if(last_kernel != (double *)NULL){
04298                 liberateMemory(&last_kernel);
04299             }
04300             last_kernel=kernel;
04301             kernel = (double *)NULL;
04302             width = getBlurKernel(width+2, sigma, &kernel);
04303         }
04304         if(last_kernel != (double *) NULL){
04305             liberateMemory(&kernel);
04306             width-=2;
04307             kernel = last_kernel;
04308         }
04309     }
04310 
04311     if(width < 3){
04312         tqWarning("KImageEffect::blur(): Kernel radius is too small!");
04313         liberateMemory(&kernel);
04314         return(dest);
04315     }
04316 
04317     dest.create(src.width(), src.height(), 32);
04318 
04319     // Horizontal convolution
04320     scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
04321     temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
04322     for(y=0; y < src.height(); ++y){
04323         p = (unsigned int *)src.scanLine(y);
04324         q = (unsigned int *)dest.scanLine(y);
04325         blurScanLine(kernel, width, p, q, src.width());
04326     }
04327 
04328     TQImage partial = dest;
04329 
04330     // Vertical convolution
04331     unsigned int **srcTable = (unsigned int **)partial.jumpTable();
04332     unsigned int **destTable = (unsigned int **)dest.jumpTable();
04333     for(x=0; x < partial.width(); ++x){
04334         for(y=0; y < partial.height(); ++y){
04335             scanline[y] = srcTable[y][x];
04336         }
04337         blurScanLine(kernel, width, scanline, temp, partial.height());
04338         for(y=0; y < partial.height(); ++y){
04339             destTable[y][x] = temp[y];
04340         }
04341     }
04342     free(scanline);
04343     free(temp);
04344     free(kernel);
04345     return(dest);
04346 }
04347 
04348 bool KImageEffect::convolveImage(TQImage *image, TQImage *dest,
04349                                  const unsigned int order,
04350                                  const double *kernel)
04351 {
04352     long width;
04353     double red, green, blue, alpha;
04354     double normalize, *normal_kernel;
04355     const double *k;
04356     unsigned int *q;
04357     int x, y, mx, my, sx, sy;
04358     long i;
04359     int mcx, mcy;
04360 
04361     width = order;
04362     if((width % 2) == 0){
04363         tqWarning("KImageEffect: Kernel width must be an odd number!");
04364         return(false);
04365     }
04366     normal_kernel = (double *)malloc(width*width*sizeof(double));
04367     if(!normal_kernel){
04368         tqWarning("KImageEffect: Unable to allocate memory!");
04369         return(false);
04370     }
04371     dest->reset();
04372     dest->create(image->width(), image->height(), 32);
04373     if(image->depth() < 32)
04374         *image = image->convertDepth(32);
04375 
04376     normalize=0.0;
04377     for(i=0; i < (width*width); i++)
04378         normalize += kernel[i];
04379     if(fabs(normalize) <= MagickEpsilon)
04380         normalize=1.0;
04381     normalize=1.0/normalize;
04382     for(i=0; i < (width*width); i++)
04383         normal_kernel[i] = normalize*kernel[i];
04384 
04385     unsigned int **jumpTable = (unsigned int **)image->jumpTable();
04386     for(y=0; y < dest->height(); ++y){
04387         sy = y-(width/2);
04388         q = (unsigned int *)dest->scanLine(y);
04389         for(x=0; x < dest->width(); ++x){
04390             k = normal_kernel;
04391             red = green = blue = alpha = 0;
04392             sy = y-(width/2);
04393             for(mcy=0; mcy < width; ++mcy, ++sy){
04394                 my = sy < 0 ? 0 : sy > image->height()-1 ?
04395                     image->height()-1 : sy;
04396                 sx = x+(-width/2);
04397                 for(mcx=0; mcx < width; ++mcx, ++sx){
04398                     mx = sx < 0 ? 0 : sx > image->width()-1 ?
04399                         image->width()-1 : sx;
04400                     red += (*k)*(tqRed(jumpTable[my][mx])*257);
04401                     green += (*k)*(tqGreen(jumpTable[my][mx])*257);
04402                     blue += (*k)*(tqBlue(jumpTable[my][mx])*257);
04403                     alpha += (*k)*(tqAlpha(jumpTable[my][mx])*257);
04404                     ++k;
04405                 }
04406             }
04407 
04408             red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
04409             green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
04410             blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
04411             alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
04412 
04413             *q++ = tqRgba((unsigned char)(red/257UL),
04414                          (unsigned char)(green/257UL),
04415                          (unsigned char)(blue/257UL),
04416                          (unsigned char)(alpha/257UL));
04417         }
04418     }
04419     free(normal_kernel);
04420     return(true);
04421 
04422 }
04423 
04424 int KImageEffect::getOptimalKernelWidth(double radius, double sigma)
04425 {
04426     double normalize, value;
04427     long width;
04428     long u;
04429 
04430     assert(sigma != 0.0);
04431     if(radius > 0.0)
04432         return((int)(2.0*ceil(radius)+1.0));
04433     for(width=5; ;){
04434         normalize=0.0;
04435         for(u=(-width/2); u <= (width/2); u++)
04436             normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
04437         u=width/2;
04438         value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
04439         if((long)(65535*value) <= 0)
04440             break;
04441         width+=2;
04442     }
04443     return((int)width-2);
04444 }
04445 
04446 TQImage KImageEffect::sharpen(TQImage &src, double /*factor*/)
04447 {
04448     /* binary compat method - remove me when possible! */
04449     return(sharpen(src, 0, 1));
04450 }
04451 
04452 TQImage KImageEffect::sharpen(TQImage &image, double radius, double sigma)
04453 {
04454     double alpha, normalize, *kernel;
04455     int width;
04456     long i, u, v;
04457     TQImage dest;
04458 
04459     if(sigma == 0.0){
04460         tqWarning("KImageEffect::sharpen(): Zero sigma is not permitted!");
04461         return(dest);
04462     }
04463     width = getOptimalKernelWidth(radius, sigma);
04464     if(image.width() < width){
04465         tqWarning("KImageEffect::sharpen(): Image is smaller than radius!");
04466         return(dest);
04467     }
04468     kernel = (double *)malloc(width*width*sizeof(double));
04469     if(!kernel){
04470         tqWarning("KImageEffect::sharpen(): Unable to allocate memory!");
04471         return(dest);
04472     }
04473 
04474     i = 0;
04475     normalize=0.0;
04476     for(v=(-width/2); v <= (width/2); v++){
04477         for(u=(-width/2); u <= (width/2); u++){
04478             alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
04479             kernel[i]=alpha/(2.0*MagickPI*sigma*sigma);
04480             normalize+=kernel[i];
04481             i++;
04482         }
04483     }
04484     kernel[i/2]=(-2.0)*normalize;
04485     convolveImage(&image, &dest, width, kernel);
04486     free(kernel);
04487     return(dest);
04488 }
04489 
04490 // End of new algorithms
04491 
04492 TQImage KImageEffect::shade(TQImage &src, bool color_shading, double azimuth,
04493              double elevation)
04494 {
04495     struct PointInfo{
04496         double x, y, z;
04497     };
04498 
04499     double distance, normal_distance, shade;
04500     int x, y;
04501 
04502     struct PointInfo light, normal;
04503 
04504     unsigned int *q;
04505 
04506     TQImage dest(src.width(), src.height(), 32);
04507 
04508     azimuth = DegreesToRadians(azimuth);
04509     elevation = DegreesToRadians(elevation);
04510     light.x = MaxRGB*cos(azimuth)*cos(elevation);
04511     light.y = MaxRGB*sin(azimuth)*cos(elevation);
04512     light.z = MaxRGB*sin(elevation);
04513     normal.z= 2*MaxRGB;  // constant Z of surface normal
04514 
04515     if(src.depth() > 8){ // DirectClass source image
04516         unsigned int *p, *s0, *s1, *s2;
04517         for(y=0; y < src.height(); ++y){
04518             p = (unsigned int *)src.scanLine(TQMIN(TQMAX(y-1,0),src.height()-3));
04519             q = (unsigned int *)dest.scanLine(y);
04520             // shade this row of pixels.
04521             *q++=(*(p+src.width()));
04522             p++;
04523             s0 = p;
04524             s1 = p + src.width();
04525             s2 = p + 2*src.width();
04526             for(x=1; x < src.width()-1; ++x){
04527                 // determine the surface normal and compute shading.
04528                 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
04529                     (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
04530                     (double) intensityValue(*(s2+1));
04531                 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
04532                     (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
04533                     (double) intensityValue(*(s0+1));
04534                 if((normal.x == 0) && (normal.y == 0))
04535                     shade=light.z;
04536                 else{
04537                     shade=0.0;
04538                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04539                     if (distance > 0.0){
04540                         normal_distance=
04541                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04542                         if(fabs(normal_distance) > 0.0000001)
04543                             shade=distance/sqrt(normal_distance);
04544                     }
04545                 }
04546                 if(!color_shading){
04547                     *q = tqRgba((unsigned char)(shade),
04548                                (unsigned char)(shade),
04549                                (unsigned char)(shade),
04550                                tqAlpha(*s1));
04551                 }
04552                 else{
04553                     *q = tqRgba((unsigned char)((shade*tqRed(*s1))/(MaxRGB+1)),
04554                                (unsigned char)((shade*tqGreen(*s1))/(MaxRGB+1)),
04555                                (unsigned char)((shade*tqBlue(*s1))/(MaxRGB+1)),
04556                                tqAlpha(*s1));
04557                 }
04558                 ++s0;
04559                 ++s1;
04560                 ++s2;
04561                 q++;
04562             }
04563             *q++=(*s1);
04564         }
04565     }
04566     else{ // PsudeoClass source image
04567         unsigned char *p, *s0, *s1, *s2;
04568         int scanLineIdx;
04569         unsigned int *cTable = (unsigned int *)src.tqcolorTable();
04570         for(y=0; y < src.height(); ++y){
04571             scanLineIdx = TQMIN(TQMAX(y-1,0),src.height()-3);
04572             p = (unsigned char *)src.scanLine(scanLineIdx);
04573             q = (unsigned int *)dest.scanLine(y);
04574             // shade this row of pixels.
04575             s0 = p;
04576             s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
04577             s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
04578             *q++=(*(cTable+(*s1)));
04579             ++p;
04580             ++s0;
04581             ++s1;
04582             ++s2;
04583             for(x=1; x < src.width()-1; ++x){
04584                 // determine the surface normal and compute shading.
04585                 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
04586                     (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
04587                     (double) intensityValue(*(cTable+(*(s2+1))));
04588                 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
04589                     (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
04590                     (double) intensityValue(*(cTable+(*(s0+1))));
04591                 if((normal.x == 0) && (normal.y == 0))
04592                     shade=light.z;
04593                 else{
04594                     shade=0.0;
04595                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04596                     if (distance > 0.0){
04597                         normal_distance=
04598                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04599                         if(fabs(normal_distance) > 0.0000001)
04600                             shade=distance/sqrt(normal_distance);
04601                     }
04602                 }
04603                 if(!color_shading){
04604                     *q = tqRgba((unsigned char)(shade),
04605                                (unsigned char)(shade),
04606                                (unsigned char)(shade),
04607                                tqAlpha(*(cTable+(*s1))));
04608                 }
04609                 else{
04610                     *q = tqRgba((unsigned char)((shade*tqRed(*(cTable+(*s1))))/(MaxRGB+1)),
04611                                (unsigned char)((shade*tqGreen(*(cTable+(*s1))))/(MaxRGB+1)),
04612                                (unsigned char)((shade*tqBlue(*(cTable+(*s1))))/(MaxRGB+1)),
04613                                tqAlpha(*s1));
04614                 }
04615                 ++s0;
04616                 ++s1;
04617                 ++s2;
04618                 q++;
04619             }
04620             *q++=(*(cTable+(*s1)));
04621         }
04622     }
04623     return(dest);
04624 }
04625 
04626 // High quality, expensive HSV contrast. You can do a faster one by just
04627 // taking a grayscale threshold (ie: 128) and incrementing RGB color
04628 // channels above it and decrementing those below it, but this gives much
04629 // better results. (mosfet 12/28/01)
04630 void KImageEffect::contrastHSV(TQImage &img, bool sharpen)
04631 {
04632     int i, sign;
04633     unsigned int *data;
04634     int count;
04635     double brightness, scale, theta;
04636     TQColor c;
04637     int h, s, v;
04638 
04639     sign = sharpen ? 1 : -1;
04640     scale=0.5000000000000001;
04641     if(img.depth() > 8){
04642         count = img.width()*img.height();
04643         data = (unsigned int *)img.bits();
04644     }
04645     else{
04646         count = img.numColors();
04647         data = (unsigned int *)img.tqcolorTable();
04648     }
04649     for(i=0; i < count; ++i){
04650         c.setRgb(data[i]);
04651         c.hsv(&h, &s, &v);
04652         brightness = v/255.0;
04653         theta=(brightness-0.5)*M_PI;
04654         brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
04655         if (brightness > 1.0)
04656             brightness=1.0;
04657         else
04658             if (brightness < 0)
04659                 brightness=0.0;
04660         v = (int)(brightness*255);
04661         c.setHsv(h, s, v);
04662         data[i] = tqRgba(c.red(), c.green(), c.blue(), tqAlpha(data[i]));
04663     }
04664 }
04665 
04666 
04667 struct BumpmapParams {
04668     BumpmapParams( double bm_azimuth, double bm_elevation,
04669                    int bm_depth, KImageEffect::BumpmapType bm_type,
04670                    bool invert ) {
04671          /* Convert to radians */
04672         double azimuth = DegreesToRadians( bm_azimuth );
04673         double elevation = DegreesToRadians( bm_elevation );
04674 
04675         /* Calculate the light vector */
04676         lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
04677         ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
04678         int lz         = (int)( sin(elevation) * 255.0 );
04679 
04680         /* Calculate constant Z component of surface normal */
04681         int nz  = (6 * 255) / bm_depth;
04682         nz2     = nz * nz;
04683         nzlz    = nz * lz;
04684 
04685         /* Optimize for vertical normals */
04686         background = lz;
04687 
04688         /* Calculate darkness compensation factor */
04689         compensation = sin(elevation);
04690 
04691         /* Create look-up table for map type */
04692         for (int i = 0; i < 256; i++)
04693         {
04694             double n = 0;
04695             switch (bm_type)
04696             {
04697             case KImageEffect::Spherical:
04698                 n = i / 255.0 - 1.0;
04699                 lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
04700                 break;
04701 
04702             case KImageEffect::Sinuosidal:
04703                 n = i / 255.0;
04704                 lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
04705                                         2.0 + 0.5);
04706                 break;
04707 
04708             case KImageEffect::Linear:
04709             default:
04710                 lut[i] = i;
04711             }
04712 
04713             if (invert)
04714                 lut[i] = 255 - lut[i];
04715         }
04716     }
04717     int lx,  ly;
04718     int nz2, nzlz;
04719     int background;
04720     double compensation;
04721     uchar lut[256];
04722 };
04723 
04724 
04725 static void bumpmap_convert_row( uint *row,
04726                                  int    width,
04727                                  int    bpp,
04728                                  int    has_alpha,
04729                                  uchar *lut,
04730                                  int waterlevel )
04731 {
04732   uint *p;
04733 
04734   p = row;
04735 
04736   has_alpha = has_alpha ? 1 : 0;
04737 
04738   if (bpp >= 3)
04739       for (; width; width--)
04740       {
04741           if (has_alpha) {
04742               unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
04743               *p++ = lut[(unsigned int) ( waterlevel +
04744                                           ( ( idx -
04745                                               waterlevel) * tqBlue( *row )) / 255.0 )];
04746           } else {
04747               unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
04748               *p++ = lut[idx];
04749           }
04750 
04751           ++row;
04752       }
04753 }
04754 
04755 static void bumpmap_row( uint           *src,
04756                          uint           *dest,
04757                          int              width,
04758                          int              bpp,
04759                          int              has_alpha,
04760                          uint           *bm_row1,
04761                          uint           *bm_row2,
04762                          uint           *bm_row3,
04763                          int              bm_width,
04764                          int              bm_xofs,
04765                          bool          tiled,
04766                          bool          row_in_bumpmap,
04767                          int           ambient,
04768                          bool          compensate,
04769                          BumpmapParams *params )
04770 {
04771     int xofs1, xofs2, xofs3;
04772     int shade;
04773     int ndotl;
04774     int nx, ny;
04775     int x;
04776     int tmp;
04777 
04778     tmp = bm_xofs;
04779     xofs2 = MOD(tmp, bm_width);
04780 
04781     for (x = 0; x < width; x++)
04782     {
04783         /* Calculate surface normal from bump map */
04784 
04785         if (tiled || (row_in_bumpmap &&
04786                       x >= - tmp && x < - tmp + bm_width)) {
04787             if (tiled) {
04788                 xofs1 = MOD(xofs2 - 1, bm_width);
04789                 xofs3 = MOD(xofs2 + 1, bm_width);
04790         } else {
04791                 xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1);
04792                 xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1);
04793         }
04794             nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
04795                   bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
04796             ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
04797                   bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
04798     } else {
04799             nx = ny = 0;
04800         }
04801 
04802       /* Shade */
04803 
04804         if ((nx == 0) && (ny == 0))
04805             shade = params->background;
04806         else {
04807             ndotl = nx * params->lx + ny * params->ly + params->nzlz;
04808 
04809             if (ndotl < 0)
04810                 shade = (int)( params->compensation * ambient );
04811             else {
04812                 shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) );
04813 
04814                 shade = (int)( shade + TQMAX(0.0, (255 * params->compensation - shade)) *
04815                                ambient / 255 );
04816         }
04817     }
04818 
04819         /* Paint */
04820 
04825         if (compensate) {
04826             int red = (int)((tqRed( *src ) * shade) / (params->compensation * 255));
04827             int green = (int)((tqGreen( *src ) * shade) / (params->compensation * 255));
04828             int blue = (int)((tqBlue( *src ) * shade) / (params->compensation * 255));
04829             int alpha = (int)((tqAlpha( *src ) * shade) / (params->compensation * 255));
04830             ++src;
04831             *dest++ = tqRgba( red, green, blue, alpha );
04832         } else {
04833             int red = tqRed( *src ) * shade / 255;
04834             int green = tqGreen( *src ) * shade / 255;
04835             int blue = tqBlue( *src ) * shade / 255;
04836             int alpha = tqAlpha( *src ) * shade / 255;
04837             ++src;
04838             *dest++ = tqRgba( red, green, blue, alpha );
04839         }
04840 
04841         /* Next pixel */
04842 
04843         if (++xofs2 == bm_width)
04844             xofs2 = 0;
04845     }
04846 }
04847 
04867 TQImage KImageEffect::bumpmap(TQImage &img, TQImage &map, double azimuth, double elevation,
04868                              int depth, int xofs, int yofs, int waterlevel,
04869                              int ambient, bool compensate, bool invert,
04870                              BumpmapType type, bool tiled)
04871 {
04872     TQImage dst;
04873 
04874     if ( img.depth() != 32 || img.depth() != 32 ) {
04875         tqWarning( "Bump-mapping effect works only with 32 bit images");
04876         return dst;
04877     }
04878 
04879     dst.create( img.width(), img.height(), img.depth() );
04880     int bm_width  = map.width();
04881     int bm_height = map.height();
04882     int bm_bpp = map.depth();
04883     int bm_has_alpha = map.hasAlphaBuffer();
04884 
04885     int yofs1, yofs2, yofs3;
04886 
04887     if ( tiled ) {
04888         yofs2 = MOD( yofs, bm_height );
04889         yofs1 = MOD( yofs2 - 1, bm_height);
04890         yofs3 = MOD( yofs2 + 1, bm_height);
04891     } else {
04892         yofs1 = 0;
04893         yofs2 = 0;
04894         yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 );
04895     }
04896 
04897     BumpmapParams params( azimuth, elevation, depth, type, invert );
04898 
04899     uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
04900     uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
04901     uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
04902 
04903     bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04904     bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04905     bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04906 
04907     for (int y = 0; y < img.height(); ++y)
04908     {
04909         int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
04910 
04911         uint* src_row = (unsigned int*)img.scanLine( y );
04912         uint* dest_row = (unsigned int*)dst.scanLine( y );
04913 
04914         bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
04915                      bm_row1, bm_row2, bm_row3, bm_width, xofs,
04916                      tiled,
04917                      row_in_bumpmap, ambient, compensate,
04918                      &params );
04919 
04920         /* Next line */
04921 
04922         if (tiled || row_in_bumpmap)
04923     {
04924             uint* bm_tmprow = bm_row1;
04925             bm_row1   = bm_row2;
04926             bm_row2   = bm_row3;
04927             bm_row3   = bm_tmprow;
04928 
04929             if (++yofs2 == bm_height)
04930                 yofs2 = 0;
04931 
04932             if (tiled)
04933                 yofs3 = MOD(yofs2 + 1, bm_height);
04934             else
04935                 yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1);
04936 
04937             bm_row3 = (unsigned int*)map.scanLine( yofs3 );
04938             bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
04939                                  params.lut, waterlevel );
04940     }
04941     }
04942     return dst;
04943 }
04944 
04953 TQImage KImageEffect::convertToPremultipliedAlpha(TQImage input) {
04954     TQImage alphaImage = input;
04955     if (!alphaImage.isNull()) alphaImage = alphaImage.convertDepth( 32 );
04956 
04957     int w = alphaImage.width();
04958     int h = alphaImage.height();
04959 
04960     int r;
04961     int g;
04962     int b;
04963     int a;
04964     float alpha_adjust;
04965     TQRgb l;
04966     TQRgb *ls;
04967     for (int y = 0; y < h; ++y) {
04968         ls = (TQRgb *)alphaImage.scanLine( y );
04969         for (int x = 0; x < w; ++x) {
04970             l = ls[x];
04971             alpha_adjust = (tqAlpha( l )/255.0);
04972             r = int( tqRed( l ) * alpha_adjust );
04973             g = int( tqGreen( l ) * alpha_adjust );
04974             b = int( tqBlue( l ) * alpha_adjust );
04975             a = int( tqAlpha( l ) * 1.0 );
04976             ls[x] = tqRgba( r, g, b, a );
04977         }
04978     }
04979     return alphaImage;
04980 }

tdefx

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

tdefx

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