kdefx Library API Documentation

kimageeffect.cpp

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