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