kdefx Library API Documentation

kimageeffect.cpp

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