kimageeffect.cpp Source File (kdefx Library)

| # kdefx Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | 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) 2000 Josef Weidendorfer <weidendo@in.tum.de>
    00006
    00007 Redistribution and use in source and binary forms, with or without
    00008 modification, are permitted provided that the following conditions
    00009 are met:
    00010
    00011 1. Redistributions of source code must retain the above copyright
    00012    notice, this list of conditions and the following disclaimer.
    00013 2. Redistributions in binary form must reproduce the above copyright
    00014    notice, this list of conditions and the following disclaimer in the
    00015    documentation and/or other materials provided with the distribution.
    00016
    00017 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
    00018 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    00019 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    00020 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    00021 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    00022 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    00023 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    00024 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    00025 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    00026 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    00027
    00028 */
    00029
    00030 #include <math.h>
    00031
    00032 #include <qimage.h>
    00033 #include <stdlib.h>
    00034 #include <iostream>
    00035
    00036 #include "kimageeffect.h"
    00037
    00038 #include <config.h>
    00039
    00040 #define MaxRGB 255L
    00041 #define DegreesToRadians(x) ((x)*M_PI/180.0)
    00042
    00043 using namespace std;
    00044
    00045 inline unsigned int intensityValue(unsigned int color)
    00046 {
    00047     return((unsigned int)((0.299*qRed(color) +
    00048                            0.587*qGreen(color) +
    00049                            0.1140000000000001*qBlue(color))));
    00050 }
    00051
    00052 //======================================================================
    00053 //
    00054 // Gradient effects
    00055 //
    00056 //======================================================================
    00057
    00058 QImage KImageEffect::gradient(const QSize &size, const QColor &ca,
    00059         const QColor &cb, GradientType eff, int ncols)
    00060 {
    00061     int rDiff, gDiff, bDiff;
    00062     int rca, gca, bca, rcb, gcb, bcb;
    00063
    00064     QImage image(size, 32);
    00065
    00066     if (size.width() == 0 || size.height() == 0) {
    00067 #ifndef NDEBUG
    00068       cerr << "WARNING: KImageEffect::gradient: invalid image" << endl;
    00069 #endif
    00070       return image;
    00071     }
    00072
    00073     register int x, y;
    00074
    00075     rDiff = (rcb = cb.red())   - (rca = ca.red());
    00076     gDiff = (gcb = cb.green()) - (gca = ca.green());
    00077     bDiff = (bcb = cb.blue())  - (bca = ca.blue());
    00078
    00079     if( eff == VerticalGradient || eff == HorizontalGradient ){
    00080
    00081         uint *p;
    00082         uint rgb;
    00083
    00084         register int rl = rca << 16;
    00085         register int gl = gca << 16;
    00086         register int bl = bca << 16;
    00087
    00088         if( eff == VerticalGradient ) {
    00089
    00090             int rcdelta = ((1<<16) / size.height()) * rDiff;
    00091             int gcdelta = ((1<<16) / size.height()) * gDiff;
    00092             int bcdelta = ((1<<16) / size.height()) * bDiff;
    00093
    00094             for ( y = 0; y < size.height(); y++ ) {
    00095                 p = (uint *) image.scanLine(y);
    00096
    00097                 rl += rcdelta;
    00098                 gl += gcdelta;
    00099                 bl += bcdelta;
    00100
    00101                 rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) );
    00102
    00103                 for( x = 0; x < size.width(); x++ ) {
    00104                     *p = rgb;
    00105                     p++;
    00106                 }
    00107             }
    00108
    00109         }
    00110         else {                  // must be HorizontalGradient
    00111
    00112             unsigned int *o_src = (unsigned int *)image.scanLine(0);
    00113             unsigned int *src = o_src;
    00114
    00115             int rcdelta = ((1<<16) / size.width()) * rDiff;
    00116             int gcdelta = ((1<<16) / size.width()) * gDiff;
    00117             int bcdelta = ((1<<16) / size.width()) * bDiff;
    00118
    00119             for( x = 0; x < size.width(); x++) {
    00120
    00121                 rl += rcdelta;
    00122                 gl += gcdelta;
    00123                 bl += bcdelta;
    00124
    00125                 *src++ = qRgb( (rl>>16), (gl>>16), (bl>>16));
    00126             }
    00127
    00128             src = o_src;
    00129
    00130             // Believe it or not, manually copying in a for loop is faster
    00131             // than calling memcpy for each scanline (on the order of ms...).
    00132             // I think this is due to the function call overhead (mosfet).
    00133
    00134             for (y = 1; y < size.height(); ++y) {
    00135
    00136                 p = (unsigned int *)image.scanLine(y);
    00137                 src = o_src;
    00138                 for(x=0; x < size.width(); ++x)
    00139                     *p++ = *src++;
    00140             }
    00141         }
    00142     }
    00143
    00144     else {
    00145
    00146         float rfd, gfd, bfd;
    00147         float rd = rca, gd = gca, bd = bca;
    00148
    00149         unsigned char *xtable[3];
    00150         unsigned char *ytable[3];
    00151
    00152         unsigned int w = size.width(), h = size.height();
    00153         xtable[0] = new unsigned char[w];
    00154         xtable[1] = new unsigned char[w];
    00155         xtable[2] = new unsigned char[w];
    00156         ytable[0] = new unsigned char[h];
    00157         ytable[1] = new unsigned char[h];
    00158         ytable[2] = new unsigned char[h];
    00159         w*=2, h*=2;
    00160
    00161         if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) {
    00162             // Diagonal dgradient code inspired by BlackBox (mosfet)
    00163             // BlackBox dgradient is (C) Brad Hughes, <bhughes@tcac.net> and
    00164             // Mike Cole <mike@mydot.com>.
    00165
    00166             rfd = (float)rDiff/w;
    00167             gfd = (float)gDiff/w;
    00168             bfd = (float)bDiff/w;
    00169
    00170             int dir;
    00171             for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) {
    00172                 dir = eff == DiagonalGradient? x : size.width() - x - 1;
    00173                 xtable[0][dir] = (unsigned char) rd;
    00174                 xtable[1][dir] = (unsigned char) gd;
    00175                 xtable[2][dir] = (unsigned char) bd;
    00176             }
    00177             rfd = (float)rDiff/h;
    00178             gfd = (float)gDiff/h;
    00179             bfd = (float)bDiff/h;
    00180             rd = gd = bd = 0;
    00181             for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) {
    00182                 ytable[0][y] = (unsigned char) rd;
    00183                 ytable[1][y] = (unsigned char) gd;
    00184                 ytable[2][y] = (unsigned char) bd;
    00185             }
    00186
    00187             for (y = 0; y < size.height(); y++) {
    00188                 unsigned int *scanline = (unsigned int *)image.scanLine(y);
    00189                 for (x = 0; x < size.width(); x++) {
    00190                     scanline[x] = qRgb(xtable[0][x] + ytable[0][y],
    00191                                        xtable[1][x] + ytable[1][y],
    00192                                        xtable[2][x] + ytable[2][y]);
    00193                 }
    00194             }
    00195         }
    00196
    00197         else if (eff == RectangleGradient ||
    00198                  eff == PyramidGradient ||
    00199                  eff == PipeCrossGradient ||
    00200                  eff == EllipticGradient)
    00201         {
    00202             int rSign = rDiff>0? 1: -1;
    00203             int gSign = gDiff>0? 1: -1;
    00204             int bSign = bDiff>0? 1: -1;
    00205
    00206             rfd = (float)rDiff / size.width();
    00207             gfd = (float)gDiff / size.width();
    00208             bfd = (float)bDiff / size.width();
    00209
    00210             rd = (float)rDiff/2;
    00211             gd = (float)gDiff/2;
    00212             bd = (float)bDiff/2;
    00213
    00214             for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd)
    00215             {
    00216                 xtable[0][x] = (unsigned char) abs((int)rd);
    00217                 xtable[1][x] = (unsigned char) abs((int)gd);
    00218                 xtable[2][x] = (unsigned char) abs((int)bd);
    00219             }
    00220
    00221             rfd = (float)rDiff/size.height();
    00222             gfd = (float)gDiff/size.height();
    00223             bfd = (float)bDiff/size.height();
    00224
    00225             rd = (float)rDiff/2;
    00226             gd = (float)gDiff/2;
    00227             bd = (float)bDiff/2;
    00228
    00229             for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd)
    00230             {
    00231                 ytable[0][y] = (unsigned char) abs((int)rd);
    00232                 ytable[1][y] = (unsigned char) abs((int)gd);
    00233                 ytable[2][y] = (unsigned char) abs((int)bd);
    00234             }
    00235             unsigned int rgb;
    00236             int h = (size.height()+1)>>1;
    00237             for (y = 0; y < h; y++) {
    00238                 unsigned int *sl1 = (unsigned int *)image.scanLine(y);
    00239                 unsigned int *sl2 = (unsigned int *)image.scanLine(QMAX(size.height()-y-1, y));
    00240
    00241                 int w = (size.width()+1)>>1;
    00242                 int x2 = size.width()-1;
    00243
    00244                 for (x = 0; x < w; x++, x2--) {
    00245                     rgb = 0;
    00246                     if (eff == PyramidGradient) {
    00247                         rgb = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
    00248                                    gcb-gSign*(xtable[1][x]+ytable[1][y]),
    00249                                    bcb-bSign*(xtable[2][x]+ytable[2][y]));
    00250                     }
    00251                     if (eff == RectangleGradient) {
    00252                         rgb = qRgb(rcb - rSign *
    00253                                    QMAX(xtable[0][x], ytable[0][y]) * 2,
    00254                                    gcb - gSign *
    00255                                    QMAX(xtable[1][x], ytable[1][y]) * 2,
    00256                                    bcb - bSign *
    00257                                    QMAX(xtable[2][x], ytable[2][y]) * 2);
    00258                     }
    00259                     if (eff == PipeCrossGradient) {
    00260                         rgb = qRgb(rcb - rSign *
    00261                                    QMIN(xtable[0][x], ytable[0][y]) * 2,
    00262                                    gcb - gSign *
    00263                                    QMIN(xtable[1][x], ytable[1][y]) * 2,
    00264                                    bcb - bSign *
    00265                                    QMIN(xtable[2][x], ytable[2][y]) * 2);
    00266                     }
    00267                     if (eff == EllipticGradient) {
    00268                         rgb = qRgb(rcb - rSign *
    00269                                    (int)sqrt((xtable[0][x]*xtable[0][x] +
    00270                                               ytable[0][y]*ytable[0][y])*2.0),
    00271                                    gcb - gSign *
    00272                                    (int)sqrt((xtable[1][x]*xtable[1][x] +
    00273                                               ytable[1][y]*ytable[1][y])*2.0),
    00274                                    bcb - bSign *
    00275                                    (int)sqrt((xtable[2][x]*xtable[2][x] +
    00276                                               ytable[2][y]*ytable[2][y])*2.0));
    00277                     }
    00278
    00279                     sl1[x] = sl2[x] = rgb;
    00280                     sl1[x2] = sl2[x2] = rgb;
    00281                 }
    00282             }
    00283         }
    00284
    00285         delete [] xtable[0];
    00286         delete [] xtable[1];
    00287         delete [] xtable[2];
    00288         delete [] ytable[0];
    00289         delete [] ytable[1];
    00290         delete [] ytable[2];
    00291     }
    00292
    00293     // dither if necessary
    00294     if (ncols && (QPixmap::defaultDepth() < 15 )) {
    00295         if ( ncols < 2 || ncols > 256 )
    00296             ncols = 3;
    00297         QColor *dPal = new QColor[ncols];
    00298         for (int i=0; i<ncols; i++) {
    00299             dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
    00300                              gca + gDiff * i / ( ncols - 1 ),
    00301                              bca + bDiff * i / ( ncols - 1 ) );
    00302         }
    00303     dither(image, dPal, ncols);
    00304         delete [] dPal;
    00305     }
    00306
    00307     return image;
    00308 }
    00309
    00310
    00311 // -----------------------------------------------------------------------------
    00312
    00313 //CT this was (before Dirk A. Mueller's speedup changes)
    00314 //   merely the same code as in the above method, but it's supposedly
    00315 //   way less performant since it introduces a lot of supplementary tests
    00316 //   and simple math operations for the calculus of the balance.
    00317 //      (surprizingly, it isn't less performant, in the contrary :-)
    00318 //   Yes, I could have merged them, but then the excellent performance of
    00319 //   the balanced code would suffer with no other gain than a mere
    00320 //   source code and byte code size economy.
    00321
    00322 QImage KImageEffect::unbalancedGradient(const QSize &size, const QColor &ca,
    00323         const QColor &cb, GradientType eff, int xfactor, int yfactor,
    00324         int ncols)
    00325 {
    00326     int dir; // general parameter used for direction switches
    00327
    00328     bool _xanti = false , _yanti = false;
    00329
    00330     if (xfactor < 0) _xanti = true; // negative on X direction
    00331     if (yfactor < 0) _yanti = true; // negative on Y direction
    00332
    00333     xfactor = abs(xfactor);
    00334     yfactor = abs(yfactor);
    00335
    00336     if (!xfactor) xfactor = 1;
    00337     if (!yfactor) yfactor = 1;
    00338
    00339     if (xfactor > 200 ) xfactor = 200;
    00340     if (yfactor > 200 ) yfactor = 200;
    00341
    00342
    00343     //    float xbal = xfactor/5000.;
    00344     //    float ybal = yfactor/5000.;
    00345     float xbal = xfactor/30./size.width();
    00346     float ybal = yfactor/30./size.height();
    00347     float rat;
    00348
    00349     int rDiff, gDiff, bDiff;
    00350     int rca, gca, bca, rcb, gcb, bcb;
    00351
    00352     QImage image(size, 32);
    00353
    00354     if (size.width() == 0 || size.height() == 0) {
    00355 #ifndef NDEBUG
    00356       cerr << "WARNING: KImageEffect::unbalancedGradient : invalid image\n";
    00357 #endif
    00358       return image;
    00359     }
    00360
    00361     register int x, y;
    00362     unsigned int *scanline;
    00363
    00364     rDiff = (rcb = cb.red())   - (rca = ca.red());
    00365     gDiff = (gcb = cb.green()) - (gca = ca.green());
    00366     bDiff = (bcb = cb.blue())  - (bca = ca.blue());
    00367
    00368     if( eff == VerticalGradient || eff == HorizontalGradient){
    00369         QColor cRow;
    00370
    00371         uint *p;
    00372         uint rgbRow;
    00373
    00374         if( eff == VerticalGradient) {
    00375           for ( y = 0; y < size.height(); y++ ) {
    00376             dir = _yanti ? y : size.height() - 1 - y;
    00377             p = (uint *) image.scanLine(dir);
    00378             rat =  1 - exp( - (float)y  * ybal );
    00379
    00380             cRow.setRgb( rcb - (int) ( rDiff * rat ),
    00381                          gcb - (int) ( gDiff * rat ),
    00382                          bcb - (int) ( bDiff * rat ) );
    00383
    00384             rgbRow = cRow.rgb();
    00385
    00386             for( x = 0; x < size.width(); x++ ) {
    00387               *p = rgbRow;
    00388               p++;
    00389             }
    00390           }
    00391         }
    00392         else {
    00393
    00394           unsigned int *src = (unsigned int *)image.scanLine(0);
    00395           for(x = 0; x < size.width(); x++ )
    00396             {
    00397               dir = _xanti ? x : size.width() - 1 - x;
    00398               rat = 1 - exp( - (float)x  * xbal );
    00399
    00400               src[dir] = qRgb(rcb - (int) ( rDiff * rat ),
    00401                             gcb - (int) ( gDiff * rat ),
    00402                             bcb - (int) ( bDiff * rat ));
    00403             }
    00404
    00405           // Believe it or not, manually copying in a for loop is faster
    00406           // than calling memcpy for each scanline (on the order of ms...).
    00407           // I think this is due to the function call overhead (mosfet).
    00408
    00409           for(y = 1; y < size.height(); ++y)
    00410             {
    00411               scanline = (unsigned int *)image.scanLine(y);
    00412               for(x=0; x < size.width(); ++x)
    00413                 scanline[x] = src[x];
    00414             }
    00415         }
    00416     }
    00417
    00418     else {
    00419       int w=size.width(), h=size.height();
    00420
    00421       unsigned char *xtable[3];
    00422       unsigned char *ytable[3];
    00423       xtable[0] = new unsigned char[w];
    00424       xtable[1] = new unsigned char[w];
    00425       xtable[2] = new unsigned char[w];
    00426       ytable[0] = new unsigned char[h];
    00427       ytable[1] = new unsigned char[h];
    00428       ytable[2] = new unsigned char[h];
    00429
    00430       if ( eff == DiagonalGradient || eff == CrossDiagonalGradient)
    00431         {
    00432           for (x = 0; x < w; x++) {
    00433               dir = _xanti ? x : w - 1 - x;
    00434               rat = 1 - exp( - (float)x * xbal );
    00435
    00436               xtable[0][dir] = (unsigned char) ( rDiff/2 * rat );
    00437               xtable[1][dir] = (unsigned char) ( gDiff/2 * rat );
    00438               xtable[2][dir] = (unsigned char) ( bDiff/2 * rat );
    00439           }
    00440
    00441           for (y = 0; y < h; y++) {
    00442               dir = _yanti ? y : h - 1 - y;
    00443               rat =  1 - exp( - (float)y  * ybal );
    00444
    00445               ytable[0][dir] = (unsigned char) ( rDiff/2 * rat );
    00446               ytable[1][dir] = (unsigned char) ( gDiff/2 * rat );
    00447               ytable[2][dir] = (unsigned char) ( bDiff/2 * rat );
    00448           }
    00449
    00450           for (y = 0; y < h; y++) {
    00451               unsigned int *scanline = (unsigned int *)image.scanLine(y);
    00452               for (x = 0; x < w; x++) {
    00453                   scanline[x] = qRgb(rcb - (xtable[0][x] + ytable[0][y]),
    00454                                      gcb - (xtable[1][x] + ytable[1][y]),
    00455                                      bcb - (xtable[2][x] + ytable[2][y]));
    00456               }
    00457           }
    00458         }
    00459
    00460       else if (eff == RectangleGradient ||
    00461                eff == PyramidGradient ||
    00462                eff == PipeCrossGradient ||
    00463                eff == EllipticGradient)
    00464       {
    00465           int rSign = rDiff>0? 1: -1;
    00466           int gSign = gDiff>0? 1: -1;
    00467           int bSign = bDiff>0? 1: -1;
    00468
    00469           for (x = 0; x < w; x++)
    00470             {
    00471                 dir = _xanti ? x : w - 1 - x;
    00472                 rat =  1 - exp( - (float)x * xbal );
    00473
    00474                 xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
    00475                 xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
    00476                 xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
    00477             }
    00478
    00479           for (y = 0; y < h; y++)
    00480           {
    00481               dir = _yanti ? y : h - 1 - y;
    00482
    00483               rat =  1 - exp( - (float)y * ybal );
    00484
    00485               ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
    00486               ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
    00487               ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
    00488           }
    00489
    00490           for (y = 0; y < h; y++) {
    00491               unsigned int *scanline = (unsigned int *)image.scanLine(y);
    00492               for (x = 0; x < w; x++) {
    00493                   if (eff == PyramidGradient)
    00494                   {
    00495                       scanline[x] = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
    00496                                          gcb-gSign*(xtable[1][x]+ytable[1][y]),
    00497                                          bcb-bSign*(xtable[2][x]+ytable[2][y]));
    00498                   }
    00499                   if (eff == RectangleGradient)
    00500                   {
    00501                       scanline[x] = qRgb(rcb - rSign *
    00502                                          QMAX(xtable[0][x], ytable[0][y]) * 2,
    00503                                          gcb - gSign *
    00504                                          QMAX(xtable[1][x], ytable[1][y]) * 2,
    00505                                          bcb - bSign *
    00506                                          QMAX(xtable[2][x], ytable[2][y]) * 2);
    00507                   }
    00508                   if (eff == PipeCrossGradient)
    00509                   {
    00510                       scanline[x] = qRgb(rcb - rSign *
    00511                                          QMIN(xtable[0][x], ytable[0][y]) * 2,
    00512                                          gcb - gSign *
    00513                                          QMIN(xtable[1][x], ytable[1][y]) * 2,
    00514                                          bcb - bSign *
    00515                                          QMIN(xtable[2][x], ytable[2][y]) * 2);
    00516                   }
    00517                   if (eff == EllipticGradient)
    00518                   {
    00519                       scanline[x] = qRgb(rcb - rSign *
    00520                                          (int)sqrt((xtable[0][x]*xtable[0][x] +
    00521                                                     ytable[0][y]*ytable[0][y])*2.0),
    00522                                          gcb - gSign *
    00523                                          (int)sqrt((xtable[1][x]*xtable[1][x] +
    00524                                                     ytable[1][y]*ytable[1][y])*2.0),
    00525                                          bcb - bSign *
    00526                                          (int)sqrt((xtable[2][x]*xtable[2][x] +
    00527                                                     ytable[2][y]*ytable[2][y])*2.0));
    00528                   }
    00529               }
    00530           }
    00531       }
    00532
    00533       if (ncols && (QPixmap::defaultDepth() < 15 )) {
    00534           if ( ncols < 2 || ncols > 256 )
    00535               ncols = 3;
    00536           QColor *dPal = new QColor[ncols];
    00537           for (int i=0; i<ncols; i++) {
    00538               dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
    00539                                gca + gDiff * i / ( ncols - 1 ),
    00540                                bca + bDiff * i / ( ncols - 1 ) );
    00541           }
    00542           dither(image, dPal, ncols);
    00543           delete [] dPal;
    00544       }
    00545
    00546       delete [] xtable[0];
    00547       delete [] xtable[1];
    00548       delete [] xtable[2];
    00549       delete [] ytable[0];
    00550       delete [] ytable[1];
    00551       delete [] ytable[2];
    00552
    00553     }
    00554
    00555     return image;
    00556 }
    00557
    00558
    00559 //======================================================================
    00560 //
    00561 // Intensity effects
    00562 //
    00563 //======================================================================
    00564
    00565
    00566 /* This builds a 256 byte unsigned char lookup table with all
    00567  * the possible percent values prior to applying the effect, then uses
    00568  * integer math for the pixels. For any image larger than 9x9 this will be
    00569  * less expensive than doing a float operation on the 3 color components of
    00570  * each pixel. (mosfet)
    00571  */
    00572
    00573 QImage& KImageEffect::intensity(QImage &image, float percent)
    00574 {
    00575     if (image.width() == 0 || image.height() == 0) {
    00576 #ifndef NDEBUG
    00577       cerr << "WARNING: KImageEffect::intensity : invalid image\n";
    00578 #endif
    00579       return image;
    00580     }
    00581
    00582     int segColors = image.depth() > 8 ? 256 : image.numColors();
    00583     unsigned char *segTbl = new unsigned char[segColors];
    00584     int pixels = image.depth() > 8 ? image.width()*image.height() :
    00585         image.numColors();
    00586     unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
    00587         (unsigned int *)image.colorTable();
    00588
    00589     bool brighten = (percent >= 0);
    00590     if(percent < 0)
    00591         percent = -percent;
    00592
    00593     if(brighten){ // keep overflow check out of loops
    00594         for(int i=0; i < segColors; ++i){
    00595             int tmp = (int)(i*percent);
    00596             if(tmp > 255)
    00597                 tmp = 255;
    00598             segTbl[i] = tmp;
    00599         }
    00600     }
    00601     else{
    00602         for(int i=0; i < segColors; ++i){
    00603             int tmp = (int)(i*percent);
    00604             if(tmp < 0)
    00605                 tmp = 0;
    00606             segTbl[i] = tmp;
    00607         }
    00608     }
    00609
    00610     if(brighten){ // same here
    00611         for(int i=0; i < pixels; ++i){
    00612             int r = qRed(data[i]);
    00613             int g = qGreen(data[i]);
    00614             int b = qBlue(data[i]);
    00615             int a = qAlpha(data[i]);
    00616             r = r + segTbl[r] > 255 ? 255 : r + segTbl[r];
    00617             g = g + segTbl[g] > 255 ? 255 : g + segTbl[g];
    00618             b = b + segTbl[b] > 255 ? 255 : b + segTbl[b];
    00619             data[i] = qRgba(r, g, b,a);
    00620         }
    00621     }
    00622     else{
    00623         for(int i=0; i < pixels; ++i){
    00624             int r = qRed(data[i]);
    00625             int g = qGreen(data[i]);
    00626             int b = qBlue(data[i]);
    00627             int a = qAlpha(data[i]);
    00628             r = r - segTbl[r] < 0 ? 0 : r - segTbl[r];
    00629             g = g - segTbl[g] < 0 ? 0 : g - segTbl[g];
    00630             b = b - segTbl[b] < 0 ? 0 : b - segTbl[b];
    00631             data[i] = qRgba(r, g, b, a);
    00632         }
    00633     }
    00634     delete [] segTbl;
    00635
    00636     return image;
    00637 }
    00638
    00639 QImage& KImageEffect::channelIntensity(QImage &image, float percent,
    00640                                        RGBComponent channel)
    00641 {
    00642     if (image.width() == 0 || image.height() == 0) {
    00643 #ifndef NDEBUG
    00644       cerr << "WARNING: KImageEffect::channelIntensity : invalid image\n";
    00645 #endif
    00646       return image;
    00647     }
    00648
    00649     int segColors = image.depth() > 8 ? 256 : image.numColors();
    00650     unsigned char *segTbl = new unsigned char[segColors];
    00651     int pixels = image.depth() > 8 ? image.width()*image.height() :
    00652         image.numColors();
    00653     unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
    00654         (unsigned int *)image.colorTable();
    00655     bool brighten = (percent >= 0);
    00656     if(percent < 0)
    00657         percent = -percent;
    00658
    00659     if(brighten){ // keep overflow check out of loops
    00660         for(int i=0; i < segColors; ++i){
    00661             int tmp = (int)(i*percent);
    00662             if(tmp > 255)
    00663                 tmp = 255;
    00664             segTbl[i] = tmp;
    00665         }
    00666     }
    00667     else{
    00668         for(int i=0; i < segColors; ++i){
    00669             int tmp = (int)(i*percent);
    00670             if(tmp < 0)
    00671                 tmp = 0;
    00672             segTbl[i] = tmp;
    00673         }
    00674     }
    00675
    00676     if(brighten){ // same here
    00677         if(channel == Red){ // and here ;-)
    00678             for(int i=0; i < pixels; ++i){
    00679                 int c = qRed(data[i]);
    00680                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
    00681                 data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
    00682             }
    00683         }
    00684         if(channel == Green){
    00685             for(int i=0; i < pixels; ++i){
    00686                 int c = qGreen(data[i]);
    00687                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
    00688                 data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
    00689             }
    00690         }
    00691         else{
    00692             for(int i=0; i < pixels; ++i){
    00693                 int c = qBlue(data[i]);
    00694                 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
    00695                 data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
    00696             }
    00697         }
    00698
    00699     }
    00700     else{
    00701         if(channel == Red){
    00702             for(int i=0; i < pixels; ++i){
    00703                 int c = qRed(data[i]);
    00704                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
    00705                 data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
    00706             }
    00707         }
    00708         if(channel == Green){
    00709             for(int i=0; i < pixels; ++i){
    00710                 int c = qGreen(data[i]);
    00711                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
    00712                 data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
    00713             }
    00714         }
    00715         else{
    00716             for(int i=0; i < pixels; ++i){
    00717                 int c = qBlue(data[i]);
    00718                 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
    00719                 data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
    00720             }
    00721         }
    00722     }
    00723     delete [] segTbl;
    00724
    00725     return image;
    00726 }
    00727
    00728 // Modulate an image with an RBG channel of another image
    00729 //
    00730 QImage& KImageEffect::modulate(QImage &image, QImage &modImage, bool reverse,
    00731         ModulationType type, int factor, RGBComponent channel)
    00732 {
    00733     if (image.width() == 0 || image.height() == 0 ||
    00734         modImage.width() == 0 || modImage.height() == 0) {
    00735 #ifndef NDEBUG
    00736       cerr << "WARNING: KImageEffect::modulate : invalid image\n";
    00737 #endif
    00738       return image;
    00739     }
    00740
    00741     int r, g, b, h, s, v, a;
    00742     QColor clr;
    00743     int mod=0;
    00744     unsigned int x1, x2, y1, y2;
    00745     register int x, y;
    00746
    00747     // for image, we handle only depth 32
    00748     if (image.depth()<32) image = image.convertDepth(32);
    00749
    00750     // for modImage, we handle depth 8 and 32
    00751     if (modImage.depth()<8) modImage = modImage.convertDepth(8);
    00752
    00753     unsigned int *colorTable2 = (modImage.depth()==8) ?
    00754                                  modImage.colorTable():0;
    00755     unsigned int *data1, *data2;
    00756     unsigned char *data2b;
    00757     unsigned int color1, color2;
    00758
    00759     x1 = image.width();    y1 = image.height();
    00760     x2 = modImage.width(); y2 = modImage.height();
    00761
    00762     for (y = 0; y < (int)y1; y++) {
    00763         data1 =  (unsigned int *) image.scanLine(y);
    00764         data2 =  (unsigned int *) modImage.scanLine( y%y2 );
    00765         data2b = (unsigned char *) modImage.scanLine( y%y2 );
    00766
    00767         x=0;
    00768         while(x < (int)x1) {
    00769           color2 = (colorTable2) ? colorTable2[*data2b] : *data2;
    00770           if (reverse) {
    00771               color1 = color2;
    00772               color2 = *data1;
    00773           }
    00774           else
    00775               color1 = *data1;
    00776
    00777           if (type == Intensity || type == Contrast) {
    00778               r = qRed(color1);
    00779               g = qGreen(color1);
    00780               b = qBlue(color1);
    00781               if (channel != All) {
    00782                 mod = (channel == Red) ? qRed(color2) :
    00783                     (channel == Green) ? qGreen(color2) :
    00784                     (channel == Blue) ? qBlue(color2) :
    00785                     (channel == Gray) ? qGray(color2) : 0;
    00786                 mod = mod*factor/50;
    00787               }
    00788
    00789               if (type == Intensity) {
    00790                 if (channel == All) {
    00791                   r += r * factor/50 * qRed(color2)/256;
    00792                   g += g * factor/50 * qGreen(color2)/256;
    00793                   b += b * factor/50 * qBlue(color2)/256;
    00794                 }
    00795                 else {
    00796                   r += r * mod/256;
    00797                   g += g * mod/256;
    00798                   b += b * mod/256;
    00799                 }
    00800               }
    00801               else { // Contrast
    00802                 if (channel == All) {
    00803                   r += (r-128) * factor/50 * qRed(color2)/128;
    00804                   g += (g-128) * factor/50 * qGreen(color2)/128;
    00805                   b += (b-128) * factor/50 * qBlue(color2)/128;
    00806                 }
    00807                 else {
    00808                   r += (r-128) * mod/128;
    00809                   g += (g-128) * mod/128;
    00810                   b += (b-128) * mod/128;
    00811                 }
    00812               }
    00813
    00814               if (r<0) r=0; if (r>255) r=255;
    00815               if (g<0) g=0; if (g>255) g=255;
    00816               if (b<0) b=0; if (b>255) b=255;
    00817               a = qAlpha(*data1);
    00818               *data1 = qRgba(r, g, b, a);
    00819           }
    00820           else if (type == Saturation || type == HueShift) {
    00821               clr.setRgb(color1);
    00822               clr.hsv(&h, &s, &v);
    00823               mod = (channel == Red) ? qRed(color2) :
    00824                     (channel == Green) ? qGreen(color2) :
    00825                     (channel == Blue) ? qBlue(color2) :
    00826                     (channel == Gray) ? qGray(color2) : 0;
    00827               mod = mod*factor/50;
    00828
    00829               if (type == Saturation) {
    00830                   s -= s * mod/256;
    00831                   if (s<0) s=0; if (s>255) s=255;
    00832               }
    00833               else { // HueShift
    00834                 h += mod;
    00835                 while(h<0) h+=360;
    00836                 h %= 360;
    00837               }
    00838
    00839               clr.setHsv(h, s, v);
    00840               a = qAlpha(*data1);
    00841               *data1 = clr.rgb() | ((uint)(a & 0xff) << 24);
    00842           }
    00843           data1++; data2++; data2b++; x++;
    00844           if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; }
    00845         }
    00846     }
    00847     return image;
    00848 }
    00849
    00850
    00851
    00852 //======================================================================
    00853 //
    00854 // Blend effects
    00855 //
    00856 //======================================================================
    00857
    00858
    00859 // Nice and fast direct pixel manipulation
    00860 QImage& KImageEffect::blend(const QColor& clr, QImage& dst, float opacity)
    00861 {
    00862     if (dst.width() <= 0 || dst.height() <= 0)
    00863         return dst;
    00864
    00865     if (opacity < 0.0 || opacity > 1.0) {
    00866 #ifndef NDEBUG
    00867         cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
    00868 #endif
    00869         return dst;
    00870     }
    00871
    00872     int depth = dst.depth();
    00873     if (depth != 32)
    00874         dst = dst.convertDepth(32);
    00875
    00876     int pixels = dst.width() * dst.height();
    00877     int rcol, gcol, bcol;
    00878     clr.rgb(&rcol, &gcol, &bcol);
    00879
    00880 #ifdef WORDS_BIGENDIAN   // ARGB (skip alpha)
    00881     register unsigned char *data = (unsigned char *)dst.bits() + 1;
    00882 #else                    // BGRA
    00883     register unsigned char *data = (unsigned char *)dst.bits();
    00884 #endif
    00885
    00886     for (register int i=0; i<pixels; i++)
    00887     {
    00888 #ifdef WORDS_BIGENDIAN
    00889         *(data++) += (unsigned char)((rcol - *data) * opacity);
    00890         *(data++) += (unsigned char)((gcol - *data) * opacity);
    00891         *(data++) += (unsigned char)((bcol - *data) * opacity);
    00892 #else
    00893         *(data++) += (unsigned char)((bcol - *data) * opacity);
    00894         *(data++) += (unsigned char)((gcol - *data) * opacity);
    00895         *(data++) += (unsigned char)((rcol - *data) * opacity);
    00896 #endif
    00897         data++; // skip alpha
    00898     }
    00899     return dst;
    00900 }
    00901
    00902 // Nice and fast direct pixel manipulation
    00903 QImage& KImageEffect::blend(QImage& src, QImage& dst, float opacity)
    00904 {
    00905     if (src.width() <= 0 || src.height() <= 0)
    00906         return dst;
    00907     if (dst.width() <= 0 || dst.height() <= 0)
    00908         return dst;
    00909
    00910     if (src.width() != dst.width() || src.height() != dst.height()) {
    00911 #ifndef NDEBUG
    00912         cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n";
    00913 #endif
    00914         return dst;
    00915     }
    00916
    00917     if (opacity < 0.0 || opacity > 1.0) {
    00918 #ifndef NDEBUG
    00919         cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n";
    00920 #endif
    00921         return dst;
    00922     }
    00923
    00924     if (src.depth() != 32) src = src.convertDepth(32);
    00925     if (dst.depth() != 32) dst = dst.convertDepth(32);
    00926
    00927     int pixels = src.width() * src.height();
    00928 #ifdef WORDS_BIGENDIAN   // ARGB (skip alpha)
    00929     register unsigned char *data1 = (unsigned char *)dst.bits() + 1;
    00930     register unsigned char *data2 = (unsigned char *)src.bits() + 1;
    00931 #else                    // BGRA
    00932     register unsigned char *data1 = (unsigned char *)dst.bits();
    00933     register unsigned char *data2 = (unsigned char *)src.bits();
    00934 #endif
    00935
    00936     for (register int i=0; i<pixels; i++)
    00937     {
    00938 #ifdef WORDS_BIGENDIAN
    00939         *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
    00940         *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
    00941         *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
    00942 #else
    00943         *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
    00944         *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
    00945         *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
    00946 #endif
    00947         data1++; // skip alpha
    00948         data2++;
    00949     }
    00950
    00951     return dst;
    00952 }
    00953
    00954
    00955 QImage& KImageEffect::blend(QImage &image, float initial_intensity,
    00956                             const QColor &bgnd, GradientType eff,
    00957                             bool anti_dir)
    00958 {
    00959     if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
    00960 #ifndef NDEBUG
    00961       cerr << "WARNING: KImageEffect::blend : invalid image\n";
    00962 #endif
    00963       return image;
    00964     }
    00965
    00966     int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
    00967     int r, g, b;
    00968     int ind;
    00969
    00970     unsigned int xi, xf, yi, yf;
    00971     unsigned int a;
    00972
    00973     // check the boundaries of the initial intesity param
    00974     float unaffected = 1;
    00975     if (initial_intensity >  1) initial_intensity =  1;
    00976     if (initial_intensity < -1) initial_intensity = -1;
    00977     if (initial_intensity < 0) {
    00978         unaffected = 1. + initial_intensity;
    00979         initial_intensity = 0;
    00980     }
    00981
    00982
    00983     float intensity = initial_intensity;
    00984     float var = 1. - initial_intensity;
    00985
    00986     if (anti_dir) {
    00987         initial_intensity = intensity = 1.;
    00988         var = -var;
    00989     }
    00990
    00991     register int x, y;
    00992
    00993     unsigned int *data =  (unsigned int *)image.bits();
    00994
    00995     int image_width = image.width(); //Those can't change
    00996     int image_height = image.height();
    00997
    00998
    00999     if( eff == VerticalGradient || eff == HorizontalGradient ) {
    01000
    01001         // set the image domain to apply the effect to
    01002         xi = 0, xf = image_width;
    01003         yi = 0, yf = image_height;
    01004         if (eff == VerticalGradient) {
    01005             if (anti_dir) yf = (int)(image_height * unaffected);
    01006             else yi = (int)(image_height * (1 - unaffected));
    01007         }
    01008         else {
    01009             if (anti_dir) xf = (int)(image_width * unaffected);
    01010             else xi = (int)(image_height * (1 - unaffected));
    01011         }
    01012
    01013         var /= (eff == VerticalGradient?yf-yi:xf-xi);
    01014
    01015         int ind_base;
    01016         for (y = yi; y < (int)yf; y++) {
    01017             intensity = eff == VerticalGradient? intensity + var :
    01018                 initial_intensity;
    01019             ind_base = image_width  * y ;
    01020             for (x = xi; x < (int)xf ; x++) {
    01021                 if (eff == HorizontalGradient) intensity += var;
    01022                 ind = x + ind_base;
    01023                 r = qRed  (data[ind]) + (int)(intensity *
    01024                                               (r_bgnd - qRed  (data[ind])));
    01025                 g = qGreen(data[ind]) + (int)(intensity *
    01026                                               (g_bgnd - qGreen(data[ind])));
    01027                 b = qBlue (data[ind]) + (int)(intensity *
    01028                                               (b_bgnd - qBlue (data[ind])));
    01029                 if (r > 255) r = 255; if (r < 0 ) r = 0;
    01030                 if (g > 255) g = 255; if (g < 0 ) g = 0;
    01031                 if (b > 255) b = 255; if (b < 0 ) b = 0;
    01032                 a = qAlpha(data[ind]);
    01033                 data[ind] = qRgba(r, g, b, a);
    01034             }
    01035         }
    01036     }
    01037     else if (eff == DiagonalGradient  || eff == CrossDiagonalGradient) {
    01038         float xvar = var / 2 / image_width;  // / unaffected;
    01039         float yvar = var / 2 / image_height; // / unaffected;
    01040         float tmp;
    01041
    01042         for (x = 0; x < image_width ; x++) {
    01043             tmp =  xvar * (eff == DiagonalGradient? x : image.width()-x-1);
    01044             ind = x;
    01045             for (y = 0; y < image_height ; y++) {
    01046                 intensity = initial_intensity + tmp + yvar * y;
    01047
    01048                 r = qRed  (data[ind]) + (int)(intensity *
    01049                                               (r_bgnd - qRed  (data[ind])));
    01050                 g = qGreen(data[ind]) + (int)(intensity *
    01051                                               (g_bgnd - qGreen(data[ind])));
    01052                 b = qBlue (data[ind]) + (int)(intensity *
    01053                                               (b_bgnd - qBlue (data[ind])));
    01054                 if (r > 255) r = 255; if (r < 0 ) r = 0;
    01055                 if (g > 255) g = 255; if (g < 0 ) g = 0;
    01056                 if (b > 255) b = 255; if (b < 0 ) b = 0;
    01057                 a = qAlpha(data[ind]);
    01058                 data[ind] = qRgba(r, g, b, a);
    01059
    01060                 ind += image_width;
    01061             }
    01062         }
    01063     }
    01064
    01065     else if (eff == RectangleGradient || eff == EllipticGradient) {
    01066         float xvar;
    01067         float yvar;
    01068
    01069         for (x = 0; x < image_width / 2 + image_width % 2; x++) {
    01070             xvar = var / image_width  * (image_width - x*2/unaffected-1);
    01071             for (y = 0; y < image_height / 2 + image_height % 2; y++) {
    01072                 yvar = var / image_height   * (image_height - y*2/unaffected -1);
    01073
    01074                 if (eff == RectangleGradient)
    01075                     intensity = initial_intensity + QMAX(xvar, yvar);
    01076                 else
    01077                     intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
    01078                 if (intensity > 1) intensity = 1;
    01079                 if (intensity < 0) intensity = 0;
    01080
    01081                 //NW
    01082                 ind = x + image_width  * y ;
    01083                 r = qRed  (data[ind]) + (int)(intensity *
    01084                                               (r_bgnd - qRed  (data[ind])));
    01085                 g = qGreen(data[ind]) + (int)(intensity *
    01086                                               (g_bgnd - qGreen(data[ind])));
    01087                 b = qBlue (data[ind]) + (int)(intensity *
    01088                                               (b_bgnd - qBlue (data[ind])));
    01089                 if (r > 255) r = 255; if (r < 0 ) r = 0;
    01090                 if (g > 255) g = 255; if (g < 0 ) g = 0;
    01091                 if (b > 255) b = 255; if (b < 0 ) b = 0;
    01092                 a = qAlpha(data[ind]);
    01093                 data[ind] = qRgba(r, g, b, a);
    01094
    01095                 //NE
    01096                 ind = image_width - x - 1 + image_width  * y ;
    01097                 r = qRed  (data[ind]) + (int)(intensity *
    01098                                               (r_bgnd - qRed  (data[ind])));
    01099                 g = qGreen(data[ind]) + (int)(intensity *
    01100                                               (g_bgnd - qGreen(data[ind])));
    01101                 b = qBlue (data[ind]) + (int)(intensity *
    01102                                               (b_bgnd - qBlue (data[ind])));
    01103                 if (r > 255) r = 255; if (r < 0 ) r = 0;
    01104                 if (g > 255) g = 255; if (g < 0 ) g = 0;
    01105                 if (b > 255) b = 255; if (b < 0 ) b = 0;
    01106                 a = qAlpha(data[ind]);
    01107                 data[ind] = qRgba(r, g, b, a);
    01108             }
    01109         }
    01110
    01111         //CT  loop is doubled because of stupid central row/column issue.
    01112         //    other solution?
    01113         for (x = 0; x < image_width / 2; x++) {
    01114             xvar = var / image_width  * (image_width - x*2/unaffected-1);
    01115             for (y = 0; y < image_height / 2; y++) {
    01116                 yvar = var / image_height   * (image_height - y*2/unaffected -1);
    01117
    01118                 if (eff == RectangleGradient)
    01119                     intensity = initial_intensity + QMAX(xvar, yvar);
    01120                 else
    01121                     intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
    01122                 if (intensity > 1) intensity = 1;
    01123                 if (intensity < 0) intensity = 0;
    01124
    01125                 //SW
    01126                 ind = x + image_width  * (image_height - y -1) ;
    01127                 r = qRed  (data[ind]) + (int)(intensity *
    01128                                               (r_bgnd - qRed  (data[ind])));
    01129                 g = qGreen(data[ind]) + (int)(intensity *
    01130                                               (g_bgnd - qGreen(data[ind])));
    01131                 b = qBlue (data[ind]) + (int)(intensity *
    01132                                               (b_bgnd - qBlue (data[ind])));
    01133                 if (r > 255) r = 255; if (r < 0 ) r = 0;
    01134                 if (g > 255) g = 255; if (g < 0 ) g = 0;
    01135                 if (b > 255) b = 255; if (b < 0 ) b = 0;
    01136                 a = qAlpha(data[ind]);
    01137                 data[ind] = qRgba(r, g, b, a);
    01138
    01139                 //SE
    01140                 ind = image_width-x-1 + image_width * (image_height - y - 1) ;
    01141                 r = qRed  (data[ind]) + (int)(intensity *
    01142                                               (r_bgnd - qRed  (data[ind])));
    01143                 g = qGreen(data[ind]) + (int)(intensity *
    01144                                               (g_bgnd - qGreen(data[ind])));
    01145                 b = qBlue (data[ind]) + (int)(intensity *
    01146                                               (b_bgnd - qBlue (data[ind])));
    01147                 if (r > 255) r = 255; if (r < 0 ) r = 0;
    01148                 if (g > 255) g = 255; if (g < 0 ) g = 0;
    01149                 if (b > 255) b = 255; if (b < 0 ) b = 0;
    01150                 a = qAlpha(data[ind]);
    01151                 data[ind] = qRgba(r, g, b, a);
    01152             }
    01153         }
    01154     }
    01155 #ifndef NDEBUG
    01156     else cerr << "KImageEffect::blend effect not implemented" << endl;
    01157 #endif
    01158     return image;
    01159 }
    01160
    01161 // Not very efficient as we create a third big image...
    01162 //
    01163 QImage& KImageEffect::blend(QImage &image1, QImage &image2,
    01164                             GradientType gt, int xf, int yf)
    01165 {
    01166   if (image1.width() == 0 || image1.height() == 0 ||
    01167       image2.width() == 0 || image2.height() == 0)
    01168     return image1;
    01169
    01170   QImage image3;
    01171
    01172   image3 = KImageEffect::unbalancedGradient(image1.size(),
    01173                                     QColor(0,0,0), QColor(255,255,255),
    01174                                     gt, xf, yf, 0);
    01175
    01176   return blend(image1,image2,image3, Red); // Channel to use is arbitrary
    01177 }
    01178
    01179 // Blend image2 into image1, using an RBG channel of blendImage
    01180 //
    01181 QImage& KImageEffect::blend(QImage &image1, QImage &image2,
    01182                             QImage &blendImage, RGBComponent channel)
    01183 {
    01184     if (image1.width() == 0 || image1.height() == 0 ||
    01185         image2.width() == 0 || image2.height() == 0 ||
    01186         blendImage.width() == 0 || blendImage.height() == 0) {
    01187 #ifndef NDEBUG
    01188       cerr << "KImageEffect::blend effect invalid image" << endl;
    01189 #endif
    01190       return image1;
    01191     }
    01192
    01193     int r, g, b;
    01194     int ind1, ind2, ind3;
    01195
    01196     unsigned int x1, x2, x3, y1, y2, y3;
    01197     unsigned int a;
    01198
    01199     register int x, y;
    01200
    01201     // for image1 and image2, we only handle depth 32
    01202     if (image1.depth()<32) image1 = image1.convertDepth(32);
    01203     if (image2.depth()<32) image2 = image2.convertDepth(32);
    01204
    01205     // for blendImage, we handle depth 8 and 32
    01206     if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
    01207
    01208     unsigned int *colorTable3 = (blendImage.depth()==8) ?
    01209                                  blendImage.colorTable():0;
    01210
    01211     unsigned int *data1 =  (unsigned int *)image1.bits();
    01212     unsigned int *data2 =  (unsigned int *)image2.bits();
    01213     unsigned int *data3   =  (unsigned int *)blendImage.bits();
    01214     unsigned char *data3b =  (unsigned char *)blendImage.bits();
    01215     unsigned int color3;
    01216
    01217     x1 = image1.width();     y1 = image1.height();
    01218     x2 = image2.width();     y2 = image2.height();
    01219     x3 = blendImage.width(); y3 = blendImage.height();
    01220
    01221     for (y = 0; y < (int)y1; y++) {
    01222         ind1 = x1*y;
    01223         ind2 = x2*(y%y2);
    01224         ind3 = x3*(y%y3);
    01225
    01226         x=0;
    01227         while(x < (int)x1) {
    01228           color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
    01229
    01230           a = (channel == Red) ? qRed(color3) :
    01231               (channel == Green) ? qGreen(color3) :
    01232               (channel == Blue) ? qBlue(color3) : qGray(color3);
    01233
    01234           r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256;
    01235           g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256;
    01236           b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256;
    01237
    01238           a = qAlpha(data1[ind1]);
    01239           data1[ind1] = qRgba(r, g, b, a);
    01240
    01241           ind1++; ind2++; ind3++; x++;
    01242           if ( (x%x2) ==0) ind2 -= x2;
    01243           if ( (x%x3) ==0) ind3 -= x3;
    01244         }
    01245     }
    01246     return image1;
    01247 }
    01248
    01249
    01250 //======================================================================
    01251 //
    01252 // Hash effects
    01253 //
    01254 //======================================================================
    01255
    01256 unsigned int KImageEffect::lHash(unsigned int c)
    01257 {
    01258     unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
    01259     unsigned char nr, ng, nb;
    01260     nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
    01261     ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
    01262     nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
    01263
    01264     return qRgba(nr, ng, nb, a);
    01265 }
    01266
    01267
    01268 // -----------------------------------------------------------------------------
    01269
    01270 unsigned int KImageEffect::uHash(unsigned int c)
    01271 {
    01272     unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
    01273     unsigned char nr, ng, nb;
    01274     nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
    01275     ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
    01276     nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
    01277
    01278     return qRgba(nr, ng, nb, a);
    01279 }
    01280
    01281
    01282 // -----------------------------------------------------------------------------
    01283
    01284 QImage& KImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing)
    01285 {
    01286     if (image.width() == 0 || image.height() == 0) {
    01287 #ifndef NDEBUG
    01288       cerr << "KImageEffect::hash effect invalid image" << endl;
    01289 #endif
    01290       return image;
    01291     }
    01292
    01293     register int x, y;
    01294     unsigned int *data =  (unsigned int *)image.bits();
    01295     unsigned int ind;
    01296
    01297     //CT no need to do it if not enough space
    01298     if ((lite == NorthLite ||
    01299          lite == SouthLite)&&
    01300         (unsigned)image.height() < 2+spacing) return image;
    01301     if ((lite == EastLite ||
    01302          lite == WestLite)&&
    01303         (unsigned)image.height() < 2+spacing) return image;
    01304
    01305     if (lite == NorthLite || lite == SouthLite) {
    01306         for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
    01307             for (x = 0; x < image.width(); x++) {
    01308                 ind = x + image.width() * y;
    01309                 data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
    01310
    01311                 ind = ind + image.width();
    01312                 data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
    01313             }
    01314         }
    01315     }
    01316
    01317     else if (lite == EastLite || lite == WestLite) {
    01318         for (y = 0 ; y < image.height(); y++) {
    01319             for (x = 0; x < image.width(); x = x + 2 + spacing) {
    01320                 ind = x + image.width() * y;
    01321                 data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
    01322
    01323                 ind++;
    01324                 data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
    01325             }
    01326         }
    01327     }
    01328
    01329     else if (lite == NWLite || lite == SELite) {
    01330         for (y = 0 ; y < image.height(); y++) {
    01331             for (x = 0;
    01332                  x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
    01333                  x = x + 2 + spacing) {
    01334                 ind = x + image.width() * y + ((y & 1)? 1 : 0);
    01335                 data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
    01336
    01337                 ind++;
    01338                 data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
    01339             }
    01340         }
    01341     }
    01342
    01343     else if (lite == SWLite || lite == NELite) {
    01344         for (y = 0 ; y < image.height(); y++) {
    01345             for (x = 0  + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
    01346                 ind = x + image.width() * y - ((y & 1)? 1 : 0);
    01347                 data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
    01348
    01349                 ind++;
    01350                 data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
    01351             }
    01352         }
    01353     }
    01354
    01355     return image;
    01356 }
    01357
    01358
    01359 //======================================================================
    01360 //
    01361 // Flatten effects
    01362 //
    01363 //======================================================================
    01364
    01365 QImage& KImageEffect::flatten(QImage &img, const QColor &ca,
    01366                             const QColor &cb, int ncols)
    01367 {
    01368     if (img.width() == 0 || img.height() == 0)
    01369       return img;
    01370
    01371     // a bitmap is easy...
    01372     if (img.depth() == 1) {
    01373         img.setColor(0, ca.rgb());
    01374         img.setColor(1, cb.rgb());
    01375         return img;
    01376     }
    01377
    01378     int r1 = ca.red(); int r2 = cb.red();
    01379     int g1 = ca.green(); int g2 = cb.green();
    01380     int b1 = ca.blue(); int b2 = cb.blue();
    01381     int min = 0, max = 255;
    01382
    01383     QRgb col;
    01384
    01385     // Get minimum and maximum greylevel.
    01386     if (img.numColors()) {
    01387         // pseudocolor
    01388         for (int i = 0; i < img.numColors(); i++) {
    01389             col = img.color(i);
    01390             int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
    01391             min = QMIN(min, mean);
    01392             max = QMAX(max, mean);
    01393         }
    01394     } else {
    01395         // truecolor
    01396         for (int y=0; y < img.height(); y++)
    01397             for (int x=0; x < img.width(); x++) {
    01398                 col = img.pixel(x, y);
    01399                 int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
    01400                 min = QMIN(min, mean);
    01401                 max = QMAX(max, mean);
    01402             }
    01403     }
    01404
    01405     // Conversion factors
    01406     float sr = ((float) r2 - r1) / (max - min);
    01407     float sg = ((float) g2 - g1) / (max - min);
    01408     float sb = ((float) b2 - b1) / (max - min);
    01409
    01410
    01411     // Repaint the image
    01412     if (img.numColors()) {
    01413         for (int i=0; i < img.numColors(); i++) {
    01414             col = img.color(i);
    01415             int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
    01416             int r = (int) (sr * (mean - min) + r1 + 0.5);
    01417             int g = (int) (sg * (mean - min) + g1 + 0.5);
    01418             int b = (int) (sb * (mean - min) + b1 + 0.5);
    01419             img.setColor(i, qRgba(r, g, b, qAlpha(col)));
    01420         }
    01421     } else {
    01422         for (int y=0; y < img.height(); y++)
    01423             for (int x=0; x < img.width(); x++) {
    01424                 col = img.pixel(x, y);
    01425                 int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
    01426                 int r = (int) (sr * (mean - min) + r1 + 0.5);
    01427                 int g = (int) (sg * (mean - min) + g1 + 0.5);
    01428                 int b = (int) (sb * (mean - min) + b1 + 0.5);
    01429                 img.setPixel(x, y, qRgba(r, g, b, qAlpha(col)));
    01430             }
    01431     }
    01432
    01433
    01434     // Dither if necessary
    01435     if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
    01436         return img;
    01437
    01438     if (ncols == 1) ncols++;
    01439     if (ncols > 256) ncols = 256;
    01440
    01441     QColor *pal = new QColor[ncols];
    01442     sr = ((float) r2 - r1) / (ncols - 1);
    01443     sg = ((float) g2 - g1) / (ncols - 1);
    01444     sb = ((float) b2 - b1) / (ncols - 1);
    01445
    01446     for (int i=0; i<ncols; i++)
    01447         pal[i] = QColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
    01448
    01449     dither(img, pal, ncols);
    01450
    01451     delete[] pal;
    01452     return img;
    01453 }
    01454
    01455
    01456 //======================================================================
    01457 //
    01458 // Fade effects
    01459 //
    01460 //======================================================================
    01461
    01462 QImage& KImageEffect::fade(QImage &img, float val, const QColor &color)
    01463 {
    01464     if (img.width() == 0 || img.height() == 0)
    01465       return img;
    01466
    01467     // We don't handle bitmaps
    01468     if (img.depth() == 1)
    01469         return img;
    01470
    01471     unsigned char tbl[256];
    01472     for (int i=0; i<256; i++)
    01473         tbl[i] = (int) (val * i + 0.5);
    01474
    01475     int red = color.red();
    01476     int green = color.green();
    01477     int blue = color.blue();
    01478
    01479     QRgb col;
    01480     int r, g, b, cr, cg, cb;
    01481
    01482     if (img.depth() <= 8) {
    01483         // pseudo color
    01484         for (int i=0; i<img.numColors(); i++) {
    01485             col = img.color(i);
    01486             cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
    01487             if (cr > red)
    01488                 r = cr - tbl[cr - red];
    01489             else
    01490                 r = cr + tbl[red - cr];
    01491             if (cg > green)
    01492                 g = cg - tbl[cg - green];
    01493             else
    01494                 g = cg + tbl[green - cg];
    01495             if (cb > blue)
    01496                 b = cb - tbl[cb - blue];
    01497             else
    01498                 b = cb + tbl[blue - cb];
    01499             img.setColor(i, qRgba(r, g, b, qAlpha(col)));
    01500         }
    01501
    01502     } else {
    01503         // truecolor
    01504         for (int y=0; y<img.height(); y++) {
    01505             QRgb *data = (QRgb *) img.scanLine(y);
    01506             for (int x=0; x<img.width(); x++) {
    01507                 col = *data;
    01508                 cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
    01509                 if (cr > red)
    01510                     r = cr - tbl[cr - red];
    01511                 else
    01512                     r = cr + tbl[red - cr];
    01513                 if (cg > green)
    01514                     g = cg - tbl[cg - green];
    01515                 else
    01516                     g = cg + tbl[green - cg];
    01517                 if (cb > blue)
    01518                     b = cb - tbl[cb - blue];
    01519                 else
    01520                     b = cb + tbl[blue - cb];
    01521                 *data++ = qRgba(r, g, b, qAlpha(col));
    01522             }
    01523         }
    01524     }
    01525
    01526     return img;
    01527 }
    01528
    01529 //======================================================================
    01530 //
    01531 // Color effects
    01532 //
    01533 //======================================================================
    01534
    01535 // This code is adapted from code (C) Rik Hemsley <rik@kde.org>
    01536 //
    01537 // The formula used (r + b + g) /3 is different from the qGray formula
    01538 // used by Qt.  This is because our formula is much much faster.  If,
    01539 // however, it turns out that this is producing sub-optimal images,
    01540 // then it will have to change (kurt)
    01541 //
    01542 // It does produce lower quality grayscale ;-) Use fast == true for the fast
    01543 // algorithm, false for the higher quality one (mosfet).
    01544 QImage& KImageEffect::toGray(QImage &img, bool fast)
    01545 {
    01546     if (img.width() == 0 || img.height() == 0)
    01547       return img;
    01548
    01549     if(fast){
    01550         if (img.depth() == 32) {
    01551             register uchar * r(img.bits());
    01552             register uchar * g(img.bits() + 1);
    01553             register uchar * b(img.bits() + 2);
    01554
    01555             uchar * end(img.bits() + img.numBytes());
    01556
    01557             while (r != end) {
    01558
    01559                 *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
    01560
    01561                 r += 4;
    01562                 g += 4;
    01563                 b += 4;
    01564             }
    01565         }
    01566         else
    01567         {
    01568             for (int i = 0; i < img.numColors(); i++)
    01569             {
    01570                 register uint r = qRed(img.color(i));
    01571                 register uint g = qGreen(img.color(i));
    01572                 register uint b = qBlue(img.color(i));
    01573
    01574                 register uint gray = (((r + g) >> 1) + b) >> 1;
    01575                 img.setColor(i, qRgba(gray, gray, gray, qAlpha(img.color(i))));
    01576             }
    01577         }
    01578     }
    01579     else{
    01580         int pixels = img.depth() > 8 ? img.width()*img.height() :
    01581             img.numColors();
    01582         unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
    01583             (unsigned int *)img.colorTable();
    01584         int val, i;
    01585         for(i=0; i < pixels; ++i){
    01586             val = qGray(data[i]);
    01587             data[i] = qRgba(val, val, val, qAlpha(data[i]));
    01588         }
    01589     }
    01590     return img;
    01591 }
    01592
    01593 // CT 29Jan2000 - desaturation algorithms
    01594 QImage& KImageEffect::desaturate(QImage &img, float desat)
    01595 {
    01596     if (img.width() == 0 || img.height() == 0)
    01597       return img;
    01598
    01599     if (desat < 0) desat = 0.;
    01600     if (desat > 1) desat = 1.;
    01601     int pixels = img.depth() > 8 ? img.width()*img.height() :
    01602         img.numColors();
    01603     unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
    01604         (unsigned int *)img.colorTable();
    01605     int h, s, v, i;
    01606     QColor clr; // keep constructor out of loop (mosfet)
    01607     for(i=0; i < pixels; ++i){
    01608         clr.setRgb(data[i]);
    01609         clr.hsv(&h, &s, &v);
    01610         clr.setHsv(h, (int)(s * (1. - desat)), v);
    01611         data[i] = clr.rgb();
    01612     }
    01613     return img;
    01614 }
    01615
    01616 // Contrast stuff (mosfet)
    01617 QImage& KImageEffect::contrast(QImage &img, int c)
    01618 {
    01619     if (img.width() == 0 || img.height() == 0)
    01620       return img;
    01621
    01622     if(c > 255)
    01623         c = 255;
    01624     if(c < -255)
    01625         c =  -255;
    01626     int pixels = img.depth() > 8 ? img.width()*img.height() :
    01627         img.numColors();
    01628     unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
    01629         (unsigned int *)img.colorTable();
    01630     int i, r, g, b;
    01631     for(i=0; i < pixels; ++i){
    01632         r = qRed(data[i]);
    01633         g = qGreen(data[i]);
    01634         b = qBlue(data[i]);
    01635         if(qGray(data[i]) <= 127){
    01636             if(r - c <= 255)
    01637                 r -= c;
    01638             if(g - c <= 255)
    01639                 g -= c;
    01640             if(b - c <= 255)
    01641                 b -= c;
    01642         }
    01643         else{
    01644             if(r + c <= 255)
    01645                 r += c;
    01646             if(g + c <= 255)
    01647                 g += c;
    01648             if(b + c <= 255)
    01649                 b += c;
    01650         }
    01651         data[i] = qRgba(r, g, b, qAlpha(data[i]));
    01652     }
    01653     return(img);
    01654 }
    01655
    01656 //======================================================================
    01657 //
    01658 // Dithering effects
    01659 //
    01660 //======================================================================
    01661
    01662 // adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
    01663 //
    01664 // Floyd-Steinberg dithering
    01665 // Ref: Bitmapped Graphics Programming in C++
    01666 //      Marv Luse, Addison-Wesley Publishing, 1993.
    01667 QImage& KImageEffect::dither(QImage &img, const QColor *palette, int size)
    01668 {
    01669     if (img.width() == 0 || img.height() == 0 ||
    01670         palette == 0 || img.depth() <= 8)
    01671       return img;
    01672
    01673     QImage dImage( img.width(), img.height(), 8, size );
    01674     int i;
    01675
    01676     dImage.setNumColors( size );
    01677     for ( i = 0; i < size; i++ )
    01678         dImage.setColor( i, palette[ i ].rgb() );
    01679
    01680     int *rerr1 = new int [ img.width() * 2 ];
    01681     int *gerr1 = new int [ img.width() * 2 ];
    01682     int *berr1 = new int [ img.width() * 2 ];
    01683
    01684     memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
    01685     memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
    01686     memset( berr1, 0, sizeof( int ) * img.width() * 2 );
    01687
    01688     int *rerr2 = rerr1 + img.width();
    01689     int *gerr2 = gerr1 + img.width();
    01690     int *berr2 = berr1 + img.width();
    01691
    01692     for ( int j = 0; j < img.height(); j++ )
    01693     {
    01694         uint *ip = (uint * )img.scanLine( j );
    01695         uchar *dp = dImage.scanLine( j );
    01696
    01697         for ( i = 0; i < img.width(); i++ )
    01698         {
    01699             rerr1[i] = rerr2[i] + qRed( *ip );
    01700             rerr2[i] = 0;
    01701             gerr1[i] = gerr2[i] + qGreen( *ip );
    01702             gerr2[i] = 0;
    01703             berr1[i] = berr2[i] + qBlue( *ip );
    01704             berr2[i] = 0;
    01705             ip++;
    01706         }
    01707
    01708         *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
    01709
    01710         for ( i = 1; i < img.width()-1; i++ )
    01711         {
    01712             int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
    01713             *dp = indx;
    01714
    01715             int rerr = rerr1[i];
    01716             rerr -= palette[indx].red();
    01717             int gerr = gerr1[i];
    01718             gerr -= palette[indx].green();
    01719             int berr = berr1[i];
    01720             berr -= palette[indx].blue();
    01721
    01722             // diffuse red error
    01723             rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
    01724             rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
    01725             rerr2[  i  ] += ( rerr * 5 ) >> 4;
    01726             rerr2[ i+1 ] += ( rerr ) >> 4;
    01727
    01728             // diffuse green error
    01729             gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
    01730             gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
    01731             gerr2[  i  ] += ( gerr * 5 ) >> 4;
    01732             gerr2[ i+1 ] += ( gerr ) >> 4;
    01733
    01734             // diffuse red error
    01735             berr1[ i+1 ] += ( berr * 7 ) >> 4;
    01736             berr2[ i-1 ] += ( berr * 3 ) >> 4;
    01737             berr2[  i  ] += ( berr * 5 ) >> 4;
    01738             berr2[ i+1 ] += ( berr ) >> 4;
    01739
    01740             dp++;
    01741         }
    01742
    01743         *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
    01744     }
    01745
    01746     delete [] rerr1;
    01747     delete [] gerr1;
    01748     delete [] berr1;
    01749
    01750     img = dImage;
    01751     return img;
    01752 }
    01753
    01754 int KImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size )
    01755 {
    01756     if (palette == 0)
    01757       return 0;
    01758
    01759     int dr = palette[0].red() - r;
    01760     int dg = palette[0].green() - g;
    01761     int db = palette[0].blue() - b;
    01762
    01763     int minDist =  dr*dr + dg*dg + db*db;
    01764     int nearest = 0;
    01765
    01766     for (int i = 1; i < size; i++ )
    01767     {
    01768         dr = palette[i].red() - r;
    01769         dg = palette[i].green() - g;
    01770         db = palette[i].blue() - b;
    01771
    01772         int dist = dr*dr + dg*dg + db*db;
    01773
    01774         if ( dist < minDist )
    01775         {
    01776             minDist = dist;
    01777             nearest = i;
    01778         }
    01779     }
    01780
    01781     return nearest;
    01782 }
    01783
    01784 bool KImageEffect::blend(
    01785     const QImage & upper,
    01786     const QImage & lower,
    01787     QImage & output
    01788 )
    01789 {
    01790   if (
    01791       upper.width()  > lower.width()  ||
    01792       upper.height() > lower.height() ||
    01793       upper.depth() != 32             ||
    01794       lower.depth() != 32
    01795   )
    01796   {
    01797 #ifndef NDEBUG
    01798     cerr << "KImageEffect::blend : Sizes not correct\n" ;
    01799 #endif
    01800     return false;
    01801   }
    01802
    01803   output = lower.copy();
    01804
    01805   register uchar *i, *o;
    01806   register int a;
    01807   register int col;
    01808   register int w = upper.width();
    01809   int row(upper.height() - 1);
    01810
    01811   do {
    01812
    01813     i = upper.scanLine(row);
    01814     o = output.scanLine(row);
    01815
    01816     col = w << 2;
    01817     --col;
    01818
    01819     do {
    01820
    01821       while (!(a = i[col]) && (col != 3)) {
    01822         --col; --col; --col; --col;
    01823       }
    01824
    01825       --col;
    01826       o[col] += ((i[col] - o[col]) * a) >> 8;
    01827
    01828       --col;
    01829       o[col] += ((i[col] - o[col]) * a) >> 8;
    01830
    01831       --col;
    01832       o[col] += ((i[col] - o[col]) * a) >> 8;
    01833
    01834     } while (col--);
    01835
    01836   } while (row--);
    01837
    01838   return true;
    01839 }
    01840
    01841 #if 0
    01842 // Not yet...
    01843 bool KImageEffect::blend(
    01844     const QImage & upper,
    01845     const QImage & lower,
    01846     QImage & output,
    01847     const QRect & destRect
    01848 )
    01849 {
    01850   output = lower.copy();
    01851   return output;
    01852 }
    01853
    01854 #endif
    01855
    01856 bool KImageEffect::blend(
    01857     int &x, int &y,
    01858     const QImage & upper,
    01859     const QImage & lower,
    01860     QImage & output
    01861 )
    01862 {
    01863   int cx=0, cy=0, cw=upper.width(), ch=upper.height();
    01864
    01865   if ( upper.width() + x > lower.width()  ||
    01866       upper.height() + y > lower.height() ||
    01867       x < 0 || y < 0 ||
    01868       upper.depth() != 32 || lower.depth() != 32 )
    01869   {
    01870     if ( x > lower.width() || y > lower.height() ) return false;
    01871     if ( upper.width()<=0 || upper.height() <= 0 ) return false;
    01872     if ( lower.width()<=0 || lower.height() <= 0 ) return false;
    01873
    01874     if (x<0) {cx=-x; cw+=x; x=0; };
    01875     if (cw + x > lower.width()) { cw=lower.width()-x; };
    01876     if (y<0) {cy=-y; ch+=y; y=0; };
    01877     if (ch + y > lower.height()) { ch=lower.height()-y; };
    01878
    01879     if ( cx >= upper.width() || cy >= upper.height() ) return true;
    01880     if ( cw <= 0 || ch <= 0 ) return true;
    01881   }
    01882
    01883   output.create(cw,ch,32);
    01884 //  output.setAlphaBuffer(true); // I should do some benchmarks to see if
    01885         // this is worth the effort
    01886
    01887   register QRgb *i, *o, *b;
    01888
    01889   register int a;
    01890   register int j,k;
    01891   for (j=0; j<ch; j++)
    01892   {
    01893     b=reinterpret_cast<QRgb *>(&lower.scanLine(y+j) [ (x+cw) << 2 ]);
    01894     i=reinterpret_cast<QRgb *>(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]);
    01895     o=reinterpret_cast<QRgb *>(&output.scanLine(j)  [ cw << 2 ]);
    01896
    01897     k=cw-1;
    01898     --b; --i; --o;
    01899     do
    01900     {
    01901       while ( !(a=qAlpha(*i)) && k>0 )
    01902       {
    01903         i--;
    01904 //      *o=0;
    01905         *o=*b;
    01906         --o; --b;
    01907         k--;
    01908       };
    01909 //      *o=0xFF;
    01910       *o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8),
    01911                 qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8),
    01912                 qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8));
    01913       --i; --o; --b;
    01914     } while (k--);
    01915   }
    01916
    01917   return true;
    01918 }
    01919
    01920 bool KImageEffect::blendOnLower(
    01921     int x, int y,
    01922     const QImage & upper,
    01923     const QImage & lower
    01924 )
    01925 {
    01926   int cx=0, cy=0, cw=upper.width(), ch=upper.height();
    01927
    01928   if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
    01929   if ( x + cw > lower.width()  ||
    01930       y + ch > lower.height() ||
    01931       x < 0 || y < 0 )
    01932   {
    01933     if ( x > lower.width() || y > lower.height() ) return true;
    01934     if ( upper.width()<=0 || upper.height() <= 0 ) return true;
    01935     if ( lower.width()<=0 || lower.height() <= 0 ) return true;
    01936
    01937     if (x<0) {cx=-x; cw+=x; x=0; };
    01938     if (cw + x > lower.width()) { cw=lower.width()-x; };
    01939     if (y<0) {cy=-y; ch+=y; y=0; };
    01940     if (ch + y > lower.height()) { ch=lower.height()-y; };
    01941
    01942     if ( cx >= upper.width() || cy >= upper.height() ) return true;
    01943     if ( cw <= 0 || ch <= 0 ) return true;
    01944   }
    01945
    01946   register uchar *i, *b;
    01947   register int a;
    01948   register int k;
    01949
    01950   for (int j=0; j<ch; j++)
    01951   {
    01952     b=&lower.scanLine(y+j) [ (x+cw) << 2 ];
    01953     i=&upper.scanLine(cy+j)[ (cx+cw) << 2 ];
    01954
    01955     k=cw-1;
    01956     --b; --i;
    01957     do
    01958     {
    01959 #ifndef WORDS_BIGENDIAN
    01960       while ( !(a=*i) && k>0 )
    01961 #else
    01962       while ( !(a=*(i-3)) && k>0 )
    01963 #endif
    01964       {
    01965         i-=4; b-=4; k--;
    01966       };
    01967
    01968 #ifndef WORDS_BIGENDIAN
    01969       --i; --b;
    01970       *b += ( ((*i - *b) * a) >> 8 );
    01971       --i; --b;
    01972       *b += ( ((*i - *b) * a) >> 8 );
    01973       --i; --b;
    01974       *b += ( ((*i - *b) * a) >> 8 );
    01975       --i; --b;
    01976 #else
    01977       *b += ( ((*i - *b) * a) >> 8 );
    01978       --i; --b;
    01979       *b += ( ((*i - *b) * a) >> 8 );
    01980       --i; --b;
    01981       *b += ( ((*i - *b) * a) >> 8 );
    01982       i -= 2; b -= 2;
    01983 #endif
    01984     } while (k--);
    01985   }
    01986
    01987   return true;
    01988 }
    01989
    01990 // For selected icons
    01991 QImage& KImageEffect::selectedImage( QImage &img, const QColor &col )
    01992 {
    01993     return blend( col, img, 0.5);
    01994 }
    01995
    01996 //
    01997 // ===================================================================
    01998 // Effects originally ported from ImageMagick for PixiePlus, plus a few
    01999 // new ones. (mosfet 12/29/01)
    02000 // ===================================================================
    02001 //
    02002
    02003 void KImageEffect::normalize(QImage &img)
    02004 {
    02005     int *histogram, threshold_intensity, intense;
    02006     int x, y, i;
    02007
    02008     unsigned int gray_value;
    02009     unsigned int *normalize_map;
    02010     unsigned int high, low;
    02011
    02012     // allocate histogram and normalize map
    02013     histogram = (int *)calloc(MaxRGB+1, sizeof(int));
    02014     normalize_map = (unsigned int *)malloc((MaxRGB+1)*sizeof(unsigned int));
    02015     if(!normalize_map || !histogram){
    02016         qWarning("Unable to allocate normalize histogram and map");
    02017         free(normalize_map);
    02018         free(histogram);
    02019         return;
    02020     }
    02021
    02022     // form histogram
    02023     if(img.depth() > 8){  // DirectClass
    02024         unsigned int *data;
    02025         for(y=0; y < img.height(); ++y){
    02026             data = (unsigned int *)img.scanLine(y);
    02027             for(x=0; x < img.width(); ++x){
    02028                 gray_value = intensityValue(data[x]);
    02029                 histogram[gray_value]++;
    02030             }
    02031         }
    02032     }
    02033     else{ // PsudeoClass
    02034         unsigned char *data;
    02035         unsigned int *cTable = img.colorTable();
    02036         for(y=0; y < img.height(); ++y){
    02037             data = (unsigned char *)img.scanLine(y);
    02038             for(x=0; x < img.width(); ++x){
    02039                 gray_value = intensityValue(*(cTable+data[x]));
    02040                 histogram[gray_value]++;
    02041             }
    02042         }
    02043     }
    02044
    02045     // find histogram boundaries by locating the 1 percent levels
    02046     threshold_intensity = (img.width()*img.height())/100;
    02047     intense = 0;
    02048     for(low=0; low < MaxRGB; ++low){
    02049         intense+=histogram[low];
    02050         if(intense > threshold_intensity)
    02051             break;
    02052     }
    02053     intense=0;
    02054     for(high=MaxRGB; high != 0; --high){
    02055         intense+=histogram[high];
    02056         if(intense > threshold_intensity)
    02057             break;
    02058     }
    02059
    02060     if (low == high){
    02061         // Unreasonable contrast;  use zero threshold to determine boundaries.
    02062         threshold_intensity=0;
    02063         intense=0;
    02064         for(low=0; low < MaxRGB; ++low){
    02065             intense+=histogram[low];
    02066             if(intense > threshold_intensity)
    02067                 break;
    02068         }
    02069         intense=0;
    02070         for(high=MaxRGB; high != 0; --high)
    02071         {
    02072             intense+=histogram[high];
    02073             if(intense > threshold_intensity)
    02074                 break;
    02075         }
    02076         if(low == high)
    02077             return;  // zero span bound
    02078     }
    02079
    02080     // Stretch the histogram to create the normalized image mapping.
    02081     for(i=0; i <= MaxRGB; i++){
    02082         if (i < (int) low)
    02083             normalize_map[i]=0;
    02084         else{
    02085             if(i > (int) high)
    02086                 normalize_map[i]=MaxRGB;
    02087             else
    02088                 normalize_map[i]=(MaxRGB-1)*(i-low)/(high-low);
    02089         }
    02090     }
    02091     // Normalize
    02092     if(img.depth() > 8){ // DirectClass
    02093         unsigned int *data;
    02094         for(y=0; y < img.height(); ++y){
    02095             data = (unsigned int *)img.scanLine(y);
    02096             for(x=0; x < img.width(); ++x){
    02097                 data[x] = qRgba(normalize_map[qRed(data[x])],
    02098                                 normalize_map[qGreen(data[x])],
    02099                                 normalize_map[qBlue(data[x])],
    02100                                 qAlpha(data[x]));
    02101             }
    02102         }
    02103     }
    02104     else{ // PsudeoClass
    02105         int colors = img.numColors();
    02106         unsigned int *cTable = img.colorTable();
    02107         for(i=0; i < colors; ++i){
    02108             cTable[i] = qRgba(normalize_map[qRed(cTable[i])],
    02109                               normalize_map[qGreen(cTable[i])],
    02110                               normalize_map[qBlue(cTable[i])],
    02111                               qAlpha(cTable[i]));
    02112         }
    02113     }
    02114     free(histogram);
    02115     free(normalize_map);
    02116 }
    02117
    02118
    02119 void KImageEffect::equalize(QImage &img)
    02120 {
    02121     int *histogram, *map, *equalize_map;
    02122     int x, y, i, j;
    02123
    02124     unsigned int high, low;
    02125
    02126     // allocate histogram and maps
    02127     histogram = (int *)calloc(MaxRGB+1, sizeof(int));
    02128     map = (int *)malloc((MaxRGB+1)*sizeof(unsigned int));
    02129     equalize_map  = (int *)malloc((MaxRGB+1)*sizeof(unsigned int));
    02130
    02131     if(!histogram || !map || !equalize_map){
    02132         qWarning("Unable to allocate equalize histogram and maps");
    02133         free(histogram);
    02134         free(map);
    02135         free(equalize_map);
    02136         return;
    02137     }
    02138     // form histogram
    02139     if(img.depth() > 8){ // DirectClass
    02140         unsigned int *data;
    02141         for(y=0; y < img.height(); ++y){
    02142             data = (unsigned int *)img.scanLine(y);
    02143             for(x=0; x < img.width(); ++x){
    02144                 histogram[intensityValue(data[x])]++;
    02145             }
    02146         }
    02147     }
    02148     else{ // PsudeoClass
    02149         unsigned char *data;
    02150         unsigned int *cTable = img.colorTable();
    02151         for(y=0; y < img.height(); ++y){
    02152             data = (unsigned char *)img.scanLine(y);
    02153             for(x=0; x < img.width(); ++x){
    02154                 histogram[intensityValue(*(cTable+data[x]))]++;
    02155             }
    02156         }
    02157     }
    02158
    02159     // integrate the histogram to get the equalization map.
    02160     j=0;
    02161     for(i=0; i <= MaxRGB; i++){
    02162         j+=histogram[i];
    02163         map[i]=j;
    02164     }
    02165     free(histogram);
    02166     if(map[MaxRGB] == 0){
    02167         free(equalize_map);
    02168         free(map);
    02169         return;
    02170     }
    02171     // equalize
    02172     low=map[0];
    02173     high=map[MaxRGB];
    02174     for(i=0; i <= MaxRGB; i++)
    02175         equalize_map[i]=(unsigned int)
    02176             ((((double) (map[i]-low))*MaxRGB)/QMAX(high-low,1));
    02177     free(map);
    02178     // stretch the histogram
    02179     if(img.depth() > 8){ // DirectClass
    02180         unsigned int *data;
    02181         for(y=0; y < img.height(); ++y){
    02182             data = (unsigned int *)img.scanLine(y);
    02183             for(x=0; x < img.width(); ++x){
    02184                 data[x] = qRgba(equalize_map[qRed(data[x])],
    02185                                 equalize_map[qGreen(data[x])],
    02186                                 equalize_map[qBlue(data[x])],
    02187                                 qAlpha(data[x]));
    02188             }
    02189         }
    02190     }
    02191     else{ // PsudeoClass
    02192         int colors = img.numColors();
    02193         unsigned int *cTable = img.colorTable();
    02194         for(i=0; i < colors; ++i){
    02195             cTable[i] = qRgba(equalize_map[qRed(cTable[i])],
    02196                               equalize_map[qGreen(cTable[i])],
    02197                               equalize_map[qBlue(cTable[i])],
    02198                               qAlpha(cTable[i]));
    02199         }
    02200     }
    02201     free(equalize_map);
    02202 }
    02203
    02204 QImage KImageEffect::sample(QImage &src, int w, int h)
    02205 {
    02206     if(w == src.width() && h == src.height())
    02207         return(src);
    02208
    02209     double *x_offset, *y_offset;
    02210     int j, k, y;
    02211     register int x;
    02212     QImage dest(w, h, src.depth());
    02213
    02214     x_offset = (double *)malloc(w*sizeof(double));
    02215     y_offset = (double *)malloc(h*sizeof(double));
    02216     if(!x_offset || !y_offset){
    02217         qWarning("Unable to allocate pixels buffer");
    02218         free(x_offset);
    02219         free(y_offset);
    02220         return(src);
    02221     }
    02222
    02223     // init pixel offsets
    02224     for(x=0; x < w; ++x)
    02225         x_offset[x] = x*src.width()/((double)w);
    02226     for(y=0; y < h; ++y)
    02227         y_offset[y] = y*src.height()/((double)h);
    02228
    02229     // sample each row
    02230     if(src.depth() > 8){ // DirectClass source image
    02231         unsigned int *srcData, *destData;
    02232         unsigned int *pixels;
    02233         pixels = (unsigned int *)malloc(src.width()*sizeof(unsigned int));
    02234         if(!pixels){
    02235             qWarning("Unable to allocate pixels buffer");
    02236             free(pixels);
    02237             free(x_offset);
    02238             free(y_offset);
    02239             return(src);
    02240         }
    02241         j = (-1);
    02242         for(y=0; y < h; ++y){
    02243             destData = (unsigned int *)dest.scanLine(y);
    02244             if(j != y_offset[y]){
    02245                 // read a scan line
    02246                 j = (int)(y_offset[y]);
    02247                 srcData = (unsigned int *)src.scanLine(j);
    02248                 (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned int));
    02249             }
    02250             // sample each column
    02251             for(x=0; x < w; ++x){
    02252                 k = (int)(x_offset[x]);
    02253                 destData[x] = pixels[k];
    02254             }
    02255         }
    02256         free(pixels);
    02257     }
    02258     else{ // PsudeoClass source image
    02259         unsigned char *srcData, *destData;
    02260         unsigned char *pixels;
    02261         pixels = (unsigned char *)malloc(src.width()*sizeof(unsigned char));
    02262         if(!pixels){
    02263             qWarning("Unable to allocate pixels buffer");
    02264             free(pixels);
    02265             free(x_offset);
    02266             free(y_offset);
    02267             return(src);
    02268         }
    02269         // copy colortable
    02270         dest.setNumColors(src.numColors());
    02271         (void)memcpy(dest.colorTable(), src.colorTable(),
    02272                      src.numColors()*sizeof(unsigned int));
    02273
    02274         // sample image
    02275         j = (-1);
    02276         for(y=0; y < h; ++y){
    02277             destData = (unsigned char *)dest.scanLine(y);
    02278             if(j != y_offset[y]){
    02279                 // read a scan line
    02280                 j = (int)(y_offset[y]);
    02281                 srcData = (unsigned char *)src.scanLine(j);
    02282                 (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned char));
    02283             }
    02284             // sample each column
    02285             for(x=0; x < w; ++x){
    02286                 k = (int)(x_offset[x]);
    02287                 destData[x] = pixels[k];
    02288             }
    02289         }
    02290         free(pixels);
    02291     }
    02292     free(x_offset);
    02293     free(y_offset);
    02294     return(dest);
    02295 }
    02296
    02297 void KImageEffect::threshold(QImage &img, unsigned int threshold)
    02298 {
    02299     int i, count;
    02300     unsigned int *data;
    02301     if(img.depth() > 8){ // DirectClass
    02302         count = img.width()*img.height();
    02303         data = (unsigned int *)img.bits();
    02304     }
    02305     else{ // PsudeoClass
    02306         count = img.numColors();
    02307         data = (unsigned int *)img.colorTable();
    02308     }
    02309     for(i=0; i < count; ++i)
    02310         data[i] = intensityValue(data[i]) < threshold ? Qt::black.rgb() : Qt::white.rgb();
    02311 }
    02312
    02313 QImage KImageEffect::charcoal(QImage &src, double factor)
    02314 {
    02315     QImage dest(src);
    02316     dest.detach();
    02317     toGray(dest);
    02318     dest = edge(dest, factor);
    02319     dest = blur(dest, factor);
    02320     normalize(dest);
    02321     dest.invertPixels(false);
    02322     return(dest);
    02323 }
    02324
    02325 void KImageEffect::hull(const int x_offset, const int y_offset,
    02326                         const int polarity, const int columns,
    02327                         const int rows,
    02328                         unsigned int *f, unsigned int *g)
    02329 {
    02330     int x, y;
    02331
    02332     unsigned int *p, *q, *r, *s;
    02333     unsigned int v;
    02334     if(f == NULL || g == NULL)
    02335         return;
    02336     p=f+(columns+2);
    02337     q=g+(columns+2);
    02338     r=p+(y_offset*(columns+2)+x_offset);
    02339     for (y=0; y < rows; y++){
    02340         p++;
    02341         q++;
    02342         r++;
    02343         if(polarity > 0)
    02344             for (x=0; x < columns; x++){
    02345                 v=(*p);
    02346                 if (*r > v)
    02347                     v++;
    02348                 *q=v;
    02349                 p++;
    02350                 q++;
    02351                 r++;
    02352             }
    02353         else
    02354             for(x=0; x < columns; x++){
    02355                 v=(*p);
    02356                 if (v > (unsigned int) (*r+1))
    02357                     v--;
    02358                 *q=v;
    02359                 p++;
    02360                 q++;
    02361                 r++;
    02362             }
    02363         p++;
    02364         q++;
    02365         r++;
    02366     }
    02367     p=f+(columns+2);
    02368     q=g+(columns+2);
    02369     r=q+(y_offset*(columns+2)+x_offset);
    02370     s=q-(y_offset*(columns+2)+x_offset);
    02371     for(y=0; y < rows; y++){
    02372         p++;
    02373         q++;
    02374         r++;
    02375         s++;
    02376         if(polarity > 0)
    02377             for(x=0; x < (int) columns; x++){
    02378                 v=(*q);
    02379                 if (((unsigned int) (*s+1) > v) && (*r > v))
    02380                     v++;
    02381                 *p=v;
    02382                 p++;
    02383                 q++;
    02384                 r++;
    02385                 s++;
    02386             }
    02387         else
    02388             for (x=0; x < columns; x++){
    02389                 v=(*q);
    02390                 if (((unsigned int) (*s+1) < v) && (*r < v))
    02391                     v--;
    02392                 *p=v;
    02393                 p++;
    02394                 q++;
    02395                 r++;
    02396                 s++;
    02397             }
    02398         p++;
    02399         q++;
    02400         r++;
    02401         s++;
    02402     }
    02403 }
    02404
    02405 QImage KImageEffect::despeckle(QImage &src)
    02406 {
    02407     int i, j, x, y;
    02408     unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
    02409         *alpha_channel;
    02410     int packets;
    02411     static const int
    02412     X[4]= {0, 1, 1,-1},
    02413     Y[4]= {1, 0, 1, 1};
    02414
    02415     unsigned int *destData;
    02416     QImage dest(src.width(), src.height(), 32);
    02417
    02418     packets = (src.width()+2)*(src.height()+2);
    02419     red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
    02420     green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
    02421     blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
    02422     alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
    02423     buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
    02424     if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
    02425        !buffer){
    02426         free(red_channel);
    02427         free(green_channel);
    02428         free(blue_channel);
    02429         free(alpha_channel);
    02430         free(buffer);
    02431         return(src);
    02432     }
    02433
    02434     // copy image pixels to color component buffers
    02435     j = src.width()+2;
    02436     if(src.depth() > 8){ // DirectClass source image
    02437         unsigned int *srcData;
    02438         for(y=0; y < src.height(); ++y){
    02439             srcData = (unsigned int *)src.scanLine(y);
    02440             ++j;
    02441             for(x=0; x < src.width(); ++x){
    02442                 red_channel[j] = qRed(srcData[x]);
    02443                 green_channel[j] = qGreen(srcData[x]);
    02444                 blue_channel[j] = qBlue(srcData[x]);
    02445                 alpha_channel[j] = qAlpha(srcData[x]);
    02446                 ++j;
    02447             }
    02448             ++j;
    02449         }
    02450     }
    02451     else{ // PsudeoClass source image
    02452         unsigned char *srcData;
    02453         unsigned int *cTable = src.colorTable();
    02454         unsigned int pixel;
    02455         for(y=0; y < src.height(); ++y){
    02456             srcData = (unsigned char *)src.scanLine(y);
    02457             ++j;
    02458             for(x=0; x < src.width(); ++x){
    02459                 pixel = *(cTable+srcData[x]);
    02460                 red_channel[j] = qRed(pixel);
    02461                 green_channel[j] = qGreen(pixel);
    02462                 blue_channel[j] = qBlue(pixel);
    02463                 alpha_channel[j] = qAlpha(pixel);
    02464                 ++j;
    02465             }
    02466             ++j;
    02467         }
    02468     }
    02469     // reduce speckle in red channel
    02470     for(i=0; i < 4; i++){
    02471         hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
    02472         hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
    02473         hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
    02474         hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
    02475     }
    02476     // reduce speckle in green channel
    02477     for (i=0; i < packets; i++)
    02478         buffer[i]=0;
    02479     for (i=0; i < 4; i++){
    02480         hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
    02481         hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
    02482         hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
    02483         hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
    02484     }
    02485     // reduce speckle in blue channel
    02486     for (i=0; i < packets; i++)
    02487         buffer[i]=0;
    02488     for (i=0; i < 4; i++){
    02489         hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
    02490         hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
    02491         hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
    02492         hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
    02493     }
    02494     // copy color component buffers to despeckled image
    02495     j = dest.width()+2;
    02496     for(y=0; y < dest.height(); ++y)
    02497     {
    02498         destData = (unsigned int *)dest.scanLine(y);
    02499         ++j;
    02500         for (x=0; x < dest.width(); ++x)
    02501         {
    02502             destData[x] = qRgba(red_channel[j], green_channel[j],
    02503                                 blue_channel[j], alpha_channel[j]);
    02504             ++j;
    02505         }
    02506         ++j;
    02507     }
    02508     free(buffer);
    02509     free(red_channel);
    02510     free(green_channel);
    02511     free(blue_channel);
    02512     free(alpha_channel);
    02513     return(dest);
    02514 }
    02515
    02516 unsigned int KImageEffect::generateNoise(unsigned int pixel,
    02517                                          NoiseType noise_type)
    02518 {
    02519 #define NoiseEpsilon  1.0e-5
    02520 #define NoiseMask  0x7fff
    02521 #define SigmaUniform  4.0
    02522 #define SigmaGaussian  4.0
    02523 #define SigmaImpulse  0.10
    02524 #define SigmaLaplacian 10.0
    02525 #define SigmaMultiplicativeGaussian  0.5
    02526 #define SigmaPoisson  0.05
    02527 #define TauGaussian  20.0
    02528
    02529     double alpha, beta, sigma, value;
    02530     alpha=(double) (rand() & NoiseMask)/NoiseMask;
    02531     if (alpha == 0.0)
    02532         alpha=1.0;
    02533     switch(noise_type){
    02534     case UniformNoise:
    02535     default:
    02536         {
    02537             value=(double) pixel+SigmaUniform*(alpha-0.5);
    02538             break;
    02539         }
    02540     case GaussianNoise:
    02541         {
    02542             double tau;
    02543
    02544             beta=(double) (rand() & NoiseMask)/NoiseMask;
    02545             sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
    02546             tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
    02547             value=(double) pixel+
    02548                 (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
    02549             break;
    02550         }
    02551     case MultiplicativeGaussianNoise:
    02552         {
    02553             if (alpha <= NoiseEpsilon)
    02554                 sigma=MaxRGB;
    02555             else
    02556                 sigma=sqrt(-2.0*log(alpha));
    02557             beta=(rand() & NoiseMask)/NoiseMask;
    02558             value=(double) pixel+
    02559                 pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
    02560             break;
    02561         }
    02562     case ImpulseNoise:
    02563         {
    02564             if (alpha < (SigmaImpulse/2.0))
    02565                 value=0;
    02566             else
    02567                 if (alpha >= (1.0-(SigmaImpulse/2.0)))
    02568                     value=MaxRGB;
    02569                 else
    02570                     value=pixel;
    02571             break;
    02572         }
    02573     case LaplacianNoise:
    02574         {
    02575             if (alpha <= 0.5)
    02576             {
    02577                 if (alpha <= NoiseEpsilon)
    02578                     value=(double) pixel-MaxRGB;
    02579                 else
    02580                     value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
    02581                 break;
    02582             }
    02583             beta=1.0-alpha;
    02584             if (beta <= (0.5*NoiseEpsilon))
    02585                 value=(double) pixel+MaxRGB;
    02586             else
    02587                 value=(double) pixel-SigmaLaplacian*log(2.0*beta);
    02588             break;
    02589         }
    02590     case PoissonNoise:
    02591         {
    02592             register int
    02593                 i;
    02594
    02595             for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
    02596             {
    02597                 beta=(double) (rand() & NoiseMask)/NoiseMask;
    02598                 alpha=alpha*beta;
    02599             }
    02600             value=i/SigmaPoisson;
    02601             break;
    02602         }
    02603     }
    02604     if(value < 0.0)
    02605         return(0);
    02606     if(value > MaxRGB)
    02607         return(MaxRGB);
    02608     return((unsigned int) (value+0.5));
    02609 }
    02610
    02611 QImage KImageEffect::addNoise(QImage &src, NoiseType noise_type)
    02612 {
    02613     int x, y;
    02614     QImage dest(src.width(), src.height(), 32);
    02615     unsigned int *destData;
    02616
    02617     if(src.depth() > 8){ // DirectClass source image
    02618         unsigned int *srcData;
    02619         for(y=0; y < src.height(); ++y){
    02620             srcData = (unsigned int *)src.scanLine(y);
    02621             destData = (unsigned int *)dest.scanLine(y);
    02622             for(x=0; x < src.width(); ++x){
    02623                 destData[x] = qRgba(generateNoise(qRed(srcData[x]), noise_type),
    02624                                     generateNoise(qGreen(srcData[x]), noise_type),
    02625                                     generateNoise(qBlue(srcData[x]), noise_type),
    02626                                     qAlpha(srcData[x]));
    02627             }
    02628         }
    02629     }
    02630     else{ // PsudeoClass source image
    02631         unsigned char *srcData;
    02632         unsigned int *cTable = src.colorTable();
    02633         unsigned int pixel;
    02634         for(y=0; y < src.height(); ++y){
    02635             srcData = (unsigned char *)src.scanLine(y);
    02636             destData = (unsigned int *)dest.scanLine(y);
    02637             for(x=0; x < src.width(); ++x){
    02638                 pixel = *(cTable+srcData[x]);
    02639                 destData[x] = qRgba(generateNoise(qRed(pixel), noise_type),
    02640                                     generateNoise(qGreen(pixel), noise_type),
    02641                                     generateNoise(qBlue(pixel), noise_type),
    02642                                     qAlpha(pixel));
    02643             }
    02644         }
    02645
    02646     }
    02647     return(dest);
    02648 }
    02649
    02650 unsigned int KImageEffect::interpolateColor(QImage *image, double x_offset,
    02651                                             double y_offset,
    02652                                             unsigned int background)
    02653 {
    02654     double alpha, beta;
    02655     unsigned int p, q, r, s;
    02656     int x, y;
    02657
    02658     x = (int)x_offset;
    02659     y = (int)y_offset;
    02660     if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
    02661         return(background);
    02662     if(image->depth() > 8){
    02663         if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1)))    {
    02664             unsigned int *t = (unsigned int *)image->scanLine(y);
    02665             p = t[x];
    02666             q = t[x+1];
    02667             r = t[x+image->width()];
    02668             s = t[x+image->width()+1];
    02669         }
    02670         else{
    02671             unsigned int *t = (unsigned int *)image->scanLine(y);
    02672             p = background;
    02673             if((x >= 0) && (y >= 0)){
    02674                 p = t[x];
    02675             }
    02676             q = background;
    02677             if(((x+1) < image->width()) && (y >= 0)){
    02678                 q = t[x+1];
    02679             }
    02680             r = background;
    02681             if((x >= 0) && ((y+1) < image->height())){
    02682                 t = (unsigned int *)image->scanLine(y+1);
    02683                 r = t[x+image->width()];
    02684             }
    02685             s = background;
    02686             if(((x+1) < image->width()) && ((y+1) < image->height())){
    02687                 t = (unsigned int *)image->scanLine(y+1);
    02688                 s = t[x+image->width()+1];
    02689             }
    02690
    02691         }
    02692     }
    02693     else{
    02694         unsigned int *colorTable = (unsigned int *)image->colorTable();
    02695         if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1)))    {
    02696             unsigned char *t;
    02697             t = (unsigned char *)image->scanLine(y);
    02698             p = *(colorTable+t[x]);
    02699             q = *(colorTable+t[x+1]);
    02700             t = (unsigned char *)image->scanLine(y+1);
    02701             r = *(colorTable+t[x]);
    02702             s = *(colorTable+t[x+1]);
    02703         }
    02704         else{
    02705             unsigned char *t;
    02706             p = background;
    02707             if((x >= 0) && (y >= 0)){
    02708                 t = (unsigned char *)image->scanLine(y);
    02709                 p = *(colorTable+t[x]);
    02710             }
    02711             q = background;
    02712             if(((x+1) < image->width()) && (y >= 0)){
    02713                 t = (unsigned char *)image->scanLine(y);
    02714                 q = *(colorTable+t[x+1]);
    02715             }
    02716             r = background;
    02717             if((x >= 0) && ((y+1) < image->height())){
    02718                 t = (unsigned char *)image->scanLine(y+1);
    02719                 r = *(colorTable+t[x]);
    02720             }
    02721             s = background;
    02722             if(((x+1) < image->width()) && ((y+1) < image->height())){
    02723                 t = (unsigned char *)image->scanLine(y+1);
    02724                 s = *(colorTable+t[x+1]);
    02725             }
    02726
    02727         }
    02728
    02729     }
    02730     x_offset -= floor(x_offset);
    02731     y_offset -= floor(y_offset);
    02732     alpha = 1.0-x_offset;
    02733     beta = 1.0-y_offset;
    02734
    02735     return(qRgba((unsigned char)(beta*(alpha*qRed(p)+x_offset*qRed(q))+y_offset*(alpha*qRed(r)+x_offset*qRed(s))),
    02736                  (unsigned char)(beta*(alpha*qGreen(p)+x_offset*qGreen(q))+y_offset*(alpha*qGreen(r)+x_offset*qGreen(s))),
    02737                  (unsigned char)(beta*(alpha*qBlue(p)+x_offset*qBlue(q))+y_offset*(alpha*qBlue(r)+x_offset*qBlue(s))),
    02738                  (unsigned char)(beta*(alpha*qAlpha(p)+x_offset*qAlpha(q))+y_offset*(alpha*qAlpha(r)+x_offset*qAlpha(s)))));
    02739 }
    02740
    02741 QImage KImageEffect::implode(QImage &src, double factor,
    02742                              unsigned int background)
    02743 {
    02744     double amount, distance, radius;
    02745     double x_center, x_distance, x_scale;
    02746     double y_center, y_distance, y_scale;
    02747     unsigned int *destData;
    02748     int x, y;
    02749
    02750     QImage dest(src.width(), src.height(), 32);
    02751
    02752     // compute scaling factor
    02753     x_scale = 1.0;
    02754     y_scale = 1.0;
    02755     x_center = (double)0.5*src.width();
    02756     y_center = (double)0.5*src.height();
    02757     radius=x_center;
    02758     if(src.width() > src.height())
    02759         y_scale = (double)src.width()/src.height();
    02760     else if(src.width() < src.height()){
    02761         x_scale = (double) src.height()/src.width();
    02762         radius = y_center;
    02763     }
    02764     amount=factor/10.0;
    02765     if(amount >= 0)
    02766         amount/=10.0;
    02767     if(src.depth() > 8){ // DirectClass source image
    02768         unsigned int *srcData;
    02769         for(y=0; y < src.height(); ++y){
    02770             srcData = (unsigned int *)src.scanLine(y);
    02771             destData = (unsigned int *)dest.scanLine(y);
    02772             y_distance=y_scale*(y-y_center);
    02773             for(x=0; x < src.width(); ++x){
    02774                 destData[x] = srcData[x];
    02775                 x_distance = x_scale*(x-x_center);
    02776                 distance= x_distance*x_distance+y_distance*y_distance;
    02777                 if(distance < (radius*radius)){
    02778                     double factor;
    02779                     // Implode the pixel.
    02780                     factor=1.0;
    02781                     if(distance > 0.0)
    02782                         factor=
    02783                             pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
    02784                     destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
    02785                                                    factor*y_distance/y_scale+y_center,
    02786                                                    background);
    02787                 }
    02788             }
    02789         }
    02790     }
    02791     else{ // PsudeoClass source image
    02792         unsigned char *srcData;
    02793         unsigned char idx;
    02794         unsigned int *cTable = src.colorTable();
    02795         for(y=0; y < src.height(); ++y){
    02796             srcData = (unsigned char *)src.scanLine(y);
    02797             destData = (unsigned int *)dest.scanLine(y);
    02798             y_distance=y_scale*(y-y_center);
    02799             for(x=0; x < src.width(); ++x){
    02800                 idx = srcData[x];
    02801                 destData[x] = cTable[idx];
    02802                 x_distance = x_scale*(x-x_center);
    02803                 distance= x_distance*x_distance+y_distance*y_distance;
    02804                 if(distance < (radius*radius)){
    02805                     double factor;
    02806                     // Implode the pixel.
    02807                     factor=1.0;
    02808                     if(distance > 0.0)
    02809                         factor=
    02810                             pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
    02811                     destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
    02812                                                    factor*y_distance/y_scale+y_center,
    02813                                                    background);
    02814                 }
    02815             }
    02816         }
    02817
    02818     }
    02819     return(dest);
    02820 }
    02821
    02822 QImage KImageEffect::rotate(QImage &img, RotateDirection r)
    02823 {
    02824     QImage dest;
    02825     int x, y;
    02826     if(img.depth() > 8){
    02827         unsigned int *srcData, *destData;
    02828         switch(r){
    02829         case Rotate90:
    02830             dest.create(img.height(), img.width(), img.depth());
    02831             for(y=0; y < img.height(); ++y){
    02832                 srcData = (unsigned int *)img.scanLine(y);
    02833                 for(x=0; x < img.width(); ++x){
    02834                     destData = (unsigned int *)dest.scanLine(x);
    02835                     destData[img.height()-y-1] = srcData[x];
    02836                 }
    02837             }
    02838             break;
    02839         case Rotate180:
    02840             dest.create(img.width(), img.height(), img.depth());
    02841             for(y=0; y < img.height(); ++y){
    02842                 srcData = (unsigned int *)img.scanLine(y);
    02843                 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
    02844                 for(x=0; x < img.width(); ++x)
    02845                     destData[img.width()-x-1] = srcData[x];
    02846             }
    02847             break;
    02848         case Rotate270:
    02849             dest.create(img.height(), img.width(), img.depth());
    02850             for(y=0; y < img.height(); ++y){
    02851                 srcData = (unsigned int *)img.scanLine(y);
    02852                 for(x=0; x < img.width(); ++x){
    02853                     destData = (unsigned int *)dest.scanLine(img.width()-x-1);
    02854                     destData[y] = srcData[x];
    02855                 }
    02856             }
    02857             break;
    02858         default:
    02859             dest = img;
    02860             break;
    02861         }
    02862     }
    02863     else{
    02864         unsigned char *srcData, *destData;
    02865         unsigned int *srcTable, *destTable;
    02866         switch(r){
    02867         case Rotate90:
    02868             dest.create(img.height(), img.width(), img.depth());
    02869             dest.setNumColors(img.numColors());
    02870             srcTable = (unsigned int *)img.colorTable();
    02871             destTable = (unsigned int *)dest.colorTable();
    02872             for(x=0; x < img.numColors(); ++x)
    02873                 destTable[x] = srcTable[x];
    02874             for(y=0; y < img.height(); ++y){
    02875                 srcData = (unsigned char *)img.scanLine(y);
    02876                 for(x=0; x < img.width(); ++x){
    02877                     destData = (unsigned char *)dest.scanLine(x);
    02878                     destData[img.height()-y-1] = srcData[x];
    02879                 }
    02880             }
    02881             break;
    02882         case Rotate180:
    02883             dest.create(img.width(), img.height(), img.depth());
    02884             dest.setNumColors(img.numColors());
    02885             srcTable = (unsigned int *)img.colorTable();
    02886             destTable = (unsigned int *)dest.colorTable();
    02887             for(x=0; x < img.numColors(); ++x)
    02888                 destTable[x] = srcTable[x];
    02889             for(y=0; y < img.height(); ++y){
    02890                 srcData = (unsigned char *)img.scanLine(y);
    02891                 destData = (unsigned char *)dest.scanLine(img.height()-y-1);
    02892                 for(x=0; x < img.width(); ++x)
    02893                     destData[img.width()-x-1] = srcData[x];
    02894             }
    02895             break;
    02896         case Rotate270:
    02897             dest.create(img.height(), img.width(), img.depth());
    02898             dest.setNumColors(img.numColors());
    02899             srcTable = (unsigned int *)img.colorTable();
    02900             destTable = (unsigned int *)dest.colorTable();
    02901             for(x=0; x < img.numColors(); ++x)
    02902                 destTable[x] = srcTable[x];
    02903             for(y=0; y < img.height(); ++y){
    02904                 srcData = (unsigned char *)img.scanLine(y);
    02905                 for(x=0; x < img.width(); ++x){
    02906                     destData = (unsigned char *)dest.scanLine(img.width()-x-1);
    02907                     destData[y] = srcData[x];
    02908                 }
    02909             }
    02910             break;
    02911         default:
    02912             dest = img;
    02913             break;
    02914         }
    02915
    02916     }
    02917     return(dest);
    02918 }
    02919
    02920 void KImageEffect::solarize(QImage &img, double factor)
    02921 {
    02922     int i, count;
    02923     int threshold;
    02924     unsigned int *data;
    02925
    02926     threshold = (int)(factor*(MaxRGB+1)/100.0);
    02927     if(img.depth() < 32){
    02928         data = (unsigned int *)img.colorTable();
    02929         count = img.numColors();
    02930     }
    02931     else{
    02932         data = (unsigned int *)img.bits();
    02933         count = img.width()*img.height();
    02934     }
    02935     for(i=0; i < count; ++i){
    02936         data[i] = qRgba(qRed(data[i]) > threshold ? MaxRGB-qRed(data[i]) : qRed(data[i]),
    02937                         qGreen(data[i]) > threshold ? MaxRGB-qGreen(data[i]) : qGreen(data[i]),
    02938                         qBlue(data[i]) > threshold ? MaxRGB-qBlue(data[i]) : qBlue(data[i]),
    02939                         qAlpha(data[i]));
    02940     }
    02941 }
    02942
    02943 QImage KImageEffect::spread(QImage &src, unsigned int amount)
    02944 {
    02945     int quantum, x, y;
    02946     int x_distance, y_distance;
    02947     if(src.width() < 3 || src.height() < 3)
    02948         return(src);
    02949     QImage dest(src);
    02950     dest.detach();
    02951     quantum=(amount+1) >> 1;
    02952     if(src.depth() > 8){ // DirectClass source image
    02953         unsigned int *p, *q;
    02954         for(y=0; y < src.height(); y++){
    02955             q = (unsigned int *)dest.scanLine(y);
    02956             for(x=0; x < src.width(); x++){
    02957                 x_distance = x + ((rand() & (amount+1))-quantum);
    02958                 y_distance = y + ((rand() & (amount+1))-quantum);
    02959                 x_distance = QMIN(x_distance, src.width()-1);
    02960                 y_distance = QMIN(y_distance, src.height()-1);
    02961                 if(x_distance < 0)
    02962                     x_distance = 0;
    02963                 if(y_distance < 0)
    02964                     y_distance = 0;
    02965                 p = (unsigned int *)src.scanLine(y_distance);
    02966                 p += x_distance;
    02967                 *q++=(*p);
    02968             }
    02969         }
    02970     }
    02971     else{ // PsudeoClass source image
    02972         // just do colortable values
    02973         unsigned char *p, *q;
    02974         for(y=0; y < src.height(); y++){
    02975             q = (unsigned char *)dest.scanLine(y);
    02976             for(x=0; x < src.width(); x++){
    02977                 x_distance = x + ((rand() & (amount+1))-quantum);
    02978                 y_distance = y + ((rand() & (amount+1))-quantum);
    02979                 x_distance = QMIN(x_distance, src.width()-1);
    02980                 y_distance = QMIN(y_distance, src.height()-1);
    02981                 if(x_distance < 0)
    02982                     x_distance = 0;
    02983                 if(y_distance < 0)
    02984                     y_distance = 0;
    02985                 p = (unsigned char *)src.scanLine(y_distance);
    02986                 p += x_distance;
    02987                 *q++=(*p);
    02988             }
    02989         }
    02990     }
    02991     return(dest);
    02992 }
    02993
    02994 QImage KImageEffect::swirl(QImage &src, double degrees,
    02995                            unsigned int background)
    02996 {
    02997     double cosine, distance, factor, radius, sine, x_center, x_distance,
    02998         x_scale, y_center, y_distance, y_scale;
    02999     int x, y;
    03000     unsigned int *q;
    03001     QImage dest(src.width(), src.height(), 32);
    03002
    03003     // compute scaling factor
    03004     x_center = src.width()/2.0;
    03005     y_center = src.height()/2.0;
    03006     radius = QMAX(x_center,y_center);
    03007     x_scale=1.0;
    03008     y_scale=1.0;
    03009     if(src.width() > src.height())
    03010         y_scale=(double)src.width()/src.height();
    03011     else if(src.width() < src.height())
    03012         x_scale=(double)src.height()/src.width();
    03013     degrees=DegreesToRadians(degrees);
    03014     // swirl each row
    03015     if(src.depth() > 8){ // DirectClass source image
    03016         unsigned int *p;
    03017         for(y=0; y < src.height(); y++){
    03018             p = (unsigned int *)src.scanLine(y);
    03019             q = (unsigned int *)dest.scanLine(y);
    03020             y_distance = y_scale*(y-y_center);
    03021             for(x=0; x < src.width(); x++){
    03022                 // determine if the pixel is within an ellipse
    03023                 *q=(*p);
    03024                 x_distance = x_scale*(x-x_center);
    03025                 distance = x_distance*x_distance+y_distance*y_distance;
    03026                 if (distance < (radius*radius)){
    03027                     // swirl
    03028                     factor = 1.0-sqrt(distance)/radius;
    03029                     sine = sin(degrees*factor*factor);
    03030                     cosine = cos(degrees*factor*factor);
    03031                     *q = interpolateColor(&src,
    03032                                           (cosine*x_distance-sine*y_distance)/x_scale+x_center,
    03033                                           (sine*x_distance+cosine*y_distance)/y_scale+y_center,
    03034                                           background);
    03035                 }
    03036                 p++;
    03037                 q++;
    03038             }
    03039         }
    03040     }
    03041     else{ // PsudeoClass source image
    03042         unsigned char *p;
    03043         unsigned int *cTable = (unsigned int *)src.colorTable();
    03044         for(y=0; y < src.height(); y++){
    03045             p = (unsigned char *)src.scanLine(y);
    03046             q = (unsigned int *)dest.scanLine(y);
    03047             y_distance = y_scale*(y-y_center);
    03048             for(x=0; x < src.width(); x++){
    03049                 // determine if the pixel is within an ellipse
    03050                 *q = *(cTable+(*p));
    03051                 x_distance = x_scale*(x-x_center);
    03052                 distance = x_distance*x_distance+y_distance*y_distance;
    03053                 if (distance < (radius*radius)){
    03054                     // swirl
    03055                     factor = 1.0-sqrt(distance)/radius;
    03056                     sine = sin(degrees*factor*factor);
    03057                     cosine = cos(degrees*factor*factor);
    03058                     *q = interpolateColor(&src,
    03059                                           (cosine*x_distance-sine*y_distance)/x_scale+x_center,
    03060                                           (sine*x_distance+cosine*y_distance)/y_scale+y_center,
    03061                                           background);
    03062                 }
    03063                 p++;
    03064                 q++;
    03065             }
    03066         }
    03067
    03068     }
    03069     return(dest);
    03070 }
    03071
    03072 QImage KImageEffect::wave(QImage &src, double amplitude, double wavelength,
    03073                           unsigned int background)
    03074 {
    03075     double *sine_map;
    03076     int x, y;
    03077     unsigned int *q;
    03078
    03079     QImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
    03080     // allocate sine map
    03081     sine_map = (double *)malloc(dest.width()*sizeof(double));
    03082     if(!sine_map)
    03083         return(src);
    03084     for(x=0; x < dest.width(); ++x)
    03085         sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
    03086     // wave image
    03087     for(y=0; y < dest.height(); ++y){
    03088         q = (unsigned int *)dest.scanLine(y);
    03089         for (x=0; x < dest.width(); x++){
    03090             *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
    03091             ++q;
    03092         }
    03093     }
    03094     free(sine_map);
    03095     return(dest);
    03096 }
    03097
    03098 QImage KImageEffect::oilPaint(QImage &src, int radius)
    03099 {
    03100     // TODO 8bpp src!
    03101     if(src.depth() < 32){
    03102         qWarning("Oil Paint source image < 32bpp. Convert before using!");
    03103         return(src);
    03104     }
    03105     int j, k, i, x, y;
    03106     unsigned int *histogram;
    03107     unsigned int *s;
    03108     unsigned int count;
    03109
    03110     unsigned int *srcData, *destData;
    03111
    03112     QImage dest(src);
    03113     dest.detach();
    03114     histogram = (unsigned int *) malloc((MaxRGB+1)*sizeof(unsigned int));
    03115     if(!histogram)
    03116         return(src);
    03117     // paint each row
    03118     k=0;
    03119     for(y = radius; y < src.height(); ++y){
    03120         srcData = (unsigned int *)src.scanLine(y-radius);
    03121         destData = (unsigned int *)dest.scanLine(y);
    03122         srcData += radius*src.width()+radius;
    03123         destData += radius;
    03124         for(x=radius; x < src.width()-radius; ++x){
    03125             // determine most frequent color
    03126             count = 0;
    03127             for(i=0; i < MaxRGB+1; ++i)
    03128                 histogram[i] = 0;
    03129             for(i=0; i < radius; ++i){
    03130                 s = srcData-(radius-1)*src.width()-i-1;
    03131                 for(j =0; j < (2*i+1); ++j){
    03132                     k = intensityValue(*s);
    03133                     histogram[k]++;
    03134                     if(histogram[k] > count){
    03135                         *destData = *s;
    03136                         count = histogram[k];
    03137                     }
    03138                     ++s;
    03139                 }
    03140                 s = srcData+(radius-i)*src.width()-i-1;
    03141                 for(j =0; j < (2*i+1); ++j){
    03142                     k = intensityValue(*s);
    03143                     histogram[k]++;
    03144                     if(histogram[k] > count){
    03145                         *destData = *s;
    03146                         count = histogram[k];
    03147                     }
    03148                     ++s;
    03149                 }
    03150             }
    03151             s = srcData-radius;
    03152             for(j =0; j < (2*i+1); ++j){
    03153                 k = intensityValue(*s);
    03154                 histogram[k]++;
    03155                 if(histogram[k] > count){
    03156                     *destData = *s;
    03157                     count = histogram[k];
    03158                 }
    03159                 ++s;
    03160             }
    03161             ++srcData;
    03162             ++destData;
    03163         }
    03164     }
    03165     free(histogram);
    03166     return(dest);
    03167 }
    03168
    03169 //
    03170 // The following methods work by computing a value from neighboring pixels
    03171 // (mosfet 12/28/01)
    03172 //
    03173
    03174 QImage KImageEffect::edge(QImage &src, double factor)
    03175 {
    03176 #define Edge(weight) \
    03177     total_red+=(weight)*qRed(*s); \
    03178     total_green+=(weight)*qGreen(*s); \
    03179     total_blue+=(weight)*qBlue(*s); \
    03180     total_opacity+=(weight)*qAlpha(*s); \
    03181     s++;
    03182
    03183 #define Edge256(weight) \
    03184     total_red+=(weight)*qRed(*(cTable+(*s))); \
    03185     total_green+=(weight)*qGreen(*(cTable+(*s))); \
    03186     total_blue+=(weight)*qBlue(*(cTable+(*s))); \
    03187     total_opacity+=(weight)*qAlpha(*(cTable+(*s))); \
    03188     s++;
    03189
    03190     if(src.width() < 3 || src.height() < 3)
    03191         return(src);
    03192
    03193     double total_blue, total_green, total_opacity, total_red, weight;
    03194
    03195     int x, y;
    03196
    03197     unsigned int *q;
    03198
    03199     QImage dest(src.width(), src.height(), 32);
    03200     weight=factor/8.0;
    03201     if(src.depth() > 8){ // DirectClass source image
    03202         unsigned int *p, *s;
    03203         for(y=0; y < src.height(); ++y){
    03204             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
    03205             q = (unsigned int *)dest.scanLine(y);
    03206             // edge detect this row of pixels.
    03207             *q++=(*(p+src.width()));
    03208             for(x=1; x < src.width()-1; ++x){
    03209                 // compute weighted average of target pixel color components.
    03210                 total_red=0.0;
    03211                 total_green=0.0;
    03212                 total_blue=0.0;
    03213                 total_opacity=0.0;
    03214                 s=p;
    03215                 Edge(-weight/8); Edge(-weight/8) Edge(-weight/8);
    03216                 s=p+src.width();
    03217                 Edge(-weight/8); Edge(weight); Edge(-weight/8);
    03218                 s=p+2*src.width();
    03219                 Edge(-weight/8); Edge(-weight/8); Edge(-weight/8);
    03220                 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
    03221                            (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
    03222                            (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
    03223                            (unsigned char)((total_opacity < 0) ? 0 : (total_opacity > MaxRGB) ? MaxRGB : total_opacity));
    03224                 p++;
    03225                 q++;
    03226             }
    03227             p++;
    03228             *q++=(*p);
    03229         }
    03230     }
    03231     else{ // PsudeoClass source image
    03232         unsigned char *p, *p2, *p3, *s;
    03233         unsigned int *cTable = src.colorTable();
    03234         int scanLineIdx;
    03235         for(y=0; y < src.height(); ++y){
    03236             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
    03237             p = (unsigned char *)src.scanLine(scanLineIdx);
    03238             p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
    03239             p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
    03240             q = (unsigned int *)dest.scanLine(y);
    03241             // edge detect this row of pixels.
    03242             *q++=(*(cTable+(*p2)));
    03243             for(x=1; x < src.width()-1; ++x){
    03244                 // compute weighted average of target pixel color components.
    03245                 total_red=0.0;
    03246                 total_green=0.0;
    03247                 total_blue=0.0;
    03248                 total_opacity=0.0;
    03249                 s=p;
    03250                 Edge256(-weight/8); Edge256(-weight/8) Edge256(-weight/8);
    03251                 s=p2;
    03252                 Edge256(-weight/8); Edge256(weight); Edge256(-weight/8);
    03253                 s=p3;
    03254                 Edge256(-weight/8); Edge256(-weight/8); Edge256(-weight/8);
    03255                 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
    03256                            (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
    03257                            (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
    03258                            (unsigned char)((total_opacity < 0) ? 0 : (total_opacity > MaxRGB) ? MaxRGB : total_opacity));
    03259                 p++;
    03260                 p2++;
    03261                 p3++;
    03262                 q++;
    03263             }
    03264             p++;
    03265             *q++=(*(cTable+(*p)));
    03266         }
    03267     }
    03268     return(dest);
    03269 }
    03270
    03271 QImage KImageEffect::sharpen(QImage &src, double factor)
    03272 {
    03273 #define Sharpen(weight) \
    03274     total_red+=(weight)*qRed(*s); \
    03275     total_green+=(weight)*qGreen(*s); \
    03276     total_blue+=(weight)*qBlue(*s); \
    03277     total_opacity+=(weight)*qAlpha(*s); \
    03278     s++;
    03279
    03280 #define Sharpen256(weight) \
    03281     total_red+=(weight)*qRed(*(cTable+(*s))); \
    03282     total_green+=(weight)*qGreen(*(cTable+(*s))); \
    03283     total_blue+=(weight)*qBlue(*(cTable+(*s))); \
    03284     total_opacity+=(weight)*qAlpha(*(cTable+(*s))); \
    03285     s++;
    03286
    03287     if(src.width() < 3 || src.height() < 3)
    03288         return(src);
    03289
    03290     double total_blue, total_green, total_opacity, total_red;
    03291     double quantum, weight;
    03292     unsigned char r, g, b, a;
    03293
    03294     int x, y;
    03295     unsigned int *q;
    03296
    03297     QImage dest(src.width(), src.height(), 32);
    03298     weight = ((100.0-factor)/2.0+13.0);
    03299     quantum = QMAX(weight-12.0, 1.0);
    03300     if(src.depth() > 8){ // DirectClass source image
    03301         unsigned int *p, *s;
    03302         for(y=0; y < src.height(); ++y){
    03303             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
    03304             q = (unsigned int *)dest.scanLine(y);
    03305             // sharpen this row of pixels.
    03306             *q++=(*(p+src.width()));
    03307             for(x=1; x < src.width()-1; ++x){
    03308                 // compute weighted average of target pixel color components.
    03309                 total_red=0.0;
    03310                 total_green=0.0;
    03311                 total_blue=0.0;
    03312                 total_opacity=0.0;
    03313                 s=p;
    03314                 Sharpen(-1); Sharpen(-2); Sharpen(-1);
    03315                 s=p+src.width();
    03316                 Sharpen(-2); Sharpen(weight); Sharpen(-2);
    03317                 s=p+2*src.width();
    03318                 Sharpen(-1); Sharpen(-2); Sharpen(-1);
    03319                 if(total_red < 0)
    03320                     r=0;
    03321                 else if(total_red > (int)(MaxRGB*quantum))
    03322                     r = (unsigned char)MaxRGB;
    03323                 else
    03324                     r = (unsigned char)((total_red+(quantum/2.0))/quantum);
    03325
    03326                 if(total_green < 0)
    03327                     g = 0;
    03328                 else if(total_green > (int)(MaxRGB*quantum))
    03329                     g = (unsigned char)MaxRGB;
    03330                 else
    03331                     g = (unsigned char)((total_green+(quantum/2.0))/quantum);
    03332
    03333                 if(total_blue < 0)
    03334                     b = 0;
    03335                 else if(total_blue > (int)(MaxRGB*quantum))
    03336                     b = (unsigned char)MaxRGB;
    03337                 else
    03338                     b = (unsigned char)((total_blue+(quantum/2.0))/quantum);
    03339
    03340                 if(total_opacity < 0)
    03341                     a = 0;
    03342                 else if(total_opacity > (int)(MaxRGB*quantum))
    03343                     a = (unsigned char)MaxRGB;
    03344                 else
    03345                     a= (unsigned char)((total_opacity+(quantum/2.0))/quantum);
    03346
    03347                 *q = qRgba(r, g, b, a);
    03348
    03349                 p++;
    03350                 q++;
    03351             }
    03352             p++;
    03353             *q++=(*p);
    03354         }
    03355     }
    03356     else{ // PsudeoClass source image
    03357         unsigned char *p, *p2, *p3, *s;
    03358         unsigned int *cTable = src.colorTable();
    03359         int scanLineIdx;
    03360         for(y=0; y < src.height(); ++y){
    03361             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
    03362             p = (unsigned char *)src.scanLine(scanLineIdx);
    03363             p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
    03364             p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
    03365             q = (unsigned int *)dest.scanLine(y);
    03366             // sharpen this row of pixels.
    03367             *q++=(*(cTable+(*p2)));
    03368             for(x=1; x < src.width()-1; ++x){
    03369                 // compute weighted average of target pixel color components.
    03370                 total_red=0.0;
    03371                 total_green=0.0;
    03372                 total_blue=0.0;
    03373                 total_opacity=0.0;
    03374                 s=p;
    03375                 Sharpen256(-1); Sharpen256(-2); Sharpen256(-1);
    03376                 s=p2;
    03377                 Sharpen256(-2); Sharpen256(weight); Sharpen256(-2);
    03378                 s=p3;
    03379                 Sharpen256(-1); Sharpen256(-2); Sharpen256(-1);
    03380                 if(total_red < 0)
    03381                     r=0;
    03382                 else if(total_red > (int)(MaxRGB*quantum))
    03383                     r = (unsigned char)MaxRGB;
    03384                 else
    03385                     r = (unsigned char)((total_red+(quantum/2.0))/quantum);
    03386
    03387                 if(total_green < 0)
    03388                     g = 0;
    03389                 else if(total_green > (int)(MaxRGB*quantum))
    03390                     g = (unsigned char)MaxRGB;
    03391                 else
    03392                     g = (unsigned char)((total_green+(quantum/2.0))/quantum);
    03393
    03394                 if(total_blue < 0)
    03395                     b = 0;
    03396                 else if(total_blue > (int)(MaxRGB*quantum))
    03397                     b = (unsigned char)MaxRGB;
    03398                 else
    03399                     b = (unsigned char)((total_blue+(quantum/2.0))/quantum);
    03400
    03401                 if(total_opacity < 0)
    03402                     a = 0;
    03403                 else if(total_opacity > (int)(MaxRGB*quantum))
    03404                     a = (unsigned char)MaxRGB;
    03405                 else
    03406                     a = (unsigned char)((total_opacity+(quantum/2.0))/quantum);
    03407
    03408                 *q = qRgba(r, g, b, a);
    03409
    03410                 p++;
    03411                 p2++;
    03412                 p3++;
    03413                 q++;
    03414             }
    03415             p++;
    03416             *q++=(*(cTable+(*p)));
    03417         }
    03418     }
    03419     return(dest);
    03420 }
    03421
    03422 QImage KImageEffect::emboss(QImage &src)
    03423 {
    03424 #define Emboss(weight) \
    03425     total_red+=(weight)*qRed(*s); \
    03426     total_green+=(weight)*qGreen(*s); \
    03427     total_blue+=(weight)*qBlue(*s); \
    03428     s++;
    03429
    03430 #define Emboss256(weight) \
    03431     total_red+=(weight)*qRed(*(cTable+(*s))); \
    03432     total_green+=(weight)*qGreen(*(cTable+(*s))); \
    03433     total_blue+=(weight)*qBlue(*(cTable+(*s))); \
    03434     s++;
    03435
    03436     if(src.width() < 3 || src.height() < 3)
    03437         return(src);
    03438
    03439     double total_blue, total_green, total_red;
    03440     int x, y;
    03441     unsigned int *q;
    03442
    03443     QImage dest(src.width(), src.height(), 32);
    03444     if(src.depth() > 8){ // DirectClass source image
    03445         unsigned int *p, *s;
    03446         for(y=0; y < src.height(); ++y){
    03447             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
    03448             q = (unsigned int *)dest.scanLine(y);
    03449             // emboss this row of pixels.
    03450             *q++=(*(p+src.width()));
    03451             for(x=1; x < src.width()-1; ++x){
    03452                 // compute weighted average of target pixel color components.
    03453                 total_red=0.0;
    03454                 total_green=0.0;
    03455                 total_blue=0.0;
    03456                 s=p;
    03457                 Emboss(-1); Emboss(-2); Emboss( 0);
    03458                 s=p+src.width();
    03459                 Emboss(-2); Emboss( 0); Emboss( 2);
    03460                 s=p+2*src.width();
    03461                 Emboss( 0); Emboss( 2); Emboss( 1);
    03462                 total_red += (MaxRGB+1)/2;
    03463                 total_green += (MaxRGB+1)/2;
    03464                 total_blue += (MaxRGB+1)/2;
    03465                 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
    03466                            (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
    03467                            (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
    03468                            255);
    03469                 p++;
    03470                 q++;
    03471             }
    03472             p++;
    03473             *q++=(*p);
    03474         }
    03475     }
    03476     else{ // PsudeoClass source image
    03477         unsigned char *p, *p2, *p3, *s;
    03478         unsigned int *cTable = src.colorTable();
    03479         int scanLineIdx;
    03480         for(y=0; y < src.height(); ++y){
    03481             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
    03482             p = (unsigned char *)src.scanLine(scanLineIdx);
    03483             p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
    03484             p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
    03485             q = (unsigned int *)dest.scanLine(y);
    03486             // emboss this row of pixels.
    03487             *q++=(*(cTable+(*p2)));
    03488             for(x=1; x < src.width()-1; ++x){
    03489                 // compute weighted average of target pixel color components.
    03490                 total_red=0.0;
    03491                 total_green=0.0;
    03492                 total_blue=0.0;
    03493                 s=p;
    03494                 Emboss256(-1); Emboss256(-2); Emboss256(0);
    03495                 s=p2;
    03496                 Emboss256(-2); Emboss256(0); Emboss256(2);
    03497                 s=p3;
    03498                 Emboss256(0); Emboss256(2); Emboss256(1);
    03499                 total_red += (MaxRGB+1)/2;
    03500                 total_green += (MaxRGB+1)/2;
    03501                 total_blue += (MaxRGB+1)/2;
    03502                 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
    03503                            (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
    03504                            (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
    03505                            255);
    03506                 p++;
    03507                 p2++;
    03508                 p3++;
    03509                 q++;
    03510             }
    03511             p++;
    03512             *q++=(*(cTable+(*p)));
    03513         }
    03514     }
    03515     toGray(dest);
    03516     normalize(dest);
    03517     return(dest);
    03518 }
    03519
    03520 QImage KImageEffect::shade(QImage &src, bool color_shading, double azimuth,
    03521              double elevation)
    03522 {
    03523     struct PointInfo{
    03524         double x, y, z;
    03525     };
    03526
    03527     double distance, normal_distance, shade;
    03528     int x, y;
    03529
    03530     struct PointInfo light, normal;
    03531
    03532     unsigned int *q;
    03533
    03534     QImage dest(src.width(), src.height(), 32);
    03535
    03536     azimuth = DegreesToRadians(azimuth);
    03537     elevation = DegreesToRadians(elevation);
    03538     light.x = MaxRGB*cos(azimuth)*cos(elevation);
    03539     light.y = MaxRGB*sin(azimuth)*cos(elevation);
    03540     light.z = MaxRGB*sin(elevation);
    03541     normal.z= 2*MaxRGB;  // constant Z of surface normal
    03542
    03543     if(src.depth() > 8){ // DirectClass source image
    03544         unsigned int *p, *s0, *s1, *s2;
    03545         for(y=0; y < src.height(); ++y){
    03546             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
    03547             q = (unsigned int *)dest.scanLine(y);
    03548             // shade this row of pixels.
    03549             *q++=(*(p+src.width()));
    03550             p++;
    03551             s0 = p;
    03552             s1 = p + src.width();
    03553             s2 = p + 2*src.width();
    03554             for(x=1; x < src.width()-1; ++x){
    03555                 // determine the surface normal and compute shading.
    03556                 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
    03557                     (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
    03558                     (double) intensityValue(*(s2+1));
    03559                 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
    03560                     (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
    03561                     (double) intensityValue(*(s0+1));
    03562                 if((normal.x == 0) && (normal.y == 0))
    03563                     shade=light.z;
    03564                 else{
    03565                     shade=0.0;
    03566                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
    03567                     if (distance > 0.0){
    03568                         normal_distance=
    03569                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
    03570                         if(fabs(normal_distance) > 0.0000001)
    03571                             shade=distance/sqrt(normal_distance);
    03572                     }
    03573                 }
    03574                 if(!color_shading){
    03575                     *q = qRgba((unsigned char)(shade),
    03576                                (unsigned char)(shade),
    03577                                (unsigned char)(shade),
    03578                                qAlpha(*s1));
    03579                 }
    03580                 else{
    03581                     *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)),
    03582                                (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)),
    03583                                (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)),
    03584                                qAlpha(*s1));
    03585                 }
    03586                 ++s0;
    03587                 ++s1;
    03588                 ++s2;
    03589                 q++;
    03590             }
    03591             *q++=(*s1);
    03592         }
    03593     }
    03594     else{ // PsudeoClass source image
    03595         unsigned char *p, *s0, *s1, *s2;
    03596         int scanLineIdx;
    03597         unsigned int *cTable = (unsigned int *)src.colorTable();
    03598         for(y=0; y < src.height(); ++y){
    03599             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
    03600             p = (unsigned char *)src.scanLine(scanLineIdx);
    03601             q = (unsigned int *)dest.scanLine(y);
    03602             // shade this row of pixels.
    03603             s0 = p;
    03604             s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
    03605             s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
    03606             *q++=(*(cTable+(*s1)));
    03607             ++p;
    03608             ++s0;
    03609             ++s1;
    03610             ++s2;
    03611             for(x=1; x < src.width()-1; ++x){
    03612                 // determine the surface normal and compute shading.
    03613                 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
    03614                     (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
    03615                     (double) intensityValue(*(cTable+(*(s2+1))));
    03616                 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
    03617                     (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
    03618                     (double) intensityValue(*(cTable+(*(s0+1))));
    03619                 if((normal.x == 0) && (normal.y == 0))
    03620                     shade=light.z;
    03621                 else{
    03622                     shade=0.0;
    03623                     distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
    03624                     if (distance > 0.0){
    03625                         normal_distance=
    03626                             normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
    03627                         if(fabs(normal_distance) > 0.0000001)
    03628                             shade=distance/sqrt(normal_distance);
    03629                     }
    03630                 }
    03631                 if(!color_shading){
    03632                     *q = qRgba((unsigned char)(shade),
    03633                                (unsigned char)(shade),
    03634                                (unsigned char)(shade),
    03635                                qAlpha(*(cTable+(*s1))));
    03636                 }
    03637                 else{
    03638                     *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)),
    03639                                (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)),
    03640                                (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)),
    03641                                qAlpha(*s1));
    03642                 }
    03643                 ++s0;
    03644                 ++s1;
    03645                 ++s2;
    03646                 q++;
    03647             }
    03648             *q++=(*(cTable+(*s1)));
    03649         }
    03650     }
    03651     return(dest);
    03652 }
    03653
    03654 QImage KImageEffect::blur(QImage &src, double factor)
    03655 {
    03656
    03657 #define Blur(weight) \
    03658     total_red+=(weight)*qRed(*s); \
    03659     total_green+=(weight)*qGreen(*s); \
    03660     total_blue+=(weight)*qBlue(*s); \
    03661     total_opacity+=(weight)*qAlpha(*s); \
    03662     s++;
    03663
    03664 #define Blur256(weight) \
    03665     total_red+=(weight)*qRed(*(cTable+(*s))); \
    03666     total_green+=(weight)*qGreen(*(cTable+(*s))); \
    03667     total_blue+=(weight)*qBlue(*(cTable+(*s))); \
    03668     total_opacity+=(weight)*qAlpha(*(cTable+(*s))); \
    03669     s++;
    03670
    03671     if(src.width() < 3 || src.height() < 3)
    03672         return(src);
    03673
    03674     double quantum, total_blue, total_green, total_opacity, total_red, weight;
    03675
    03676     int x, y;
    03677     unsigned int *q;
    03678
    03679     QImage dest(src.width(), src.height(), 32);
    03680     weight=((100.0-factor)/2)+1;
    03681     quantum = QMAX(weight+12.0, 1.0);
    03682     if(src.depth() > 8){ // DirectClass source image
    03683         unsigned int *p, *s;
    03684         for(y=0; y < src.height(); ++y){
    03685             p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
    03686             q = (unsigned int *)dest.scanLine(y);
    03687             // blur this row of pixels.
    03688             *q++=(*(p+src.width()));
    03689             for(x=1; x < src.width()-1; ++x){
    03690                 // compute weighted average of target pixel color components.
    03691                 total_red=0.0;
    03692                 total_green=0.0;
    03693                 total_blue=0.0;
    03694                 total_opacity=0.0;
    03695                 s=p;
    03696                 Blur(1); Blur(2); Blur(1);
    03697                 s=p+src.width();
    03698                 Blur(2); Blur(weight); Blur(2);
    03699                 s=p+2*src.width();
    03700                 Blur(1); Blur(2); Blur(1);
    03701                 *q = qRgba((unsigned char)((total_red+(quantum/2))/quantum),
    03702                            (unsigned char)((total_green+(quantum/2))/quantum),
    03703                            (unsigned char)((total_blue+(quantum/2))/quantum),
    03704                            (unsigned char)((total_opacity+(quantum/2))/quantum));
    03705                 p++;
    03706                 q++;
    03707             }
    03708             p++;
    03709             *q++=(*p);
    03710         }
    03711     }
    03712     else{ // PsudeoClass source image
    03713         unsigned char *p, *p2, *p3, *s;
    03714         unsigned int *cTable = src.colorTable();
    03715         int scanLineIdx;
    03716         for(y=0; y < src.height(); ++y){
    03717             scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
    03718             p = (unsigned char *)src.scanLine(scanLineIdx);
    03719             p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
    03720             p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
    03721             q = (unsigned int *)dest.scanLine(y);
    03722             // blur this row of pixels.
    03723             *q++=(*(cTable+(*p2)));
    03724             for(x=1; x < src.width()-1; ++x){
    03725                 // compute weighted average of target pixel color components.
    03726                 total_red=0.0;
    03727                 total_green=0.0;
    03728                 total_blue=0.0;
    03729                 total_opacity=0.0;
    03730                 s=p;
    03731                 Blur256(1); Blur256(2); Blur256(1);
    03732                 s=p2;
    03733                 Blur256(2); Blur256(weight); Blur256(2);
    03734                 s=p3;
    03735                 Blur256(1); Blur256(2); Blur256(1);
    03736                 *q = qRgba((unsigned char)((total_red+(quantum/2))/quantum),
    03737                            (unsigned char)((total_green+(quantum/2))/quantum),
    03738                            (unsigned char)((total_blue+(quantum/2))/quantum),
    03739                            (unsigned char)((total_opacity+(quantum/2))/quantum));
    03740                 p++;
    03741                 p2++;
    03742                 p3++;
    03743                 q++;
    03744             }
    03745             p++;
    03746             *q++=(*(cTable+(*p)));
    03747         }
    03748     }
    03749     return(dest);
    03750 }
    03751
    03752 // High quality, expensive HSV contrast. You can do a faster one by just
    03753 // taking a grayscale threshold (ie: 128) and incrementing RGB color
    03754 // channels above it and decrementing those below it, but this gives much
    03755 // better results. (mosfet 12/28/01)
    03756 void KImageEffect::contrastHSV(QImage &img, bool sharpen)
    03757 {
    03758     int i, sign;
    03759     unsigned int *data;
    03760     int count;
    03761     double brightness, scale, theta;
    03762     QColor c;
    03763     int h, s, v;
    03764
    03765     sign = sharpen ? 1 : -1;
    03766     scale=0.5000000000000001;
    03767     if(img.depth() > 8){
    03768         count = img.width()*img.height();
    03769         data = (unsigned int *)img.bits();
    03770     }
    03771     else{
    03772         count = img.numColors();
    03773         data = (unsigned int *)img.colorTable();
    03774     }
    03775     for(i=0; i < count; ++i){
    03776         c.setRgb(data[i]);
    03777         c.hsv(&h, &s, &v);
    03778         brightness = v/255.0;
    03779         theta=(brightness-0.5)*M_PI;
    03780         brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
    03781         if (brightness > 1.0)
    03782             brightness=1.0;
    03783         else
    03784             if (brightness < 0)
    03785                 brightness=0.0;
    03786         v = (int)(brightness*255);
    03787         c.setHsv(h, s, v);
    03788         data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i]));
    03789     }
    03790 }
    03791
    03792
    03793
    03794

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:44:22 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001