# kdefx Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# kimageeffect.cpp

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

This file is part of the documentation for kdefx Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:17:09 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003