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