• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kdefx

kimageeffect.cpp

Go to the documentation of this file.
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 <qimage.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*qRed(color) +
00088                            0.587*qGreen(color) +
00089                            0.1140000000000001*qBlue(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 QImage KImageEffect::gradient(const QSize &size, const QColor &ca,
00125     const QColor &cb, GradientType eff, int ncols)
00126 {
00127     int rDiff, gDiff, bDiff;
00128     int rca, gca, bca, rcb, gcb, bcb;
00129 
00130     QImage 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     register 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         register int rl = rca << 16;
00151         register int gl = gca << 16;
00152         register 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 = qRgb( (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++ = qRgb( (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] = qRgb(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(QMAX(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 = qRgb(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 = qRgb(rcb - rSign *
00319                                    QMAX(xtable[0][x], ytable[0][y]) * 2,
00320                                    gcb - gSign *
00321                                    QMAX(xtable[1][x], ytable[1][y]) * 2,
00322                                    bcb - bSign *
00323                                    QMAX(xtable[2][x], ytable[2][y]) * 2);
00324                     }
00325                     if (eff == PipeCrossGradient) {
00326                         rgb = qRgb(rcb - rSign *
00327                                    QMIN(xtable[0][x], ytable[0][y]) * 2,
00328                                    gcb - gSign *
00329                                    QMIN(xtable[1][x], ytable[1][y]) * 2,
00330                                    bcb - bSign *
00331                                    QMIN(xtable[2][x], ytable[2][y]) * 2);
00332                     }
00333                     if (eff == EllipticGradient) {
00334                         rgb = qRgb(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 && (QPixmap::defaultDepth() < 15 )) {
00361     if ( ncols < 2 || ncols > 256 )
00362         ncols = 3;
00363     QColor *dPal = new QColor[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 QImage KImageEffect::unbalancedGradient(const QSize &size, const QColor &ca,
00389     const QColor &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     QImage 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     register 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         QColor 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] = qRgb(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] = qRgb(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] = qRgb(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] = qRgb(rcb - rSign *
00568                                          QMAX(xtable[0][x], ytable[0][y]) * 2,
00569                                          gcb - gSign *
00570                                          QMAX(xtable[1][x], ytable[1][y]) * 2,
00571                                          bcb - bSign *
00572                                          QMAX(xtable[2][x], ytable[2][y]) * 2);
00573                   }
00574                   else if (eff == PipeCrossGradient)
00575                   {
00576                       scanline[x] = qRgb(rcb - rSign *
00577                                          QMIN(xtable[0][x], ytable[0][y]) * 2,
00578                                          gcb - gSign *
00579                                          QMIN(xtable[1][x], ytable[1][y]) * 2,
00580                                          bcb - bSign *
00581                                          QMIN(xtable[2][x], ytable[2][y]) * 2);
00582                   }
00583                   else if (eff == EllipticGradient)
00584                   {
00585                       scanline[x] = qRgb(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 && (QPixmap::defaultDepth() < 15 )) {
00600           if ( ncols < 2 || ncols > 256 )
00601               ncols = 3;
00602           QColor *dPal = new QColor[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     Q_UINT16 data[4];
00632 };
00633 
00634 struct KIE8Pack
00635 {
00636     Q_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 QImage& KImageEffect::intensity(QImage &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.colorTable();
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         Q_UINT16 p = Q_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         Q_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 = qRed(data[i]);
00819                 int g = qGreen(data[i]);
00820                 int b = qBlue(data[i]);
00821                 int a = qAlpha(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] = qRgba(r, g, b,a);
00826             }
00827         }
00828         else{
00829             for(int i=0; i < pixels; ++i){
00830                 int r = qRed(data[i]);
00831                 int g = qGreen(data[i]);
00832                 int b = qBlue(data[i]);
00833                 int a = qAlpha(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] = qRgba(r, g, b, a);
00838             }
00839         }
00840         delete [] segTbl;
00841     }
00842 
00843     return image;
00844 }
00845 
00846 QImage& KImageEffect::channelIntensity(QImage &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.colorTable();
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 = qRed(data[i]);
00887                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
00888                 data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
00889             }
00890         }
00891         else if(channel == Green){
00892             for(int i=0; i < pixels; ++i){
00893                 int c = qGreen(data[i]);
00894                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
00895                 data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
00896             }
00897         }
00898         else{
00899             for(int i=0; i < pixels; ++i){
00900                 int c = qBlue(data[i]);
00901                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
00902                 data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
00903             }
00904         }
00905 
00906     }
00907     else{
00908         if(channel == Red){
00909             for(int i=0; i < pixels; ++i){
00910                 int c = qRed(data[i]);
00911                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
00912                 data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
00913             }
00914         }
00915         else if(channel == Green){
00916             for(int i=0; i < pixels; ++i){
00917                 int c = qGreen(data[i]);
00918                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
00919                 data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
00920             }
00921         }
00922         else{
00923             for(int i=0; i < pixels; ++i){
00924                 int c = qBlue(data[i]);
00925                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
00926                 data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(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 QImage& KImageEffect::modulate(QImage &image, QImage &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     QColor clr;
00950     int mod=0;
00951     unsigned int x1, x2, y1, y2;
00952     register 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.colorTable():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 = qRed(color1);
00986           g = qGreen(color1);
00987           b = qBlue(color1);
00988           if (channel != All) {
00989                 mod = (channel == Red) ? qRed(color2) :
00990             (channel == Green) ? qGreen(color2) :
00991                 (channel == Blue) ? qBlue(color2) :
00992             (channel == Gray) ? qGray(color2) : 0;
00993             mod = mod*factor/50;
00994           }
00995 
00996           if (type == Intensity) {
00997             if (channel == All) {
00998               r += r * factor/50 * qRed(color2)/256;
00999               g += g * factor/50 * qGreen(color2)/256;
01000               b += b * factor/50 * qBlue(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 * qRed(color2)/128;
01011               g += (g-128) * factor/50 * qGreen(color2)/128;
01012               b += (b-128) * factor/50 * qBlue(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 = qAlpha(*data1);
01025           *data1 = qRgba(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) ? qRed(color2) :
01031             (channel == Green) ? qGreen(color2) :
01032                 (channel == Blue) ? qBlue(color2) :
01033             (channel == Gray) ? qGray(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 = qAlpha(*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 QImage& KImageEffect::blend(const QColor& clr, QImage& 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     int pixels = dst.width() * dst.height();
01083 
01084 #ifdef USE_SSE2_INLINE_ASM
01085     if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
01086         Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 );
01087 
01088         KIE8Pack packedalpha = { { alpha, alpha, alpha, 256,
01089                                    alpha, alpha, alpha, 256 } };
01090 
01091         Q_UINT16 red   = Q_UINT16( clr.red()   * 256 * opacity );
01092         Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity );
01093         Q_UINT16 blue  = Q_UINT16( clr.blue()  * 256 * opacity );
01094 
01095         KIE8Pack packedcolor = { { blue, green, red, 0,
01096                                    blue, green, red, 0 } };
01097 
01098         // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending
01099         __asm__ __volatile__(
01100         "pxor        %%xmm7,  %%xmm7\n\t" // Zero out XMM7 for unpacking
01101         "movdqu        (%0),  %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6
01102         "movdqu        (%1),  %%xmm5\n\t" // Set up color * alpha * 256 in XMM5
01103         : : "r"(&packedalpha), "r"(&packedcolor),
01104             "m"(packedcolor),  "m"(packedalpha) );
01105 
01106         Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dst.bits() );
01107 
01108         // Check how many pixels we need to process to achieve 16 byte alignment
01109         int offset = (16 - (Q_UINT32( data ) & 0x0f)) / 4;
01110 
01111         // The main loop processes 8 pixels / iteration
01112         int remainder = (pixels - offset) % 8;
01113         pixels -= remainder;
01114 
01115         // Alignment loop
01116         for ( int i = 0; i < offset; i++ ) {
01117             __asm__ __volatile__(
01118             "movd         (%0,%1,4),      %%xmm0\n\t"  // Load one pixel to XMM1
01119             "punpcklbw       %%xmm7,      %%xmm0\n\t"  // Unpack the pixel
01120             "pmullw          %%xmm6,      %%xmm0\n\t"  // Multiply the pixel with (1 - alpha) * 256
01121             "paddw           %%xmm5,      %%xmm0\n\t"  // Add color * alpha * 256 to the result
01122             "psrlw               $8,      %%xmm0\n\t"  // Divide by 256
01123             "packuswb        %%xmm1,      %%xmm0\n\t"  // Pack the pixel to a dword
01124             "movd            %%xmm0,   (%0,%1,4)\n\t"  // Write the pixel to the image
01125             : : "r"(data), "r"(i) );
01126         }
01127 
01128         // Main loop
01129         for ( int i = offset; i < pixels; i += 8 ) {
01130             __asm__ __volatile(
01131             // Load 8 pixels to XMM registers 1 - 4
01132             "movq         (%0,%1,4),      %%xmm0\n\t"  // Load pixels 1 and 2 to XMM1
01133             "movq        8(%0,%1,4),      %%xmm1\n\t"  // Load pixels 3 and 4 to XMM2
01134             "movq       16(%0,%1,4),      %%xmm2\n\t"  // Load pixels 5 and 6 to XMM3
01135             "movq       24(%0,%1,4),      %%xmm3\n\t"  // Load pixels 7 and 8 to XMM4
01136 
01137             // Prefetch the pixels for next iteration
01138             "prefetchnta 32(%0,%1,4)            \n\t"
01139 
01140             // Blend pixels 1 and 2
01141             "punpcklbw       %%xmm7,      %%xmm0\n\t"  // Unpack the pixels
01142             "pmullw          %%xmm6,      %%xmm0\n\t"  // Multiply the pixels with (1 - alpha) * 256
01143             "paddw           %%xmm5,      %%xmm0\n\t"  // Add color * alpha * 256 to the result
01144             "psrlw               $8,      %%xmm0\n\t"  // Divide by 256
01145 
01146             // Blend pixels 3 and 4
01147             "punpcklbw       %%xmm7,      %%xmm1\n\t"  // Unpack the pixels
01148             "pmullw          %%xmm6,      %%xmm1\n\t"  // Multiply the pixels with (1 - alpha) * 256
01149             "paddw           %%xmm5,      %%xmm1\n\t"  // Add color * alpha * 256 to the result
01150             "psrlw               $8,      %%xmm1\n\t"  // Divide by 256
01151 
01152             // Blend pixels 5 and 6
01153             "punpcklbw       %%xmm7,      %%xmm2\n\t"  // Unpack the pixels
01154             "pmullw          %%xmm6,      %%xmm2\n\t"  // Multiply the pixels with (1 - alpha) * 256
01155             "paddw           %%xmm5,      %%xmm2\n\t"  // Add color * alpha * 256 to the result
01156             "psrlw               $8,      %%xmm2\n\t"  // Divide by 256
01157 
01158             // Blend pixels 7 and 8
01159             "punpcklbw       %%xmm7,      %%xmm3\n\t"  // Unpack the pixels
01160             "pmullw          %%xmm6,      %%xmm3\n\t"  // Multiply the pixels with (1 - alpha) * 256
01161             "paddw           %%xmm5,      %%xmm3\n\t"  // Add color * alpha * 256 to the result
01162             "psrlw               $8,      %%xmm3\n\t"  // Divide by 256
01163 
01164             // Pack the pixels into 2 double quadwords
01165             "packuswb        %%xmm1,      %%xmm0\n\t"  // Pack pixels 1 - 4 to a double qword
01166             "packuswb        %%xmm3,      %%xmm2\n\t"  // Pack pixles 5 - 8 to a double qword
01167 
01168             // Write the pixels back to the image
01169             "movdqa          %%xmm0,   (%0,%1,4)\n\t"  // Store pixels 1 - 4
01170             "movdqa          %%xmm2, 16(%0,%1,4)\n\t"  // Store pixels 5 - 8
01171             : : "r"(data), "r"(i) );
01172         }
01173 
01174         // Cleanup loop
01175         for ( int i = pixels; i < pixels + remainder; i++ ) {
01176             __asm__ __volatile__(
01177             "movd         (%0,%1,4),      %%xmm0\n\t"  // Load one pixel to XMM1
01178             "punpcklbw       %%xmm7,      %%xmm0\n\t"  // Unpack the pixel
01179             "pmullw          %%xmm6,      %%xmm0\n\t"  // Multiply the pixel with (1 - alpha) * 256
01180             "paddw           %%xmm5,      %%xmm0\n\t"  // Add color * alpha * 256 to the result
01181             "psrlw               $8,      %%xmm0\n\t"  // Divide by 256
01182             "packuswb        %%xmm1,      %%xmm0\n\t"  // Pack the pixel to a dword
01183             "movd            %%xmm0,   (%0,%1,4)\n\t"  // Write the pixel to the image
01184             : : "r"(data), "r"(i) );
01185         }
01186     } else
01187 #endif
01188 
01189 #ifdef USE_MMX_INLINE_ASM
01190     if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
01191         Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 );
01192         KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } };
01193 
01194         Q_UINT16 red   = Q_UINT16( clr.red()   * 256 * opacity );
01195         Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity );
01196         Q_UINT16 blue  = Q_UINT16( clr.blue()  * 256 * opacity );
01197 
01198         KIE4Pack packedcolor = { { blue, green, red, 0 } };
01199 
01200         __asm__ __volatile__(
01201         "pxor        %%mm7,    %%mm7\n\t"       // Zero out MM7 for unpacking
01202         "movq         (%0),    %%mm6\n\t"       // Set up (1 - alpha) * 256 in MM6
01203         "movq         (%1),    %%mm5\n\t"       // Set up color * alpha * 256 in MM5
01204         : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) );
01205 
01206         Q_UINT32 *data = reinterpret_cast<Q_UINT32*>( dst.bits() );
01207 
01208         // The main loop processes 4 pixels / iteration
01209         int remainder = pixels % 4;
01210         pixels -= remainder;
01211 
01212         // Main loop
01213         for ( int i = 0; i < pixels; i += 4 ) {
01214             __asm__ __volatile__(
01215             // Load 4 pixels to MM registers 1 - 4
01216             "movd         (%0,%1,4),      %%mm0\n\t"  // Load the 1st pixel to MM0
01217             "movd        4(%0,%1,4),      %%mm1\n\t"  // Load the 2nd pixel to MM1
01218             "movd        8(%0,%1,4),      %%mm2\n\t"  // Load the 3rd pixel to MM2
01219             "movd       12(%0,%1,4),      %%mm3\n\t"  // Load the 4th pixel to MM3
01220 
01221             // Blend the first pixel
01222             "punpcklbw        %%mm7,      %%mm0\n\t"  // Unpack the pixel
01223             "pmullw           %%mm6,      %%mm0\n\t"  // Multiply the pixel with (1 - alpha) * 256
01224             "paddw            %%mm5,      %%mm0\n\t"  // Add color * alpha * 256 to the result
01225             "psrlw               $8,      %%mm0\n\t"  // Divide by 256
01226 
01227             // Blend the second pixel
01228             "punpcklbw        %%mm7,      %%mm1\n\t"  // Unpack the pixel
01229             "pmullw           %%mm6,      %%mm1\n\t"  // Multiply the pixel with (1 - alpha) * 256
01230             "paddw            %%mm5,      %%mm1\n\t"  // Add color * alpha * 256 to the result
01231             "psrlw               $8,      %%mm1\n\t"  // Divide by 256
01232 
01233             // Blend the third pixel
01234             "punpcklbw        %%mm7,      %%mm2\n\t"  // Unpack the pixel
01235             "pmullw           %%mm6,      %%mm2\n\t"  // Multiply the pixel with (1 - alpha) * 256
01236             "paddw            %%mm5,      %%mm2\n\t"  // Add color * alpha * 256 to the result
01237             "psrlw               $8,      %%mm2\n\t"  // Divide by 256
01238 
01239             // Blend the fourth pixel
01240             "punpcklbw        %%mm7,      %%mm3\n\t"  // Unpack the pixel
01241             "pmullw           %%mm6,      %%mm3\n\t"  // Multiply the pixel with (1 - alpha) * 256
01242             "paddw            %%mm5,      %%mm3\n\t"  // Add color * alpha * 256 to the result
01243             "psrlw               $8,      %%mm3\n\t"  // Divide by 256
01244 
01245             // Pack the pixels into 2 quadwords
01246             "packuswb         %%mm1,      %%mm0\n\t"  // Pack pixels 1 and 2 to a qword
01247             "packuswb         %%mm3,      %%mm2\n\t"  // Pack pixels 3 and 4 to a qword
01248 
01249             // Write the pixels back to the image
01250             "movq             %%mm0,  (%0,%1,4)\n\t"  // Store pixels 1 and 2
01251             "movq             %%mm2, 8(%0,%1,4)\n\t"  // Store pixels 3 and 4
01252             : : "r"(data), "r"(i) );
01253         }
01254 
01255         // Cleanup loop
01256         for ( int i = pixels; i < pixels + remainder; i++ ) {
01257             __asm__ __volatile__(
01258             "movd         (%0,%1,4),      %%mm0\n\t"  // Load one pixel to MM1
01259             "punpcklbw        %%mm7,      %%mm0\n\t"  // Unpack the pixel
01260             "pmullw           %%mm6,      %%mm0\n\t"  // Multiply the pixel with 1 - alpha * 256
01261             "paddw            %%mm5,      %%mm0\n\t"  // Add color * alpha * 256 to the result
01262             "psrlw               $8,      %%mm0\n\t"  // Divide by 256
01263             "packuswb         %%mm0,      %%mm0\n\t"  // Pack the pixel to a dword
01264             "movd             %%mm0,  (%0,%1,4)\n\t"  // Write the pixel to the image
01265             : : "r"(data), "r"(i) );
01266         }
01267 
01268         // Empty the MMX state
01269         __asm__ __volatile__("emms");
01270     } else
01271 #endif // USE_MMX_INLINE_ASM
01272 
01273     {
01274         int rcol, gcol, bcol;
01275         clr.rgb(&rcol, &gcol, &bcol);
01276 
01277 #ifdef WORDS_BIGENDIAN   // ARGB (skip alpha)
01278         register unsigned char *data = (unsigned char *)dst.bits() + 1;
01279 #else                    // BGRA
01280         register unsigned char *data = (unsigned char *)dst.bits();
01281 #endif
01282 
01283         for (register int i=0; i<pixels; i++)
01284         {
01285 #ifdef WORDS_BIGENDIAN
01286             *data += (unsigned char)((rcol - *data) * opacity);
01287             data++;
01288             *data += (unsigned char)((gcol - *data) * opacity);
01289             data++;
01290             *data += (unsigned char)((bcol - *data) * opacity);
01291             data++;
01292 #else
01293             *data += (unsigned char)((bcol - *data) * opacity);
01294             data++;
01295             *data += (unsigned char)((gcol - *data) * opacity);
01296             data++;
01297             *data += (unsigned char)((rcol - *data) * opacity);
01298             data++;
01299 #endif
01300             data++; // skip alpha
01301         }
01302     }
01303 
01304     return dst;
01305 }
01306 
01307 // Nice and fast direct pixel manipulation
01308 QImage& KImageEffect::blend(QImage& src, QImage& dst, float opacity)
01309 {
01310     if (src.width() <= 0 || src.height() <= 0)
01311         return dst;
01312     if (dst.width() <= 0 || dst.height() <= 0)
01313         return dst;
01314 
01315     if (src.width() != dst.width() || src.height() != dst.height()) {
01316 #ifndef NDEBUG
01317         std::cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n";
01318 #endif
01319         return dst;
01320     }
01321 
01322     if (opacity < 0.0 || opacity > 1.0) {
01323 #ifndef NDEBUG
01324         std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
01325 #endif
01326         return dst;
01327     }
01328 
01329     if (src.depth() != 32) src = src.convertDepth(32);
01330     if (dst.depth() != 32) dst = dst.convertDepth(32);
01331 
01332     int pixels = src.width() * src.height();
01333 
01334 #ifdef USE_SSE2_INLINE_ASM
01335     if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
01336         Q_UINT16 alpha = Q_UINT16( opacity * 256.0 );
01337         KIE8Pack packedalpha = { { alpha, alpha, alpha, 0,
01338                                    alpha, alpha, alpha, 0 } };
01339 
01340         // Prepare the XMM6 and XMM7 registers for unpacking and blending
01341         __asm__ __volatile__(
01342         "pxor      %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking
01343         "movdqu      (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6
01344         : : "r"(&packedalpha), "m"(packedalpha) );
01345 
01346         Q_UINT32 *data1 = reinterpret_cast<Q_UINT32*>( src.bits() );
01347         Q_UINT32 *data2 = reinterpret_cast<Q_UINT32*>( dst.bits() );
01348 
01349         // Check how many pixels we need to process to achieve 16 byte alignment
01350         int offset = (16 - (Q_UINT32( data2 ) & 0x0f)) / 4;
01351 
01352         // The main loop processes 4 pixels / iteration
01353         int remainder = (pixels - offset) % 4;
01354         pixels -= remainder;
01355 
01356         // Alignment loop
01357         for ( int i = 0; i < offset; i++ ) {
01358             __asm__ __volatile__(
01359             "movd       (%1,%2,4),    %%xmm1\n\t"  // Load one dst pixel to XMM1
01360             "punpcklbw     %%xmm7,    %%xmm1\n\t"  // Unpack the pixel
01361             "movd       (%0,%2,4),    %%xmm0\n\t"  // Load one src pixel to XMM0
01362             "punpcklbw     %%xmm7,    %%xmm0\n\t"  // Unpack the pixel
01363             "psubw         %%xmm1,    %%xmm0\n\t"  // Subtract dst from src
01364             "pmullw        %%xmm6,    %%xmm0\n\t"  // Multiply the result with alpha * 256
01365             "psllw             $8,    %%xmm1\n\t"  // Multiply dst with 256
01366             "paddw         %%xmm1,    %%xmm0\n\t"  // Add dst to result
01367             "psrlw             $8,    %%xmm0\n\t"  // Divide by 256
01368             "packuswb      %%xmm1,    %%xmm0\n\t"  // Pack the pixel to a dword
01369             "movd          %%xmm0, (%1,%2,4)\n\t"  // Write the pixel to the image
01370             : : "r"(data1), "r"(data2), "r"(i) );
01371         }
01372 
01373         // Main loop
01374         for ( int i = offset; i < pixels; i += 4 ) {
01375             __asm__ __volatile__(
01376             // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3
01377             "movq       (%0,%2,4),    %%xmm0\n\t"  // Load two src pixels to XMM0
01378             "movq       (%1,%2,4),    %%xmm1\n\t"  // Load two dst pixels to XMM1
01379             "movq      8(%0,%2,4),    %%xmm2\n\t"  // Load two src pixels to XMM2
01380             "movq      8(%1,%2,4),    %%xmm3\n\t"  // Load two dst pixels to XMM3
01381 
01382             // Prefetch the pixels for the iteration after the next one
01383             "prefetchnta 32(%0,%2,4)        \n\t"
01384             "prefetchnta 32(%1,%2,4)        \n\t"
01385 
01386             // Blend the first two pixels
01387             "punpcklbw     %%xmm7,    %%xmm1\n\t"  // Unpack the dst pixels
01388             "punpcklbw     %%xmm7,    %%xmm0\n\t"  // Unpack the src pixels
01389             "psubw         %%xmm1,    %%xmm0\n\t"  // Subtract dst from src
01390             "pmullw        %%xmm6,    %%xmm0\n\t"  // Multiply the result with alpha * 256
01391             "psllw             $8,    %%xmm1\n\t"  // Multiply dst with 256
01392             "paddw         %%xmm1,    %%xmm0\n\t"  // Add dst to the result
01393             "psrlw             $8,    %%xmm0\n\t"  // Divide by 256
01394 
01395             // Blend the next two pixels
01396             "punpcklbw     %%xmm7,    %%xmm3\n\t"  // Unpack the dst pixels
01397             "punpcklbw     %%xmm7,    %%xmm2\n\t"  // Unpack the src pixels
01398             "psubw         %%xmm3,    %%xmm2\n\t"  // Subtract dst from src
01399             "pmullw        %%xmm6,    %%xmm2\n\t"  // Multiply the result with alpha * 256
01400             "psllw             $8,    %%xmm3\n\t"  // Multiply dst with 256
01401             "paddw         %%xmm3,    %%xmm2\n\t"  // Add dst to the result
01402             "psrlw             $8,    %%xmm2\n\t"  // Divide by 256
01403 
01404             // Write the pixels back to the image
01405             "packuswb      %%xmm2,    %%xmm0\n\t"  // Pack the pixels to a double qword
01406             "movdqa        %%xmm0, (%1,%2,4)\n\t"  // Store the pixels
01407             : : "r"(data1), "r"(data2), "r"(i) );
01408         }
01409 
01410         // Cleanup loop
01411         for ( int i = pixels; i < pixels + remainder; i++ ) {
01412             __asm__ __volatile__(
01413             "movd       (%1,%2,4),    %%xmm1\n\t"  // Load one dst pixel to XMM1
01414             "punpcklbw     %%xmm7,    %%xmm1\n\t"  // Unpack the pixel
01415             "movd       (%0,%2,4),    %%xmm0\n\t"  // Load one src pixel to XMM0
01416             "punpcklbw     %%xmm7,    %%xmm0\n\t"  // Unpack the pixel
01417             "psubw         %%xmm1,    %%xmm0\n\t"  // Subtract dst from src
01418             "pmullw        %%xmm6,    %%xmm0\n\t"  // Multiply the result with alpha * 256
01419             "psllw             $8,    %%xmm1\n\t"  // Multiply dst with 256
01420             "paddw         %%xmm1,    %%xmm0\n\t"  // Add dst to result
01421             "psrlw             $8,    %%xmm0\n\t"  // Divide by 256
01422             "packuswb      %%xmm1,    %%xmm0\n\t"  // Pack the pixel to a dword
01423             "movd          %%xmm0, (%1,%2,4)\n\t"  // Write the pixel to the image
01424             : : "r"(data1), "r"(data2), "r"(i) );
01425         }
01426     } else
01427 #endif // USE_SSE2_INLINE_ASM
01428 
01429 #ifdef USE_MMX_INLINE_ASM
01430     if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) {
01431         Q_UINT16 alpha = Q_UINT16( opacity * 256.0 );
01432         KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } };
01433 
01434         // Prepare the MM6 and MM7 registers for blending and unpacking
01435         __asm__ __volatile__(
01436         "pxor       %%mm7,   %%mm7\n\t"      // Zero out MM7 for unpacking
01437         "movq        (%0),   %%mm6\n\t"      // Set up alpha * 256 in MM6
01438         : : "r"(&packedalpha), "m"(packedalpha) );
01439 
01440         Q_UINT32 *data1 = reinterpret_cast<Q_UINT32*>( src.bits() );
01441         Q_UINT32 *data2 = reinterpret_cast<Q_UINT32*>( dst.bits() );
01442 
01443         // The main loop processes 2 pixels / iteration
01444         int remainder = pixels % 2;
01445         pixels -= remainder;
01446 
01447         // Main loop
01448         for ( int i = 0; i < pixels; i += 2 ) {
01449             __asm__ __volatile__(
01450             // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3
01451             "movd        (%0,%2,4),     %%mm0\n\t"  // Load the 1st src pixel to MM0
01452             "movd        (%1,%2,4),     %%mm1\n\t"  // Load the 1st dst pixel to MM1
01453             "movd       4(%0,%2,4),     %%mm2\n\t"  // Load the 2nd src pixel to MM2
01454             "movd       4(%1,%2,4),     %%mm3\n\t"  // Load the 2nd dst pixel to MM3
01455 
01456             // Blend the first pixel
01457             "punpcklbw       %%mm7,     %%mm0\n\t"  // Unpack the src pixel
01458             "punpcklbw       %%mm7,     %%mm1\n\t"  // Unpack the dst pixel
01459             "psubw           %%mm1,     %%mm0\n\t"  // Subtract dst from src
01460             "pmullw          %%mm6,     %%mm0\n\t"  // Multiply the result with alpha * 256
01461             "psllw              $8,     %%mm1\n\t"  // Multiply dst with 256
01462             "paddw           %%mm1,     %%mm0\n\t"  // Add dst to the result
01463             "psrlw              $8,     %%mm0\n\t"  // Divide by 256
01464 
01465             // Blend the second pixel
01466             "punpcklbw       %%mm7,     %%mm2\n\t"  // Unpack the src pixel
01467             "punpcklbw       %%mm7,     %%mm3\n\t"  // Unpack the dst pixel
01468             "psubw           %%mm3,     %%mm2\n\t"  // Subtract dst from src
01469             "pmullw          %%mm6,     %%mm2\n\t"  // Multiply the result with alpha * 256
01470             "psllw              $8,     %%mm3\n\t"  // Multiply dst with 256
01471             "paddw           %%mm3,     %%mm2\n\t"  // Add dst to the result
01472             "psrlw              $8,     %%mm2\n\t"  // Divide by 256
01473 
01474             // Write the pixels back to the image
01475             "packuswb        %%mm2,     %%mm0\n\t"  // Pack the pixels to a qword
01476             "movq            %%mm0, (%1,%2,4)\n\t"  // Store the pixels
01477             : : "r"(data1), "r"(data2), "r"(i) );
01478         }
01479 
01480         // Blend the remaining pixel (if there is one)
01481         if ( remainder ) {
01482              __asm__ __volatile__(
01483             "movd             (%0),     %%mm0\n\t"  // Load one src pixel to MM0
01484             "punpcklbw       %%mm7,     %%mm0\n\t"  // Unpack the src pixel
01485             "movd             (%1),     %%mm1\n\t"  // Load one dst pixel to MM1
01486             "punpcklbw       %%mm7,     %%mm1\n\t"  // Unpack the dst pixel
01487             "psubw           %%mm1,     %%mm0\n\t"  // Subtract dst from src
01488             "pmullw          %%mm6,     %%mm0\n\t"  // Multiply the result with alpha * 256
01489             "psllw              $8,     %%mm1\n\t"  // Multiply dst with 256
01490             "paddw           %%mm1,     %%mm0\n\t"  // Add dst to result
01491             "psrlw              $8,     %%mm0\n\t"  // Divide by 256
01492             "packuswb        %%mm0,     %%mm0\n\t"  // Pack the pixel to a dword
01493             "movd            %%mm0,      (%1)\n\t"  // Write the pixel to the image
01494             : : "r"(data1 + pixels), "r"(data2 + pixels) );
01495         }
01496 
01497         // Empty the MMX state
01498         __asm__ __volatile__("emms");
01499     } else
01500 #endif // USE_MMX_INLINE_ASM
01501 
01502     {
01503 #ifdef WORDS_BIGENDIAN   // ARGB (skip alpha)
01504         register unsigned char *data1 = (unsigned char *)dst.bits() + 1;
01505         register unsigned char *data2 = (unsigned char *)src.bits() + 1;
01506 #else                    // BGRA
01507         register unsigned char *data1 = (unsigned char *)dst.bits();
01508         register unsigned char *data2 = (unsigned char *)src.bits();
01509 #endif
01510 
01511         for (register int i=0; i<pixels; i++)
01512         {
01513 #ifdef WORDS_BIGENDIAN
01514             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01515             data1++;
01516             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01517             data1++;
01518             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01519             data1++;
01520 #else
01521             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01522             data1++;
01523             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01524             data1++;
01525             *data1 += (unsigned char)((*(data2++) - *data1) * opacity);
01526             data1++;
01527 #endif
01528             data1++; // skip alpha
01529             data2++;
01530         }
01531     }
01532 
01533     return dst;
01534 }
01535 
01536 
01537 QImage& KImageEffect::blend(QImage &image, float initial_intensity,
01538                             const QColor &bgnd, GradientType eff,
01539                             bool anti_dir)
01540 {
01541     if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
01542 #ifndef NDEBUG
01543       std::cerr << "WARNING: KImageEffect::blend : invalid image\n";
01544 #endif
01545       return image;
01546     }
01547 
01548     int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
01549     int r, g, b;
01550     int ind;
01551 
01552     unsigned int xi, xf, yi, yf;
01553     unsigned int a;
01554 
01555     // check the boundaries of the initial intesity param
01556     float unaffected = 1;
01557     if (initial_intensity >  1) initial_intensity =  1;
01558     if (initial_intensity < -1) initial_intensity = -1;
01559     if (initial_intensity < 0) {
01560         unaffected = 1. + initial_intensity;
01561         initial_intensity = 0;
01562     }
01563 
01564 
01565     float intensity = initial_intensity;
01566     float var = 1. - initial_intensity;
01567 
01568     if (anti_dir) {
01569         initial_intensity = intensity = 1.;
01570         var = -var;
01571     }
01572 
01573     register int x, y;
01574 
01575     unsigned int *data =  (unsigned int *)image.bits();
01576 
01577     int image_width = image.width(); //Those can't change
01578     int image_height = image.height();
01579 
01580 
01581     if( eff == VerticalGradient || eff == HorizontalGradient ) {
01582 
01583         // set the image domain to apply the effect to
01584         xi = 0, xf = image_width;
01585         yi = 0, yf = image_height;
01586         if (eff == VerticalGradient) {
01587             if (anti_dir) yf = (int)(image_height * unaffected);
01588             else yi = (int)(image_height * (1 - unaffected));
01589         }
01590         else {
01591             if (anti_dir) xf = (int)(image_width * unaffected);
01592             else xi = (int)(image_height * (1 - unaffected));
01593         }
01594 
01595         var /= (eff == VerticalGradient?yf-yi:xf-xi);
01596 
01597         int ind_base;
01598         for (y = yi; y < (int)yf; y++) {
01599             intensity = eff == VerticalGradient? intensity + var :
01600                 initial_intensity;
01601             ind_base = image_width  * y ;
01602             for (x = xi; x < (int)xf ; x++) {
01603                 if (eff == HorizontalGradient) intensity += var;
01604                 ind = x + ind_base;
01605                 r = qRed  (data[ind]) + (int)(intensity *
01606                                               (r_bgnd - qRed  (data[ind])));
01607                 g = qGreen(data[ind]) + (int)(intensity *
01608                                               (g_bgnd - qGreen(data[ind])));
01609                 b = qBlue (data[ind]) + (int)(intensity *
01610                                               (b_bgnd - qBlue (data[ind])));
01611                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01612                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01613                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01614                 a = qAlpha(data[ind]);
01615                 data[ind] = qRgba(r, g, b, a);
01616             }
01617         }
01618     }
01619     else if (eff == DiagonalGradient  || eff == CrossDiagonalGradient) {
01620         float xvar = var / 2 / image_width;  // / unaffected;
01621         float yvar = var / 2 / image_height; // / unaffected;
01622         float tmp;
01623 
01624         for (x = 0; x < image_width ; x++) {
01625             tmp =  xvar * (eff == DiagonalGradient? x : image.width()-x-1);
01626             ind = x;
01627             for (y = 0; y < image_height ; y++) {
01628                 intensity = initial_intensity + tmp + yvar * y;
01629 
01630                 r = qRed  (data[ind]) + (int)(intensity *
01631                                               (r_bgnd - qRed  (data[ind])));
01632                 g = qGreen(data[ind]) + (int)(intensity *
01633                                               (g_bgnd - qGreen(data[ind])));
01634                 b = qBlue (data[ind]) + (int)(intensity *
01635                                               (b_bgnd - qBlue (data[ind])));
01636                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01637                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01638                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01639                 a = qAlpha(data[ind]);
01640                 data[ind] = qRgba(r, g, b, a);
01641 
01642                 ind += image_width;
01643             }
01644         }
01645     }
01646 
01647     else if (eff == RectangleGradient || eff == EllipticGradient) {
01648         float xvar;
01649         float yvar;
01650 
01651         for (x = 0; x < image_width / 2 + image_width % 2; x++) {
01652             xvar = var / image_width  * (image_width - x*2/unaffected-1);
01653             for (y = 0; y < image_height / 2 + image_height % 2; y++) {
01654                 yvar = var / image_height   * (image_height - y*2/unaffected -1);
01655 
01656                 if (eff == RectangleGradient)
01657                     intensity = initial_intensity + QMAX(xvar, yvar);
01658                 else
01659                     intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
01660                 if (intensity > 1) intensity = 1;
01661                 if (intensity < 0) intensity = 0;
01662 
01663                 //NW
01664                 ind = x + image_width  * y ;
01665                 r = qRed  (data[ind]) + (int)(intensity *
01666                                               (r_bgnd - qRed  (data[ind])));
01667                 g = qGreen(data[ind]) + (int)(intensity *
01668                                               (g_bgnd - qGreen(data[ind])));
01669                 b = qBlue (data[ind]) + (int)(intensity *
01670                                               (b_bgnd - qBlue (data[ind])));
01671                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01672                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01673                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01674                 a = qAlpha(data[ind]);
01675                 data[ind] = qRgba(r, g, b, a);
01676 
01677                 //NE
01678                 ind = image_width - x - 1 + image_width  * y ;
01679                 r = qRed  (data[ind]) + (int)(intensity *
01680                                               (r_bgnd - qRed  (data[ind])));
01681                 g = qGreen(data[ind]) + (int)(intensity *
01682                                               (g_bgnd - qGreen(data[ind])));
01683                 b = qBlue (data[ind]) + (int)(intensity *
01684                                               (b_bgnd - qBlue (data[ind])));
01685                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01686                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01687                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01688                 a = qAlpha(data[ind]);
01689                 data[ind] = qRgba(r, g, b, a);
01690             }
01691         }
01692 
01693         //CT  loop is doubled because of stupid central row/column issue.
01694         //    other solution?
01695         for (x = 0; x < image_width / 2; x++) {
01696             xvar = var / image_width  * (image_width - x*2/unaffected-1);
01697             for (y = 0; y < image_height / 2; y++) {
01698                 yvar = var / image_height   * (image_height - y*2/unaffected -1);
01699 
01700                 if (eff == RectangleGradient)
01701                     intensity = initial_intensity + QMAX(xvar, yvar);
01702                 else
01703                     intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
01704                 if (intensity > 1) intensity = 1;
01705                 if (intensity < 0) intensity = 0;
01706 
01707                 //SW
01708                 ind = x + image_width  * (image_height - y -1) ;
01709                 r = qRed  (data[ind]) + (int)(intensity *
01710                                               (r_bgnd - qRed  (data[ind])));
01711                 g = qGreen(data[ind]) + (int)(intensity *
01712                                               (g_bgnd - qGreen(data[ind])));
01713                 b = qBlue (data[ind]) + (int)(intensity *
01714                                               (b_bgnd - qBlue (data[ind])));
01715                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01716                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01717                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01718                 a = qAlpha(data[ind]);
01719                 data[ind] = qRgba(r, g, b, a);
01720 
01721                 //SE
01722                 ind = image_width-x-1 + image_width * (image_height - y - 1) ;
01723                 r = qRed  (data[ind]) + (int)(intensity *
01724                                               (r_bgnd - qRed  (data[ind])));
01725                 g = qGreen(data[ind]) + (int)(intensity *
01726                                               (g_bgnd - qGreen(data[ind])));
01727                 b = qBlue (data[ind]) + (int)(intensity *
01728                                               (b_bgnd - qBlue (data[ind])));
01729                 if (r > 255) r = 255; if (r < 0 ) r = 0;
01730                 if (g > 255) g = 255; if (g < 0 ) g = 0;
01731                 if (b > 255) b = 255; if (b < 0 ) b = 0;
01732                 a = qAlpha(data[ind]);
01733                 data[ind] = qRgba(r, g, b, a);
01734             }
01735         }
01736     }
01737 #ifndef NDEBUG
01738     else std::cerr << "KImageEffect::blend effect not implemented" << std::endl;
01739 #endif
01740     return image;
01741 }
01742 
01743 // Not very efficient as we create a third big image...
01744 //
01745 QImage& KImageEffect::blend(QImage &image1, QImage &image2,
01746                 GradientType gt, int xf, int yf)
01747 {
01748   if (image1.width() == 0 || image1.height() == 0 ||
01749       image2.width() == 0 || image2.height() == 0)
01750     return image1;
01751 
01752   QImage image3;
01753 
01754   image3 = KImageEffect::unbalancedGradient(image1.size(),
01755                     QColor(0,0,0), QColor(255,255,255),
01756                     gt, xf, yf, 0);
01757 
01758   return blend(image1,image2,image3, Red); // Channel to use is arbitrary
01759 }
01760 
01761 // Blend image2 into image1, using an RBG channel of blendImage
01762 //
01763 QImage& KImageEffect::blend(QImage &image1, QImage &image2,
01764                 QImage &blendImage, RGBComponent channel)
01765 {
01766     if (image1.width() == 0 || image1.height() == 0 ||
01767         image2.width() == 0 || image2.height() == 0 ||
01768         blendImage.width() == 0 || blendImage.height() == 0) {
01769 #ifndef NDEBUG
01770         std::cerr << "KImageEffect::blend effect invalid image" << std::endl;
01771 #endif
01772       return image1;
01773     }
01774 
01775     int r, g, b;
01776     int ind1, ind2, ind3;
01777 
01778     unsigned int x1, x2, x3, y1, y2, y3;
01779     unsigned int a;
01780 
01781     register int x, y;
01782 
01783     // for image1 and image2, we only handle depth 32
01784     if (image1.depth()<32) image1 = image1.convertDepth(32);
01785     if (image2.depth()<32) image2 = image2.convertDepth(32);
01786 
01787     // for blendImage, we handle depth 8 and 32
01788     if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
01789 
01790     unsigned int *colorTable3 = (blendImage.depth()==8) ?
01791                  blendImage.colorTable():0;
01792 
01793     unsigned int *data1 =  (unsigned int *)image1.bits();
01794     unsigned int *data2 =  (unsigned int *)image2.bits();
01795     unsigned int *data3   =  (unsigned int *)blendImage.bits();
01796     unsigned char *data3b =  (unsigned char *)blendImage.bits();
01797     unsigned int color3;
01798 
01799     x1 = image1.width();     y1 = image1.height();
01800     x2 = image2.width();     y2 = image2.height();
01801     x3 = blendImage.width(); y3 = blendImage.height();
01802 
01803     for (y = 0; y < (int)y1; y++) {
01804     ind1 = x1*y;
01805     ind2 = x2*(y%y2);
01806     ind3 = x3*(y%y3);
01807 
01808     x=0;
01809     while(x < (int)x1) {
01810       color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
01811 
01812           a = (channel == Red) ? qRed(color3) :
01813               (channel == Green) ? qGreen(color3) :
01814           (channel == Blue) ? qBlue(color3) : qGray(color3);
01815 
01816       r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256;
01817       g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256;
01818       b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256;
01819 
01820       a = qAlpha(data1[ind1]);
01821       data1[ind1] = qRgba(r, g, b, a);
01822 
01823       ind1++; ind2++; ind3++; x++;
01824       if ( (x%x2) ==0) ind2 -= x2;
01825       if ( (x%x3) ==0) ind3 -= x3;
01826         }
01827     }
01828     return image1;
01829 }
01830 
01831 
01832 //======================================================================
01833 //
01834 // Hash effects
01835 //
01836 //======================================================================
01837 
01838 unsigned int KImageEffect::lHash(unsigned int c)
01839 {
01840     unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
01841     unsigned char nr, ng, nb;
01842     nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
01843     ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
01844     nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
01845 
01846     return qRgba(nr, ng, nb, a);
01847 }
01848 
01849 
01850 // -----------------------------------------------------------------------------
01851 
01852 unsigned int KImageEffect::uHash(unsigned int c)
01853 {
01854     unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
01855     unsigned char nr, ng, nb;
01856     nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
01857     ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
01858     nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
01859 
01860     return qRgba(nr, ng, nb, a);
01861 }
01862 
01863 
01864 // -----------------------------------------------------------------------------
01865 
01866 QImage& KImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing)
01867 {
01868     if (image.width() == 0 || image.height() == 0) {
01869 #ifndef NDEBUG
01870         std::cerr << "KImageEffect::hash effect invalid image" << std::endl;
01871 #endif
01872       return image;
01873     }
01874 
01875     register int x, y;
01876     unsigned int *data =  (unsigned int *)image.bits();
01877     unsigned int ind;
01878 
01879     //CT no need to do it if not enough space
01880     if ((lite == NorthLite ||
01881          lite == SouthLite)&&
01882         (unsigned)image.height() < 2+spacing) return image;
01883     if ((lite == EastLite ||
01884          lite == WestLite)&&
01885         (unsigned)image.height() < 2+spacing) return image;
01886 
01887     if (lite == NorthLite || lite == SouthLite) {
01888         for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
01889             for (x = 0; x < image.width(); x++) {
01890                 ind = x + image.width() * y;
01891                 data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
01892 
01893                 ind = ind + image.width();
01894                 data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
01895             }
01896         }
01897     }
01898 
01899     else if (lite == EastLite || lite == WestLite) {
01900         for (y = 0 ; y < image.height(); y++) {
01901             for (x = 0; x < image.width(); x = x + 2 + spacing) {
01902                 ind = x + image.width() * y;
01903                 data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
01904 
01905                 ind++;
01906                 data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
01907             }
01908         }
01909     }
01910 
01911     else if (lite == NWLite || lite == SELite) {
01912         for (y = 0 ; y < image.height(); y++) {
01913             for (x = 0;
01914                  x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
01915                  x = x + 2 + spacing) {
01916                 ind = x + image.width() * y + ((y & 1)? 1 : 0);
01917                 data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
01918 
01919                 ind++;
01920                 data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
01921             }
01922         }
01923     }
01924 
01925     else if (lite == SWLite || lite == NELite) {
01926         for (y = 0 ; y < image.height(); y++) {
01927             for (x = 0  + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
01928                 ind = x + image.width() * y - ((y & 1)? 1 : 0);
01929                 data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
01930 
01931                 ind++;
01932                 data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
01933             }
01934         }
01935     }
01936 
01937     return image;
01938 }
01939 
01940 
01941 //======================================================================
01942 //
01943 // Flatten effects
01944 //
01945 //======================================================================
01946 
01947 QImage& KImageEffect::flatten(QImage &img, const QColor &ca,
01948                             const QColor &cb, int ncols)
01949 {
01950     if (img.width() == 0 || img.height() == 0)
01951       return img;
01952 
01953     // a bitmap is easy...
01954     if (img.depth() == 1) {
01955     img.setColor(0, ca.rgb());
01956     img.setColor(1, cb.rgb());
01957     return img;
01958     }
01959 
01960     int r1 = ca.red(); int r2 = cb.red();
01961     int g1 = ca.green(); int g2 = cb.green();
01962     int b1 = ca.blue(); int b2 = cb.blue();
01963     int min = 0, max = 255;
01964 
01965     QRgb col;
01966 
01967     // Get minimum and maximum greylevel.
01968     if (img.numColors()) {
01969     // pseudocolor
01970     for (int i = 0; i < img.numColors(); i++) {
01971         col = img.color(i);
01972         int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
01973         min = QMIN(min, mean);
01974         max = QMAX(max, mean);
01975     }
01976     } else {
01977     // truecolor
01978     for (int y=0; y < img.height(); y++)
01979         for (int x=0; x < img.width(); x++) {
01980         col = img.pixel(x, y);
01981         int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
01982         min = QMIN(min, mean);
01983         max = QMAX(max, mean);
01984         }
01985     }
01986 
01987     // Conversion factors
01988     float sr = ((float) r2 - r1) / (max - min);
01989     float sg = ((float) g2 - g1) / (max - min);
01990     float sb = ((float) b2 - b1) / (max - min);
01991 
01992 
01993     // Repaint the image
01994     if (img.numColors()) {
01995     for (int i=0; i < img.numColors(); i++) {
01996         col = img.color(i);
01997         int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
01998         int r = (int) (sr * (mean - min) + r1 + 0.5);
01999         int g = (int) (sg * (mean - min) + g1 + 0.5);
02000         int b = (int) (sb * (mean - min) + b1 + 0.5);
02001         img.setColor(i, qRgba(r, g, b, qAlpha(col)));
02002     }
02003     } else {
02004     for (int y=0; y < img.height(); y++)
02005         for (int x=0; x < img.width(); x++) {
02006         col = img.pixel(x, y);
02007         int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
02008         int r = (int) (sr * (mean - min) + r1 + 0.5);
02009         int g = (int) (sg * (mean - min) + g1 + 0.5);
02010         int b = (int) (sb * (mean - min) + b1 + 0.5);
02011         img.setPixel(x, y, qRgba(r, g, b, qAlpha(col)));
02012         }
02013     }
02014 
02015 
02016     // Dither if necessary
02017     if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
02018     return img;
02019 
02020     if (ncols == 1) ncols++;
02021     if (ncols > 256) ncols = 256;
02022 
02023     QColor *pal = new QColor[ncols];
02024     sr = ((float) r2 - r1) / (ncols - 1);
02025     sg = ((float) g2 - g1) / (ncols - 1);
02026     sb = ((float) b2 - b1) / (ncols - 1);
02027 
02028     for (int i=0; i<ncols; i++)
02029     pal[i] = QColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
02030 
02031     dither(img, pal, ncols);
02032 
02033     delete[] pal;
02034     return img;
02035 }
02036 
02037 
02038 //======================================================================
02039 //
02040 // Fade effects
02041 //
02042 //======================================================================
02043 
02044 QImage& KImageEffect::fade(QImage &img, float val, const QColor &color)
02045 {
02046     if (img.width() == 0 || img.height() == 0)
02047       return img;
02048 
02049     // We don't handle bitmaps
02050     if (img.depth() == 1)
02051     return img;
02052 
02053     unsigned char tbl[256];
02054     for (int i=0; i<256; i++)
02055     tbl[i] = (int) (val * i + 0.5);
02056 
02057     int red = color.red();
02058     int green = color.green();
02059     int blue = color.blue();
02060 
02061     QRgb col;
02062     int r, g, b, cr, cg, cb;
02063 
02064     if (img.depth() <= 8) {
02065     // pseudo color
02066     for (int i=0; i<img.numColors(); i++) {
02067         col = img.color(i);
02068         cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
02069         if (cr > red)
02070         r = cr - tbl[cr - red];
02071         else
02072         r = cr + tbl[red - cr];
02073         if (cg > green)
02074         g = cg - tbl[cg - green];
02075         else
02076         g = cg + tbl[green - cg];
02077         if (cb > blue)
02078         b = cb - tbl[cb - blue];
02079         else
02080         b = cb + tbl[blue - cb];
02081         img.setColor(i, qRgba(r, g, b, qAlpha(col)));
02082     }
02083 
02084     } else {
02085     // truecolor
02086         for (int y=0; y<img.height(); y++) {
02087             QRgb *data = (QRgb *) img.scanLine(y);
02088             for (int x=0; x<img.width(); x++) {
02089                 col = *data;
02090                 cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
02091                 if (cr > red)
02092                     r = cr - tbl[cr - red];
02093                 else
02094                     r = cr + tbl[red - cr];
02095                 if (cg > green)
02096                     g = cg - tbl[cg - green];
02097                 else
02098                     g = cg + tbl[green - cg];
02099                 if (cb > blue)
02100                     b = cb - tbl[cb - blue];
02101                 else
02102                     b = cb + tbl[blue - cb];
02103                 *data++ = qRgba(r, g, b, qAlpha(col));
02104             }
02105         }
02106     }
02107 
02108     return img;
02109 }
02110 
02111 //======================================================================
02112 //
02113 // Color effects
02114 //
02115 //======================================================================
02116 
02117 // This code is adapted from code (C) Rik Hemsley <rik@kde.org>
02118 //
02119 // The formula used (r + b + g) /3 is different from the qGray formula
02120 // used by Qt.  This is because our formula is much much faster.  If,
02121 // however, it turns out that this is producing sub-optimal images,
02122 // then it will have to change (kurt)
02123 //
02124 // It does produce lower quality grayscale ;-) Use fast == true for the fast
02125 // algorithm, false for the higher quality one (mosfet).
02126 QImage& KImageEffect::toGray(QImage &img, bool fast)
02127 {
02128     if (img.width() == 0 || img.height() == 0)
02129       return img;
02130 
02131     if(fast){
02132         if (img.depth() == 32) {
02133             register uchar * r(img.bits());
02134             register uchar * g(img.bits() + 1);
02135             register uchar * b(img.bits() + 2);
02136 
02137             uchar * end(img.bits() + img.numBytes());
02138 
02139             while (r != end) {
02140 
02141                 *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
02142 
02143                 r += 4;
02144                 g += 4;
02145                 b += 4;
02146             }
02147         }
02148         else
02149         {
02150             for (int i = 0; i < img.numColors(); i++)
02151             {
02152                 register uint r = qRed(img.color(i));
02153                 register uint g = qGreen(img.color(i));
02154                 register uint b = qBlue(img.color(i));
02155 
02156                 register uint gray = (((r + g) >> 1) + b) >> 1;
02157                 img.setColor(i, qRgba(gray, gray, gray, qAlpha(img.color(i))));
02158             }
02159         }
02160     }
02161     else{
02162         int pixels = img.depth() > 8 ? img.width()*img.height() :
02163             img.numColors();
02164         unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
02165             (unsigned int *)img.colorTable();
02166         int val, i;
02167         for(i=0; i < pixels; ++i){
02168             val = qGray(data[i]);
02169             data[i] = qRgba(val, val, val, qAlpha(data[i]));
02170         }
02171     }
02172     return img;
02173 }
02174 
02175 // CT 29Jan2000 - desaturation algorithms
02176 QImage& KImageEffect::desaturate(QImage &img, float desat)
02177 {
02178     if (img.width() == 0 || img.height() == 0)
02179       return img;
02180 
02181     if (desat < 0) desat = 0.;
02182     if (desat > 1) desat = 1.;
02183     int pixels = img.depth() > 8 ? img.width()*img.height() :
02184         img.numColors();
02185     unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
02186         (unsigned int *)img.colorTable();
02187     int h, s, v, i;
02188     QColor clr; // keep constructor out of loop (mosfet)
02189     for(i=0; i < pixels; ++i){
02190         clr.setRgb(data[i]);
02191     clr.hsv(&h, &s, &v);
02192     clr.setHsv(h, (int)(s * (1. - desat)), v);
02193     data[i] = clr.rgb();
02194     }
02195     return img;
02196 }
02197 
02198 // Contrast stuff (mosfet)
02199 QImage& KImageEffect::contrast(QImage &img, int c)
02200 {
02201     if (img.width() == 0 || img.height() == 0)
02202       return img;
02203 
02204     if(c > 255)
02205         c = 255;
02206     if(c < -255)
02207         c =  -255;
02208     int pixels = img.depth() > 8 ? img.width()*img.height() :
02209         img.numColors();
02210     unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
02211         (unsigned int *)img.colorTable();
02212     int i, r, g, b;
02213     for(i=0; i < pixels; ++i){
02214         r = qRed(data[i]);
02215         g = qGreen(data[i]);
02216         b = qBlue(data[i]);
02217         if(qGray(data[i]) <= 127){
02218             if(r - c > 0)
02219                 r -= c;
02220             else
02221                 r = 0;
02222             if(g - c > 0)
02223                 g -= c;
02224             else
02225                 g = 0;
02226             if(b - c > 0)
02227                 b -= c;
02228             else
02229                 b = 0;
02230         }
02231         else{
02232             if(r + c <= 255)
02233                 r += c;
02234             else
02235                 r = 255;
02236             if(g + c <= 255)
02237                 g += c;
02238             else
02239                 g = 255;
02240             if(b + c <= 255)
02241                 b += c;
02242             else
02243                 b = 255;
02244         }
02245         data[i] = qRgba(r, g, b, qAlpha(data[i]));
02246     }
02247     return(img);
02248 }
02249 
02250 //======================================================================
02251 //
02252 // Dithering effects
02253 //
02254 //======================================================================
02255 
02256 // adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
02257 //
02258 // Floyd-Steinberg dithering
02259 // Ref: Bitmapped Graphics Programming in C++
02260 //      Marv Luse, Addison-Wesley Publishing, 1993.
02261 QImage& KImageEffect::dither(QImage &img, const QColor *palette, int size)
02262 {
02263     if (img.width() == 0 || img.height() == 0 ||
02264         palette == 0 || img.depth() <= 8)
02265       return img;
02266 
02267     QImage dImage( img.width(), img.height(), 8, size );
02268     int i;
02269 
02270     dImage.setNumColors( size );
02271     for ( i = 0; i < size; i++ )
02272         dImage.setColor( i, palette[ i ].rgb() );
02273 
02274     int *rerr1 = new int [ img.width() * 2 ];
02275     int *gerr1 = new int [ img.width() * 2 ];
02276     int *berr1 = new int [ img.width() * 2 ];
02277 
02278     memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
02279     memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
02280     memset( berr1, 0, sizeof( int ) * img.width() * 2 );
02281 
02282     int *rerr2 = rerr1 + img.width();
02283     int *gerr2 = gerr1 + img.width();
02284     int *berr2 = berr1 + img.width();
02285 
02286     for ( int j = 0; j < img.height(); j++ )
02287     {
02288         uint *ip = (uint * )img.scanLine( j );
02289         uchar *dp = dImage.scanLine( j );
02290 
02291         for ( i = 0; i < img.width(); i++ )
02292         {
02293             rerr1[i] = rerr2[i] + qRed( *ip );
02294             rerr2[i] = 0;
02295             gerr1[i] = gerr2[i] + qGreen( *ip );
02296             gerr2[i] = 0;
02297             berr1[i] = berr2[i] + qBlue( *ip );
02298             berr2[i] = 0;
02299             ip++;
02300         }
02301 
02302         *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
02303 
02304         for ( i = 1; i < img.width()-1; i++ )
02305         {
02306             int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
02307             *dp = indx;
02308 
02309             int rerr = rerr1[i];
02310             rerr -= palette[indx].red();
02311             int gerr = gerr1[i];
02312             gerr -= palette[indx].green();
02313             int berr = berr1[i];
02314             berr -= palette[indx].blue();
02315 
02316             // diffuse red error
02317             rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
02318             rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
02319             rerr2[  i  ] += ( rerr * 5 ) >> 4;
02320             rerr2[ i+1 ] += ( rerr ) >> 4;
02321 
02322             // diffuse green error
02323             gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
02324             gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
02325             gerr2[  i  ] += ( gerr * 5 ) >> 4;
02326             gerr2[ i+1 ] += ( gerr ) >> 4;
02327 
02328             // diffuse red error
02329             berr1[ i+1 ] += ( berr * 7 ) >> 4;
02330             berr2[ i-1 ] += ( berr * 3 ) >> 4;
02331             berr2[  i  ] += ( berr * 5 ) >> 4;
02332             berr2[ i+1 ] += ( berr ) >> 4;
02333 
02334             dp++;
02335         }
02336 
02337         *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
02338     }
02339 
02340     delete [] rerr1;
02341     delete [] gerr1;
02342     delete [] berr1;
02343 
02344     img = dImage;
02345     return img;
02346 }
02347 
02348 int KImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size )
02349 {
02350     if (palette == 0)
02351       return 0;
02352 
02353     int dr = palette[0].red() - r;
02354     int dg = palette[0].green() - g;
02355     int db = palette[0].blue() - b;
02356 
02357     int minDist =  dr*dr + dg*dg + db*db;
02358     int nearest = 0;
02359 
02360     for (int i = 1; i < size; i++ )
02361     {
02362         dr = palette[i].red() - r;
02363         dg = palette[i].green() - g;
02364         db = palette[i].blue() - b;
02365 
02366         int dist = dr*dr + dg*dg + db*db;
02367 
02368         if ( dist < minDist )
02369         {
02370             minDist = dist;
02371             nearest = i;
02372         }
02373     }
02374 
02375     return nearest;
02376 }
02377 
02378 bool KImageEffect::blend(
02379     const QImage & upper,
02380     const QImage & lower,
02381     QImage & output
02382 )
02383 {
02384   if (
02385       upper.width()  > lower.width()  ||
02386       upper.height() > lower.height() ||
02387       upper.depth() != 32             ||
02388       lower.depth() != 32
02389   )
02390   {
02391 #ifndef NDEBUG
02392     std::cerr << "KImageEffect::blend : Sizes not correct\n" ;
02393 #endif
02394     return false;
02395   }
02396 
02397   output = lower.copy();
02398 
02399   register uchar *i, *o;
02400   register int a;
02401   register int col;
02402   register int w = upper.width();
02403   int row(upper.height() - 1);
02404 
02405   do {
02406 
02407     i = upper.scanLine(row);
02408     o = output.scanLine(row);
02409 
02410     col = w << 2;
02411     --col;
02412 
02413     do {
02414 
02415       while (!(a = i[col]) && (col != 3)) {
02416         --col; --col; --col; --col;
02417       }
02418 
02419       --col;
02420       o[col] += ((i[col] - o[col]) * a) >> 8;
02421 
02422       --col;
02423       o[col] += ((i[col] - o[col]) * a) >> 8;
02424 
02425       --col;
02426       o[col] += ((i[col] - o[col]) * a) >> 8;
02427 
02428     } while (col--);
02429 
02430   } while (row--);
02431 
02432   return true;
02433 }
02434 
02435 #if 0
02436 // Not yet...
02437 bool KImageEffect::blend(
02438     const QImage & upper,
02439     const QImage & lower,
02440     QImage & output,
02441     const QRect & destRect
02442 )
02443 {
02444   output = lower.copy();
02445   return output;
02446 }
02447 
02448 #endif
02449 
02450 bool KImageEffect::blend(
02451     int &x, int &y,
02452     const QImage & upper,
02453     const QImage & lower,
02454     QImage & output
02455 )
02456 {
02457   int cx=0, cy=0, cw=upper.width(), ch=upper.height();
02458 
02459   if ( upper.width() + x > lower.width()  ||
02460       upper.height() + y > lower.height() ||
02461       x < 0 || y < 0 ||
02462       upper.depth() != 32 || lower.depth() != 32 )
02463   {
02464     if ( x > lower.width() || y > lower.height() ) return false;
02465     if ( upper.width()<=0 || upper.height() <= 0 ) return false;
02466     if ( lower.width()<=0 || lower.height() <= 0 ) return false;
02467 
02468     if (x<0) {cx=-x; cw+=x; x=0; };
02469     if (cw + x > lower.width()) { cw=lower.width()-x; };
02470     if (y<0) {cy=-y; ch+=y; y=0; };
02471     if (ch + y > lower.height()) { ch=lower.height()-y; };
02472 
02473     if ( cx >= upper.width() || cy >= upper.height() ) return true;
02474     if ( cw <= 0 || ch <= 0 ) return true;
02475   }
02476 
02477   output.create(cw,ch,32);
02478 //  output.setAlphaBuffer(true); // I should do some benchmarks to see if
02479     // this is worth the effort
02480 
02481   register QRgb *i, *o, *b;
02482 
02483   register int a;
02484   register int j,k;
02485   for (j=0; j<ch; j++)
02486   {
02487     b=reinterpret_cast<QRgb *>(&lower.scanLine(y+j) [ (x+cw) << 2 ]);
02488     i=reinterpret_cast<QRgb *>(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]);
02489     o=reinterpret_cast<QRgb *>(&output.scanLine(j)  [ cw << 2 ]);
02490 
02491     k=cw-1;
02492     --b; --i; --o;
02493     do
02494     {
02495       while ( !(a=qAlpha(*i)) && k>0 )
02496       {
02497         i--;
02498 //  *o=0;
02499     *o=*b;
02500     --o; --b;
02501     k--;
02502       };
02503 //      *o=0xFF;
02504       *o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8),
02505                 qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8),
02506                 qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8));
02507       --i; --o; --b;
02508     } while (k--);
02509   }
02510 
02511   return true;
02512 }
02513 
02514 bool KImageEffect::blendOnLower(
02515     int x, int y,
02516     const QImage & upper,
02517     const QImage & lower
02518 )
02519 {
02520   int cx=0, cy=0, cw=upper.width(), ch=upper.height();
02521 
02522   if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
02523   if ( x + cw > lower.width()  ||
02524       y + ch > lower.height() ||
02525       x < 0 || y < 0 )
02526   {
02527     if ( x > lower.width() || y > lower.height() ) return true;
02528     if ( upper.width()<=0 || upper.height() <= 0 ) return true;
02529     if ( lower.width()<=0 || lower.height() <= 0 ) return true;
02530 
02531     if (x<0) {cx=-x; cw+=x; x=0; };
02532     if (cw + x > lower.width()) { cw=lower.width()-x; };
02533     if (y<0) {cy=-y; ch+=y; y=0; };
02534     if (ch + y > lower.height()) { ch=lower.height()-y; };
02535 
02536     if ( cx >= upper.width() || cy >= upper.height() ) return true;
02537     if ( cw <= 0 || ch <= 0 ) return true;
02538   }
02539 
02540   register uchar *i, *b;
02541   register int a;
02542   register int k;
02543 
02544   for (int j=0; j<ch; j++)
02545   {
02546     b=&lower.scanLine(y+j) [ (x+cw) << 2 ];
02547     i=&upper.scanLine(cy+j)[ (cx+cw) << 2 ];
02548 
02549     k=cw-1;
02550     --b; --i;
02551     do
02552     {
02553 #ifndef WORDS_BIGENDIAN
02554       while ( !(a=*i) && k>0 )
02555 #else
02556       while ( !(a=*(i-3)) && k>0 )
02557 #endif
02558       {
02559         i-=4; b-=4; k--;
02560       };
02561 
02562 #ifndef WORDS_BIGENDIAN
02563       --i; --b;
02564       *b += ( ((*i - *b) * a) >> 8 );
02565       --i; --b;
02566       *b += ( ((*i - *b) * a) >> 8 );
02567       --i; --b;
02568       *b += ( ((*i - *b) * a) >> 8 );
02569       --i; --b;
02570 #else
02571       *b += ( ((*i - *b) * a) >> 8 );
02572       --i; --b;
02573       *b += ( ((*i - *b) * a) >> 8 );
02574       --i; --b;
02575       *b += ( ((*i - *b) * a) >> 8 );
02576       i -= 2; b -= 2;
02577 #endif
02578     } while (k--);
02579   }
02580 
02581   return true;
02582 }
02583 
02584 void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset,
02585                                 QImage &lower, const QRect &lowerRect)
02586 {
02587     // clip rect
02588     QRect lr =  lowerRect & lower.rect();
02589     lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) );
02590     lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) );
02591     if ( !lr.isValid() ) return;
02592 
02593     // blend
02594     for (int y = 0; y < lr.height(); y++) {
02595         for (int x = 0; x < lr.width(); x++) {
02596             QRgb *b = reinterpret_cast<QRgb*>(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb));
02597             QRgb *d = reinterpret_cast<QRgb*>(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb));
02598             int a = qAlpha(*d);
02599             *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8),
02600                       qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8),
02601                       qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8));
02602         }
02603     }
02604 }
02605 
02606 void KImageEffect::blendOnLower(const QImage &upper, const QPoint &upperOffset,
02607                           QImage &lower, const QRect &lowerRect, float opacity)
02608 {
02609     // clip rect
02610     QRect lr =  lowerRect & lower.rect();
02611     lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) );
02612     lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) );
02613     if ( !lr.isValid() ) return;
02614 
02615     // blend
02616     for (int y = 0; y < lr.height(); y++) {
02617         for (int x = 0; x < lr.width(); x++) {
02618             QRgb *b = reinterpret_cast<QRgb*>(lower.scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb));
02619             QRgb *d = reinterpret_cast<QRgb*>(upper.scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb));
02620             int a = qRound(opacity * qAlpha(*d));
02621             *b = qRgb(qRed(*b) - (((qRed(*b) - qRed(*d)) * a) >> 8),
02622                       qGreen(*b) - (((qGreen(*b) - qGreen(*d)) * a) >> 8),
02623                       qBlue(*b) - (((qBlue(*b) - qBlue(*d)) * a) >> 8));
02624         }
02625     }
02626 }
02627 
02628 QRect KImageEffect::computeDestinationRect(const QSize &lowerSize,
02629                                        Disposition disposition, QImage &upper)
02630 {
02631     int w = lowerSize.width();
02632     int h = lowerSize.height();
02633     int ww = upper.width();
02634     int wh = upper.height();
02635     QRect d;
02636 
02637     switch (disposition) {
02638     case NoImage:
02639         break;
02640     case Centered:
02641         d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
02642         break;
02643     case Tiled:
02644         d.setRect(0, 0, w, h);
02645         break;
02646     case CenterTiled:
02647         d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh,
02648                     w-1, h-1);
02649         break;
02650     case Scaled:
02651         upper = upper.smoothScale(w, h);
02652         d.setRect(0, 0, w, h);
02653         break;
02654     case CenteredAutoFit:
02655         if( ww <= w && wh <= h ) {
02656             d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered
02657             break;
02658         }
02659         // fall through
02660     case CenteredMaxpect: {
02661         double sx = (double) w / ww;
02662         double sy = (double) h / wh;
02663         if (sx > sy) {
02664             ww = (int)(sy * ww);
02665             wh = h;
02666         } else {
02667             wh = (int)(sx * wh);
02668             ww = w;
02669         }
02670         upper = upper.smoothScale(ww, wh);
02671         d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh);
02672         break;
02673     }
02674     case TiledMaxpect: {
02675         double sx = (double) w / ww;
02676         double sy = (double) h / wh;
02677         if (sx > sy) {
02678             ww = (int)(sy * ww);
02679             wh = h;
02680         } else {
02681             wh = (int)(sx * wh);
02682             ww = w;
02683         }
02684         upper = upper.smoothScale(ww, wh);
02685         d.setRect(0, 0, w, h);
02686         break;
02687     }
02688     }
02689 
02690     return d;
02691 }
02692 
02693 void KImageEffect::blendOnLower(QImage &upper, QImage &lower,
02694                                 Disposition disposition, float opacity)
02695 {
02696     QRect r = computeDestinationRect(lower.size(), disposition, upper);
02697     for (int y = r.top(); y<r.bottom(); y += upper.height())
02698         for (int x = r.left(); x<r.right(); x += upper.width())
02699             blendOnLower(upper, QPoint(-QMIN(x, 0), -QMIN(y, 0)),
02700                    lower, QRect(x, y, upper.width(), upper.height()), opacity);
02701 }
02702 
02703 
02704 // For selected icons
02705 QImage& KImageEffect::selectedImage( QImage &img, const QColor &col )
02706 {
02707     return blend( col, img, 0.5);
02708 }
02709 
02710 //
02711 // ===================================================================
02712 // Effects originally ported from ImageMagick for PixiePlus, plus a few
02713 // new ones. (mosfet 05/26/2003)
02714 // ===================================================================
02715 //
02716 /*
02717  Portions of this software are based on ImageMagick. Such portions are clearly
02718 marked as being ported from ImageMagick. ImageMagick is copyrighted under the
02719 following conditions:
02720 
02721 Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to
02722 making software imaging solutions freely available.
02723 
02724 Permission is hereby granted, free of charge, to any person obtaining a copy
02725 of this software and associated documentation files ("ImageMagick"), to deal
02726 in ImageMagick without restriction, including without limitation the rights
02727 to use, copy, modify, merge, publish, distribute, sublicense,  and/or sell
02728 copies of ImageMagick, and to permit persons to whom the ImageMagick is
02729 furnished to do so, subject to the following conditions:
02730 
02731 The above copyright notice and this permission notice shall be included in all
02732 copies or substantial portions of ImageMagick.
02733 
02734 The software is provided "as is", without warranty of any kind, express or
02735 implied, including but not limited to the warranties of merchantability,
02736 fitness for a particular purpose and noninfringement.  In no event shall
02737 ImageMagick Studio be liable for any claim, damages or other liability,
02738 whether in an action of contract, tort or otherwise, arising from, out of or
02739 in connection with ImageMagick or the use or other dealings in ImageMagick.
02740 
02741 Except as contained in this notice, the name of the ImageMagick Studio shall
02742 not be used in advertising or otherwise to promote the sale, use or other
02743 dealings in ImageMagick without prior written authorization from the
02744 ImageMagick Studio.
02745 */
02746 
02747 QImage KImageEffect::sample(QImage &src, int w, int h)
02748 {
02749     if(w == src.width() && h == src.height())
02750         return(src);
02751 
02752     int depth = src.depth();
02753     QImage dest(w, h, depth, depth <= 8 ? src.numColors() : 0,
02754     depth == 1 ? QImage::LittleEndian : QImage::IgnoreEndian);
02755     int *x_offset = (int *)malloc(w*sizeof(int));
02756     int *y_offset = (int *)malloc(h*sizeof(int));
02757     if(!x_offset || !y_offset){
02758 #ifndef NDEBUG
02759         qWarning("KImageEffect::sample(): Unable to allocate pixel buffer");
02760 #endif
02761         free(x_offset);
02762         free(y_offset);
02763         return(src);
02764     }
02765 
02766     // init pixel offsets
02767     for(int x=0; x < w; ++x)
02768         x_offset[x] = (int)(x*src.width()/((double)w));
02769     for(int y=0; y < h; ++y)
02770         y_offset[y] = (int)(y*src.height()/((double)h));
02771 
02772     if(depth > 8){ // DirectClass source image
02773         for(int y=0; y < h; ++y){
02774             unsigned int *destData = (unsigned int *)dest.scanLine(y);
02775             unsigned int *srcData = (unsigned int *)src.scanLine(y_offset[y]);
02776             for(int x=0; x < w; ++x)
02777                 destData[x] = srcData[x_offset[x]];
02778         }
02779     }
02780     else if(depth == 1) {
02781         int r = src.bitOrder() == QImage::LittleEndian;
02782         memcpy(dest.colorTable(), src.colorTable(), src.numColors()*sizeof(QRgb));
02783         for(int y=0; y < h; ++y){
02784             unsigned char *destData = dest.scanLine(y);
02785             unsigned char *srcData = src.scanLine(y_offset[y]);
02786             for(int x=0; x < w; ++x){
02787                 int k = x_offset[x];
02788                 int l = r ? (k & 7) : (7 - (k&7));
02789                 if(srcData[k >> 3] & (1 << l))
02790                     destData[x >> 3] |= 1 << (x & 7);
02791                 else
02792                     destData[x >> 3] &= ~(1 << (x & 7));
02793             }
02794         }
02795     }
02796     else{ // PseudoClass source image
02797         memcpy(dest.colorTable(), src.colorTable(), src.numColors()*sizeof(QRgb));
02798         for(int y=0; y < h; ++y){
02799             unsigned char *destData = dest.scanLine(y);
02800             unsigned char *srcData = src.scanLine(y_offset[y]);
02801             for(int x=0; x < w; ++x)
02802                 destData[x] = srcData[x_offset[x]];
02803         }
02804     }
02805     free(x_offset);
02806     free(y_offset);
02807     return(dest);
02808 }
02809 
02810 void KImageEffect::threshold(QImage &img, unsigned int threshold)
02811 {
02812     int i, count;
02813     unsigned int *data;
02814     if(img.depth() > 8){ // DirectClass
02815         count = img.width()*img.height();
02816         data = (unsigned int *)img.bits();
02817     }
02818     else{ // PsudeoClass
02819         count = img.numColors();
02820         data = (unsigned int *)img.colorTable();
02821     }
02822     for(i=0; i < count; ++i)
02823         data[i] = intensityValue(data[i]) < threshold ? Qt::black.rgb() : Qt::white.rgb();
02824 }
02825 
02826 void KImageEffect::hull(const int x_offset, const int y_offset,
02827                         const int polarity, const int columns,
02828                         const int rows,
02829                         unsigned int *f, unsigned int *g)
02830 {
02831     int x, y;
02832 
02833     unsigned int *p, *q, *r, *s;
02834     unsigned int v;
02835     if(f == NULL || g == NULL)
02836         return;
02837     p=f+(columns+2);
02838     q=g+(columns+2);
02839     r=p+(y_offset*(columns+2)+x_offset);
02840     for (y=0; y < rows; y++){
02841         p++;
02842         q++;
02843         r++;
02844         if(polarity > 0)
02845             for (x=0; x < columns; x++){
02846                 v=(*p);
02847                 if (*r > v)
02848                     v++;
02849                 *q=v;
02850                 p++;
02851                 q++;
02852                 r++;
02853             }
02854         else
02855             for(x=0; x < columns; x++){
02856                 v=(*p);
02857                 if (v > (unsigned int) (*r+1))
02858                     v--;
02859                 *q=v;
02860                 p++;
02861                 q++;
02862                 r++;
02863             }
02864         p++;
02865         q++;
02866         r++;
02867     }
02868     p=f+(columns+2);
02869     q=g+(columns+2);
02870     r=q+(y_offset*(columns+2)+x_offset);
02871     s=q-(y_offset*(columns+2)+x_offset);
02872     for(y=0; y < rows; y++){
02873         p++;
02874         q++;
02875         r++;
02876         s++;
02877         if(polarity > 0)
02878             for(x=0; x < (int) columns; x++){
02879                 v=(*q);
02880                 if (((unsigned int) (*s+1) > v) && (*r > v))
02881                     v++;
02882                 *p=v;
02883                 p++;
02884                 q++;
02885                 r++;
02886                 s++;
02887             }
02888         else
02889             for (x=0; x < columns; x++){
02890                 v=(*q);
02891                 if (((unsigned int) (*s+1) < v) && (*r < v))
02892                     v--;
02893                 *p=v;
02894                 p++;
02895                 q++;
02896                 r++;
02897                 s++;
02898             }
02899         p++;
02900         q++;
02901         r++;
02902         s++;
02903     }
02904 }
02905 
02906 QImage KImageEffect::despeckle(QImage &src)
02907 {
02908     int i, j, x, y;
02909     unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
02910         *alpha_channel;
02911     int packets;
02912     static const int
02913     X[4]= {0, 1, 1,-1},
02914     Y[4]= {1, 0, 1, 1};
02915 
02916     unsigned int *destData;
02917     QImage dest(src.width(), src.height(), 32);
02918 
02919     packets = (src.width()+2)*(src.height()+2);
02920     red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02921     green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02922     blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02923     alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
02924     buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
02925     if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
02926        !buffer){
02927         free(red_channel);
02928         free(green_channel);
02929         free(blue_channel);
02930         free(alpha_channel);
02931         free(buffer);
02932         return(src);
02933     }
02934 
02935     // copy image pixels to color component buffers
02936     j = src.width()+2;
02937     if(src.depth() > 8){ // DirectClass source image
02938         unsigned int *srcData;
02939         for(y=0; y < src.height(); ++y){
02940             srcData = (unsigned int *)src.scanLine(y);
02941             ++j;
02942             for(x=0; x < src.width(); ++x){
02943                 red_channel[j] = qRed(srcData[x]);
02944                 green_channel[j] = qGreen(srcData[x]);
02945                 blue_channel[j] = qBlue(srcData[x]);
02946                 alpha_channel[j] = qAlpha(srcData[x]);
02947                 ++j;
02948             }
02949             ++j;
02950         }
02951     }
02952     else{ // PsudeoClass source image
02953         unsigned char *srcData;
02954         unsigned int *cTable = src.colorTable();
02955         unsigned int pixel;
02956         for(y=0; y < src.height(); ++y){
02957             srcData = (unsigned char *)src.scanLine(y);
02958             ++j;
02959             for(x=0; x < src.width(); ++x){
02960                 pixel = *(cTable+srcData[x]);
02961                 red_channel[j] = qRed(pixel);
02962                 green_channel[j] = qGreen(pixel);
02963                 blue_channel[j] = qBlue(pixel);
02964                 alpha_channel[j] = qAlpha(pixel);
02965                 ++j;
02966             }
02967             ++j;
02968         }
02969     }
02970     // reduce speckle in red channel
02971     for(i=0; i < 4; i++){
02972         hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
02973         hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
02974         hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
02975         hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
02976     }
02977     // reduce speckle in green channel
02978     for (i=0; i < packets; i++)
02979         buffer[i]=0;
02980     for (i=0; i < 4; i++){
02981         hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
02982         hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
02983         hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
02984         hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
02985     }
02986     // reduce speckle in blue channel
02987     for (i=0; i < packets; i++)
02988         buffer[i]=0;
02989     for (i=0; i < 4; i++){
02990         hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
02991         hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
02992         hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
02993         hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
02994     }
02995     // copy color component buffers to despeckled image
02996     j = dest.width()+2;
02997     for(y=0; y < dest.height(); ++y)
02998     {
02999         destData = (unsigned int *)dest.scanLine(y);
03000         ++j;
03001         for (x=0; x < dest.width(); ++x)
03002         {
03003             destData[x] = qRgba(red_channel[j], green_channel[j],
03004                                 blue_channel[j], alpha_channel[j]);
03005             ++j;
03006         }
03007         ++j;
03008     }
03009     free(buffer);
03010     free(red_channel);
03011     free(green_channel);
03012     free(blue_channel);
03013     free(alpha_channel);
03014     return(dest);
03015 }
03016 
03017 unsigned int KImageEffect::generateNoise(unsigned int pixel,
03018                                          NoiseType noise_type)
03019 {
03020 #define NoiseEpsilon  1.0e-5
03021 #define NoiseMask  0x7fff
03022 #define SigmaUniform  4.0
03023 #define SigmaGaussian  4.0
03024 #define SigmaImpulse  0.10
03025 #define SigmaLaplacian 10.0
03026 #define SigmaMultiplicativeGaussian  0.5
03027 #define SigmaPoisson  0.05
03028 #define TauGaussian  20.0
03029 
03030     double alpha, beta, sigma, value;
03031     alpha=(double) (rand() & NoiseMask)/NoiseMask;
03032     if (alpha == 0.0)
03033         alpha=1.0;
03034     switch(noise_type){
03035     case UniformNoise:
03036     default:
03037         {
03038             value=(double) pixel+SigmaUniform*(alpha-0.5);
03039             break;
03040         }
03041     case GaussianNoise:
03042         {
03043             double tau;
03044 
03045             beta=(double) (rand() & NoiseMask)/NoiseMask;
03046             sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
03047             tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
03048             value=(double) pixel+
03049                 (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
03050             break;
03051         }
03052     case MultiplicativeGaussianNoise:
03053         {
03054             if (alpha <= NoiseEpsilon)
03055                 sigma=MaxRGB;
03056             else
03057                 sigma=sqrt(-2.0*log(alpha));
03058             beta=(rand() & NoiseMask)/NoiseMask;
03059             value=(double) pixel+
03060                 pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
03061             break;
03062         }
03063     case ImpulseNoise:
03064         {
03065             if (alpha < (SigmaImpulse/2.0))
03066                 value=0;
03067             else
03068                 if (alpha >= (1.0-(SigmaImpulse/2.0)))
03069                     value=MaxRGB;
03070                 else
03071                     value=pixel;
03072             break;
03073         }
03074     case LaplacianNoise:
03075         {
03076             if (alpha <= 0.5)
03077             {
03078                 if (alpha <= NoiseEpsilon)
03079                     value=(double) pixel-MaxRGB;
03080                 else
03081                     value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
03082                 break;
03083             }
03084             beta=1.0-alpha;
03085             if (beta <= (0.5*NoiseEpsilon))
03086                 value=(double) pixel+MaxRGB;
03087             else
03088                 value=(double) pixel-SigmaLaplacian*log(2.0*beta);
03089             break;
03090         }
03091     case PoissonNoise:
03092         {
03093             register int
03094                 i;
03095 
03096             for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
03097             {
03098                 beta=(double) (rand() & NoiseMask)/NoiseMask;
03099                 alpha=alpha*beta;
03100             }
03101             value=i/SigmaPoisson;
03102             break;
03103         }
03104     }
03105     if(value < 0.0)
03106         return(0);
03107     if(value > MaxRGB)
03108         return(MaxRGB);
03109     return((unsigned int) (value+0.5));
03110 }
03111 
03112 QImage KImageEffect::addNoise(QImage &src, NoiseType noise_type)
03113 {
03114     int x, y;
03115     QImage dest(src.width(), src.height(), 32);
03116     unsigned int *destData;
03117 
03118     if(src.depth() > 8){ // DirectClass source image
03119         unsigned int *srcData;
03120         for(y=0; y < src.height(); ++y){
03121             srcData = (unsigned int *)src.scanLine(y);
03122             destData = (unsigned int *)dest.scanLine(y);
03123             for(x=0; x < src.width(); ++x){
03124                 destData[x] = qRgba(generateNoise(qRed(srcData[x]), noise_type),
03125                                     generateNoise(qGreen(srcData[x]), noise_type),
03126                                     generateNoise(qBlue(srcData[x]), noise_type),
03127                                     qAlpha(srcData[x]));
03128             }
03129         }
03130     }
03131     else{ // PsudeoClass source image
03132         unsigned char *srcData;
03133         unsigned int *cTable = src.colorTable();
03134         unsigned int pixel;
03135         for(y=0; y < src.height(); ++y){
03136             srcData = (unsigned char *)src.scanLine(y);
03137             destData = (unsigned int *)dest.scanLine(y);
03138             for(x=0; x < src.width(); ++x){
03139                 pixel = *(cTable+srcData[x]);
03140                 destData[x] = qRgba(generateNoise(qRed(pixel), noise_type),
03141                                     generateNoise(qGreen(pixel), noise_type),
03142                                     generateNoise(qBlue(pixel), noise_type),
03143                                     qAlpha(pixel));
03144             }
03145         }
03146 
03147     }
03148     return(dest);
03149 }
03150 
03151 unsigned int KImageEffect::interpolateColor(QImage *image, double x_offset,
03152                                             double y_offset,
03153                                             unsigned int background)
03154 {
03155     double alpha, beta;
03156     unsigned int p, q, r, s;
03157     int x, y;
03158 
03159     x = (int)x_offset;
03160     y = (int)y_offset;
03161     if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
03162         return(background);
03163     if(image->depth() > 8){
03164         if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1)))    {
03165             unsigned int *t = (unsigned int *)image->scanLine(y);
03166             p = t[x];
03167             q = t[x+1];
03168             r = t[x+image->width()];
03169             s = t[x+image->width()+1];
03170         }
03171         else{
03172             unsigned int *t = (unsigned int *)image->scanLine(y);
03173             p = background;
03174             if((x >= 0) && (y >= 0)){
03175                 p = t[x];
03176             }
03177             q = background;
03178             if(((x+1) < image->width()) && (y >= 0)){
03179                 q = t[x+1];
03180             }
03181             r = background;
03182             if((x >= 0) && ((y+1) < image->height())){
03183                 t = (unsigned int *)image->scanLine(y+1);
03184                 r = t[x+image->width()];
03185             }
03186             s = background;
03187             if(((x+1) < image->width()) && ((y+1) < image->height())){
03188                 t = (unsigned int *)image->scanLine(y+1);
03189                 s = t[x+image->width()+1];
03190             }
03191 
03192         }
03193     }
03194     else{
03195         unsigned int *colorTable = (unsigned int *)image->colorTable();
03196         if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1)))    {
03197             unsigned char *t;
03198             t = (unsigned char *)image->scanLine(y);
03199             p = *(colorTable+t[x]);
03200             q = *(colorTable+t[x+1]);
03201             t = (unsigned char *)image->scanLine(y+1);
03202             r = *(colorTable+t[x]);
03203             s = *(colorTable+t[x+1]);
03204         }
03205         else{
03206             unsigned char *t;
03207             p = background;
03208             if((x >= 0) && (y >= 0)){
03209                 t = (unsigned char *)image->scanLine(y);
03210                 p = *(colorTable+t[x]);
03211             }
03212             q = background;
03213             if(((x+1) < image->width()) && (y >= 0)){
03214                 t = (unsigned char *)image->scanLine(y);
03215                 q = *(colorTable+t[x+1]);
03216             }
03217             r = background;
03218             if((x >= 0) && ((y+1) < image->height())){
03219                 t = (unsigned char *)image->scanLine(y+1);
03220                 r = *(colorTable+t[x]);
03221             }
03222             s = background;
03223             if(((x+1) < image->width()) && ((y+1) < image->height())){
03224                 t = (unsigned char *)image->scanLine(y+1);
03225                 s = *(colorTable+t[x+1]);
03226             }
03227 
03228         }
03229 
03230     }
03231     x_offset -= floor(x_offset);
03232     y_offset -= floor(y_offset);
03233     alpha = 1.0-x_offset;
03234     beta = 1.0-y_offset;
03235 
03236     return(qRgba((unsigned char)(beta*(alpha*qRed(p)+x_offset*qRed(q))+y_offset*(alpha*qRed(r)+x_offset*qRed(s))),
03237                  (unsigned char)(beta*(alpha*qGreen(p)+x_offset*qGreen(q))+y_offset*(alpha*qGreen(r)+x_offset*qGreen(s))),
03238                  (unsigned char)(beta*(alpha*qBlue(p)+x_offset*qBlue(q))+y_offset*(alpha*qBlue(r)+x_offset*qBlue(s))),
03239                  (unsigned char)(beta*(alpha*qAlpha(p)+x_offset*qAlpha(q))+y_offset*(alpha*qAlpha(r)+x_offset*qAlpha(s)))));
03240 }
03241 
03242 QImage KImageEffect::implode(QImage &src, double factor,
03243                              unsigned int background)
03244 {
03245     double amount, distance, radius;
03246     double x_center, x_distance, x_scale;
03247     double y_center, y_distance, y_scale;
03248     unsigned int *destData;
03249     int x, y;
03250 
03251     QImage dest(src.width(), src.height(), 32);
03252 
03253     // compute scaling factor
03254     x_scale = 1.0;
03255     y_scale = 1.0;
03256     x_center = (double)0.5*src.width();
03257     y_center = (double)0.5*src.height();
03258     radius=x_center;
03259     if(src.width() > src.height())
03260         y_scale = (double)src.width()/src.height();
03261     else if(src.width() < src.height()){
03262         x_scale = (double) src.height()/src.width();
03263         radius = y_center;
03264     }
03265     amount=factor/10.0;
03266     if(amount >= 0)
03267         amount/=10.0;
03268     if(src.depth() > 8){ // DirectClass source image
03269         unsigned int *srcData;
03270         for(y=0; y < src.height(); ++y){
03271             srcData = (unsigned int *)src.scanLine(y);
03272             destData = (unsigned int *)dest.scanLine(y);
03273             y_distance=y_scale*(y-y_center);
03274             for(x=0; x < src.width(); ++x){
03275                 destData[x] = srcData[x];
03276                 x_distance = x_scale*(x-x_center);
03277                 distance= x_distance*x_distance+y_distance*y_distance;
03278                 if(distance < (radius*radius)){
03279                     double factor;
03280                     // Implode the pixel.
03281                     factor=1.0;
03282                     if(distance > 0.0)
03283                         factor=
03284                             pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
03285                     destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
03286                                                    factor*y_distance/y_scale+y_center,
03287                                                    background);
03288                 }
03289             }
03290         }
03291     }
03292     else{ // PsudeoClass source image
03293         unsigned char *srcData;
03294         unsigned char idx;
03295         unsigned int *cTable = src.colorTable();
03296         for(y=0; y < src.height(); ++y){
03297             srcData = (unsigned char *)src.scanLine(y);
03298             destData = (unsigned int *)dest.scanLine(y);
03299             y_distance=y_scale*(y-y_center);
03300             for(x=0; x < src.width(); ++x){
03301                 idx = srcData[x];
03302                 destData[x] = cTable[idx];
03303                 x_distance = x_scale*(x-x_center);
03304                 distance= x_distance*x_distance+y_distance*y_distance;
03305                 if(distance < (radius*radius)){
03306                     double factor;
03307                     // Implode the pixel.
03308                     factor=1.0;
03309                     if(distance > 0.0)
03310                         factor=
03311                             pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
03312                     destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
03313                                                    factor*y_distance/y_scale+y_center,
03314                                                    background);
03315                 }
03316             }
03317         }
03318 
03319     }
03320     return(dest);
03321 }
03322 
03323 QImage KImageEffect::rotate(QImage &img, RotateDirection r)
03324 {
03325     QImage dest;
03326     int x, y;
03327     if(img.depth() > 8){
03328         unsigned int *srcData, *destData;
03329         switch(r){
03330         case Rotate90:
03331             dest.create(img.height(), img.width(), img.depth());
03332             for(y=0; y < img.height(); ++y){
03333                 srcData = (unsigned int *)img.scanLine(y);
03334                 for(x=0; x < img.width(); ++x){
03335                     destData = (unsigned int *)dest.scanLine(x);
03336                     destData[img.height()-y-1] = srcData[x];
03337                 }
03338             }
03339             break;
03340         case Rotate180:
03341             dest.create(img.width(), img.height(), img.depth());
03342             for(y=0; y < img.height(); ++y){
03343                 srcData = (unsigned int *)img.scanLine(y);
03344                 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
03345                 for(x=0; x < img.width(); ++x)
03346                     destData[img.width()-x-1] = srcData[x];
03347             }
03348             break;
03349         case Rotate270:
03350             dest.create(img.height(), img.width(), img.depth());
03351             for(y=0; y < img.height(); ++y){
03352                 srcData = (unsigned int *)img.scanLine(y);
03353                 for(x=0; x < img.width(); ++x){
03354                     destData = (unsigned int *)dest.scanLine(img.width()-x-1);
03355                     destData[y] = srcData[x];
03356                 }
03357             }
03358             break;
03359         default:
03360             dest = img;
03361             break;
03362         }
03363     }
03364     else{
03365         unsigned char *srcData, *destData;
03366         unsigned int *srcTable, *destTable;
03367         switch(r){
03368         case Rotate90:
03369             dest.create(img.height(), img.width(), img.depth());
03370             dest.setNumColors(img.numColors());
03371             srcTable = (unsigned int *)img.colorTable();
03372             destTable = (unsigned int *)dest.colorTable();
03373             for(x=0; x < img.numColors(); ++x)
03374                 destTable[x] = srcTable[x];
03375             for(y=0; y < img.height(); ++y){
03376                 srcData = (unsigned char *)img.scanLine(y);
03377                 for(x=0; x < img.width(); ++x){
03378                     destData = (unsigned char *)dest.scanLine(x);
03379                     destData[img.height()-y-1] = srcData[x];
03380                 }
03381             }
03382             break;
03383         case Rotate180:
03384             dest.create(img.width(), img.height(), img.depth());
03385             dest.setNumColors(img.numColors());
03386             srcTable = (unsigned int *)img.colorTable();
03387             destTable = (unsigned int *)dest.colorTable();
03388             for(x=0; x < img.numColors(); ++x)
03389                 destTable[x] = srcTable[x];
03390             for(y=0; y < img.height(); ++y){
03391                 srcData = (unsigned char *)img.scanLine(y);
03392                 destData = (unsigned char *)dest.scanLine(img.height()-y-1);
03393                 for(x=0; x < img.width(); ++x)
03394                     destData[img.width()-x-1] = srcData[x];
03395             }
03396             break;
03397         case Rotate270:
03398             dest.create(img.height(), img.width(), img.depth());
03399             dest.setNumColors(img.numColors());
03400             srcTable = (unsigned int *)img.colorTable();
03401             destTable = (unsigned int *)dest.colorTable();
03402             for(x=0; x < img.numColors(); ++x)
03403                 destTable[x] = srcTable[x];
03404             for(y=0; y < img.height(); ++y){
03405                 srcData = (unsigned char *)img.scanLine(y);
03406                 for(x=0; x < img.width(); ++x){
03407                     destData = (unsigned char *)dest.scanLine(img.width()-x-1);
03408                     destData[y] = srcData[x];
03409                 }
03410             }
03411             break;
03412         default:
03413             dest = img;
03414             break;
03415         }
03416 
03417     }
03418     return(dest);
03419 }
03420 
03421 void KImageEffect::solarize(QImage &img, double factor)
03422 {
03423     int i, count;
03424     int threshold;
03425     unsigned int *data;
03426 
03427     threshold = (int)(factor*(MaxRGB+1)/100.0);
03428     if(img.depth() < 32){
03429         data = (unsigned int *)img.colorTable();
03430         count = img.numColors();
03431     }
03432     else{
03433         data = (unsigned int *)img.bits();
03434         count = img.width()*img.height();
03435     }
03436     for(i=0; i < count; ++i){
03437         data[i] = qRgba(qRed(data[i]) > threshold ? MaxRGB-qRed(data[i]) : qRed(data[i]),
03438                         qGreen(data[i]) > threshold ? MaxRGB-qGreen(data[i]) : qGreen(data[i]),
03439                         qBlue(data[i]) > threshold ? MaxRGB-qBlue(data[i]) : qBlue(data[i]),
03440                         qAlpha(data[i]));
03441     }
03442 }
03443 
03444 QImage KImageEffect::spread(QImage &src, unsigned int amount)
03445 {
03446     int quantum, x, y;
03447     int x_distance, y_distance;
03448     if(src.width() < 3 || src.height() < 3)
03449         return(src);
03450     QImage dest(src);
03451     dest.detach();
03452     quantum=(amount+1) >> 1;
03453     if(src.depth() > 8){ // DirectClass source image
03454         unsigned int *p, *q;
03455         for(y=0; y < src.height(); y++){
03456             q = (unsigned int *)dest.scanLine(y);
03457             for(x=0; x < src.width(); x++){
03458                 x_distance = x + ((rand() & (amount+1))-quantum);
03459                 y_distance = y + ((rand() & (amount+1))-quantum);
03460                 x_distance = QMIN(x_distance, src.width()-1);
03461                 y_distance = QMIN(y_distance, src.height()-1);
03462                 if(x_distance < 0)
03463                     x_distance = 0;
03464                 if(y_distance < 0)
03465                     y_distance = 0;
03466                 p = (unsigned int *)src.scanLine(y_distance);
03467                 p += x_distance;
03468                 *q++=(*p);
03469             }
03470         }
03471     }
03472     else{ // PsudeoClass source image
03473         // just do colortable values
03474         unsigned char *p, *q;
03475         for(y=0; y < src.height(); y++){
03476             q = (unsigned char *)dest.scanLine(y);
03477             for(x=0; x < src.width(); x++){
03478                 x_distance = x + ((rand() & (amount+1))-quantum);
03479                 y_distance = y + ((rand() & (amount+1))-quantum);
03480                 x_distance = QMIN(x_distance, src.width()-1);
03481                 y_distance = QMIN(y_distance, src.height()-1);
03482                 if(x_distance < 0)
03483                     x_distance = 0;
03484                 if(y_distance < 0)
03485                     y_distance = 0;
03486                 p = (unsigned char *)src.scanLine(y_distance);
03487                 p += x_distance;
03488                 *q++=(*p);
03489             }
03490         }
03491     }
03492     return(dest);
03493 }
03494 
03495 QImage KImageEffect::swirl(QImage &src, double degrees,
03496                            unsigned int background)
03497 {
03498     double cosine, distance, factor, radius, sine, x_center, x_distance,
03499         x_scale, y_center, y_distance, y_scale;
03500     int x, y;
03501     unsigned int *q;
03502     QImage dest(src.width(), src.height(), 32);
03503 
03504     // compute scaling factor
03505     x_center = src.width()/2.0;
03506     y_center = src.height()/2.0;
03507     radius = QMAX(x_center,y_center);
03508     x_scale=1.0;
03509     y_scale=1.0;
03510     if(src.width() > src.height())
03511         y_scale=(double)src.width()/src.height();
03512     else if(src.width() < src.height())
03513         x_scale=(double)src.height()/src.width();
03514     degrees=DegreesToRadians(degrees);
03515     // swirl each row
03516     if(src.depth() > 8){ // DirectClass source image
03517         unsigned int *p;
03518         for(y=0; y < src.height(); y++){
03519             p = (unsigned int *)src.scanLine(y);
03520             q = (unsigned int *)dest.scanLine(y);
03521             y_distance = y_scale*(y-y_center);
03522             for(x=0; x < src.width(); x++){
03523                 // determine if the pixel is within an ellipse
03524                 *q=(*p);
03525                 x_distance = x_scale*(x-x_center);
03526                 distance = x_distance*x_distance+y_distance*y_distance;
03527                 if (distance < (radius*radius)){
03528                     // swirl
03529                     factor = 1.0-sqrt(distance)/radius;
03530                     sine = sin(degrees*factor*factor);
03531                     cosine = cos(degrees*factor*factor);
03532                     *q = interpolateColor(&src,
03533                                           (cosine*x_distance-sine*y_distance)/x_scale+x_center,
03534                                           (sine*x_distance+cosine*y_distance)/y_scale+y_center,
03535                                           background);
03536                 }
03537                 p++;
03538                 q++;
03539             }
03540         }
03541     }
03542     else{ // PsudeoClass source image
03543         unsigned char *p;
03544         unsigned int *cTable = (unsigned int *)src.colorTable();
03545         for(y=0; y < src.height(); y++){
03546             p = (unsigned char *)src.scanLine(y);
03547             q = (unsigned int *)dest.scanLine(y);
03548             y_distance = y_scale*(y-y_center);
03549             for(x=0; x < src.width(); x++){
03550                 // determine if the pixel is within an ellipse
03551                 *q = *(cTable+(*p));
03552                 x_distance = x_scale*(x-x_center);
03553                 distance = x_distance*x_distance+y_distance*y_distance;
03554                 if (distance < (radius*radius)){
03555                     // swirl
03556                     factor = 1.0-sqrt(distance)/radius;
03557                     sine = sin(degrees*factor*factor);
03558                     cosine = cos(degrees*factor*factor);
03559                     *q = interpolateColor(&src,
03560                                           (cosine*x_distance-sine*y_distance)/x_scale+x_center,
03561                                           (sine*x_distance+cosine*y_distance)/y_scale+y_center,
03562                                           background);
03563                 }
03564                 p++;
03565                 q++;
03566             }
03567         }
03568 
03569     }
03570     return(dest);
03571 }
03572 
03573 QImage KImageEffect::wave(QImage &src, double amplitude, double wavelength,
03574                           unsigned int background)
03575 {
03576     double *sine_map;
03577     int x, y;
03578     unsigned int *q;
03579 
03580     QImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
03581     // allocate sine map
03582     sine_map = (double *)malloc(dest.width()*sizeof(double));
03583     if(!sine_map)
03584         return(src);
03585     for(x=0; x < dest.width(); ++x)
03586         sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
03587     // wave image
03588     for(y=0; y < dest.height(); ++y){
03589         q = (unsigned int *)dest.scanLine(y);
03590         for (x=0; x < dest.width(); x++){
03591             *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
03592             ++q;
03593         }
03594     }
03595     free(sine_map);
03596     return(dest);
03597 }
03598 
03599 //
03600 // The following methods work by computing a value from neighboring pixels
03601 // (mosfet 05/26/03)
03602 //
03603 
03604 // New algorithms based on ImageMagick 5.5.6 (05/26/03)
03605 
03606 QImage KImageEffect::oilPaint(QImage &src, int /*radius*/)
03607 {
03608     /* binary compat method - remove me when possible! */
03609     return(oilPaintConvolve(src, 0));
03610 }
03611 
03612 QImage KImageEffect::oilPaintConvolve(QImage &src, double radius)
03613 {
03614     unsigned long count /*,*histogram*/;
03615     unsigned long histogram[256];
03616     unsigned int k;
03617     int width;
03618     int x, y, mx, my, sx, sy;
03619     int mcx, mcy;
03620     unsigned int *s=0, *q;
03621 
03622     if(src.depth() < 32)
03623         src.convertDepth(32);
03624     QImage dest(src);
03625     dest.detach();
03626 
03627     width = getOptimalKernelWidth(radius, 0.5);
03628     if(src.width() < width){
03629         qWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!");
03630         return(dest);
03631     }
03632     /*
03633     histogram = (unsigned long *)malloc(256*sizeof(unsigned long));
03634     if(!histogram){
03635         qWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!");
03636         return(dest);
03637     }
03638     */
03639     unsigned int **jumpTable = (unsigned int **)src.jumpTable();
03640     for(y=0; y < dest.height(); ++y){
03641         sy = y-(width/2);
03642         q = (unsigned int *)dest.scanLine(y);
03643         for(x=0; x < dest.width(); ++x){
03644             count = 0;
03645             memset(histogram, 0, 256*sizeof(unsigned long));
03646             //memset(histogram, 0, 256);
03647             sy = y-(width/2);
03648             for(mcy=0; mcy < width; ++mcy, ++sy){
03649                 my = sy < 0 ? 0 : sy > src.height()-1 ?
03650                     src.height()-1 : sy;
03651                 sx = x+(-width/2);
03652                 for(mcx=0; mcx < width; ++mcx, ++sx){
03653                     mx = sx < 0 ? 0 : sx > src.width()-1 ?
03654                         src.width()-1 : sx;
03655 
03656                     k = intensityValue(jumpTable[my][mx]);
03657                     if(k > 255){
03658                         qWarning("KImageEffect::oilPaintConvolve(): k is %d",
03659                                  k);
03660                         k = 255;
03661                     }
03662                     histogram[k]++;
03663                     if(histogram[k] > count){
03664                         count = histogram[k];
03665                         s = jumpTable[my]+mx;
03666                     }
03667                 }
03668             }
03669             if (s)
03670                 *q++ = (*s);
03671         }
03672     }
03673     /* liberateMemory((histogram); */
03674     return(dest);
03675 }
03676 
03677 QImage KImageEffect::charcoal(QImage &src, double /*factor*/)
03678 {
03679     /* binary compat method - remove me when possible! */
03680     return(charcoal(src, 0, 1));
03681 }
03682 
03683 QImage KImageEffect::charcoal(QImage &src, double radius, double sigma)
03684 {
03685     QImage img(edge(src, radius));
03686     img = blur(img, radius, sigma);
03687     normalize(img);
03688     img.invertPixels(false);
03689     KImageEffect::toGray(img);
03690     return(img);
03691 }
03692 
03693 void KImageEffect::normalize(QImage &image)
03694 {
03695     struct double_packet high, low, intensity, *histogram;
03696     struct short_packet *normalize_map;
03697     Q_INT64 number_pixels;
03698     int x, y;
03699     unsigned int *p, *q;
03700     register long i;
03701     unsigned long threshold_intensity;
03702     unsigned char r, g, b, a;
03703 
03704     if(image.depth() < 32) // result will always be 32bpp
03705         image = image.convertDepth(32);
03706 
03707     histogram = (struct double_packet *)
03708         malloc(256*sizeof(struct double_packet));
03709     normalize_map = (struct short_packet *)
03710         malloc(256*sizeof(struct short_packet));
03711 
03712     if(!histogram || !normalize_map){
03713         if(histogram)
03714             liberateMemory(&histogram);
03715         if(normalize_map)
03716             liberateMemory(&normalize_map);
03717         qWarning("KImageEffect::normalize(): Unable to allocate memory!");
03718         return;
03719     }
03720 
03721     /*
03722     Form histogram.
03723     */
03724     memset(histogram, 0, 256*sizeof(struct double_packet));
03725     for(y=0; y < image.height(); ++y){
03726         p = (unsigned int *)image.scanLine(y);
03727         for(x=0; x < image.width(); ++x){
03728             histogram[(unsigned char)(qRed(*p))].red++;
03729             histogram[(unsigned char)(qGreen(*p))].green++;
03730             histogram[(unsigned char)(qBlue(*p))].blue++;
03731             histogram[(unsigned char)(qAlpha(*p))].alpha++;
03732             p++;
03733         }
03734     }
03735 
03736     /*
03737     Find the histogram boundaries by locating the 0.1 percent levels.
03738     */
03739     number_pixels = (Q_INT64)image.width()*image.height();
03740     threshold_intensity = number_pixels/1000;
03741 
03742     /* red */
03743     memset(&intensity, 0, sizeof(struct double_packet));
03744     memset(&high, 0, sizeof(struct double_packet));
03745     memset(&low, 0, sizeof(struct double_packet));
03746     for(high.red=255; high.red != 0; high.red--){
03747         intensity.red+=histogram[(unsigned char)high.red].red;
03748         if(intensity.red > threshold_intensity)
03749             break;
03750     }
03751     if(low.red == high.red){
03752         threshold_intensity = 0;
03753         memset(&intensity, 0, sizeof(struct double_packet));
03754         for(low.red=0; low.red < 255; low.red++){
03755             intensity.red+=histogram[(unsigned char)low.red].red;
03756             if(intensity.red > threshold_intensity)
03757                 break;
03758         }
03759         memset(&intensity, 0, sizeof(struct double_packet));
03760         for(high.red=255; high.red != 0; high.red--){
03761             intensity.red+=histogram[(unsigned char)high.red].red;
03762             if(intensity.red > threshold_intensity)
03763                 break;
03764         }
03765     }
03766 
03767     /* green */
03768     memset(&intensity, 0, sizeof(struct double_packet));
03769     for(high.green=255; high.green != 0; high.green--){
03770         intensity.green+=histogram[(unsigned char)high.green].green;
03771         if(intensity.green > threshold_intensity)
03772             break;
03773     }
03774     if(low.green == high.green){
03775         threshold_intensity = 0;
03776         memset(&intensity, 0, sizeof(struct double_packet));
03777         for(low.green=0; low.green < 255; low.green++){
03778             intensity.green+=histogram[(unsigned char)low.green].green;
03779             if(intensity.green > threshold_intensity)
03780                 break;
03781         }
03782         memset(&intensity,0,sizeof(struct double_packet));
03783         for(high.green=255; high.green != 0; high.green--){
03784             intensity.green+=histogram[(unsigned char)high.green].green;
03785             if(intensity.green > threshold_intensity)
03786                 break;
03787         }
03788     }
03789 
03790     /* blue */
03791     memset(&intensity, 0, sizeof(struct double_packet));
03792     for(high.blue=255; high.blue != 0; high.blue--){
03793         intensity.blue+=histogram[(unsigned char)high.blue].blue;
03794         if(intensity.blue > threshold_intensity)
03795             break;
03796     }
03797     if(low.blue == high.blue){
03798         threshold_intensity = 0;
03799         memset(&intensity, 0, sizeof(struct double_packet));
03800         for(low.blue=0; low.blue < 255; low.blue++){
03801             intensity.blue+=histogram[(unsigned char)low.blue].blue;
03802             if(intensity.blue > threshold_intensity)
03803                 break;
03804         }
03805         memset(&intensity,0,sizeof(struct double_packet));
03806         for(high.blue=255; high.blue != 0; high.blue--){
03807             intensity.blue+=histogram[(unsigned char)high.blue].blue;
03808             if(intensity.blue > threshold_intensity)
03809                 break;
03810         }
03811     }
03812 
03813     /* alpha */
03814     memset(&intensity, 0, sizeof(struct double_packet));
03815     for(high.alpha=255; high.alpha != 0; high.alpha--){
03816         intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
03817         if(intensity.alpha > threshold_intensity)
03818             break;
03819     }
03820     if(low.alpha == high.alpha){
03821         threshold_intensity = 0;
03822         memset(&intensity, 0, sizeof(struct double_packet));
03823         for(low.alpha=0; low.alpha < 255; low.alpha++){
03824             intensity.alpha+=histogram[(unsigned char)low.alpha].alpha;
03825             if(intensity.alpha > threshold_intensity)
03826                 break;
03827         }
03828         memset(&intensity,0,sizeof(struct double_packet));
03829         for(high.alpha=255; high.alpha != 0; high.alpha--){
03830             intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
03831             if(intensity.alpha > threshold_intensity)
03832                 break;
03833         }
03834     }
03835     liberateMemory(&histogram);
03836 
03837     /*
03838      Stretch the histogram to create the normalized image mapping.
03839      */
03840 
03841     // should the maxes be 65535?
03842     memset(normalize_map, 0 ,256*sizeof(struct short_packet));
03843     for(i=0; i <= (long) 255; i++){
03844         if(i < (long) low.red)
03845             normalize_map[i].red=0;
03846         else if (i > (long) high.red)
03847             normalize_map[i].red=65535;
03848         else if (low.red != high.red)
03849             normalize_map[i].red =
03850                 (unsigned short)((65535*(i-low.red))/(high.red-low.red));
03851 
03852         if(i < (long) low.green)
03853             normalize_map[i].green=0;
03854         else if (i > (long) high.green)
03855             normalize_map[i].green=65535;
03856         else if (low.green != high.green)
03857             normalize_map[i].green =
03858                 (unsigned short)((65535*(i-low.green))/(high.green-low.green));
03859 
03860         if(i < (long) low.blue)
03861             normalize_map[i].blue=0;
03862         else if (i > (long) high.blue)
03863             normalize_map[i].blue=65535;
03864         else if (low.blue != high.blue)
03865             normalize_map[i].blue =
03866                 (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue));
03867 
03868         if(i < (long) low.alpha)
03869             normalize_map[i].alpha=0;
03870         else if (i > (long) high.alpha)
03871             normalize_map[i].alpha=65535;
03872         else if (low.alpha != high.alpha)
03873             normalize_map[i].alpha =
03874                 (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha));
03875 
03876     }
03877 
03878     for(y=0; y < image.height(); ++y){
03879         q = (unsigned int *)image.scanLine(y);
03880         for(x=0; x < image.width(); ++x){
03881             if(low.red != high.red)
03882                 r = (normalize_map[(unsigned short)(qRed(q[x]))].red)/257;
03883             else
03884                 r = qRed(q[x]);
03885             if(low.green != high.green)
03886                 g = (normalize_map[(unsigned short)(qGreen(q[x]))].green)/257;
03887             else
03888                 g = qGreen(q[x]);
03889             if(low.blue != high.blue)
03890                 b = (normalize_map[(unsigned short)(qBlue(q[x]))].blue)/257;
03891             else
03892                 b = qBlue(q[x]);
03893             if(low.alpha != high.alpha)
03894                 a = (normalize_map[(unsigned short)(qAlpha(q[x]))].alpha)/257;
03895             else
03896                 a = qAlpha(q[x]);
03897             q[x] = qRgba(r, g, b, a);
03898         }
03899     }
03900     liberateMemory(&normalize_map);
03901 }
03902 
03903 void KImageEffect::equalize(QImage &image)
03904 {
03905     struct double_packet high, low, intensity, *map, *histogram;
03906     struct short_packet *equalize_map;
03907     int x, y;
03908     unsigned int *p, *q;
03909     long i;
03910     unsigned char r, g, b, a;
03911 
03912     if(image.depth() < 32) // result will always be 32bpp
03913         image = image.convertDepth(32);
03914 
03915     histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet));
03916     map=(struct double_packet *) malloc(256*sizeof(struct double_packet));
03917     equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet));
03918     if(!histogram || !map || !equalize_map){
03919         if(histogram)
03920             liberateMemory(&histogram);
03921         if(map)
03922             liberateMemory(&map);
03923         if(equalize_map)
03924             liberateMemory(&equalize_map);
03925         qWarning("KImageEffect::equalize(): Unable to allocate memory!");
03926         return;
03927     }
03928 
03929     /*
03930     Form histogram.
03931     */
03932     memset(histogram, 0, 256*sizeof(struct double_packet));
03933     for(y=0; y < image.height(); ++y){
03934         p = (unsigned int *)image.scanLine(y);
03935         for(x=0; x < image.width(); ++x){
03936             histogram[(unsigned char)(qRed(*p))].red++;
03937             histogram[(unsigned char)(qGreen(*p))].green++;
03938             histogram[(unsigned char)(qBlue(*p))].blue++;
03939             histogram[(unsigned char)(qAlpha(*p))].alpha++;
03940             p++;
03941         }
03942     }
03943     /*
03944      Integrate the histogram to get the equalization map.
03945      */
03946     memset(&intensity, 0 ,sizeof(struct double_packet));
03947     for(i=0; i <= 255; ++i){
03948         intensity.red += histogram[i].red;
03949         intensity.green += histogram[i].green;
03950         intensity.blue += histogram[i].blue;
03951         intensity.alpha += histogram[i].alpha;
03952         map[i]=intensity;
03953     }
03954     low=map[0];
03955     high=map[255];
03956     memset(equalize_map, 0, 256*sizeof(short_packet));
03957     for(i=0; i <= 255; ++i){
03958         if(high.red != low.red)
03959             equalize_map[i].red=(unsigned short)
03960                 ((65535*(map[i].red-low.red))/(high.red-low.red));
03961         if(high.green != low.green)
03962             equalize_map[i].green=(unsigned short)
03963                 ((65535*(map[i].green-low.green))/(high.green-low.green));
03964         if(high.blue != low.blue)
03965             equalize_map[i].blue=(unsigned short)
03966                 ((65535*(map[i].blue-low.blue))/(high.blue-low.blue));
03967         if(high.alpha != low.alpha)
03968             equalize_map[i].alpha=(unsigned short)
03969                 ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha));
03970     }
03971     liberateMemory(&histogram);
03972     liberateMemory(&map);
03973 
03974     /*
03975      Stretch the histogram.
03976      */
03977     for(y=0; y < image.height(); ++y){
03978         q = (unsigned int *)image.scanLine(y);
03979         for(x=0; x < image.width(); ++x){
03980             if(low.red != high.red)
03981                 r = (equalize_map[(unsigned short)(qRed(q[x]))].red/257);
03982             else
03983                 r = qRed(q[x]);
03984             if(low.green != high.green)
03985                 g = (equalize_map[(unsigned short)(qGreen(q[x]))].green/257);
03986             else
03987                 g = qGreen(q[x]);
03988             if(low.blue != high.blue)
03989                 b = (equalize_map[(unsigned short)(qBlue(q[x]))].blue/257);
03990             else
03991                 b = qBlue(q[x]);
03992             if(low.alpha != high.alpha)
03993                 a = (equalize_map[(unsigned short)(qAlpha(q[x]))].alpha/257);
03994             else
03995                 a = qAlpha(q[x]);
03996             q[x] = qRgba(r, g, b, a);
03997         }
03998     }
03999     liberateMemory(&equalize_map);
04000 
04001 }
04002 
04003 QImage KImageEffect::edge(QImage &image, double radius)
04004 {
04005     double *kernel;
04006     int width;
04007     register long i;
04008     QImage dest;
04009 
04010     if(radius == 50.0){
04011         /* For binary compatability! Remove me when possible! This used to
04012          * take a different parameter, a factor, and this was the default
04013          * value */
04014         radius = 0.0;
04015     }
04016 
04017     width = getOptimalKernelWidth(radius, 0.5);
04018     if(image.width() < width || image.height() < width){
04019         qWarning("KImageEffect::edge(): Image is smaller than radius!");
04020         return(dest);
04021     }
04022     kernel= (double *)malloc(width*width*sizeof(double));
04023     if(!kernel){
04024         qWarning("KImageEffect::edge(): Unable to allocate memory!");
04025         return(dest);
04026     }
04027     for(i=0; i < (width*width); i++)
04028         kernel[i]=(-1.0);
04029     kernel[i/2]=width*width-1.0;
04030     convolveImage(&image, &dest, width, kernel);
04031     free(kernel);
04032     return(dest);
04033 }
04034 
04035 QImage KImageEffect::emboss(QImage &src)
04036 {
04037     /* binary compat method - remove me when possible! */
04038     return(emboss(src, 0, 1));
04039 }
04040 
04041 QImage KImageEffect::emboss(QImage &image, double radius, double sigma)
04042 {
04043     double alpha, *kernel;
04044     int j, width;
04045     register long i, u, v;
04046     QImage dest;
04047 
04048     if(sigma == 0.0){
04049         qWarning("KImageEffect::emboss(): Zero sigma is not permitted!");
04050         return(dest);
04051     }
04052 
04053     width = getOptimalKernelWidth(radius, sigma);
04054     if(image.width() < width || image.height() < width){
04055         qWarning("KImageEffect::emboss(): Image is smaller than radius!");
04056         return(dest);
04057     }
04058     kernel= (double *)malloc(width*width*sizeof(double));
04059     if(!kernel){
04060         qWarning("KImageEffect::emboss(): Unable to allocate memory!");
04061         return(dest);
04062     }
04063     if(image.depth() < 32)
04064         image = image.convertDepth(32);
04065 
04066     i=0;
04067     j=width/2;
04068     for(v=(-width/2); v <= (width/2); v++){
04069         for(u=(-width/2); u <= (width/2); u++){
04070             alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
04071             kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
04072                 (2.0*MagickPI*sigma*sigma);
04073             if (u == j)
04074                 kernel[i]=0.0;
04075             i++;
04076         }
04077         j--;
04078     }
04079     convolveImage(&image, &dest, width, kernel);
04080     liberateMemory(&kernel);
04081 
04082     equalize(dest);
04083     return(dest);
04084 }
04085 
04086 void KImageEffect::blurScanLine(double *kernel, int width,
04087                                 unsigned int *src, unsigned int *dest,
04088                                 int columns)
04089 {
04090     register double *p;
04091     unsigned int *q;
04092     register int x;
04093     register long i;
04094     double red, green, blue, alpha;
04095     double scale = 0.0;
04096 
04097     if(width > columns){
04098         for(x=0; x < columns; ++x){
04099             scale = 0.0;
04100             red = blue = green = alpha = 0.0;
04101             p = kernel;
04102             q = src;
04103             for(i=0; i < columns; ++i){
04104                 if((i >= (x-width/2)) && (i <= (x+width/2))){
04105                     red += (*p)*(qRed(*q)*257);
04106                     green += (*p)*(qGreen(*q)*257);
04107                     blue += (*p)*(qBlue(*q)*257);
04108                     alpha += (*p)*(qAlpha(*q)*257);
04109                 }
04110                 if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
04111                     scale+=kernel[i+width/2-x];
04112                 p++;
04113                 q++;
04114             }
04115             scale = 1.0/scale;
04116             red = scale*(red+0.5);
04117             green = scale*(green+0.5);
04118             blue = scale*(blue+0.5);
04119             alpha = scale*(alpha+0.5);
04120 
04121             red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04122             green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04123             blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04124             alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04125 
04126             dest[x] = qRgba((unsigned char)(red/257UL),
04127                             (unsigned char)(green/257UL),
04128                             (unsigned char)(blue/257UL),
04129                             (unsigned char)(alpha/257UL));
04130         }
04131         return;
04132     }
04133 
04134     for(x=0; x < width/2; ++x){
04135         scale = 0.0;
04136         red = blue = green = alpha = 0.0;
04137         p = kernel+width/2-x;
04138         q = src;
04139         for(i=width/2-x; i < width; ++i){
04140             red += (*p)*(qRed(*q)*257);
04141             green += (*p)*(qGreen(*q)*257);
04142             blue += (*p)*(qBlue(*q)*257);
04143             alpha += (*p)*(qAlpha(*q)*257);
04144             scale += (*p);
04145             p++;
04146             q++;
04147         }
04148         scale=1.0/scale;
04149 
04150         red = scale*(red+0.5);
04151         green = scale*(green+0.5);
04152         blue = scale*(blue+0.5);
04153         alpha = scale*(alpha+0.5);
04154 
04155         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04156         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04157         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04158         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04159 
04160         dest[x] = qRgba((unsigned char)(red/257UL),
04161                         (unsigned char)(green/257UL),
04162                         (unsigned char)(blue/257UL),
04163                         (unsigned char)(alpha/257UL));
04164     }
04165 
04166     for(; x < columns-width/2; ++x){
04167         red = blue = green = alpha = 0.0;
04168         p = kernel;
04169         q = src+(x-width/2);
04170         for (i=0; i < (long) width; ++i){
04171             red += (*p)*(qRed(*q)*257);
04172             green += (*p)*(qGreen(*q)*257);
04173             blue += (*p)*(qBlue(*q)*257);
04174             alpha += (*p)*(qAlpha(*q)*257);
04175             p++;
04176             q++;
04177         }
04178         red = scale*(red+0.5);
04179         green = scale*(green+0.5);
04180         blue = scale*(blue+0.5);
04181         alpha = scale*(alpha+0.5);
04182 
04183         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04184         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04185         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04186         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04187 
04188         dest[x] = qRgba((unsigned char)(red/257UL),
04189                         (unsigned char)(green/257UL),
04190                         (unsigned char)(blue/257UL),
04191                         (unsigned char)(alpha/257UL));
04192     }
04193 
04194     for(; x < columns; ++x){
04195         red = blue = green = alpha = 0.0;
04196         scale=0;
04197         p = kernel;
04198         q = src+(x-width/2);
04199         for(i=0; i < columns-x+width/2; ++i){
04200             red += (*p)*(qRed(*q)*257);
04201             green += (*p)*(qGreen(*q)*257);
04202             blue += (*p)*(qBlue(*q)*257);
04203             alpha += (*p)*(qAlpha(*q)*257);
04204             scale += (*p);
04205             p++;
04206             q++;
04207         }
04208         scale=1.0/scale;
04209         red = scale*(red+0.5);
04210         green = scale*(green+0.5);
04211         blue = scale*(blue+0.5);
04212         alpha = scale*(alpha+0.5);
04213 
04214         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04215         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04216         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04217         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04218 
04219         dest[x] = qRgba((unsigned char)(red/257UL),
04220                         (unsigned char)(green/257UL),
04221                         (unsigned char)(blue/257UL),
04222                         (unsigned char)(alpha/257UL));
04223     }
04224 }
04225 
04226 int KImageEffect::getBlurKernel(int width, double sigma, double **kernel)
04227 {
04228 #define KernelRank 3
04229     double alpha, normalize;
04230     register long i;
04231     int bias;
04232 
04233     assert(sigma != 0.0);
04234     if(width == 0)
04235         width = 3;
04236     *kernel=(double *)malloc(width*sizeof(double));
04237     if(*kernel == (double *)NULL)
04238         return(0);
04239     memset(*kernel, 0, width*sizeof(double));
04240     bias = KernelRank*width/2;
04241     for(i=(-bias); i <= bias; i++){
04242         alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
04243         (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma);
04244     }
04245     normalize=0;
04246     for(i=0; i < width; i++)
04247         normalize+=(*kernel)[i];
04248     for(i=0; i < width; i++)
04249         (*kernel)[i]/=normalize;
04250 
04251     return(width);
04252 }
04253 
04254 QImage KImageEffect::blur(QImage &src, double /*factor*/)
04255 {
04256     /* binary compat method - remove me when possible! */
04257     return(blur(src, 0, 1));
04258 }
04259 
04260 QImage KImageEffect::blur(QImage &src, double radius, double sigma)
04261 {
04262     double *kernel;
04263     QImage dest;
04264     int width;
04265     int x, y;
04266     unsigned int *scanline, *temp;
04267     unsigned int *p, *q;
04268 
04269     if(sigma == 0.0){
04270         qWarning("KImageEffect::blur(): Zero sigma is not permitted!");
04271         return(dest);
04272     }
04273     if(src.depth() < 32)
04274         src = src.convertDepth(32);
04275 
04276     kernel=(double *) NULL;
04277     if(radius > 0)
04278         width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
04279     else{
04280         double *last_kernel;
04281         last_kernel=(double *) NULL;
04282         width=getBlurKernel(3,sigma,&kernel);
04283 
04284         while ((long) (MaxRGB*kernel[0]) > 0){
04285             if(last_kernel != (double *)NULL){
04286                 liberateMemory(&last_kernel);
04287             }
04288             last_kernel=kernel;
04289             kernel = (double *)NULL;
04290             width = getBlurKernel(width+2, sigma, &kernel);
04291         }
04292         if(last_kernel != (double *) NULL){
04293             liberateMemory(&kernel);
04294             width-=2;
04295             kernel = last_kernel;
04296         }
04297     }
04298 
04299     if(width < 3){
04300         qWarning("KImageEffect::blur(): Kernel radius is too small!");
04301         liberateMemory(&kernel);
04302         return(dest);
04303     }
04304 
04305     dest.create(src.width(), src.height(), 32);
04306 
04307     scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
04308     temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
04309     for(y=0; y < src.height(); ++y){
04310         p = (unsigned int *)src.scanLine(y);
04311         q = (unsigned int *)dest.scanLine(y);
04312         blurScanLine(kernel, width, p, q, src.width());
04313     }
04314 
04315     unsigned int **srcTable = (unsigned int **)src.jumpTable();
04316     unsigned int **destTable = (unsigned int **)dest.jumpTable();
04317     for(x=0; x < src.width(); ++x){
04318         for(y=0; y < src.height(); ++y){
04319             scanline[y] = srcTable[y][x];
04320         }
04321         blurScanLine(kernel, width, scanline, temp, src.height());
04322         for(y=0; y < src.height(); ++y){
04323             destTable[y][x] = temp[y];
04324         }
04325     }
04326     free(scanline);
04327     free(temp);
04328     free(kernel);
04329     return(dest);
04330 }
04331 
04332 bool KImageEffect::convolveImage(QImage *image, QImage *dest,
04333                                  const unsigned int order,
04334                                  const double *kernel)
04335 {
04336     long width;
04337     double red, green, blue, alpha;
04338     double normalize, *normal_kernel;
04339     register const double *k;
04340     register unsigned int *q;
04341     int x, y, mx, my, sx, sy;
04342     long i;
04343     int mcx, mcy;
04344 
04345     width = order;
04346     if((width % 2) == 0){
04347         qWarning("KImageEffect: Kernel width must be an odd number!");
04348         return(false);
04349     }
04350     normal_kernel = (double *)malloc(width*width*sizeof(double));
04351     if(!normal_kernel){
04352         qWarning("KImageEffect: Unable to allocate memory!");
04353         return(false);
04354     }
04355     dest->reset();
04356     dest->create(image->width(), image->height(), 32);
04357     if(image->depth() < 32)
04358         *image = image->convertDepth(32);
04359 
04360     normalize=0.0;
04361     for(i=0; i < (width*width); i++)
04362         normalize += kernel[i];
04363     if(fabs(normalize) <= MagickEpsilon)
04364         normalize=1.0;
04365     normalize=1.0/normalize;
04366     for(i=0; i < (width*width); i++)
04367         normal_kernel[i] = normalize*kernel[i];
04368 
04369     unsigned int **jumpTable = (unsigned int **)image->jumpTable();
04370     for(y=0; y < dest->height(); ++y){
04371         sy = y-(width/2);
04372         q = (unsigned int *)dest->scanLine(y);
04373         for(x=0; x < dest->width(); ++x){
04374             k = normal_kernel;
04375             red = green = blue = alpha = 0;
04376             sy = y-(width/2);
04377             for(mcy=0; mcy < width; ++mcy, ++sy){
04378                 my = sy < 0 ? 0 : sy > image->height()-1 ?
04379                     image->height()-1 : sy;
04380                 sx = x+(-width/2);
04381                 for(mcx=0; mcx < width; ++mcx, ++sx){
04382                     mx = sx < 0 ? 0 : sx > image->width()-1 ?
04383                         image->width()-1 : sx;
04384                     red += (*k)*(qRed(jumpTable[my][mx])*257);
04385                     green += (*k)*(qGreen(jumpTable[my][mx])*257);
04386                     blue += (*k)*(qBlue(jumpTable[my][mx])*257);
04387                     alpha += (*k)*(qAlpha(jumpTable[my][mx])*257);
04388                     ++k;
04389                 }
04390             }
04391 
04392             red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
04393             green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
04394             blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
04395             alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
04396 
04397             *q++ = qRgba((unsigned char)(red/257UL),
04398                          (unsigned char)(green/257UL),
04399                          (unsigned char)(blue/257UL),
04400                          (unsigned char)(alpha/257UL));
04401         }
04402     }
04403     free(normal_kernel);
04404     return(true);
04405 
04406 }
04407 
04408 int KImageEffect::getOptimalKernelWidth(double radius, double sigma)
04409 {
04410     double normalize, value;
04411     long width;
04412     register long u;
04413 
04414     assert(sigma != 0.0);
04415     if(radius > 0.0)
04416         return((int)(2.0*ceil(radius)+1.0));
04417     for(width=5; ;){
04418         normalize=0.0;
04419         for(u=(-width/2); u <= (width/2); u++)
04420             normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
04421         u=width/2;
04422         value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
04423         if((long)(65535*value) <= 0)
04424             break;
04425         width+=2;
04426     }
04427     return((int)width-2);
04428 }
04429 
04430 QImage KImageEffect::sharpen(QImage &src, double /*factor*/)
04431 {
04432     /* binary compat method - remove me when possible! */
04433     return(sharpen(src, 0, 1));
04434 }
04435 
04436 QImage KImageEffect::sharpen(QImage &image, double radius, double sigma)
04437 {
04438     double alpha, normalize, *kernel;
04439     int width;
04440     register long i, u, v;
04441     QImage dest;
04442 
04443     if(sigma == 0.0){
04444         qWarning("KImageEffect::sharpen(): Zero sigma is not permitted!");
04445         return(dest);
04446     }
04447     width = getOptimalKernelWidth(radius, sigma);
04448     if(image.width() < width){
04449         qWarning("KImageEffect::sharpen(): Image is smaller than radius!");
04450         return(dest);
04451     }
04452     kernel = (double *)malloc(width*width*sizeof(double));
04453     if(!kernel){
04454         qWarning("KImageEffect::sharpen(): Unable to allocate memory!");
04455         return(dest);
04456     }
04457 
04458     i = 0;
04459     normalize=0.0;
04460     for(v=(-width/2); v <= (width/2); v++){
04461         for(u=(-width/2); u <= (width/2); u++){
04462             alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
04463             kernel[i]=alpha/(2.0*MagickPI*sigma*sigma);
04464             normalize+=kernel[i];
04465             i++;
04466         }
04467     }
04468     kernel[i/2]=(-2.0)*normalize;
04469     convolveImage(&image, &dest, width, kernel);
04470     free(kernel);
04471     return(dest);
04472 }
04473 
04474 // End of new algorithms
04475 
04476 QImage KImageEffect::shade(QImage &src, bool color_shading, double azimuth,
04477              double elevation)
04478 {
04479     struct PointInfo{
04480         double x, y, z;
04481     };
04482 
04483     double distance, normal_distance, shade;
04484     int x, y;
04485 
04486     struct PointInfo light, normal;
04487 
04488     unsigned int *q;
04489 
04490     QImage dest(src.width(), src.height(), 32);
04491 
04492     azimuth = DegreesToRadians(azimuth);
04493     elevation = DegreesToRadians(elevation);
04494     light.x = MaxRGB*cos(azimuth)*cos(elevation);
04495     light.y = MaxRGB*sin(azimuth)*cos(elevation);
04496     light.z = MaxRGB*sin(elevation);
04497     normal.z= 2*MaxRGB;  // constant Z of surface normal
04498 
04499     if(src.depth() > 8){ // DirectClass source image
04500         unsigned int *p, *s0, *s1, *s2;
04501         for(y=0; y < src.height(); ++y){
04502             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
04503             q = (unsigned int *)dest.scanLine(y);
04504             // shade this row of pixels.
04505             *q++=(*(p+src.width()));
04506             p++;
04507             s0 = p;
04508             s1 = p + src.width();
04509             s2 = p + 2*src.width();
04510             for(x=1; x < src.width()-1; ++x){
04511                 // determine the surface normal and compute shading.
04512                 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
04513                     (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
04514                     (double) intensityValue(*(s2+1));
04515                 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
04516                     (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
04517                     (double) intensityValue(*(s0+1));
04518                 if((normal.x == 0) && (normal.y == 0))
04519                     shade=light.z;
04520                 else{
04521                     shade=0.0;
04522                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04523                     if (distance > 0.0){
04524                         normal_distance=
04525                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04526                         if(fabs(normal_distance) > 0.0000001)
04527                             shade=distance/sqrt(normal_distance);
04528                     }
04529                 }
04530                 if(!color_shading){
04531                     *q = qRgba((unsigned char)(shade),
04532                                (unsigned char)(shade),
04533                                (unsigned char)(shade),
04534                                qAlpha(*s1));
04535                 }
04536                 else{
04537                     *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)),
04538                                (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)),
04539                                (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)),
04540                                qAlpha(*s1));
04541                 }
04542                 ++s0;
04543                 ++s1;
04544                 ++s2;
04545                 q++;
04546             }
04547             *q++=(*s1);
04548         }
04549     }
04550     else{ // PsudeoClass source image
04551         unsigned char *p, *s0, *s1, *s2;
04552         int scanLineIdx;
04553         unsigned int *cTable = (unsigned int *)src.colorTable();
04554         for(y=0; y < src.height(); ++y){
04555             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
04556             p = (unsigned char *)src.scanLine(scanLineIdx);
04557             q = (unsigned int *)dest.scanLine(y);
04558             // shade this row of pixels.
04559             s0 = p;
04560             s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
04561             s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
04562             *q++=(*(cTable+(*s1)));
04563             ++p;
04564             ++s0;
04565             ++s1;
04566             ++s2;
04567             for(x=1; x < src.width()-1; ++x){
04568                 // determine the surface normal and compute shading.
04569                 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
04570                     (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
04571                     (double) intensityValue(*(cTable+(*(s2+1))));
04572                 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
04573                     (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
04574                     (double) intensityValue(*(cTable+(*(s0+1))));
04575                 if((normal.x == 0) && (normal.y == 0))
04576                     shade=light.z;
04577                 else{
04578                     shade=0.0;
04579                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04580                     if (distance > 0.0){
04581                         normal_distance=
04582                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04583                         if(fabs(normal_distance) > 0.0000001)
04584                             shade=distance/sqrt(normal_distance);
04585                     }
04586                 }
04587                 if(!color_shading){
04588                     *q = qRgba((unsigned char)(shade),
04589                                (unsigned char)(shade),
04590                                (unsigned char)(shade),
04591                                qAlpha(*(cTable+(*s1))));
04592                 }
04593                 else{
04594                     *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)),
04595                                (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)),
04596                                (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)),
04597                                qAlpha(*s1));
04598                 }
04599                 ++s0;
04600                 ++s1;
04601                 ++s2;
04602                 q++;
04603             }
04604             *q++=(*(cTable+(*s1)));
04605         }
04606     }
04607     return(dest);
04608 }
04609 
04610 // High quality, expensive HSV contrast. You can do a faster one by just
04611 // taking a grayscale threshold (ie: 128) and incrementing RGB color
04612 // channels above it and decrementing those below it, but this gives much
04613 // better results. (mosfet 12/28/01)
04614 void KImageEffect::contrastHSV(QImage &img, bool sharpen)
04615 {
04616     int i, sign;
04617     unsigned int *data;
04618     int count;
04619     double brightness, scale, theta;
04620     QColor c;
04621     int h, s, v;
04622 
04623     sign = sharpen ? 1 : -1;
04624     scale=0.5000000000000001;
04625     if(img.depth() > 8){
04626         count = img.width()*img.height();
04627         data = (unsigned int *)img.bits();
04628     }
04629     else{
04630         count = img.numColors();
04631         data = (unsigned int *)img.colorTable();
04632     }
04633     for(i=0; i < count; ++i){
04634         c.setRgb(data[i]);
04635         c.hsv(&h, &s, &v);
04636         brightness = v/255.0;
04637         theta=(brightness-0.5)*M_PI;
04638         brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
04639         if (brightness > 1.0)
04640             brightness=1.0;
04641         else
04642             if (brightness < 0)
04643                 brightness=0.0;
04644         v = (int)(brightness*255);
04645         c.setHsv(h, s, v);
04646         data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i]));
04647     }
04648 }
04649 
04650 
04651 struct BumpmapParams {
04652     BumpmapParams( double bm_azimuth, double bm_elevation,
04653                    int bm_depth, KImageEffect::BumpmapType bm_type,
04654                    bool invert ) {
04655          /* Convert to radians */
04656         double azimuth = DegreesToRadians( bm_azimuth );
04657         double elevation = DegreesToRadians( bm_elevation );
04658 
04659         /* Calculate the light vector */
04660         lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
04661         ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
04662         int lz         = (int)( sin(elevation) * 255.0 );
04663 
04664         /* Calculate constant Z component of surface normal */
04665         int nz  = (6 * 255) / bm_depth;
04666         nz2     = nz * nz;
04667         nzlz    = nz * lz;
04668 
04669         /* Optimize for vertical normals */
04670         background = lz;
04671 
04672         /* Calculate darkness compensation factor */
04673         compensation = sin(elevation);
04674 
04675         /* Create look-up table for map type */
04676         for (int i = 0; i < 256; i++)
04677         {
04678             double n = 0;
04679             switch (bm_type)
04680             {
04681             case KImageEffect::Spherical:
04682                 n = i / 255.0 - 1.0;
04683                 lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
04684                 break;
04685 
04686             case KImageEffect::Sinuosidal:
04687                 n = i / 255.0;
04688                 lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
04689                                         2.0 + 0.5);
04690                 break;
04691 
04692             case KImageEffect::Linear:
04693             default:
04694                 lut[i] = i;
04695             }
04696 
04697             if (invert)
04698                 lut[i] = 255 - lut[i];
04699         }
04700     }
04701     int lx,  ly;
04702     int nz2, nzlz;
04703     int background;
04704     double compensation;
04705     uchar lut[256];
04706 };
04707 
04708 
04709 static void bumpmap_convert_row( uint *row,
04710                                  int    width,
04711                                  int    bpp,
04712                                  int    has_alpha,
04713                                  uchar *lut,
04714                                  int waterlevel )
04715 {
04716   uint *p;
04717 
04718   p = row;
04719 
04720   has_alpha = has_alpha ? 1 : 0;
04721 
04722   if (bpp >= 3)
04723       for (; width; width--)
04724       {
04725           if (has_alpha) {
04726               unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
04727               *p++ = lut[(unsigned int) ( waterlevel +
04728                                           ( ( idx -
04729                                               waterlevel) * qBlue( *row )) / 255.0 )];
04730           } else {
04731               unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
04732               *p++ = lut[idx];
04733           }
04734 
04735           ++row;
04736       }
04737 }
04738 
04739 static void bumpmap_row( uint           *src,
04740                          uint           *dest,
04741                          int              width,
04742                          int              bpp,
04743                          int              has_alpha,
04744                          uint           *bm_row1,
04745                          uint           *bm_row2,
04746                          uint           *bm_row3,
04747                          int              bm_width,
04748                          int              bm_xofs,
04749                          bool          tiled,
04750                          bool          row_in_bumpmap,
04751                          int           ambient,
04752                          bool          compensate,
04753                          BumpmapParams *params )
04754 {
04755     int xofs1, xofs2, xofs3;
04756     int shade;
04757     int ndotl;
04758     int nx, ny;
04759     int x;
04760     int tmp;
04761 
04762     tmp = bm_xofs;
04763     xofs2 = MOD(tmp, bm_width);
04764 
04765     for (x = 0; x < width; x++)
04766     {
04767         /* Calculate surface normal from bump map */
04768 
04769         if (tiled || (row_in_bumpmap &&
04770                       x >= - tmp && x < - tmp + bm_width)) {
04771             if (tiled) {
04772                 xofs1 = MOD(xofs2 - 1, bm_width);
04773                 xofs3 = MOD(xofs2 + 1, bm_width);
04774         } else {
04775                 xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1);
04776                 xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1);
04777         }
04778             nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
04779                   bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
04780             ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
04781                   bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
04782     } else {
04783             nx = ny = 0;
04784         }
04785 
04786       /* Shade */
04787 
04788         if ((nx == 0) && (ny == 0))
04789             shade = params->background;
04790         else {
04791             ndotl = nx * params->lx + ny * params->ly + params->nzlz;
04792 
04793             if (ndotl < 0)
04794                 shade = (int)( params->compensation * ambient );
04795             else {
04796                 shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) );
04797 
04798                 shade = (int)( shade + QMAX(0.0, (255 * params->compensation - shade)) *
04799                                ambient / 255 );
04800         }
04801     }
04802 
04803         /* Paint */
04804 
04809         if (compensate) {
04810             int red = (int)((qRed( *src ) * shade) / (params->compensation * 255));
04811             int green = (int)((qGreen( *src ) * shade) / (params->compensation * 255));
04812             int blue = (int)((qBlue( *src ) * shade) / (params->compensation * 255));
04813             int alpha = (int)((qAlpha( *src ) * shade) / (params->compensation * 255));
04814             ++src;
04815             *dest++ = qRgba( red, green, blue, alpha );
04816         } else {
04817             int red = qRed( *src ) * shade / 255;
04818             int green = qGreen( *src ) * shade / 255;
04819             int blue = qBlue( *src ) * shade / 255;
04820             int alpha = qAlpha( *src ) * shade / 255;
04821             ++src;
04822             *dest++ = qRgba( red, green, blue, alpha );
04823         }
04824 
04825         /* Next pixel */
04826 
04827         if (++xofs2 == bm_width)
04828             xofs2 = 0;
04829     }
04830 }
04831 
04851 QImage KImageEffect::bumpmap(QImage &img, QImage &map, double azimuth, double elevation,
04852                              int depth, int xofs, int yofs, int waterlevel,
04853                              int ambient, bool compensate, bool invert,
04854                              BumpmapType type, bool tiled)
04855 {
04856     QImage dst;
04857 
04858     if ( img.depth() != 32 || img.depth() != 32 ) {
04859         qWarning( "Bump-mapping effect works only with 32 bit images");
04860         return dst;
04861     }
04862 
04863     dst.create( img.width(), img.height(), img.depth() );
04864     int bm_width  = map.width();
04865     int bm_height = map.height();
04866     int bm_bpp = map.depth();
04867     int bm_has_alpha = map.hasAlphaBuffer();
04868 
04869     int yofs1, yofs2, yofs3;
04870 
04871     if ( tiled ) {
04872         yofs2 = MOD( yofs, bm_height );
04873         yofs1 = MOD( yofs2 - 1, bm_height);
04874         yofs3 = MOD( yofs2 + 1, bm_height);
04875     } else {
04876         yofs1 = 0;
04877         yofs2 = 0;
04878         yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 );
04879     }
04880 
04881     BumpmapParams params( azimuth, elevation, depth, type, invert );
04882 
04883     uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
04884     uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
04885     uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
04886 
04887     bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04888     bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04889     bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04890 
04891     for (int y = 0; y < img.height(); ++y)
04892     {
04893         int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
04894 
04895         uint* src_row = (unsigned int*)img.scanLine( y );
04896         uint* dest_row = (unsigned int*)dst.scanLine( y );
04897 
04898         bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
04899                      bm_row1, bm_row2, bm_row3, bm_width, xofs,
04900                      tiled,
04901                      row_in_bumpmap, ambient, compensate,
04902                      &params );
04903 
04904         /* Next line */
04905 
04906         if (tiled || row_in_bumpmap)
04907     {
04908             uint* bm_tmprow = bm_row1;
04909             bm_row1   = bm_row2;
04910             bm_row2   = bm_row3;
04911             bm_row3   = bm_tmprow;
04912 
04913             if (++yofs2 == bm_height)
04914                 yofs2 = 0;
04915 
04916             if (tiled)
04917                 yofs3 = MOD(yofs2 + 1, bm_height);
04918             else
04919                 yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1);
04920 
04921             bm_row3 = (unsigned int*)map.scanLine( yofs3 );
04922             bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
04923                                  params.lut, waterlevel );
04924     }
04925     }
04926     return dst;
04927 }

kdefx

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

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal