kdefx Library API Documentation

kimageeffect.cpp

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