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.58 2004/09/30 10:55:31 staniek 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 
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             unsigned int 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                   else 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                   else 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                   else 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         else 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         else 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     if (dst.depth() != 32)
01079         dst = dst.convertDepth(32);
01080 
01081     int pixels = dst.width() * dst.height();
01082 
01083 #ifdef USE_SSE2_INLINE_ASM
01084     if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) {
01085         Q_UINT16 alpha = Q_UINT16( ( 1.0 - opacity ) * 256.0 );
01086 
01087         KIE8Pack packedalpha = { { alpha, alpha, alpha, 256,
01088                                    alpha, alpha, alpha, 256 } };
01089 
01090         Q_UINT16 red   = Q_UINT16( clr.red()   * 256 * opacity );
01091         Q_UINT16 green = Q_UINT16( clr.green() * 256 * opacity );
01092         Q_UINT16 blue  = Q_UINT16( clr.blue()  * 256 * opacity );
01093 
01094         KIE8Pack packedcolor = { { blue, green, red, 0,
01095                                    blue, green, red, 0 } };
01096 
01097         // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending
01098         __asm__ __volatile__(
01099         "pxor        %%xmm7,  %%xmm7\n\t" // Zero out XMM7 for unpacking
01100         "movdqu        (%0),  %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6
01101         "movdqu        (%1),  %%xmm5\n\t" // Set up color * alpha * 256 in XMM5
01102         : : "r"(&packedalpha), "r"(&packedcolor),
01103             "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     Q_INT64 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 = (Q_INT64)image.width()*image.height();
03768     threshold_intensity = number_pixels/1000;
03769 
03770     /* red */
03771     memset(&intensity, 0, sizeof(struct double_packet));
03772     memset(&high, 0, sizeof(struct double_packet));
03773     memset(&low, 0, sizeof(struct double_packet));
03774     for(high.red=255; high.red != 0; high.red--){
03775         intensity.red+=histogram[(unsigned char)high.red].red;
03776         if(intensity.red > threshold_intensity)
03777             break;
03778     }
03779     if(low.red == high.red){
03780         threshold_intensity = 0;
03781         memset(&intensity, 0, sizeof(struct double_packet));
03782         for(low.red=0; low.red < 255; low.red++){
03783             intensity.red+=histogram[(unsigned char)low.red].red;
03784             if(intensity.red > threshold_intensity)
03785                 break;
03786         }
03787         memset(&intensity, 0, sizeof(struct double_packet));
03788         for(high.red=255; high.red != 0; high.red--){
03789             intensity.red+=histogram[(unsigned char)high.red].red;
03790             if(intensity.red > threshold_intensity)
03791                 break;
03792         }
03793     }
03794 
03795     /* green */
03796     memset(&intensity, 0, sizeof(struct double_packet));
03797     for(high.green=255; high.green != 0; high.green--){
03798         intensity.green+=histogram[(unsigned char)high.green].green;
03799         if(intensity.green > threshold_intensity)
03800             break;
03801     }
03802     if(low.green == high.green){
03803         threshold_intensity = 0;
03804         memset(&intensity, 0, sizeof(struct double_packet));
03805         for(low.green=0; low.green < 255; low.green++){
03806             intensity.green+=histogram[(unsigned char)low.green].green;
03807             if(intensity.green > threshold_intensity)
03808                 break;
03809         }
03810         memset(&intensity,0,sizeof(struct double_packet));
03811         for(high.green=255; high.green != 0; high.green--){
03812             intensity.green+=histogram[(unsigned char)high.green].green;
03813             if(intensity.green > threshold_intensity)
03814                 break;
03815         }
03816     }
03817 
03818     /* blue */
03819     memset(&intensity, 0, sizeof(struct double_packet));
03820     for(high.blue=255; high.blue != 0; high.blue--){
03821         intensity.blue+=histogram[(unsigned char)high.blue].blue;
03822         if(intensity.blue > threshold_intensity)
03823             break;
03824     }
03825     if(low.blue == high.blue){
03826         threshold_intensity = 0;
03827         memset(&intensity, 0, sizeof(struct double_packet));
03828         for(low.blue=0; low.blue < 255; low.blue++){
03829             intensity.blue+=histogram[(unsigned char)low.blue].blue;
03830             if(intensity.blue > threshold_intensity)
03831                 break;
03832         }
03833         memset(&intensity,0,sizeof(struct double_packet));
03834         for(high.blue=255; high.blue != 0; high.blue--){
03835             intensity.blue+=histogram[(unsigned char)high.blue].blue;
03836             if(intensity.blue > threshold_intensity)
03837                 break;
03838         }
03839     }
03840 
03841     /* alpha */
03842     memset(&intensity, 0, sizeof(struct double_packet));
03843     for(high.alpha=255; high.alpha != 0; high.alpha--){
03844         intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
03845         if(intensity.alpha > threshold_intensity)
03846             break;
03847     }
03848     if(low.alpha == high.alpha){
03849         threshold_intensity = 0;
03850         memset(&intensity, 0, sizeof(struct double_packet));
03851         for(low.alpha=0; low.alpha < 255; low.alpha++){
03852             intensity.alpha+=histogram[(unsigned char)low.alpha].alpha;
03853             if(intensity.alpha > threshold_intensity)
03854                 break;
03855         }
03856         memset(&intensity,0,sizeof(struct double_packet));
03857         for(high.alpha=255; high.alpha != 0; high.alpha--){
03858             intensity.alpha+=histogram[(unsigned char)high.alpha].alpha;
03859             if(intensity.alpha > threshold_intensity)
03860                 break;
03861         }
03862     }
03863     liberateMemory((void **) &histogram);
03864 
03865     /*
03866      Stretch the histogram to create the normalized image mapping.
03867      */
03868 
03869     // should the maxes be 65535?
03870     memset(normalize_map, 0 ,256*sizeof(struct short_packet));
03871     for(i=0; i <= (long) 255; i++){
03872         if(i < (long) low.red)
03873             normalize_map[i].red=0;
03874         else if (i > (long) high.red)
03875             normalize_map[i].red=65535;
03876         else if (low.red != high.red)
03877             normalize_map[i].red =
03878                 (unsigned short)((65535*(i-low.red))/(high.red-low.red));
03879 
03880         if(i < (long) low.green)
03881             normalize_map[i].green=0;
03882         else if (i > (long) high.green)
03883             normalize_map[i].green=65535;
03884         else if (low.green != high.green)
03885             normalize_map[i].green =
03886                 (unsigned short)((65535*(i-low.green))/(high.green-low.green));
03887 
03888         if(i < (long) low.blue)
03889             normalize_map[i].blue=0;
03890         else if (i > (long) high.blue)
03891             normalize_map[i].blue=65535;
03892         else if (low.blue != high.blue)
03893             normalize_map[i].blue =
03894                 (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue));
03895 
03896         if(i < (long) low.alpha)
03897             normalize_map[i].alpha=0;
03898         else if (i > (long) high.alpha)
03899             normalize_map[i].alpha=65535;
03900         else if (low.alpha != high.alpha)
03901             normalize_map[i].alpha =
03902                 (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha));
03903 
03904     }
03905 
03906     for(y=0; y < image.height(); ++y){
03907         q = (unsigned int *)image.scanLine(y);
03908         for(x=0; x < image.width(); ++x){
03909             if(low.red != high.red)
03910                 r = (normalize_map[(unsigned short)(qRed(q[x]))].red)/257;
03911             else
03912                 r = qRed(q[x]);
03913             if(low.green != high.green)
03914                 g = (normalize_map[(unsigned short)(qGreen(q[x]))].green)/257;
03915             else
03916                 g = qGreen(q[x]);
03917             if(low.blue != high.blue)
03918                 b = (normalize_map[(unsigned short)(qBlue(q[x]))].blue)/257;
03919             else
03920                 b = qBlue(q[x]);
03921             if(low.alpha != high.alpha)
03922                 a = (normalize_map[(unsigned short)(qAlpha(q[x]))].alpha)/257;
03923             else
03924                 a = qAlpha(q[x]);
03925             q[x] = qRgba(r, g, b, a);
03926         }
03927     }
03928     liberateMemory((void **) &normalize_map);
03929 }
03930 
03931 void KImageEffect::equalize(QImage &image)
03932 {
03933     struct double_packet high, low, intensity, *map, *histogram;
03934     struct short_packet *equalize_map;
03935     int x, y;
03936     unsigned int *p, *q;
03937     long i;
03938     unsigned char r, g, b, a;
03939 
03940     if(image.depth() < 32) // result will always be 32bpp
03941         image = image.convertDepth(32);
03942 
03943     histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet));
03944     map=(struct double_packet *) malloc(256*sizeof(struct double_packet));
03945     equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet));
03946     if(!histogram || !map || !equalize_map){
03947         if(histogram)
03948             liberateMemory((void **) &histogram);
03949         if(map)
03950             liberateMemory((void **) &map);
03951         if(equalize_map)
03952             liberateMemory((void **) &equalize_map);
03953         qWarning("KImageEffect::equalize(): Unable to allocate memory!");
03954         return;
03955     }
03956 
03957     /*
03958     Form histogram.
03959     */
03960     memset(histogram, 0, 256*sizeof(struct double_packet));
03961     for(y=0; y < image.height(); ++y){
03962         p = (unsigned int *)image.scanLine(y);
03963         for(x=0; x < image.width(); ++x){
03964             histogram[(unsigned char)(qRed(*p))].red++;
03965             histogram[(unsigned char)(qGreen(*p))].green++;
03966             histogram[(unsigned char)(qBlue(*p))].blue++;
03967             histogram[(unsigned char)(qAlpha(*p))].alpha++;
03968             p++;
03969         }
03970     }
03971     /*
03972      Integrate the histogram to get the equalization map.
03973      */
03974     memset(&intensity, 0 ,sizeof(struct double_packet));
03975     for(i=0; i <= 255; ++i){
03976         intensity.red += histogram[i].red;
03977         intensity.green += histogram[i].green;
03978         intensity.blue += histogram[i].blue;
03979         intensity.alpha += histogram[i].alpha;
03980         map[i]=intensity;
03981     }
03982     low=map[0];
03983     high=map[255];
03984     memset(equalize_map, 0, 256*sizeof(short_packet));
03985     for(i=0; i <= 255; ++i){
03986         if(high.red != low.red)
03987             equalize_map[i].red=(unsigned short)
03988                 ((65535*(map[i].red-low.red))/(high.red-low.red));
03989         if(high.green != low.green)
03990             equalize_map[i].green=(unsigned short)
03991                 ((65535*(map[i].green-low.green))/(high.green-low.green));
03992         if(high.blue != low.blue)
03993             equalize_map[i].blue=(unsigned short)
03994                 ((65535*(map[i].blue-low.blue))/(high.blue-low.blue));
03995         if(high.alpha != low.alpha)
03996             equalize_map[i].alpha=(unsigned short)
03997                 ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha));
03998     }
03999     liberateMemory((void **) &histogram);
04000     liberateMemory((void **) &map);
04001 
04002     /*
04003      Stretch the histogram.
04004      */
04005     for(y=0; y < image.height(); ++y){
04006         q = (unsigned int *)image.scanLine(y);
04007         for(x=0; x < image.width(); ++x){
04008             if(low.red != high.red)
04009                 r = (equalize_map[(unsigned short)(qRed(q[x]))].red/257);
04010             else
04011                 r = qRed(q[x]);
04012             if(low.green != high.green)
04013                 g = (equalize_map[(unsigned short)(qGreen(q[x]))].green/257);
04014             else
04015                 g = qGreen(q[x]);
04016             if(low.blue != high.blue)
04017                 b = (equalize_map[(unsigned short)(qBlue(q[x]))].blue/257);
04018             else
04019                 b = qBlue(q[x]);
04020             if(low.alpha != high.alpha)
04021                 a = (equalize_map[(unsigned short)(qAlpha(q[x]))].alpha/257);
04022             else
04023                 a = qAlpha(q[x]);
04024             q[x] = qRgba(r, g, b, a);
04025         }
04026     }
04027     liberateMemory((void **) &equalize_map);
04028 
04029 }
04030 
04031 QImage KImageEffect::edge(QImage &image, double radius)
04032 {
04033     double *kernel;
04034     int width;
04035     register long i;
04036     QImage dest;
04037 
04038     if(radius == 50.0){
04039         /* For binary compatability! Remove me when possible! This used to
04040          * take a different parameter, a factor, and this was the default
04041          * value */
04042         radius = 0.0;
04043     }
04044 
04045     width = getOptimalKernelWidth(radius, 0.5);
04046     if(image.width() < width || image.height() < width){
04047         qWarning("KImageEffect::edge(): Image is smaller than radius!");
04048         return(dest);
04049     }
04050     kernel= (double *)malloc(width*width*sizeof(double));
04051     if(!kernel){
04052         qWarning("KImageEffect::edge(): Unable to allocate memory!");
04053         return(dest);
04054     }
04055     for(i=0; i < (width*width); i++)
04056         kernel[i]=(-1.0);
04057     kernel[i/2]=width*width-1.0;
04058     convolveImage(&image, &dest, width, kernel);
04059     liberateMemory((void **)&kernel);
04060     return(dest);
04061 }
04062 
04063 QImage KImageEffect::emboss(QImage &src)
04064 {
04065     /* binary compat method - remove me when possible! */
04066     return(emboss(src, 0, 1));
04067 }
04068 
04069 QImage KImageEffect::emboss(QImage &image, double radius, double sigma)
04070 {
04071     double alpha, *kernel;
04072     int j, width;
04073     register long i, u, v;
04074     QImage dest;
04075 
04076     if(sigma == 0.0){
04077         qWarning("KImageEffect::emboss(): Zero sigma is not permitted!");
04078         return(dest);
04079     }
04080 
04081     width = getOptimalKernelWidth(radius, sigma);
04082     if(image.width() < width || image.height() < width){
04083         qWarning("KImageEffect::emboss(): Image is smaller than radius!");
04084         return(dest);
04085     }
04086     kernel= (double *)malloc(width*width*sizeof(double));
04087     if(!kernel){
04088         qWarning("KImageEffect::emboss(): Unable to allocate memory!");
04089         return(dest);
04090     }
04091     if(image.depth() < 32)
04092         image = image.convertDepth(32);
04093 
04094     i=0;
04095     j=width/2;
04096     for(v=(-width/2); v <= (width/2); v++){
04097         for(u=(-width/2); u <= (width/2); u++){
04098             alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
04099             kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/
04100                 (2.0*MagickPI*sigma*sigma);
04101             if (u == j)
04102                 kernel[i]=0.0;
04103             i++;
04104         }
04105         j--;
04106     }
04107     convolveImage(&image, &dest, width, kernel);
04108     liberateMemory((void **)&kernel);
04109 
04110     equalize(dest);
04111     return(dest);
04112 }
04113 
04114 void KImageEffect::blurScanLine(double *kernel, int width,
04115                                 unsigned int *src, unsigned int *dest,
04116                                 int columns)
04117 {
04118     register double *p;
04119     unsigned int *q;
04120     register int x;
04121     register long i;
04122     double red, green, blue, alpha;
04123     double scale = 0.0;
04124 
04125     if(width > columns){
04126         for(x=0; x < columns; ++x){
04127             scale = 0.0;
04128             red = blue = green = alpha = 0.0;
04129             p = kernel;
04130             q = src;
04131             for(i=0; i < columns; ++i){
04132                 if((i >= (x-width/2)) && (i <= (x+width/2))){
04133                     red += (*p)*(qRed(*q)*257);
04134                     green += (*p)*(qGreen(*q)*257);
04135                     blue += (*p)*(qBlue(*q)*257);
04136                     alpha += (*p)*(qAlpha(*q)*257);
04137                 }
04138                 if(((i+width/2-x) >= 0) && ((i+width/2-x) < width))
04139                     scale+=kernel[i+width/2-x];
04140                 p++;
04141                 q++;
04142             }
04143             scale = 1.0/scale;
04144             red = scale*(red+0.5);
04145             green = scale*(green+0.5);
04146             blue = scale*(blue+0.5);
04147             alpha = scale*(alpha+0.5);
04148 
04149             red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04150             green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04151             blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04152             alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04153 
04154             dest[x] = qRgba((unsigned char)(red/257UL),
04155                             (unsigned char)(green/257UL),
04156                             (unsigned char)(blue/257UL),
04157                             (unsigned char)(alpha/257UL));
04158         }
04159         return;
04160     }
04161 
04162     for(x=0; x < width/2; ++x){
04163         scale = 0.0;
04164         red = blue = green = alpha = 0.0;
04165         p = kernel+width/2-x;
04166         q = src;
04167         for(i=width/2-x; i < width; ++i){
04168             red += (*p)*(qRed(*q)*257);
04169             green += (*p)*(qGreen(*q)*257);
04170             blue += (*p)*(qBlue(*q)*257);
04171             alpha += (*p)*(qAlpha(*q)*257);
04172             scale += (*p);
04173             p++;
04174             q++;
04175         }
04176         scale=1.0/scale;
04177 
04178         red = scale*(red+0.5);
04179         green = scale*(green+0.5);
04180         blue = scale*(blue+0.5);
04181         alpha = scale*(alpha+0.5);
04182 
04183         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04184         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04185         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04186         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04187 
04188         dest[x] = qRgba((unsigned char)(red/257UL),
04189                         (unsigned char)(green/257UL),
04190                         (unsigned char)(blue/257UL),
04191                         (unsigned char)(alpha/257UL));
04192     }
04193 
04194     for(; x < columns-width/2; ++x){
04195         red = blue = green = alpha = 0.0;
04196         p = kernel;
04197         q = src+(x-width/2);
04198         for (i=0; i < (long) width; ++i){
04199             red += (*p)*(qRed(*q)*257);
04200             green += (*p)*(qGreen(*q)*257);
04201             blue += (*p)*(qBlue(*q)*257);
04202             alpha += (*p)*(qAlpha(*q)*257);
04203             p++;
04204             q++;
04205         }
04206         red = scale*(red+0.5);
04207         green = scale*(green+0.5);
04208         blue = scale*(blue+0.5);
04209         alpha = scale*(alpha+0.5);
04210 
04211         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04212         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04213         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04214         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04215 
04216         dest[x] = qRgba((unsigned char)(red/257UL),
04217                         (unsigned char)(green/257UL),
04218                         (unsigned char)(blue/257UL),
04219                         (unsigned char)(alpha/257UL));
04220     }
04221 
04222     for(; x < columns; ++x){
04223         red = blue = green = alpha = 0.0;
04224         scale=0;
04225         p = kernel;
04226         q = src+(x-width/2);
04227         for(i=0; i < columns-x+width/2; ++i){
04228             red += (*p)*(qRed(*q)*257);
04229             green += (*p)*(qGreen(*q)*257);
04230             blue += (*p)*(qBlue(*q)*257);
04231             alpha += (*p)*(qAlpha(*q)*257);
04232             scale += (*p);
04233             p++;
04234             q++;
04235         }
04236         scale=1.0/scale;
04237         red = scale*(red+0.5);
04238         green = scale*(green+0.5);
04239         blue = scale*(blue+0.5);
04240         alpha = scale*(alpha+0.5);
04241 
04242         red = red < 0 ? 0 : red > 65535 ? 65535 : red;
04243         green = green < 0 ? 0 : green > 65535 ? 65535 : green;
04244         blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue;
04245         alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha;
04246 
04247         dest[x] = qRgba((unsigned char)(red/257UL),
04248                         (unsigned char)(green/257UL),
04249                         (unsigned char)(blue/257UL),
04250                         (unsigned char)(alpha/257UL));
04251     }
04252 }
04253 
04254 int KImageEffect::getBlurKernel(int width, double sigma, double **kernel)
04255 {
04256 #define KernelRank 3
04257     double alpha, normalize;
04258     register long i;
04259     int bias;
04260 
04261     assert(sigma != 0.0);
04262     if(width == 0)
04263         width = 3;
04264     *kernel=(double *)malloc(width*sizeof(double));
04265     if(*kernel == (double *)NULL)
04266         return(0);
04267     memset(*kernel, 0, width*sizeof(double));
04268     bias = KernelRank*width/2;
04269     for(i=(-bias); i <= bias; i++){
04270         alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma));
04271         (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma);
04272     }
04273     normalize=0;
04274     for(i=0; i < width; i++)
04275         normalize+=(*kernel)[i];
04276     for(i=0; i < width; i++)
04277         (*kernel)[i]/=normalize;
04278 
04279     return(width);
04280 }
04281 
04282 QImage KImageEffect::blur(QImage &src, double /*factor*/)
04283 {
04284     /* binary compat method - remove me when possible! */
04285     return(blur(src, 0, 1));
04286 }
04287 
04288 QImage KImageEffect::blur(QImage &src, double radius, double sigma)
04289 {
04290     double *kernel;
04291     QImage dest;
04292     int width;
04293     int x, y;
04294     unsigned int *scanline, *temp;
04295     unsigned int *p, *q;
04296 
04297     if(sigma == 0.0){
04298         qWarning("KImageEffect::blur(): Zero sigma is not permitted!");
04299         return(dest);
04300     }
04301     if(src.depth() < 32)
04302         src = src.convertDepth(32);
04303 
04304     kernel=(double *) NULL;
04305     if(radius > 0)
04306         width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel);
04307     else{
04308         double *last_kernel;
04309         last_kernel=(double *) NULL;
04310         width=getBlurKernel(3,sigma,&kernel);
04311 
04312         while ((long) (MaxRGB*kernel[0]) > 0){
04313             if(last_kernel != (double *)NULL){
04314                 liberateMemory((void **) &last_kernel);
04315             }
04316             last_kernel=kernel;
04317             kernel = (double *)NULL;
04318             width = getBlurKernel(width+2, sigma, &kernel);
04319         }
04320         if(last_kernel != (double *) NULL){
04321             liberateMemory((void **) &kernel);
04322             width-=2;
04323             kernel = last_kernel;
04324         }
04325     }
04326 
04327     if(width < 3){
04328         qWarning("KImageEffect::blur(): Kernel radius is too small!");
04329         liberateMemory((void **) &kernel);
04330         return(dest);
04331     }
04332 
04333     dest.create(src.width(), src.height(), 32);
04334 
04335     scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
04336     temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height());
04337     for(y=0; y < src.height(); ++y){
04338         p = (unsigned int *)src.scanLine(y);
04339         q = (unsigned int *)dest.scanLine(y);
04340         blurScanLine(kernel, width, p, q, src.width());
04341     }
04342 
04343     unsigned int **srcTable = (unsigned int **)src.jumpTable();
04344     unsigned int **destTable = (unsigned int **)dest.jumpTable();
04345     for(x=0; x < src.width(); ++x){
04346         for(y=0; y < src.height(); ++y){
04347             scanline[y] = srcTable[y][x];
04348         }
04349         blurScanLine(kernel, width, scanline, temp, src.height());
04350         for(y=0; y < src.height(); ++y){
04351             destTable[y][x] = temp[y];
04352         }
04353     }
04354     liberateMemory((void **) &scanline);
04355     liberateMemory((void **) &temp);
04356     liberateMemory((void **) &kernel);
04357     return(dest);
04358 }
04359 
04360 bool KImageEffect::convolveImage(QImage *image, QImage *dest,
04361                                  const unsigned int order,
04362                                  const double *kernel)
04363 {
04364     long width;
04365     double red, green, blue, alpha;
04366     double normalize, *normal_kernel;
04367     register const double *k;
04368     register unsigned int *q;
04369     int x, y, mx, my, sx, sy;
04370     long i;
04371     int mcx, mcy;
04372 
04373     width = order;
04374     if((width % 2) == 0){
04375         qWarning("KImageEffect: Kernel width must be an odd number!");
04376         return(false);
04377     }
04378     normal_kernel = (double *)malloc(width*width*sizeof(double));
04379     if(!normal_kernel){
04380         qWarning("KImageEffect: Unable to allocate memory!");
04381         return(false);
04382     }
04383     dest->reset();
04384     dest->create(image->width(), image->height(), 32);
04385     if(image->depth() < 32)
04386         *image = image->convertDepth(32);
04387 
04388     normalize=0.0;
04389     for(i=0; i < (width*width); i++)
04390         normalize += kernel[i];
04391     if(fabs(normalize) <= MagickEpsilon)
04392         normalize=1.0;
04393     normalize=1.0/normalize;
04394     for(i=0; i < (width*width); i++)
04395         normal_kernel[i] = normalize*kernel[i];
04396 
04397     unsigned int **jumpTable = (unsigned int **)image->jumpTable();
04398     for(y=0; y < dest->height(); ++y){
04399         sy = y-(width/2);
04400         q = (unsigned int *)dest->scanLine(y);
04401         for(x=0; x < dest->width(); ++x){
04402             k = normal_kernel;
04403             red = green = blue = alpha = 0;
04404             sy = y-(width/2);
04405             for(mcy=0; mcy < width; ++mcy, ++sy){
04406                 my = sy < 0 ? 0 : sy > image->height()-1 ?
04407                     image->height()-1 : sy;
04408                 sx = x+(-width/2);
04409                 for(mcx=0; mcx < width; ++mcx, ++sx){
04410                     mx = sx < 0 ? 0 : sx > image->width()-1 ?
04411                         image->width()-1 : sx;
04412                     red += (*k)*(qRed(jumpTable[my][mx])*257);
04413                     green += (*k)*(qGreen(jumpTable[my][mx])*257);
04414                     blue += (*k)*(qBlue(jumpTable[my][mx])*257);
04415                     alpha += (*k)*(qAlpha(jumpTable[my][mx])*257);
04416                     ++k;
04417                 }
04418             }
04419 
04420             red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5;
04421             green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5;
04422             blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5;
04423             alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5;
04424 
04425             *q++ = qRgba((unsigned char)(red/257UL),
04426                          (unsigned char)(green/257UL),
04427                          (unsigned char)(blue/257UL),
04428                          (unsigned char)(alpha/257UL));
04429         }
04430     }
04431     free(normal_kernel);
04432     return(true);
04433 
04434 }
04435 
04436 int KImageEffect::getOptimalKernelWidth(double radius, double sigma)
04437 {
04438     double normalize, value;
04439     long width;
04440     register long u;
04441 
04442     assert(sigma != 0.0);
04443     if(radius > 0.0)
04444         return((int)(2.0*ceil(radius)+1.0));
04445     for(width=5; ;){
04446         normalize=0.0;
04447         for(u=(-width/2); u <= (width/2); u++)
04448             normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma);
04449         u=width/2;
04450         value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize;
04451         if((long)(65535*value) <= 0)
04452             break;
04453         width+=2;
04454     }
04455     return((int)width-2);
04456 }
04457 
04458 QImage KImageEffect::sharpen(QImage &src, double /*factor*/)
04459 {
04460     /* binary compat method - remove me when possible! */
04461     return(sharpen(src, 0, 1));
04462 }
04463 
04464 QImage KImageEffect::sharpen(QImage &image, double radius, double sigma)
04465 {
04466     double alpha, normalize, *kernel;
04467     int width;
04468     register long i, u, v;
04469     QImage dest;
04470 
04471     if(sigma == 0.0){
04472         qWarning("KImageEffect::sharpen(): Zero sigma is not permitted!");
04473         return(dest);
04474     }
04475     width = getOptimalKernelWidth(radius, sigma);
04476     if(image.width() < width){
04477         qWarning("KImageEffect::sharpen(): Image is smaller than radius!");
04478         return(dest);
04479     }
04480     kernel = (double *)malloc(width*width*sizeof(double));
04481     if(!kernel){
04482         qWarning("KImageEffect::sharpen(): Unable to allocate memory!");
04483         return(dest);
04484     }
04485 
04486     i = 0;
04487     normalize=0.0;
04488     for(v=(-width/2); v <= (width/2); v++){
04489         for(u=(-width/2); u <= (width/2); u++){
04490             alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma));
04491             kernel[i]=alpha/(2.0*MagickPI*sigma*sigma);
04492             normalize+=kernel[i];
04493             i++;
04494         }
04495     }
04496     kernel[i/2]=(-2.0)*normalize;
04497     convolveImage(&image, &dest, width, kernel);
04498     liberateMemory((void **) &kernel);
04499     return(dest);
04500 }
04501 
04502 // End of new algorithms
04503 
04504 QImage KImageEffect::shade(QImage &src, bool color_shading, double azimuth,
04505              double elevation)
04506 {
04507     struct PointInfo{
04508         double x, y, z;
04509     };
04510 
04511     double distance, normal_distance, shade;
04512     int x, y;
04513 
04514     struct PointInfo light, normal;
04515 
04516     unsigned int *q;
04517 
04518     QImage dest(src.width(), src.height(), 32);
04519 
04520     azimuth = DegreesToRadians(azimuth);
04521     elevation = DegreesToRadians(elevation);
04522     light.x = MaxRGB*cos(azimuth)*cos(elevation);
04523     light.y = MaxRGB*sin(azimuth)*cos(elevation);
04524     light.z = MaxRGB*sin(elevation);
04525     normal.z= 2*MaxRGB;  // constant Z of surface normal
04526 
04527     if(src.depth() > 8){ // DirectClass source image
04528         unsigned int *p, *s0, *s1, *s2;
04529         for(y=0; y < src.height(); ++y){
04530             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
04531             q = (unsigned int *)dest.scanLine(y);
04532             // shade this row of pixels.
04533             *q++=(*(p+src.width()));
04534             p++;
04535             s0 = p;
04536             s1 = p + src.width();
04537             s2 = p + 2*src.width();
04538             for(x=1; x < src.width()-1; ++x){
04539                 // determine the surface normal and compute shading.
04540                 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
04541                     (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
04542                     (double) intensityValue(*(s2+1));
04543                 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
04544                     (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
04545                     (double) intensityValue(*(s0+1));
04546                 if((normal.x == 0) && (normal.y == 0))
04547                     shade=light.z;
04548                 else{
04549                     shade=0.0;
04550                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04551                     if (distance > 0.0){
04552                         normal_distance=
04553                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04554                         if(fabs(normal_distance) > 0.0000001)
04555                             shade=distance/sqrt(normal_distance);
04556                     }
04557                 }
04558                 if(!color_shading){
04559                     *q = qRgba((unsigned char)(shade),
04560                                (unsigned char)(shade),
04561                                (unsigned char)(shade),
04562                                qAlpha(*s1));
04563                 }
04564                 else{
04565                     *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)),
04566                                (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)),
04567                                (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)),
04568                                qAlpha(*s1));
04569                 }
04570                 ++s0;
04571                 ++s1;
04572                 ++s2;
04573                 q++;
04574             }
04575             *q++=(*s1);
04576         }
04577     }
04578     else{ // PsudeoClass source image
04579         unsigned char *p, *s0, *s1, *s2;
04580         int scanLineIdx;
04581         unsigned int *cTable = (unsigned int *)src.colorTable();
04582         for(y=0; y < src.height(); ++y){
04583             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
04584             p = (unsigned char *)src.scanLine(scanLineIdx);
04585             q = (unsigned int *)dest.scanLine(y);
04586             // shade this row of pixels.
04587             s0 = p;
04588             s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
04589             s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
04590             *q++=(*(cTable+(*s1)));
04591             ++p;
04592             ++s0;
04593             ++s1;
04594             ++s2;
04595             for(x=1; x < src.width()-1; ++x){
04596                 // determine the surface normal and compute shading.
04597                 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
04598                     (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
04599                     (double) intensityValue(*(cTable+(*(s2+1))));
04600                 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
04601                     (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
04602                     (double) intensityValue(*(cTable+(*(s0+1))));
04603                 if((normal.x == 0) && (normal.y == 0))
04604                     shade=light.z;
04605                 else{
04606                     shade=0.0;
04607                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
04608                     if (distance > 0.0){
04609                         normal_distance=
04610                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
04611                         if(fabs(normal_distance) > 0.0000001)
04612                             shade=distance/sqrt(normal_distance);
04613                     }
04614                 }
04615                 if(!color_shading){
04616                     *q = qRgba((unsigned char)(shade),
04617                                (unsigned char)(shade),
04618                                (unsigned char)(shade),
04619                                qAlpha(*(cTable+(*s1))));
04620                 }
04621                 else{
04622                     *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)),
04623                                (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)),
04624                                (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)),
04625                                qAlpha(*s1));
04626                 }
04627                 ++s0;
04628                 ++s1;
04629                 ++s2;
04630                 q++;
04631             }
04632             *q++=(*(cTable+(*s1)));
04633         }
04634     }
04635     return(dest);
04636 }
04637 
04638 // High quality, expensive HSV contrast. You can do a faster one by just
04639 // taking a grayscale threshold (ie: 128) and incrementing RGB color
04640 // channels above it and decrementing those below it, but this gives much
04641 // better results. (mosfet 12/28/01)
04642 void KImageEffect::contrastHSV(QImage &img, bool sharpen)
04643 {
04644     int i, sign;
04645     unsigned int *data;
04646     int count;
04647     double brightness, scale, theta;
04648     QColor c;
04649     int h, s, v;
04650 
04651     sign = sharpen ? 1 : -1;
04652     scale=0.5000000000000001;
04653     if(img.depth() > 8){
04654         count = img.width()*img.height();
04655         data = (unsigned int *)img.bits();
04656     }
04657     else{
04658         count = img.numColors();
04659         data = (unsigned int *)img.colorTable();
04660     }
04661     for(i=0; i < count; ++i){
04662         c.setRgb(data[i]);
04663         c.hsv(&h, &s, &v);
04664         brightness = v/255.0;
04665         theta=(brightness-0.5)*M_PI;
04666         brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
04667         if (brightness > 1.0)
04668             brightness=1.0;
04669         else
04670             if (brightness < 0)
04671                 brightness=0.0;
04672         v = (int)(brightness*255);
04673         c.setHsv(h, s, v);
04674         data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i]));
04675     }
04676 }
04677 
04678 
04679 struct BumpmapParams {
04680     BumpmapParams( double bm_azimuth, double bm_elevation,
04681                    int bm_depth, KImageEffect::BumpmapType bm_type,
04682                    bool invert ) {
04683          /* Convert to radians */
04684         double azimuth = DegreesToRadians( bm_azimuth );
04685         double elevation = DegreesToRadians( bm_elevation );
04686 
04687         /* Calculate the light vector */
04688         lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
04689         ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
04690         int lz         = (int)( sin(elevation) * 255.0 );
04691 
04692         /* Calculate constant Z component of surface normal */
04693         int nz  = (6 * 255) / bm_depth;
04694         nz2     = nz * nz;
04695         nzlz    = nz * lz;
04696 
04697         /* Optimize for vertical normals */
04698         background = lz;
04699 
04700         /* Calculate darkness compensation factor */
04701         compensation = sin(elevation);
04702 
04703         /* Create look-up table for map type */
04704         for (int i = 0; i < 256; i++)
04705         {
04706             double n = 0;
04707             switch (bm_type)
04708             {
04709             case KImageEffect::Spherical:
04710                 n = i / 255.0 - 1.0;
04711                 lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
04712                 break;
04713 
04714             case KImageEffect::Sinuosidal:
04715                 n = i / 255.0;
04716                 lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
04717                                         2.0 + 0.5);
04718                 break;
04719 
04720             case KImageEffect::Linear:
04721             default:
04722                 lut[i] = i;
04723             }
04724 
04725             if (invert)
04726                 lut[i] = 255 - lut[i];
04727         }
04728     }
04729     int lx,  ly;
04730     int nz2, nzlz;
04731     int background;
04732     double compensation;
04733     uchar lut[256];
04734 };
04735 
04736 
04737 static void bumpmap_convert_row( uint *row,
04738                                  int    width,
04739                                  int    bpp,
04740                                  int    has_alpha,
04741                                  uchar *lut,
04742                                  int waterlevel )
04743 {
04744   uint *p;
04745 
04746   p = row;
04747 
04748   has_alpha = has_alpha ? 1 : 0;
04749 
04750   if (bpp >= 3)
04751       for (; width; width--)
04752       {
04753           if (has_alpha) {
04754               unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
04755               *p++ = lut[(unsigned int) ( waterlevel +
04756                                           ( ( idx -
04757                                               waterlevel) * qBlue( *row )) / 255.0 )];
04758           } else {
04759               unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
04760               *p++ = lut[idx];
04761           }
04762 
04763           ++row;
04764       }
04765 }
04766 
04767 static void bumpmap_row( uint           *src,
04768                          uint           *dest,
04769                          int              width,
04770                          int              bpp,
04771                          int              has_alpha,
04772                          uint           *bm_row1,
04773                          uint           *bm_row2,
04774                          uint           *bm_row3,
04775                          int              bm_width,
04776                          int              bm_xofs,
04777                          bool          tiled,
04778                          bool          row_in_bumpmap,
04779                          int           ambient,
04780                          bool          compensate,
04781                          BumpmapParams *params )
04782 {
04783     int xofs1, xofs2, xofs3;
04784     int shade;
04785     int ndotl;
04786     int nx, ny;
04787     int x;
04788     int pbpp;
04789     int tmp;
04790 
04791     if (has_alpha)
04792         pbpp = bpp - 1;
04793     else
04794         pbpp = bpp;
04795 
04796     tmp = bm_xofs;
04797     xofs2 = MOD(tmp, bm_width);
04798 
04799     for (x = 0; x < width; x++)
04800     {
04801         /* Calculate surface normal from bump map */
04802 
04803         if (tiled || (row_in_bumpmap &&
04804                       x >= - tmp && x < - tmp + bm_width)) {
04805             if (tiled) {
04806                 xofs1 = MOD(xofs2 - 1, bm_width);
04807                 xofs3 = MOD(xofs2 + 1, bm_width);
04808         } else {
04809                 xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1);
04810                 xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1);
04811         }
04812             nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
04813                   bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
04814             ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
04815                   bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
04816     } else {
04817             nx = ny = 0;
04818         }
04819 
04820       /* Shade */
04821 
04822         if ((nx == 0) && (ny == 0))
04823             shade = params->background;
04824         else {
04825             ndotl = nx * params->lx + ny * params->ly + params->nzlz;
04826 
04827             if (ndotl < 0)
04828                 shade = (int)( params->compensation * ambient );
04829             else {
04830                 shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) );
04831 
04832                 shade = (int)( shade + QMAX(0.0, (255 * params->compensation - shade)) *
04833                                ambient / 255 );
04834         }
04835     }
04836 
04837         /* Paint */
04838 
04843         if (compensate) {
04844             int red = (int)((qRed( *src ) * shade) / (params->compensation * 255));
04845             int green = (int)((qGreen( *src ) * shade) / (params->compensation * 255));
04846             int blue = (int)((qBlue( *src ) * shade) / (params->compensation * 255));
04847             int alpha = (int)((qAlpha( *src ) * shade) / (params->compensation * 255));
04848             ++src;
04849             *dest++ = qRgba( red, green, blue, alpha );
04850         } else {
04851             int red = qRed( *src ) * shade / 255;
04852             int green = qGreen( *src ) * shade / 255;
04853             int blue = qBlue( *src ) * shade / 255;
04854             int alpha = qAlpha( *src ) * shade / 255;
04855             ++src;
04856             *dest++ = qRgba( red, green, blue, alpha );
04857         }
04858 
04859         /* Next pixel */
04860 
04861         if (++xofs2 == bm_width)
04862             xofs2 = 0;
04863     }
04864 }
04865 
04885 QImage KImageEffect::bumpmap(QImage &img, QImage &map, double azimuth, double elevation,
04886                              int depth, int xofs, int yofs, int waterlevel,
04887                              int ambient, bool compensate, bool invert,
04888                              BumpmapType type, bool tiled)
04889 {
04890     QImage dst;
04891 
04892     if ( img.depth() != 32 || img.depth() != 32 ) {
04893         qWarning( "Bump-mapping effect works only with 32 bit images");
04894         return dst;
04895     }
04896 
04897     dst.create( img.width(), img.height(), img.depth() );
04898     int bm_width  = map.width();
04899     int bm_height = map.height();
04900     int bm_bpp = map.depth();
04901     int bm_has_alpha = map.hasAlphaBuffer();
04902 
04903     int yofs1, yofs2, yofs3;
04904 
04905     if ( tiled ) {
04906         yofs2 = MOD( yofs, bm_height );
04907         yofs1 = MOD( yofs2 - 1, bm_height);
04908         yofs3 = MOD( yofs2 + 1, bm_height);
04909     } else {
04910         yofs1 = 0;
04911         yofs2 = 0;
04912         yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 );
04913     }
04914 
04915     BumpmapParams params( azimuth, elevation, depth, type, invert );
04916 
04917     uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
04918     uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
04919     uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
04920 
04921     bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04922     bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04923     bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
04924 
04925     for (int y = 0; y < img.height(); ++y)
04926     {
04927         int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
04928 
04929         uint* src_row = (unsigned int*)img.scanLine( y );
04930         uint* dest_row = (unsigned int*)dst.scanLine( y );
04931 
04932         bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
04933                      bm_row1, bm_row2, bm_row3, bm_width, xofs,
04934                      tiled,
04935                      row_in_bumpmap, ambient, compensate,
04936                      &params );
04937 
04938         /* Next line */
04939 
04940         if (tiled || row_in_bumpmap)
04941     {
04942             uint* bm_tmprow = bm_row1;
04943             bm_row1   = bm_row2;
04944             bm_row2   = bm_row3;
04945             bm_row3   = bm_tmprow;
04946 
04947             if (++yofs2 == bm_height)
04948                 yofs2 = 0;
04949 
04950             if (tiled)
04951                 yofs3 = MOD(yofs2 + 1, bm_height);
04952             else
04953                 yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1);
04954 
04955             bm_row3 = (unsigned int*)map.scanLine( yofs3 );
04956             bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
04957                                  params.lut, waterlevel );
04958     }
04959     }
04960     return dst;
04961 }
KDE Logo
This file is part of the documentation for kdefx Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 28 01:33:10 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003