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 <tqimage.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*tqRed(color) + 00088 0.587*tqGreen(color) + 00089 0.1140000000000001*tqBlue(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 TQImage KImageEffect::gradient(const TQSize &size, const TQColor &ca, 00125 const TQColor &cb, GradientType eff, int ncols) 00126 { 00127 int rDiff, gDiff, bDiff; 00128 int rca, gca, bca, rcb, gcb, bcb; 00129 00130 TQImage 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 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 int rl = rca << 16; 00151 int gl = gca << 16; 00152 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 = tqRgb( (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++ = tqRgb( (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] = tqRgb(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 = tqRgb(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 = tqRgb(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 = tqRgb(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 = tqRgb(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 && (TQPixmap::defaultDepth() < 15 )) { 00361 if ( ncols < 2 || ncols > 256 ) 00362 ncols = 3; 00363 TQColor *dPal = new TQColor[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 TQImage KImageEffect::unbalancedGradient(const TQSize &size, const TQColor &ca, 00389 const TQColor &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 TQImage 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 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 TQColor 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] = tqRgb(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] = tqRgb(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] = tqRgb(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] = tqRgb(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] = tqRgb(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] = tqRgb(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 && (TQPixmap::defaultDepth() < 15 )) { 00600 if ( ncols < 2 || ncols > 256 ) 00601 ncols = 3; 00602 TQColor *dPal = new TQColor[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 TQ_UINT16 data[4]; 00632 }; 00633 00634 struct KIE8Pack 00635 { 00636 TQ_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 TQImage& KImageEffect::intensity(TQImage &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.tqcolorTable(); 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 TQ_UINT16 p = TQ_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 TQ_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 = tqRed(data[i]); 00819 int g = tqGreen(data[i]); 00820 int b = tqBlue(data[i]); 00821 int a = tqAlpha(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] = tqRgba(r, g, b,a); 00826 } 00827 } 00828 else{ 00829 for(int i=0; i < pixels; ++i){ 00830 int r = tqRed(data[i]); 00831 int g = tqGreen(data[i]); 00832 int b = tqBlue(data[i]); 00833 int a = tqAlpha(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] = tqRgba(r, g, b, a); 00838 } 00839 } 00840 delete [] segTbl; 00841 } 00842 00843 return image; 00844 } 00845 00846 TQImage& KImageEffect::channelIntensity(TQImage &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.tqcolorTable(); 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 = tqRed(data[i]); 00887 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; 00888 data[i] = tqRgba(c, tqGreen(data[i]), tqBlue(data[i]), tqAlpha(data[i])); 00889 } 00890 } 00891 else if(channel == Green){ 00892 for(int i=0; i < pixels; ++i){ 00893 int c = tqGreen(data[i]); 00894 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; 00895 data[i] = tqRgba(tqRed(data[i]), c, tqBlue(data[i]), tqAlpha(data[i])); 00896 } 00897 } 00898 else{ 00899 for(int i=0; i < pixels; ++i){ 00900 int c = tqBlue(data[i]); 00901 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c]; 00902 data[i] = tqRgba(tqRed(data[i]), tqGreen(data[i]), c, tqAlpha(data[i])); 00903 } 00904 } 00905 00906 } 00907 else{ 00908 if(channel == Red){ 00909 for(int i=0; i < pixels; ++i){ 00910 int c = tqRed(data[i]); 00911 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; 00912 data[i] = tqRgba(c, tqGreen(data[i]), tqBlue(data[i]), tqAlpha(data[i])); 00913 } 00914 } 00915 else if(channel == Green){ 00916 for(int i=0; i < pixels; ++i){ 00917 int c = tqGreen(data[i]); 00918 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; 00919 data[i] = tqRgba(tqRed(data[i]), c, tqBlue(data[i]), tqAlpha(data[i])); 00920 } 00921 } 00922 else{ 00923 for(int i=0; i < pixels; ++i){ 00924 int c = tqBlue(data[i]); 00925 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c]; 00926 data[i] = tqRgba(tqRed(data[i]), tqGreen(data[i]), c, tqAlpha(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 TQImage& KImageEffect::modulate(TQImage &image, TQImage &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 TQColor clr; 00950 int mod=0; 00951 unsigned int x1, x2, y1, y2; 00952 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.tqcolorTable():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 = tqRed(color1); 00986 g = tqGreen(color1); 00987 b = tqBlue(color1); 00988 if (channel != All) { 00989 mod = (channel == Red) ? tqRed(color2) : 00990 (channel == Green) ? tqGreen(color2) : 00991 (channel == Blue) ? tqBlue(color2) : 00992 (channel == Gray) ? tqGray(color2) : 0; 00993 mod = mod*factor/50; 00994 } 00995 00996 if (type == Intensity) { 00997 if (channel == All) { 00998 r += r * factor/50 * tqRed(color2)/256; 00999 g += g * factor/50 * tqGreen(color2)/256; 01000 b += b * factor/50 * tqBlue(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 * tqRed(color2)/128; 01011 g += (g-128) * factor/50 * tqGreen(color2)/128; 01012 b += (b-128) * factor/50 * tqBlue(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 = tqAlpha(*data1); 01025 *data1 = tqRgba(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) ? tqRed(color2) : 01031 (channel == Green) ? tqGreen(color2) : 01032 (channel == Blue) ? tqBlue(color2) : 01033 (channel == Gray) ? tqGray(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 = tqAlpha(*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 TQImage& KImageEffect::blend(const TQColor& clr, TQImage& 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 #ifdef USE_QT4 01083 if (dst.format() != QImage::Format_ARGB32) 01084 dst = dst.convertToFormat(QImage::Format_ARGB32); // This is needed because Qt4 has multiple variants with a 32 bit depth, and the routines below expect one specific variant (ARGB) 01085 #endif 01086 01087 int pixels = dst.width() * dst.height(); 01088 01089 #ifdef USE_SSE2_INLINE_ASM 01090 if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) { 01091 TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 ); 01092 01093 KIE8Pack packedalpha = { { alpha, alpha, alpha, 256, 01094 alpha, alpha, alpha, 256 } }; 01095 01096 TQ_UINT16 red = TQ_UINT16( clr.red() * 256 * opacity ); 01097 TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity ); 01098 TQ_UINT16 blue = TQ_UINT16( clr.blue() * 256 * opacity ); 01099 01100 KIE8Pack packedcolor = { { blue, green, red, 0, 01101 blue, green, red, 0 } }; 01102 01103 // Prepare the XMM5, XMM6 and XMM7 registers for unpacking and blending 01104 __asm__ __volatile__( 01105 "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking 01106 "movdqu (%0), %%xmm6\n\t" // Set up (1 - alpha) * 256 in XMM6 01107 "movdqu (%1), %%xmm5\n\t" // Set up color * alpha * 256 in XMM5 01108 : : "r"(&packedalpha), "r"(&packedcolor), 01109 "m"(packedcolor), "m"(packedalpha) ); 01110 01111 TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() ); 01112 01113 // Check how many pixels we need to process to achieve 16 byte alignment 01114 int offset = (16 - (TQ_UINT32( data ) & 0x0f)) / 4; 01115 01116 // The main loop processes 8 pixels / iteration 01117 int remainder = (pixels - offset) % 8; 01118 pixels -= remainder; 01119 01120 // Alignment loop 01121 for ( int i = 0; i < offset; i++ ) { 01122 __asm__ __volatile__( 01123 "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1 01124 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel 01125 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256 01126 "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result 01127 "psrlw $8, %%xmm0\n\t" // Divide by 256 01128 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword 01129 "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image 01130 : : "r"(data), "r"(i) ); 01131 } 01132 01133 // Main loop 01134 for ( int i = offset; i < pixels; i += 8 ) { 01135 __asm__ __volatile( 01136 // Load 8 pixels to XMM registers 1 - 4 01137 "movq (%0,%1,4), %%xmm0\n\t" // Load pixels 1 and 2 to XMM1 01138 "movq 8(%0,%1,4), %%xmm1\n\t" // Load pixels 3 and 4 to XMM2 01139 "movq 16(%0,%1,4), %%xmm2\n\t" // Load pixels 5 and 6 to XMM3 01140 "movq 24(%0,%1,4), %%xmm3\n\t" // Load pixels 7 and 8 to XMM4 01141 01142 // Prefetch the pixels for next iteration 01143 "prefetchnta 32(%0,%1,4) \n\t" 01144 01145 // Blend pixels 1 and 2 01146 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixels 01147 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixels with (1 - alpha) * 256 01148 "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result 01149 "psrlw $8, %%xmm0\n\t" // Divide by 256 01150 01151 // Blend pixels 3 and 4 01152 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixels 01153 "pmullw %%xmm6, %%xmm1\n\t" // Multiply the pixels with (1 - alpha) * 256 01154 "paddw %%xmm5, %%xmm1\n\t" // Add color * alpha * 256 to the result 01155 "psrlw $8, %%xmm1\n\t" // Divide by 256 01156 01157 // Blend pixels 5 and 6 01158 "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the pixels 01159 "pmullw %%xmm6, %%xmm2\n\t" // Multiply the pixels with (1 - alpha) * 256 01160 "paddw %%xmm5, %%xmm2\n\t" // Add color * alpha * 256 to the result 01161 "psrlw $8, %%xmm2\n\t" // Divide by 256 01162 01163 // Blend pixels 7 and 8 01164 "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the pixels 01165 "pmullw %%xmm6, %%xmm3\n\t" // Multiply the pixels with (1 - alpha) * 256 01166 "paddw %%xmm5, %%xmm3\n\t" // Add color * alpha * 256 to the result 01167 "psrlw $8, %%xmm3\n\t" // Divide by 256 01168 01169 // Pack the pixels into 2 double quadwords 01170 "packuswb %%xmm1, %%xmm0\n\t" // Pack pixels 1 - 4 to a double qword 01171 "packuswb %%xmm3, %%xmm2\n\t" // Pack pixles 5 - 8 to a double qword 01172 01173 // Write the pixels back to the image 01174 "movdqa %%xmm0, (%0,%1,4)\n\t" // Store pixels 1 - 4 01175 "movdqa %%xmm2, 16(%0,%1,4)\n\t" // Store pixels 5 - 8 01176 : : "r"(data), "r"(i) ); 01177 } 01178 01179 // Cleanup loop 01180 for ( int i = pixels; i < pixels + remainder; i++ ) { 01181 __asm__ __volatile__( 01182 "movd (%0,%1,4), %%xmm0\n\t" // Load one pixel to XMM1 01183 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel 01184 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the pixel with (1 - alpha) * 256 01185 "paddw %%xmm5, %%xmm0\n\t" // Add color * alpha * 256 to the result 01186 "psrlw $8, %%xmm0\n\t" // Divide by 256 01187 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword 01188 "movd %%xmm0, (%0,%1,4)\n\t" // Write the pixel to the image 01189 : : "r"(data), "r"(i) ); 01190 } 01191 } else 01192 #endif 01193 01194 #ifdef USE_MMX_INLINE_ASM 01195 if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) { 01196 TQ_UINT16 alpha = TQ_UINT16( ( 1.0 - opacity ) * 256.0 ); 01197 KIE4Pack packedalpha = { { alpha, alpha, alpha, 256 } }; 01198 01199 TQ_UINT16 red = TQ_UINT16( clr.red() * 256 * opacity ); 01200 TQ_UINT16 green = TQ_UINT16( clr.green() * 256 * opacity ); 01201 TQ_UINT16 blue = TQ_UINT16( clr.blue() * 256 * opacity ); 01202 01203 KIE4Pack packedcolor = { { blue, green, red, 0 } }; 01204 01205 __asm__ __volatile__( 01206 "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking 01207 "movq (%0), %%mm6\n\t" // Set up (1 - alpha) * 256 in MM6 01208 "movq (%1), %%mm5\n\t" // Set up color * alpha * 256 in MM5 01209 : : "r"(&packedalpha), "r"(&packedcolor), "m"(packedcolor), "m"(packedalpha) ); 01210 01211 TQ_UINT32 *data = reinterpret_cast<TQ_UINT32*>( dst.bits() ); 01212 01213 // The main loop processes 4 pixels / iteration 01214 int remainder = pixels % 4; 01215 pixels -= remainder; 01216 01217 // Main loop 01218 for ( int i = 0; i < pixels; i += 4 ) { 01219 __asm__ __volatile__( 01220 // Load 4 pixels to MM registers 1 - 4 01221 "movd (%0,%1,4), %%mm0\n\t" // Load the 1st pixel to MM0 01222 "movd 4(%0,%1,4), %%mm1\n\t" // Load the 2nd pixel to MM1 01223 "movd 8(%0,%1,4), %%mm2\n\t" // Load the 3rd pixel to MM2 01224 "movd 12(%0,%1,4), %%mm3\n\t" // Load the 4th pixel to MM3 01225 01226 // Blend the first pixel 01227 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel 01228 "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with (1 - alpha) * 256 01229 "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result 01230 "psrlw $8, %%mm0\n\t" // Divide by 256 01231 01232 // Blend the second pixel 01233 "punpcklbw %%mm7, %%mm1\n\t" // Unpack the pixel 01234 "pmullw %%mm6, %%mm1\n\t" // Multiply the pixel with (1 - alpha) * 256 01235 "paddw %%mm5, %%mm1\n\t" // Add color * alpha * 256 to the result 01236 "psrlw $8, %%mm1\n\t" // Divide by 256 01237 01238 // Blend the third pixel 01239 "punpcklbw %%mm7, %%mm2\n\t" // Unpack the pixel 01240 "pmullw %%mm6, %%mm2\n\t" // Multiply the pixel with (1 - alpha) * 256 01241 "paddw %%mm5, %%mm2\n\t" // Add color * alpha * 256 to the result 01242 "psrlw $8, %%mm2\n\t" // Divide by 256 01243 01244 // Blend the fourth pixel 01245 "punpcklbw %%mm7, %%mm3\n\t" // Unpack the pixel 01246 "pmullw %%mm6, %%mm3\n\t" // Multiply the pixel with (1 - alpha) * 256 01247 "paddw %%mm5, %%mm3\n\t" // Add color * alpha * 256 to the result 01248 "psrlw $8, %%mm3\n\t" // Divide by 256 01249 01250 // Pack the pixels into 2 quadwords 01251 "packuswb %%mm1, %%mm0\n\t" // Pack pixels 1 and 2 to a qword 01252 "packuswb %%mm3, %%mm2\n\t" // Pack pixels 3 and 4 to a qword 01253 01254 // Write the pixels back to the image 01255 "movq %%mm0, (%0,%1,4)\n\t" // Store pixels 1 and 2 01256 "movq %%mm2, 8(%0,%1,4)\n\t" // Store pixels 3 and 4 01257 : : "r"(data), "r"(i) ); 01258 } 01259 01260 // Cleanup loop 01261 for ( int i = pixels; i < pixels + remainder; i++ ) { 01262 __asm__ __volatile__( 01263 "movd (%0,%1,4), %%mm0\n\t" // Load one pixel to MM1 01264 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the pixel 01265 "pmullw %%mm6, %%mm0\n\t" // Multiply the pixel with 1 - alpha * 256 01266 "paddw %%mm5, %%mm0\n\t" // Add color * alpha * 256 to the result 01267 "psrlw $8, %%mm0\n\t" // Divide by 256 01268 "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword 01269 "movd %%mm0, (%0,%1,4)\n\t" // Write the pixel to the image 01270 : : "r"(data), "r"(i) ); 01271 } 01272 01273 // Empty the MMX state 01274 __asm__ __volatile__("emms"); 01275 } else 01276 #endif // USE_MMX_INLINE_ASM 01277 01278 { 01279 int rcol, gcol, bcol; 01280 clr.rgb(&rcol, &gcol, &bcol); 01281 01282 #ifdef WORDS_BIGENDIAN // ARGB (skip alpha) 01283 unsigned char *data = (unsigned char *)dst.bits() + 1; 01284 #else // BGRA 01285 unsigned char *data = (unsigned char *)dst.bits(); 01286 #endif 01287 01288 for (int i=0; i<pixels; i++) 01289 { 01290 #ifdef WORDS_BIGENDIAN 01291 *data += (unsigned char)((rcol - *data) * opacity); 01292 data++; 01293 *data += (unsigned char)((gcol - *data) * opacity); 01294 data++; 01295 *data += (unsigned char)((bcol - *data) * opacity); 01296 data++; 01297 #else 01298 *data += (unsigned char)((bcol - *data) * opacity); 01299 data++; 01300 *data += (unsigned char)((gcol - *data) * opacity); 01301 data++; 01302 *data += (unsigned char)((rcol - *data) * opacity); 01303 data++; 01304 #endif 01305 data++; // skip alpha 01306 } 01307 } 01308 01309 return dst; 01310 } 01311 01312 // Nice and fast direct pixel manipulation 01313 TQImage& KImageEffect::blend(TQImage& src, TQImage& dst, float opacity) 01314 { 01315 if (src.width() <= 0 || src.height() <= 0) 01316 return dst; 01317 if (dst.width() <= 0 || dst.height() <= 0) 01318 return dst; 01319 01320 if (src.width() != dst.width() || src.height() != dst.height()) { 01321 #ifndef NDEBUG 01322 std::cerr << "WARNING: KImageEffect::blend : src and destination images are not the same size\n"; 01323 #endif 01324 return dst; 01325 } 01326 01327 if (opacity < 0.0 || opacity > 1.0) { 01328 #ifndef NDEBUG 01329 std::cerr << "WARNING: KImageEffect::blend : invalid opacity. Range [0, 1]\n"; 01330 #endif 01331 return dst; 01332 } 01333 01334 if (src.depth() != 32) src = src.convertDepth(32); 01335 if (dst.depth() != 32) dst = dst.convertDepth(32); 01336 01337 #ifdef USE_QT4 01338 if (src.format() != QImage::Format_ARGB32) 01339 src = dst.convertToFormat(QImage::Format_ARGB32); // This is needed because Qt4 has multiple variants with a 32 bit depth, and the routines below expect one specific variant (ARGB) 01340 if (dst.format() != QImage::Format_ARGB32) 01341 dst = dst.convertToFormat(QImage::Format_ARGB32); // This is needed because Qt4 has multiple variants with a 32 bit depth, and the routines below expect one specific variant (ARGB) 01342 #endif 01343 01344 int pixels = src.width() * src.height(); 01345 01346 #ifdef USE_SSE2_INLINE_ASM 01347 if ( KCPUInfo::haveExtension( KCPUInfo::IntelSSE2 ) && pixels > 16 ) { 01348 TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 ); 01349 KIE8Pack packedalpha = { { alpha, alpha, alpha, 0, 01350 alpha, alpha, alpha, 0 } }; 01351 01352 // Prepare the XMM6 and XMM7 registers for unpacking and blending 01353 __asm__ __volatile__( 01354 "pxor %%xmm7, %%xmm7\n\t" // Zero out XMM7 for unpacking 01355 "movdqu (%0), %%xmm6\n\t" // Set up alpha * 256 in XMM6 01356 : : "r"(&packedalpha), "m"(packedalpha) ); 01357 01358 TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() ); 01359 TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() ); 01360 01361 // Check how many pixels we need to process to achieve 16 byte alignment 01362 int offset = (16 - (TQ_UINT32( data2 ) & 0x0f)) / 4; 01363 01364 // The main loop processes 4 pixels / iteration 01365 int remainder = (pixels - offset) % 4; 01366 pixels -= remainder; 01367 01368 // Alignment loop 01369 for ( int i = 0; i < offset; i++ ) { 01370 __asm__ __volatile__( 01371 "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1 01372 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel 01373 "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0 01374 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel 01375 "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src 01376 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 01377 "psllw $8, %%xmm1\n\t" // Multiply dst with 256 01378 "paddw %%xmm1, %%xmm0\n\t" // Add dst to result 01379 "psrlw $8, %%xmm0\n\t" // Divide by 256 01380 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword 01381 "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image 01382 : : "r"(data1), "r"(data2), "r"(i) ); 01383 } 01384 01385 // Main loop 01386 for ( int i = offset; i < pixels; i += 4 ) { 01387 __asm__ __volatile__( 01388 // Load 4 src pixels to XMM0 and XMM2 and 4 dst pixels to XMM1 and XMM3 01389 "movq (%0,%2,4), %%xmm0\n\t" // Load two src pixels to XMM0 01390 "movq (%1,%2,4), %%xmm1\n\t" // Load two dst pixels to XMM1 01391 "movq 8(%0,%2,4), %%xmm2\n\t" // Load two src pixels to XMM2 01392 "movq 8(%1,%2,4), %%xmm3\n\t" // Load two dst pixels to XMM3 01393 01394 // Prefetch the pixels for the iteration after the next one 01395 "prefetchnta 32(%0,%2,4) \n\t" 01396 "prefetchnta 32(%1,%2,4) \n\t" 01397 01398 // Blend the first two pixels 01399 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the dst pixels 01400 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the src pixels 01401 "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src 01402 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 01403 "psllw $8, %%xmm1\n\t" // Multiply dst with 256 01404 "paddw %%xmm1, %%xmm0\n\t" // Add dst to the result 01405 "psrlw $8, %%xmm0\n\t" // Divide by 256 01406 01407 // Blend the next two pixels 01408 "punpcklbw %%xmm7, %%xmm3\n\t" // Unpack the dst pixels 01409 "punpcklbw %%xmm7, %%xmm2\n\t" // Unpack the src pixels 01410 "psubw %%xmm3, %%xmm2\n\t" // Subtract dst from src 01411 "pmullw %%xmm6, %%xmm2\n\t" // Multiply the result with alpha * 256 01412 "psllw $8, %%xmm3\n\t" // Multiply dst with 256 01413 "paddw %%xmm3, %%xmm2\n\t" // Add dst to the result 01414 "psrlw $8, %%xmm2\n\t" // Divide by 256 01415 01416 // Write the pixels back to the image 01417 "packuswb %%xmm2, %%xmm0\n\t" // Pack the pixels to a double qword 01418 "movdqa %%xmm0, (%1,%2,4)\n\t" // Store the pixels 01419 : : "r"(data1), "r"(data2), "r"(i) ); 01420 } 01421 01422 // Cleanup loop 01423 for ( int i = pixels; i < pixels + remainder; i++ ) { 01424 __asm__ __volatile__( 01425 "movd (%1,%2,4), %%xmm1\n\t" // Load one dst pixel to XMM1 01426 "punpcklbw %%xmm7, %%xmm1\n\t" // Unpack the pixel 01427 "movd (%0,%2,4), %%xmm0\n\t" // Load one src pixel to XMM0 01428 "punpcklbw %%xmm7, %%xmm0\n\t" // Unpack the pixel 01429 "psubw %%xmm1, %%xmm0\n\t" // Subtract dst from src 01430 "pmullw %%xmm6, %%xmm0\n\t" // Multiply the result with alpha * 256 01431 "psllw $8, %%xmm1\n\t" // Multiply dst with 256 01432 "paddw %%xmm1, %%xmm0\n\t" // Add dst to result 01433 "psrlw $8, %%xmm0\n\t" // Divide by 256 01434 "packuswb %%xmm1, %%xmm0\n\t" // Pack the pixel to a dword 01435 "movd %%xmm0, (%1,%2,4)\n\t" // Write the pixel to the image 01436 : : "r"(data1), "r"(data2), "r"(i) ); 01437 } 01438 } else 01439 #endif // USE_SSE2_INLINE_ASM 01440 01441 #ifdef USE_MMX_INLINE_ASM 01442 if ( KCPUInfo::haveExtension( KCPUInfo::IntelMMX ) && pixels > 1 ) { 01443 TQ_UINT16 alpha = TQ_UINT16( opacity * 256.0 ); 01444 KIE4Pack packedalpha = { { alpha, alpha, alpha, 0 } }; 01445 01446 // Prepare the MM6 and MM7 registers for blending and unpacking 01447 __asm__ __volatile__( 01448 "pxor %%mm7, %%mm7\n\t" // Zero out MM7 for unpacking 01449 "movq (%0), %%mm6\n\t" // Set up alpha * 256 in MM6 01450 : : "r"(&packedalpha), "m"(packedalpha) ); 01451 01452 TQ_UINT32 *data1 = reinterpret_cast<TQ_UINT32*>( src.bits() ); 01453 TQ_UINT32 *data2 = reinterpret_cast<TQ_UINT32*>( dst.bits() ); 01454 01455 // The main loop processes 2 pixels / iteration 01456 int remainder = pixels % 2; 01457 pixels -= remainder; 01458 01459 // Main loop 01460 for ( int i = 0; i < pixels; i += 2 ) { 01461 __asm__ __volatile__( 01462 // Load 2 src pixels to MM0 and MM2 and 2 dst pixels to MM1 and MM3 01463 "movd (%0,%2,4), %%mm0\n\t" // Load the 1st src pixel to MM0 01464 "movd (%1,%2,4), %%mm1\n\t" // Load the 1st dst pixel to MM1 01465 "movd 4(%0,%2,4), %%mm2\n\t" // Load the 2nd src pixel to MM2 01466 "movd 4(%1,%2,4), %%mm3\n\t" // Load the 2nd dst pixel to MM3 01467 01468 // Blend the first pixel 01469 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel 01470 "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel 01471 "psubw %%mm1, %%mm0\n\t" // Subtract dst from src 01472 "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256 01473 "psllw $8, %%mm1\n\t" // Multiply dst with 256 01474 "paddw %%mm1, %%mm0\n\t" // Add dst to the result 01475 "psrlw $8, %%mm0\n\t" // Divide by 256 01476 01477 // Blend the second pixel 01478 "punpcklbw %%mm7, %%mm2\n\t" // Unpack the src pixel 01479 "punpcklbw %%mm7, %%mm3\n\t" // Unpack the dst pixel 01480 "psubw %%mm3, %%mm2\n\t" // Subtract dst from src 01481 "pmullw %%mm6, %%mm2\n\t" // Multiply the result with alpha * 256 01482 "psllw $8, %%mm3\n\t" // Multiply dst with 256 01483 "paddw %%mm3, %%mm2\n\t" // Add dst to the result 01484 "psrlw $8, %%mm2\n\t" // Divide by 256 01485 01486 // Write the pixels back to the image 01487 "packuswb %%mm2, %%mm0\n\t" // Pack the pixels to a qword 01488 "movq %%mm0, (%1,%2,4)\n\t" // Store the pixels 01489 : : "r"(data1), "r"(data2), "r"(i) ); 01490 } 01491 01492 // Blend the remaining pixel (if there is one) 01493 if ( remainder ) { 01494 __asm__ __volatile__( 01495 "movd (%0), %%mm0\n\t" // Load one src pixel to MM0 01496 "punpcklbw %%mm7, %%mm0\n\t" // Unpack the src pixel 01497 "movd (%1), %%mm1\n\t" // Load one dst pixel to MM1 01498 "punpcklbw %%mm7, %%mm1\n\t" // Unpack the dst pixel 01499 "psubw %%mm1, %%mm0\n\t" // Subtract dst from src 01500 "pmullw %%mm6, %%mm0\n\t" // Multiply the result with alpha * 256 01501 "psllw $8, %%mm1\n\t" // Multiply dst with 256 01502 "paddw %%mm1, %%mm0\n\t" // Add dst to result 01503 "psrlw $8, %%mm0\n\t" // Divide by 256 01504 "packuswb %%mm0, %%mm0\n\t" // Pack the pixel to a dword 01505 "movd %%mm0, (%1)\n\t" // Write the pixel to the image 01506 : : "r"(data1 + pixels), "r"(data2 + pixels) ); 01507 } 01508 01509 // Empty the MMX state 01510 __asm__ __volatile__("emms"); 01511 } else 01512 #endif // USE_MMX_INLINE_ASM 01513 01514 { 01515 #ifdef WORDS_BIGENDIAN // ARGB (skip alpha) 01516 unsigned char *data1 = (unsigned char *)dst.bits() + 1; 01517 unsigned char *data2 = (unsigned char *)src.bits() + 1; 01518 #else // BGRA 01519 unsigned char *data1 = (unsigned char *)dst.bits(); 01520 unsigned char *data2 = (unsigned char *)src.bits(); 01521 #endif 01522 01523 for (int i=0; i<pixels; i++) 01524 { 01525 #ifdef WORDS_BIGENDIAN 01526 *data1 += (unsigned char)((*(data2++) - *data1) * opacity); 01527 data1++; 01528 *data1 += (unsigned char)((*(data2++) - *data1) * opacity); 01529 data1++; 01530 *data1 += (unsigned char)((*(data2++) - *data1) * opacity); 01531 data1++; 01532 #else 01533 *data1 += (unsigned char)((*(data2++) - *data1) * opacity); 01534 data1++; 01535 *data1 += (unsigned char)((*(data2++) - *data1) * opacity); 01536 data1++; 01537 *data1 += (unsigned char)((*(data2++) - *data1) * opacity); 01538 data1++; 01539 #endif 01540 data1++; // skip alpha 01541 data2++; 01542 } 01543 } 01544 01545 return dst; 01546 } 01547 01548 01549 TQImage& KImageEffect::blend(TQImage &image, float initial_intensity, 01550 const TQColor &bgnd, GradientType eff, 01551 bool anti_dir) 01552 { 01553 if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) { 01554 #ifndef NDEBUG 01555 std::cerr << "WARNING: KImageEffect::blend : invalid image\n"; 01556 #endif 01557 return image; 01558 } 01559 01560 int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue(); 01561 int r, g, b; 01562 int ind; 01563 01564 unsigned int xi, xf, yi, yf; 01565 unsigned int a; 01566 01567 // check the boundaries of the initial intesity param 01568 float unaffected = 1; 01569 if (initial_intensity > 1) initial_intensity = 1; 01570 if (initial_intensity < -1) initial_intensity = -1; 01571 if (initial_intensity < 0) { 01572 unaffected = 1. + initial_intensity; 01573 initial_intensity = 0; 01574 } 01575 01576 01577 float intensity = initial_intensity; 01578 float var = 1. - initial_intensity; 01579 01580 if (anti_dir) { 01581 initial_intensity = intensity = 1.; 01582 var = -var; 01583 } 01584 01585 int x, y; 01586 01587 unsigned int *data = (unsigned int *)image.bits(); 01588 01589 int image_width = image.width(); //Those can't change 01590 int image_height = image.height(); 01591 01592 01593 if( eff == VerticalGradient || eff == HorizontalGradient ) { 01594 01595 // set the image domain to apply the effect to 01596 xi = 0, xf = image_width; 01597 yi = 0, yf = image_height; 01598 if (eff == VerticalGradient) { 01599 if (anti_dir) yf = (int)(image_height * unaffected); 01600 else yi = (int)(image_height * (1 - unaffected)); 01601 } 01602 else { 01603 if (anti_dir) xf = (int)(image_width * unaffected); 01604 else xi = (int)(image_height * (1 - unaffected)); 01605 } 01606 01607 var /= (eff == VerticalGradient?yf-yi:xf-xi); 01608 01609 int ind_base; 01610 for (y = yi; y < (int)yf; y++) { 01611 intensity = eff == VerticalGradient? intensity + var : 01612 initial_intensity; 01613 ind_base = image_width * y ; 01614 for (x = xi; x < (int)xf ; x++) { 01615 if (eff == HorizontalGradient) intensity += var; 01616 ind = x + ind_base; 01617 r = tqRed (data[ind]) + (int)(intensity * 01618 (r_bgnd - tqRed (data[ind]))); 01619 g = tqGreen(data[ind]) + (int)(intensity * 01620 (g_bgnd - tqGreen(data[ind]))); 01621 b = tqBlue (data[ind]) + (int)(intensity * 01622 (b_bgnd - tqBlue (data[ind]))); 01623 if (r > 255) r = 255; if (r < 0 ) r = 0; 01624 if (g > 255) g = 255; if (g < 0 ) g = 0; 01625 if (b > 255) b = 255; if (b < 0 ) b = 0; 01626 a = tqAlpha(data[ind]); 01627 data[ind] = tqRgba(r, g, b, a); 01628 } 01629 } 01630 } 01631 else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) { 01632 float xvar = var / 2 / image_width; // / unaffected; 01633 float yvar = var / 2 / image_height; // / unaffected; 01634 float tmp; 01635 01636 for (x = 0; x < image_width ; x++) { 01637 tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1); 01638 ind = x; 01639 for (y = 0; y < image_height ; y++) { 01640 intensity = initial_intensity + tmp + yvar * y; 01641 01642 r = tqRed (data[ind]) + (int)(intensity * 01643 (r_bgnd - tqRed (data[ind]))); 01644 g = tqGreen(data[ind]) + (int)(intensity * 01645 (g_bgnd - tqGreen(data[ind]))); 01646 b = tqBlue (data[ind]) + (int)(intensity * 01647 (b_bgnd - tqBlue (data[ind]))); 01648 if (r > 255) r = 255; if (r < 0 ) r = 0; 01649 if (g > 255) g = 255; if (g < 0 ) g = 0; 01650 if (b > 255) b = 255; if (b < 0 ) b = 0; 01651 a = tqAlpha(data[ind]); 01652 data[ind] = tqRgba(r, g, b, a); 01653 01654 ind += image_width; 01655 } 01656 } 01657 } 01658 01659 else if (eff == RectangleGradient || eff == EllipticGradient) { 01660 float xvar; 01661 float yvar; 01662 01663 for (x = 0; x < image_width / 2 + image_width % 2; x++) { 01664 xvar = var / image_width * (image_width - x*2/unaffected-1); 01665 for (y = 0; y < image_height / 2 + image_height % 2; y++) { 01666 yvar = var / image_height * (image_height - y*2/unaffected -1); 01667 01668 if (eff == RectangleGradient) 01669 intensity = initial_intensity + QMAX(xvar, yvar); 01670 else 01671 intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar); 01672 if (intensity > 1) intensity = 1; 01673 if (intensity < 0) intensity = 0; 01674 01675 //NW 01676 ind = x + image_width * y ; 01677 r = tqRed (data[ind]) + (int)(intensity * 01678 (r_bgnd - tqRed (data[ind]))); 01679 g = tqGreen(data[ind]) + (int)(intensity * 01680 (g_bgnd - tqGreen(data[ind]))); 01681 b = tqBlue (data[ind]) + (int)(intensity * 01682 (b_bgnd - tqBlue (data[ind]))); 01683 if (r > 255) r = 255; if (r < 0 ) r = 0; 01684 if (g > 255) g = 255; if (g < 0 ) g = 0; 01685 if (b > 255) b = 255; if (b < 0 ) b = 0; 01686 a = tqAlpha(data[ind]); 01687 data[ind] = tqRgba(r, g, b, a); 01688 01689 //NE 01690 ind = image_width - x - 1 + image_width * y ; 01691 r = tqRed (data[ind]) + (int)(intensity * 01692 (r_bgnd - tqRed (data[ind]))); 01693 g = tqGreen(data[ind]) + (int)(intensity * 01694 (g_bgnd - tqGreen(data[ind]))); 01695 b = tqBlue (data[ind]) + (int)(intensity * 01696 (b_bgnd - tqBlue (data[ind]))); 01697 if (r > 255) r = 255; if (r < 0 ) r = 0; 01698 if (g > 255) g = 255; if (g < 0 ) g = 0; 01699 if (b > 255) b = 255; if (b < 0 ) b = 0; 01700 a = tqAlpha(data[ind]); 01701 data[ind] = tqRgba(r, g, b, a); 01702 } 01703 } 01704 01705 //CT loop is doubled because of stupid central row/column issue. 01706 // other solution? 01707 for (x = 0; x < image_width / 2; x++) { 01708 xvar = var / image_width * (image_width - x*2/unaffected-1); 01709 for (y = 0; y < image_height / 2; y++) { 01710 yvar = var / image_height * (image_height - y*2/unaffected -1); 01711 01712 if (eff == RectangleGradient) 01713 intensity = initial_intensity + QMAX(xvar, yvar); 01714 else 01715 intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar); 01716 if (intensity > 1) intensity = 1; 01717 if (intensity < 0) intensity = 0; 01718 01719 //SW 01720 ind = x + image_width * (image_height - y -1) ; 01721 r = tqRed (data[ind]) + (int)(intensity * 01722 (r_bgnd - tqRed (data[ind]))); 01723 g = tqGreen(data[ind]) + (int)(intensity * 01724 (g_bgnd - tqGreen(data[ind]))); 01725 b = tqBlue (data[ind]) + (int)(intensity * 01726 (b_bgnd - tqBlue (data[ind]))); 01727 if (r > 255) r = 255; if (r < 0 ) r = 0; 01728 if (g > 255) g = 255; if (g < 0 ) g = 0; 01729 if (b > 255) b = 255; if (b < 0 ) b = 0; 01730 a = tqAlpha(data[ind]); 01731 data[ind] = tqRgba(r, g, b, a); 01732 01733 //SE 01734 ind = image_width-x-1 + image_width * (image_height - y - 1) ; 01735 r = tqRed (data[ind]) + (int)(intensity * 01736 (r_bgnd - tqRed (data[ind]))); 01737 g = tqGreen(data[ind]) + (int)(intensity * 01738 (g_bgnd - tqGreen(data[ind]))); 01739 b = tqBlue (data[ind]) + (int)(intensity * 01740 (b_bgnd - tqBlue (data[ind]))); 01741 if (r > 255) r = 255; if (r < 0 ) r = 0; 01742 if (g > 255) g = 255; if (g < 0 ) g = 0; 01743 if (b > 255) b = 255; if (b < 0 ) b = 0; 01744 a = tqAlpha(data[ind]); 01745 data[ind] = tqRgba(r, g, b, a); 01746 } 01747 } 01748 } 01749 #ifndef NDEBUG 01750 else std::cerr << "KImageEffect::blend effect not implemented" << std::endl; 01751 #endif 01752 return image; 01753 } 01754 01755 // Not very efficient as we create a third big image... 01756 // 01757 TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2, 01758 GradientType gt, int xf, int yf) 01759 { 01760 if (image1.width() == 0 || image1.height() == 0 || 01761 image2.width() == 0 || image2.height() == 0) 01762 return image1; 01763 01764 TQImage image3; 01765 01766 image3 = KImageEffect::unbalancedGradient(image1.size(), 01767 TQColor(0,0,0), TQColor(255,255,255), 01768 gt, xf, yf, 0); 01769 01770 return blend(image1,image2,image3, Red); // Channel to use is arbitrary 01771 } 01772 01773 // Blend image2 into image1, using an RBG channel of blendImage 01774 // 01775 TQImage& KImageEffect::blend(TQImage &image1, TQImage &image2, 01776 TQImage &blendImage, RGBComponent channel) 01777 { 01778 if (image1.width() == 0 || image1.height() == 0 || 01779 image2.width() == 0 || image2.height() == 0 || 01780 blendImage.width() == 0 || blendImage.height() == 0) { 01781 #ifndef NDEBUG 01782 std::cerr << "KImageEffect::blend effect invalid image" << std::endl; 01783 #endif 01784 return image1; 01785 } 01786 01787 int r, g, b; 01788 int ind1, ind2, ind3; 01789 01790 unsigned int x1, x2, x3, y1, y2, y3; 01791 unsigned int a; 01792 01793 int x, y; 01794 01795 // for image1 and image2, we only handle depth 32 01796 if (image1.depth()<32) image1 = image1.convertDepth(32); 01797 if (image2.depth()<32) image2 = image2.convertDepth(32); 01798 01799 // for blendImage, we handle depth 8 and 32 01800 if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8); 01801 01802 unsigned int *colorTable3 = (blendImage.depth()==8) ? 01803 blendImage.tqcolorTable():0; 01804 01805 unsigned int *data1 = (unsigned int *)image1.bits(); 01806 unsigned int *data2 = (unsigned int *)image2.bits(); 01807 unsigned int *data3 = (unsigned int *)blendImage.bits(); 01808 unsigned char *data3b = (unsigned char *)blendImage.bits(); 01809 unsigned int color3; 01810 01811 x1 = image1.width(); y1 = image1.height(); 01812 x2 = image2.width(); y2 = image2.height(); 01813 x3 = blendImage.width(); y3 = blendImage.height(); 01814 01815 for (y = 0; y < (int)y1; y++) { 01816 ind1 = x1*y; 01817 ind2 = x2*(y%y2); 01818 ind3 = x3*(y%y3); 01819 01820 x=0; 01821 while(x < (int)x1) { 01822 color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3]; 01823 01824 a = (channel == Red) ? tqRed(color3) : 01825 (channel == Green) ? tqGreen(color3) : 01826 (channel == Blue) ? tqBlue(color3) : tqGray(color3); 01827 01828 r = (a*tqRed(data1[ind1]) + (256-a)*tqRed(data2[ind2]))/256; 01829 g = (a*tqGreen(data1[ind1]) + (256-a)*tqGreen(data2[ind2]))/256; 01830 b = (a*tqBlue(data1[ind1]) + (256-a)*tqBlue(data2[ind2]))/256; 01831 01832 a = tqAlpha(data1[ind1]); 01833 data1[ind1] = tqRgba(r, g, b, a); 01834 01835 ind1++; ind2++; ind3++; x++; 01836 if ( (x%x2) ==0) ind2 -= x2; 01837 if ( (x%x3) ==0) ind3 -= x3; 01838 } 01839 } 01840 return image1; 01841 } 01842 01843 01844 //====================================================================== 01845 // 01846 // Hash effects 01847 // 01848 //====================================================================== 01849 01850 unsigned int KImageEffect::lHash(unsigned int c) 01851 { 01852 unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c); 01853 unsigned char nr, ng, nb; 01854 nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr; 01855 ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng; 01856 nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb; 01857 01858 return tqRgba(nr, ng, nb, a); 01859 } 01860 01861 01862 // ----------------------------------------------------------------------------- 01863 01864 unsigned int KImageEffect::uHash(unsigned int c) 01865 { 01866 unsigned char r = tqRed(c), g = tqGreen(c), b = tqBlue(c), a = tqAlpha(c); 01867 unsigned char nr, ng, nb; 01868 nr = r + (r >> 3); nr = nr < r ? ~0 : nr; 01869 ng = g + (g >> 3); ng = ng < g ? ~0 : ng; 01870 nb = b + (b >> 3); nb = nb < b ? ~0 : nb; 01871 01872 return tqRgba(nr, ng, nb, a); 01873 } 01874 01875 01876 // ----------------------------------------------------------------------------- 01877 01878 TQImage& KImageEffect::hash(TQImage &image, Lighting lite, unsigned int spacing) 01879 { 01880 if (image.width() == 0 || image.height() == 0) { 01881 #ifndef NDEBUG 01882 std::cerr << "KImageEffect::hash effect invalid image" << std::endl; 01883 #endif 01884 return image; 01885 } 01886 01887 int x, y; 01888 unsigned int *data = (unsigned int *)image.bits(); 01889 unsigned int ind; 01890 01891 //CT no need to do it if not enough space 01892 if ((lite == NorthLite || 01893 lite == SouthLite)&& 01894 (unsigned)image.height() < 2+spacing) return image; 01895 if ((lite == EastLite || 01896 lite == WestLite)&& 01897 (unsigned)image.height() < 2+spacing) return image; 01898 01899 if (lite == NorthLite || lite == SouthLite) { 01900 for (y = 0 ; y < image.height(); y = y + 2 + spacing) { 01901 for (x = 0; x < image.width(); x++) { 01902 ind = x + image.width() * y; 01903 data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]); 01904 01905 ind = ind + image.width(); 01906 data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]); 01907 } 01908 } 01909 } 01910 01911 else if (lite == EastLite || lite == WestLite) { 01912 for (y = 0 ; y < image.height(); y++) { 01913 for (x = 0; x < image.width(); x = x + 2 + spacing) { 01914 ind = x + image.width() * y; 01915 data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]); 01916 01917 ind++; 01918 data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]); 01919 } 01920 } 01921 } 01922 01923 else if (lite == NWLite || lite == SELite) { 01924 for (y = 0 ; y < image.height(); y++) { 01925 for (x = 0; 01926 x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing); 01927 x = x + 2 + spacing) { 01928 ind = x + image.width() * y + ((y & 1)? 1 : 0); 01929 data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]); 01930 01931 ind++; 01932 data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]); 01933 } 01934 } 01935 } 01936 01937 else if (lite == SWLite || lite == NELite) { 01938 for (y = 0 ; y < image.height(); y++) { 01939 for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) { 01940 ind = x + image.width() * y - ((y & 1)? 1 : 0); 01941 data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]); 01942 01943 ind++; 01944 data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]); 01945 } 01946 } 01947 } 01948 01949 return image; 01950 } 01951 01952 01953 //====================================================================== 01954 // 01955 // Flatten effects 01956 // 01957 //====================================================================== 01958 01959 TQImage& KImageEffect::flatten(TQImage &img, const TQColor &ca, 01960 const TQColor &cb, int ncols) 01961 { 01962 if (img.width() == 0 || img.height() == 0) 01963 return img; 01964 01965 // a bitmap is easy... 01966 if (img.depth() == 1) { 01967 img.setColor(0, ca.rgb()); 01968 img.setColor(1, cb.rgb()); 01969 return img; 01970 } 01971 01972 int r1 = ca.red(); int r2 = cb.red(); 01973 int g1 = ca.green(); int g2 = cb.green(); 01974 int b1 = ca.blue(); int b2 = cb.blue(); 01975 int min = 0, max = 255; 01976 01977 QRgb col; 01978 01979 // Get minimum and maximum greylevel. 01980 if (img.numColors()) { 01981 // pseudocolor 01982 for (int i = 0; i < img.numColors(); i++) { 01983 col = img.color(i); 01984 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3; 01985 min = QMIN(min, mean); 01986 max = QMAX(max, mean); 01987 } 01988 } else { 01989 // truecolor 01990 for (int y=0; y < img.height(); y++) 01991 for (int x=0; x < img.width(); x++) { 01992 col = img.pixel(x, y); 01993 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3; 01994 min = QMIN(min, mean); 01995 max = QMAX(max, mean); 01996 } 01997 } 01998 01999 // Conversion factors 02000 float sr = ((float) r2 - r1) / (max - min); 02001 float sg = ((float) g2 - g1) / (max - min); 02002 float sb = ((float) b2 - b1) / (max - min); 02003 02004 02005 // Repaint the image 02006 if (img.numColors()) { 02007 for (int i=0; i < img.numColors(); i++) { 02008 col = img.color(i); 02009 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3; 02010 int r = (int) (sr * (mean - min) + r1 + 0.5); 02011 int g = (int) (sg * (mean - min) + g1 + 0.5); 02012 int b = (int) (sb * (mean - min) + b1 + 0.5); 02013 img.setColor(i, tqRgba(r, g, b, tqAlpha(col))); 02014 } 02015 } else { 02016 for (int y=0; y < img.height(); y++) 02017 for (int x=0; x < img.width(); x++) { 02018 col = img.pixel(x, y); 02019 int mean = (tqRed(col) + tqGreen(col) + tqBlue(col)) / 3; 02020 int r = (int) (sr * (mean - min) + r1 + 0.5); 02021 int g = (int) (sg * (mean - min) + g1 + 0.5); 02022 int b = (int) (sb * (mean - min) + b1 + 0.5); 02023 img.setPixel(x, y, tqRgba(r, g, b, tqAlpha(col))); 02024 } 02025 } 02026 02027 02028 // Dither if necessary 02029 if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols))) 02030 return img; 02031 02032 if (ncols == 1) ncols++; 02033 if (ncols > 256) ncols = 256; 02034 02035 TQColor *pal = new TQColor[ncols]; 02036 sr = ((float) r2 - r1) / (ncols - 1); 02037 sg = ((float) g2 - g1) / (ncols - 1); 02038 sb = ((float) b2 - b1) / (ncols - 1); 02039 02040 for (int i=0; i<ncols; i++) 02041 pal[i] = TQColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i)); 02042 02043 dither(img, pal, ncols); 02044 02045 delete[] pal; 02046 return img; 02047 } 02048 02049 02050 //====================================================================== 02051 // 02052 // Fade effects 02053 // 02054 //====================================================================== 02055 02056 TQImage& KImageEffect::fade(TQImage &img, float val, const TQColor &color) 02057 { 02058 if (img.width() == 0 || img.height() == 0) 02059 return img; 02060 02061 // We don't handle bitmaps 02062 if (img.depth() == 1) 02063 return img; 02064 02065 unsigned char tbl[256]; 02066 for (int i=0; i<256; i++) 02067 tbl[i] = (int) (val * i + 0.5); 02068 02069 int red = color.red(); 02070 int green = color.green(); 02071 int blue = color.blue(); 02072 02073 QRgb col; 02074 int r, g, b, cr, cg, cb; 02075 02076 if (img.depth() <= 8) { 02077 // pseudo color 02078 for (int i=0; i<img.numColors(); i++) { 02079 col = img.color(i); 02080 cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col); 02081 if (cr > red) 02082 r = cr - tbl[cr - red]; 02083 else 02084 r = cr + tbl[red - cr]; 02085 if (cg > green) 02086 g = cg - tbl[cg - green]; 02087 else 02088 g = cg + tbl[green - cg]; 02089 if (cb > blue) 02090 b = cb - tbl[cb - blue]; 02091 else 02092 b = cb + tbl[blue - cb]; 02093 img.setColor(i, tqRgba(r, g, b, tqAlpha(col))); 02094 } 02095 02096 } else { 02097 // truecolor 02098 for (int y=0; y<img.height(); y++) { 02099 QRgb *data = (QRgb *) img.scanLine(y); 02100 for (int x=0; x<img.width(); x++) { 02101 col = *data; 02102 cr = tqRed(col); cg = tqGreen(col); cb = tqBlue(col); 02103 if (cr > red) 02104 r = cr - tbl[cr - red]; 02105 else 02106 r = cr + tbl[red - cr]; 02107 if (cg > green) 02108 g = cg - tbl[cg - green]; 02109 else 02110 g = cg + tbl[green - cg]; 02111 if (cb > blue) 02112 b = cb - tbl[cb - blue]; 02113 else 02114 b = cb + tbl[blue - cb]; 02115 *data++ = tqRgba(r, g, b, tqAlpha(col)); 02116 } 02117 } 02118 } 02119 02120 return img; 02121 } 02122 02123 //====================================================================== 02124 // 02125 // Color effects 02126 // 02127 //====================================================================== 02128 02129 // This code is adapted from code (C) Rik Hemsley <rik@kde.org> 02130 // 02131 // The formula used (r + b + g) /3 is different from the tqGray formula 02132 // used by Qt. This is because our formula is much much faster. If, 02133 // however, it turns out that this is producing sub-optimal images, 02134 // then it will have to change (kurt) 02135 // 02136 // It does produce lower quality grayscale ;-) Use fast == true for the fast 02137 // algorithm, false for the higher quality one (mosfet). 02138 TQImage& KImageEffect::toGray(TQImage &img, bool fast) 02139 { 02140 if (img.width() == 0 || img.height() == 0) 02141 return img; 02142 02143 if(fast){ 02144 if (img.depth() == 32) { 02145 uchar * r(img.bits()); 02146 uchar * g(img.bits() + 1); 02147 uchar * b(img.bits() + 2); 02148 02149 uchar * end(img.bits() + img.numBytes()); 02150 02151 while (r != end) { 02152 02153 *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3 02154 02155 r += 4; 02156 g += 4; 02157 b += 4; 02158 } 02159 } 02160 else 02161 { 02162 for (int i = 0; i < img.numColors(); i++) 02163 { 02164 uint r = tqRed(img.color(i)); 02165 uint g = tqGreen(img.color(i)); 02166 uint b = tqBlue(img.color(i)); 02167 02168 uint gray = (((r + g) >> 1) + b) >> 1; 02169 img.setColor(i, tqRgba(gray, gray, gray, tqAlpha(img.color(i)))); 02170 } 02171 } 02172 } 02173 else{ 02174 int pixels = img.depth() > 8 ? img.width()*img.height() : 02175 img.numColors(); 02176 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : 02177 (unsigned int *)img.tqcolorTable(); 02178 int val, i; 02179 for(i=0; i < pixels; ++i){ 02180 val = tqGray(data[i]); 02181 data[i] = tqRgba(val, val, val, tqAlpha(data[i])); 02182 } 02183 } 02184 return img; 02185 } 02186 02187 // CT 29Jan2000 - desaturation algorithms 02188 TQImage& KImageEffect::desaturate(TQImage &img, float desat) 02189 { 02190 if (img.width() == 0 || img.height() == 0) 02191 return img; 02192 02193 if (desat < 0) desat = 0.; 02194 if (desat > 1) desat = 1.; 02195 int pixels = img.depth() > 8 ? img.width()*img.height() : 02196 img.numColors(); 02197 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : 02198 (unsigned int *)img.tqcolorTable(); 02199 int h, s, v, i; 02200 TQColor clr; // keep constructor out of loop (mosfet) 02201 for(i=0; i < pixels; ++i){ 02202 clr.setRgb(data[i]); 02203 clr.hsv(&h, &s, &v); 02204 clr.setHsv(h, (int)(s * (1. - desat)), v); 02205 data[i] = clr.rgb(); 02206 } 02207 return img; 02208 } 02209 02210 // Contrast stuff (mosfet) 02211 TQImage& KImageEffect::contrast(TQImage &img, int c) 02212 { 02213 if (img.width() == 0 || img.height() == 0) 02214 return img; 02215 02216 if(c > 255) 02217 c = 255; 02218 if(c < -255) 02219 c = -255; 02220 int pixels = img.depth() > 8 ? img.width()*img.height() : 02221 img.numColors(); 02222 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() : 02223 (unsigned int *)img.tqcolorTable(); 02224 int i, r, g, b; 02225 for(i=0; i < pixels; ++i){ 02226 r = tqRed(data[i]); 02227 g = tqGreen(data[i]); 02228 b = tqBlue(data[i]); 02229 if(tqGray(data[i]) <= 127){ 02230 if(r - c > 0) 02231 r -= c; 02232 else 02233 r = 0; 02234 if(g - c > 0) 02235 g -= c; 02236 else 02237 g = 0; 02238 if(b - c > 0) 02239 b -= c; 02240 else 02241 b = 0; 02242 } 02243 else{ 02244 if(r + c <= 255) 02245 r += c; 02246 else 02247 r = 255; 02248 if(g + c <= 255) 02249 g += c; 02250 else 02251 g = 255; 02252 if(b + c <= 255) 02253 b += c; 02254 else 02255 b = 255; 02256 } 02257 data[i] = tqRgba(r, g, b, tqAlpha(data[i])); 02258 } 02259 return(img); 02260 } 02261 02262 //====================================================================== 02263 // 02264 // Dithering effects 02265 // 02266 //====================================================================== 02267 02268 // adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org) 02269 // 02270 // Floyd-Steinberg dithering 02271 // Ref: Bitmapped Graphics Programming in C++ 02272 // Marv Luse, Addison-Wesley Publishing, 1993. 02273 TQImage& KImageEffect::dither(TQImage &img, const TQColor *palette, int size) 02274 { 02275 if (img.width() == 0 || img.height() == 0 || 02276 palette == 0 || img.depth() <= 8) 02277 return img; 02278 02279 TQImage dImage( img.width(), img.height(), 8, size ); 02280 int i; 02281 02282 dImage.setNumColors( size ); 02283 for ( i = 0; i < size; i++ ) 02284 dImage.setColor( i, palette[ i ].rgb() ); 02285 02286 int *rerr1 = new int [ img.width() * 2 ]; 02287 int *gerr1 = new int [ img.width() * 2 ]; 02288 int *berr1 = new int [ img.width() * 2 ]; 02289 02290 memset( rerr1, 0, sizeof( int ) * img.width() * 2 ); 02291 memset( gerr1, 0, sizeof( int ) * img.width() * 2 ); 02292 memset( berr1, 0, sizeof( int ) * img.width() * 2 ); 02293 02294 int *rerr2 = rerr1 + img.width(); 02295 int *gerr2 = gerr1 + img.width(); 02296 int *berr2 = berr1 + img.width(); 02297 02298 for ( int j = 0; j < img.height(); j++ ) 02299 { 02300 uint *ip = (uint * )img.scanLine( j ); 02301 uchar *dp = dImage.scanLine( j ); 02302 02303 for ( i = 0; i < img.width(); i++ ) 02304 { 02305 rerr1[i] = rerr2[i] + tqRed( *ip ); 02306 rerr2[i] = 0; 02307 gerr1[i] = gerr2[i] + tqGreen( *ip ); 02308 gerr2[i] = 0; 02309 berr1[i] = berr2[i] + tqBlue( *ip ); 02310 berr2[i] = 0; 02311 ip++; 02312 } 02313 02314 *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size ); 02315 02316 for ( i = 1; i < img.width()-1; i++ ) 02317 { 02318 int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size ); 02319 *dp = indx; 02320 02321 int rerr = rerr1[i]; 02322 rerr -= palette[indx].red(); 02323 int gerr = gerr1[i]; 02324 gerr -= palette[indx].green(); 02325 int berr = berr1[i]; 02326 berr -= palette[indx].blue(); 02327 02328 // diffuse red error 02329 rerr1[ i+1 ] += ( rerr * 7 ) >> 4; 02330 rerr2[ i-1 ] += ( rerr * 3 ) >> 4; 02331 rerr2[ i ] += ( rerr * 5 ) >> 4; 02332 rerr2[ i+1 ] += ( rerr ) >> 4; 02333 02334 // diffuse green error 02335 gerr1[ i+1 ] += ( gerr * 7 ) >> 4; 02336 gerr2[ i-1 ] += ( gerr * 3 ) >> 4; 02337 gerr2[ i ] += ( gerr * 5 ) >> 4; 02338 gerr2[ i+1 ] += ( gerr ) >> 4; 02339 02340 // diffuse red error 02341 berr1[ i+1 ] += ( berr * 7 ) >> 4; 02342 berr2[ i-1 ] += ( berr * 3 ) >> 4; 02343 berr2[ i ] += ( berr * 5 ) >> 4; 02344 berr2[ i+1 ] += ( berr ) >> 4; 02345 02346 dp++; 02347 } 02348 02349 *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size ); 02350 } 02351 02352 delete [] rerr1; 02353 delete [] gerr1; 02354 delete [] berr1; 02355 02356 img = dImage; 02357 return img; 02358 } 02359 02360 int KImageEffect::nearestColor( int r, int g, int b, const TQColor *palette, int size ) 02361 { 02362 if (palette == 0) 02363 return 0; 02364 02365 int dr = palette[0].red() - r; 02366 int dg = palette[0].green() - g; 02367 int db = palette[0].blue() - b; 02368 02369 int minDist = dr*dr + dg*dg + db*db; 02370 int nearest = 0; 02371 02372 for (int i = 1; i < size; i++ ) 02373 { 02374 dr = palette[i].red() - r; 02375 dg = palette[i].green() - g; 02376 db = palette[i].blue() - b; 02377 02378 int dist = dr*dr + dg*dg + db*db; 02379 02380 if ( dist < minDist ) 02381 { 02382 minDist = dist; 02383 nearest = i; 02384 } 02385 } 02386 02387 return nearest; 02388 } 02389 02390 bool KImageEffect::blend( 02391 const TQImage & upper, 02392 const TQImage & lower, 02393 TQImage & output 02394 ) 02395 { 02396 if ( 02397 upper.width() > lower.width() || 02398 upper.height() > lower.height() || 02399 upper.depth() != 32 || 02400 lower.depth() != 32 02401 ) 02402 { 02403 #ifndef NDEBUG 02404 std::cerr << "KImageEffect::blend : Sizes not correct\n" ; 02405 #endif 02406 return false; 02407 } 02408 02409 output = lower.copy(); 02410 02411 uchar *i, *o; 02412 int a; 02413 int col; 02414 int w = upper.width(); 02415 int row(upper.height() - 1); 02416 02417 do { 02418 02419 i = const_cast<TQImage&>(upper).scanLine(row); 02420 o = const_cast<TQImage&>(output).scanLine(row); 02421 02422 col = w << 2; 02423 --col; 02424 02425 do { 02426 02427 while (!(a = i[col]) && (col != 3)) { 02428 --col; --col; --col; --col; 02429 } 02430 02431 --col; 02432 o[col] += ((i[col] - o[col]) * a) >> 8; 02433 02434 --col; 02435 o[col] += ((i[col] - o[col]) * a) >> 8; 02436 02437 --col; 02438 o[col] += ((i[col] - o[col]) * a) >> 8; 02439 02440 } while (col--); 02441 02442 } while (row--); 02443 02444 return true; 02445 } 02446 02447 #if 0 02448 // Not yet... 02449 bool KImageEffect::blend( 02450 const TQImage & upper, 02451 const TQImage & lower, 02452 TQImage & output, 02453 const TQRect & destRect 02454 ) 02455 { 02456 output = lower.copy(); 02457 return output; 02458 } 02459 02460 #endif 02461 02462 bool KImageEffect::blend( 02463 int &x, int &y, 02464 const TQImage & upper, 02465 const TQImage & lower, 02466 TQImage & output 02467 ) 02468 { 02469 int cx=0, cy=0, cw=upper.width(), ch=upper.height(); 02470 02471 if ( upper.width() + x > lower.width() || 02472 upper.height() + y > lower.height() || 02473 x < 0 || y < 0 || 02474 upper.depth() != 32 || lower.depth() != 32 ) 02475 { 02476 if ( x > lower.width() || y > lower.height() ) return false; 02477 if ( upper.width()<=0 || upper.height() <= 0 ) return false; 02478 if ( lower.width()<=0 || lower.height() <= 0 ) return false; 02479 02480 if (x<0) {cx=-x; cw+=x; x=0; }; 02481 if (cw + x > lower.width()) { cw=lower.width()-x; }; 02482 if (y<0) {cy=-y; ch+=y; y=0; }; 02483 if (ch + y > lower.height()) { ch=lower.height()-y; }; 02484 02485 if ( cx >= upper.width() || cy >= upper.height() ) return true; 02486 if ( cw <= 0 || ch <= 0 ) return true; 02487 } 02488 02489 output.create(cw,ch,32); 02490 // output.setAlphaBuffer(true); // I should do some benchmarks to see if 02491 // this is worth the effort 02492 02493 QRgb *i, *o, *b; 02494 02495 int a; 02496 int j,k; 02497 for (j=0; j<ch; j++) 02498 { 02499 b=reinterpret_cast<QRgb *>(&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ]); 02500 i=reinterpret_cast<QRgb *>(&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ]); 02501 o=reinterpret_cast<QRgb *>(&const_cast<TQImage&>(output).scanLine(j) [ cw << 2 ]); 02502 02503 k=cw-1; 02504 --b; --i; --o; 02505 do 02506 { 02507 while ( !(a=tqAlpha(*i)) && k>0 ) 02508 { 02509 i--; 02510 // *o=0; 02511 *o=*b; 02512 --o; --b; 02513 k--; 02514 }; 02515 // *o=0xFF; 02516 *o = tqRgb(tqRed(*b) + (((tqRed(*i) - tqRed(*b)) * a) >> 8), 02517 tqGreen(*b) + (((tqGreen(*i) - tqGreen(*b)) * a) >> 8), 02518 tqBlue(*b) + (((tqBlue(*i) - tqBlue(*b)) * a) >> 8)); 02519 --i; --o; --b; 02520 } while (k--); 02521 } 02522 02523 return true; 02524 } 02525 02526 bool KImageEffect::blendOnLower( 02527 int x, int y, 02528 const TQImage & upper, 02529 const TQImage & lower 02530 ) 02531 { 02532 int cx=0, cy=0, cw=upper.width(), ch=upper.height(); 02533 02534 if ( upper.depth() != 32 || lower.depth() != 32 ) return false; 02535 if ( x + cw > lower.width() || 02536 y + ch > lower.height() || 02537 x < 0 || y < 0 ) 02538 { 02539 if ( x > lower.width() || y > lower.height() ) return true; 02540 if ( upper.width()<=0 || upper.height() <= 0 ) return true; 02541 if ( lower.width()<=0 || lower.height() <= 0 ) return true; 02542 02543 if (x<0) {cx=-x; cw+=x; x=0; }; 02544 if (cw + x > lower.width()) { cw=lower.width()-x; }; 02545 if (y<0) {cy=-y; ch+=y; y=0; }; 02546 if (ch + y > lower.height()) { ch=lower.height()-y; }; 02547 02548 if ( cx >= upper.width() || cy >= upper.height() ) return true; 02549 if ( cw <= 0 || ch <= 0 ) return true; 02550 } 02551 02552 uchar *i, *b; 02553 int a; 02554 int k; 02555 02556 for (int j=0; j<ch; j++) 02557 { 02558 b=&const_cast<TQImage&>(lower).scanLine(y+j) [ (x+cw) << 2 ]; 02559 i=&const_cast<TQImage&>(upper).scanLine(cy+j)[ (cx+cw) << 2 ]; 02560 02561 k=cw-1; 02562 --b; --i; 02563 do 02564 { 02565 #ifndef WORDS_BIGENDIAN 02566 while ( !(a=*i) && k>0 ) 02567 #else 02568 while ( !(a=*(i-3)) && k>0 ) 02569 #endif 02570 { 02571 i-=4; b-=4; k--; 02572 }; 02573 02574 #ifndef WORDS_BIGENDIAN 02575 --i; --b; 02576 *b += ( ((*i - *b) * a) >> 8 ); 02577 --i; --b; 02578 *b += ( ((*i - *b) * a) >> 8 ); 02579 --i; --b; 02580 *b += ( ((*i - *b) * a) >> 8 ); 02581 --i; --b; 02582 #else 02583 *b += ( ((*i - *b) * a) >> 8 ); 02584 --i; --b; 02585 *b += ( ((*i - *b) * a) >> 8 ); 02586 --i; --b; 02587 *b += ( ((*i - *b) * a) >> 8 ); 02588 i -= 2; b -= 2; 02589 #endif 02590 } while (k--); 02591 } 02592 02593 return true; 02594 } 02595 02596 void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset, 02597 TQImage &lower, const TQRect &lowerRect) 02598 { 02599 // clip rect 02600 TQRect lr = lowerRect & lower.rect(); 02601 lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) ); 02602 lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) ); 02603 if ( !lr.isValid() ) return; 02604 02605 // blend 02606 for (int y = 0; y < lr.height(); y++) { 02607 for (int x = 0; x < lr.width(); x++) { 02608 QRgb *b = reinterpret_cast<QRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb)); 02609 QRgb *d = reinterpret_cast<QRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb)); 02610 int a = tqAlpha(*d); 02611 *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8), 02612 tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8), 02613 tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8)); 02614 } 02615 } 02616 } 02617 02618 void KImageEffect::blendOnLower(const TQImage &upper, const TQPoint &upperOffset, 02619 TQImage &lower, const TQRect &lowerRect, float opacity) 02620 { 02621 // clip rect 02622 TQRect lr = lowerRect & lower.rect(); 02623 lr.setWidth( QMIN(lr.width(), upper.width()-upperOffset.x()) ); 02624 lr.setHeight( QMIN(lr.height(), upper.height()-upperOffset.y()) ); 02625 if ( !lr.isValid() ) return; 02626 02627 // blend 02628 for (int y = 0; y < lr.height(); y++) { 02629 for (int x = 0; x < lr.width(); x++) { 02630 QRgb *b = reinterpret_cast<QRgb*>(const_cast<TQImage&>(lower).scanLine(lr.y() + y)+ (lr.x() + x) * sizeof(QRgb)); 02631 QRgb *d = reinterpret_cast<QRgb*>(const_cast<TQImage&>(upper).scanLine(upperOffset.y() + y) + (upperOffset.x() + x) * sizeof(QRgb)); 02632 int a = tqRound(opacity * tqAlpha(*d)); 02633 *b = tqRgb(tqRed(*b) - (((tqRed(*b) - tqRed(*d)) * a) >> 8), 02634 tqGreen(*b) - (((tqGreen(*b) - tqGreen(*d)) * a) >> 8), 02635 tqBlue(*b) - (((tqBlue(*b) - tqBlue(*d)) * a) >> 8)); 02636 } 02637 } 02638 } 02639 02640 TQRect KImageEffect::computeDestinationRect(const TQSize &lowerSize, 02641 Disposition disposition, TQImage &upper) 02642 { 02643 int w = lowerSize.width(); 02644 int h = lowerSize.height(); 02645 int ww = upper.width(); 02646 int wh = upper.height(); 02647 TQRect d; 02648 02649 switch (disposition) { 02650 case NoImage: 02651 break; 02652 case Centered: 02653 d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); 02654 break; 02655 case Tiled: 02656 d.setRect(0, 0, w, h); 02657 break; 02658 case CenterTiled: 02659 d.setCoords(-ww + ((w - ww) / 2) % ww, -wh + ((h - wh) / 2) % wh, 02660 w-1, h-1); 02661 break; 02662 case Scaled: 02663 upper = upper.smoothScale(w, h); 02664 d.setRect(0, 0, w, h); 02665 break; 02666 case CenteredAutoFit: 02667 if( ww <= w && wh <= h ) { 02668 d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); // like Centered 02669 break; 02670 } 02671 // fall through 02672 case CenteredMaxpect: { 02673 double sx = (double) w / ww; 02674 double sy = (double) h / wh; 02675 if (sx > sy) { 02676 ww = (int)(sy * ww); 02677 wh = h; 02678 } else { 02679 wh = (int)(sx * wh); 02680 ww = w; 02681 } 02682 upper = upper.smoothScale(ww, wh); 02683 d.setRect((w - ww) / 2, (h - wh) / 2, ww, wh); 02684 break; 02685 } 02686 case TiledMaxpect: { 02687 double sx = (double) w / ww; 02688 double sy = (double) h / wh; 02689 if (sx > sy) { 02690 ww = (int)(sy * ww); 02691 wh = h; 02692 } else { 02693 wh = (int)(sx * wh); 02694 ww = w; 02695 } 02696 upper = upper.smoothScale(ww, wh); 02697 d.setRect(0, 0, w, h); 02698 break; 02699 } 02700 } 02701 02702 return d; 02703 } 02704 02705 void KImageEffect::blendOnLower(TQImage &upper, TQImage &lower, 02706 Disposition disposition, float opacity) 02707 { 02708 TQRect r = computeDestinationRect(lower.size(), disposition, upper); 02709 for (int y = r.top(); y<r.bottom(); y += upper.height()) 02710 for (int x = r.left(); x<r.right(); x += upper.width()) 02711 blendOnLower(upper, TQPoint(-QMIN(x, 0), -QMIN(y, 0)), 02712 lower, TQRect(x, y, upper.width(), upper.height()), opacity); 02713 } 02714 02715 02716 // For selected icons 02717 TQImage& KImageEffect::selectedImage( TQImage &img, const TQColor &col ) 02718 { 02719 return blend( col, img, 0.5); 02720 } 02721 02722 // 02723 // =================================================================== 02724 // Effects originally ported from ImageMagick for PixiePlus, plus a few 02725 // new ones. (mosfet 05/26/2003) 02726 // =================================================================== 02727 // 02728 /* 02729 Portions of this software are based on ImageMagick. Such portions are clearly 02730 marked as being ported from ImageMagick. ImageMagick is copyrighted under the 02731 following conditions: 02732 02733 Copyright (C) 2003 ImageMagick Studio, a non-profit organization dedicated to 02734 making software imaging solutions freely available. 02735 02736 Permission is hereby granted, free of charge, to any person obtaining a copy 02737 of this software and associated documentation files ("ImageMagick"), to deal 02738 in ImageMagick without restriction, including without limitation the rights 02739 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 02740 copies of ImageMagick, and to permit persons to whom the ImageMagick is 02741 furnished to do so, subject to the following conditions: 02742 02743 The above copyright notice and this permission notice shall be included in all 02744 copies or substantial portions of ImageMagick. 02745 02746 The software is provided "as is", without warranty of any kind, express or 02747 implied, including but not limited to the warranties of merchantability, 02748 fitness for a particular purpose and noninfringement. In no event shall 02749 ImageMagick Studio be liable for any claim, damages or other liability, 02750 whether in an action of contract, tort or otherwise, arising from, out of or 02751 in connection with ImageMagick or the use or other dealings in ImageMagick. 02752 02753 Except as contained in this notice, the name of the ImageMagick Studio shall 02754 not be used in advertising or otherwise to promote the sale, use or other 02755 dealings in ImageMagick without prior written authorization from the 02756 ImageMagick Studio. 02757 */ 02758 02759 TQImage KImageEffect::sample(TQImage &src, int w, int h) 02760 { 02761 if(w == src.width() && h == src.height()) 02762 return(src); 02763 02764 int depth = src.depth(); 02765 TQImage dest(w, h, depth, depth <= 8 ? src.numColors() : 0, 02766 depth == 1 ? TQImage::LittleEndian : TQImage::IgnoreEndian); 02767 int *x_offset = (int *)malloc(w*sizeof(int)); 02768 int *y_offset = (int *)malloc(h*sizeof(int)); 02769 if(!x_offset || !y_offset){ 02770 #ifndef NDEBUG 02771 qWarning("KImageEffect::sample(): Unable to allocate pixel buffer"); 02772 #endif 02773 free(x_offset); 02774 free(y_offset); 02775 return(src); 02776 } 02777 02778 // init pixel offsets 02779 for(int x=0; x < w; ++x) 02780 x_offset[x] = (int)(x*src.width()/((double)w)); 02781 for(int y=0; y < h; ++y) 02782 y_offset[y] = (int)(y*src.height()/((double)h)); 02783 02784 if(depth > 8){ // DirectClass source image 02785 for(int y=0; y < h; ++y){ 02786 unsigned int *destData = (unsigned int *)dest.scanLine(y); 02787 unsigned int *srcData = (unsigned int *)src.scanLine(y_offset[y]); 02788 for(int x=0; x < w; ++x) 02789 destData[x] = srcData[x_offset[x]]; 02790 } 02791 } 02792 else if(depth == 1) { 02793 int r = src.bitOrder() == TQImage::LittleEndian; 02794 memcpy(dest.tqcolorTable(), src.tqcolorTable(), src.numColors()*sizeof(QRgb)); 02795 for(int y=0; y < h; ++y){ 02796 unsigned char *destData = dest.scanLine(y); 02797 unsigned char *srcData = src.scanLine(y_offset[y]); 02798 for(int x=0; x < w; ++x){ 02799 int k = x_offset[x]; 02800 int l = r ? (k & 7) : (7 - (k&7)); 02801 if(srcData[k >> 3] & (1 << l)) 02802 destData[x >> 3] |= 1 << (x & 7); 02803 else 02804 destData[x >> 3] &= ~(1 << (x & 7)); 02805 } 02806 } 02807 } 02808 else{ // PseudoClass source image 02809 memcpy(dest.tqcolorTable(), src.tqcolorTable(), src.numColors()*sizeof(QRgb)); 02810 for(int y=0; y < h; ++y){ 02811 unsigned char *destData = dest.scanLine(y); 02812 unsigned char *srcData = src.scanLine(y_offset[y]); 02813 for(int x=0; x < w; ++x) 02814 destData[x] = srcData[x_offset[x]]; 02815 } 02816 } 02817 free(x_offset); 02818 free(y_offset); 02819 return(dest); 02820 } 02821 02822 void KImageEffect::threshold(TQImage &img, unsigned int threshold) 02823 { 02824 int i, count; 02825 unsigned int *data; 02826 if(img.depth() > 8){ // DirectClass 02827 count = img.width()*img.height(); 02828 data = (unsigned int *)img.bits(); 02829 } 02830 else{ // PsudeoClass 02831 count = img.numColors(); 02832 data = (unsigned int *)img.tqcolorTable(); 02833 } 02834 for(i=0; i < count; ++i) 02835 data[i] = intensityValue(data[i]) < threshold ? QColor(Qt::black).rgb() : QColor(Qt::white).rgb(); 02836 } 02837 02838 void KImageEffect::hull(const int x_offset, const int y_offset, 02839 const int polarity, const int columns, 02840 const int rows, 02841 unsigned int *f, unsigned int *g) 02842 { 02843 int x, y; 02844 02845 unsigned int *p, *q, *r, *s; 02846 unsigned int v; 02847 if(f == NULL || g == NULL) 02848 return; 02849 p=f+(columns+2); 02850 q=g+(columns+2); 02851 r=p+(y_offset*(columns+2)+x_offset); 02852 for (y=0; y < rows; y++){ 02853 p++; 02854 q++; 02855 r++; 02856 if(polarity > 0) 02857 for (x=0; x < columns; x++){ 02858 v=(*p); 02859 if (*r > v) 02860 v++; 02861 *q=v; 02862 p++; 02863 q++; 02864 r++; 02865 } 02866 else 02867 for(x=0; x < columns; x++){ 02868 v=(*p); 02869 if (v > (unsigned int) (*r+1)) 02870 v--; 02871 *q=v; 02872 p++; 02873 q++; 02874 r++; 02875 } 02876 p++; 02877 q++; 02878 r++; 02879 } 02880 p=f+(columns+2); 02881 q=g+(columns+2); 02882 r=q+(y_offset*(columns+2)+x_offset); 02883 s=q-(y_offset*(columns+2)+x_offset); 02884 for(y=0; y < rows; y++){ 02885 p++; 02886 q++; 02887 r++; 02888 s++; 02889 if(polarity > 0) 02890 for(x=0; x < (int) columns; x++){ 02891 v=(*q); 02892 if (((unsigned int) (*s+1) > v) && (*r > v)) 02893 v++; 02894 *p=v; 02895 p++; 02896 q++; 02897 r++; 02898 s++; 02899 } 02900 else 02901 for (x=0; x < columns; x++){ 02902 v=(*q); 02903 if (((unsigned int) (*s+1) < v) && (*r < v)) 02904 v--; 02905 *p=v; 02906 p++; 02907 q++; 02908 r++; 02909 s++; 02910 } 02911 p++; 02912 q++; 02913 r++; 02914 s++; 02915 } 02916 } 02917 02918 TQImage KImageEffect::despeckle(TQImage &src) 02919 { 02920 int i, j, x, y; 02921 unsigned int *blue_channel, *red_channel, *green_channel, *buffer, 02922 *alpha_channel; 02923 int packets; 02924 static const int 02925 X[4]= {0, 1, 1,-1}, 02926 Y[4]= {1, 0, 1, 1}; 02927 02928 unsigned int *destData; 02929 TQImage dest(src.width(), src.height(), 32); 02930 02931 packets = (src.width()+2)*(src.height()+2); 02932 red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); 02933 green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); 02934 blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); 02935 alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int)); 02936 buffer = (unsigned int *)calloc(packets, sizeof(unsigned int)); 02937 if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel || 02938 !buffer){ 02939 free(red_channel); 02940 free(green_channel); 02941 free(blue_channel); 02942 free(alpha_channel); 02943 free(buffer); 02944 return(src); 02945 } 02946 02947 // copy image pixels to color component buffers 02948 j = src.width()+2; 02949 if(src.depth() > 8){ // DirectClass source image 02950 unsigned int *srcData; 02951 for(y=0; y < src.height(); ++y){ 02952 srcData = (unsigned int *)src.scanLine(y); 02953 ++j; 02954 for(x=0; x < src.width(); ++x){ 02955 red_channel[j] = tqRed(srcData[x]); 02956 green_channel[j] = tqGreen(srcData[x]); 02957 blue_channel[j] = tqBlue(srcData[x]); 02958 alpha_channel[j] = tqAlpha(srcData[x]); 02959 ++j; 02960 } 02961 ++j; 02962 } 02963 } 02964 else{ // PsudeoClass source image 02965 unsigned char *srcData; 02966 unsigned int *cTable = src.tqcolorTable(); 02967 unsigned int pixel; 02968 for(y=0; y < src.height(); ++y){ 02969 srcData = (unsigned char *)src.scanLine(y); 02970 ++j; 02971 for(x=0; x < src.width(); ++x){ 02972 pixel = *(cTable+srcData[x]); 02973 red_channel[j] = tqRed(pixel); 02974 green_channel[j] = tqGreen(pixel); 02975 blue_channel[j] = tqBlue(pixel); 02976 alpha_channel[j] = tqAlpha(pixel); 02977 ++j; 02978 } 02979 ++j; 02980 } 02981 } 02982 // reduce speckle in red channel 02983 for(i=0; i < 4; i++){ 02984 hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer); 02985 hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer); 02986 hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer); 02987 hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer); 02988 } 02989 // reduce speckle in green channel 02990 for (i=0; i < packets; i++) 02991 buffer[i]=0; 02992 for (i=0; i < 4; i++){ 02993 hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer); 02994 hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer); 02995 hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer); 02996 hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer); 02997 } 02998 // reduce speckle in blue channel 02999 for (i=0; i < packets; i++) 03000 buffer[i]=0; 03001 for (i=0; i < 4; i++){ 03002 hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer); 03003 hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer); 03004 hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer); 03005 hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer); 03006 } 03007 // copy color component buffers to despeckled image 03008 j = dest.width()+2; 03009 for(y=0; y < dest.height(); ++y) 03010 { 03011 destData = (unsigned int *)dest.scanLine(y); 03012 ++j; 03013 for (x=0; x < dest.width(); ++x) 03014 { 03015 destData[x] = tqRgba(red_channel[j], green_channel[j], 03016 blue_channel[j], alpha_channel[j]); 03017 ++j; 03018 } 03019 ++j; 03020 } 03021 free(buffer); 03022 free(red_channel); 03023 free(green_channel); 03024 free(blue_channel); 03025 free(alpha_channel); 03026 return(dest); 03027 } 03028 03029 unsigned int KImageEffect::generateNoise(unsigned int pixel, 03030 NoiseType noise_type) 03031 { 03032 #define NoiseEpsilon 1.0e-5 03033 #define NoiseMask 0x7fff 03034 #define SigmaUniform 4.0 03035 #define SigmaGaussian 4.0 03036 #define SigmaImpulse 0.10 03037 #define SigmaLaplacian 10.0 03038 #define SigmaMultiplicativeGaussian 0.5 03039 #define SigmaPoisson 0.05 03040 #define TauGaussian 20.0 03041 03042 double alpha, beta, sigma, value; 03043 alpha=(double) (rand() & NoiseMask)/NoiseMask; 03044 if (alpha == 0.0) 03045 alpha=1.0; 03046 switch(noise_type){ 03047 case UniformNoise: 03048 default: 03049 { 03050 value=(double) pixel+SigmaUniform*(alpha-0.5); 03051 break; 03052 } 03053 case GaussianNoise: 03054 { 03055 double tau; 03056 03057 beta=(double) (rand() & NoiseMask)/NoiseMask; 03058 sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta); 03059 tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta); 03060 value=(double) pixel+ 03061 (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau); 03062 break; 03063 } 03064 case MultiplicativeGaussianNoise: 03065 { 03066 if (alpha <= NoiseEpsilon) 03067 sigma=MaxRGB; 03068 else 03069 sigma=sqrt(-2.0*log(alpha)); 03070 beta=(rand() & NoiseMask)/NoiseMask; 03071 value=(double) pixel+ 03072 pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta); 03073 break; 03074 } 03075 case ImpulseNoise: 03076 { 03077 if (alpha < (SigmaImpulse/2.0)) 03078 value=0; 03079 else 03080 if (alpha >= (1.0-(SigmaImpulse/2.0))) 03081 value=MaxRGB; 03082 else 03083 value=pixel; 03084 break; 03085 } 03086 case LaplacianNoise: 03087 { 03088 if (alpha <= 0.5) 03089 { 03090 if (alpha <= NoiseEpsilon) 03091 value=(double) pixel-MaxRGB; 03092 else 03093 value=(double) pixel+SigmaLaplacian*log(2.0*alpha); 03094 break; 03095 } 03096 beta=1.0-alpha; 03097 if (beta <= (0.5*NoiseEpsilon)) 03098 value=(double) pixel+MaxRGB; 03099 else 03100 value=(double) pixel-SigmaLaplacian*log(2.0*beta); 03101 break; 03102 } 03103 case PoissonNoise: 03104 { 03105 int 03106 i; 03107 03108 for (i=0; alpha > exp(-SigmaPoisson*pixel); i++) 03109 { 03110 beta=(double) (rand() & NoiseMask)/NoiseMask; 03111 alpha=alpha*beta; 03112 } 03113 value=i/SigmaPoisson; 03114 break; 03115 } 03116 } 03117 if(value < 0.0) 03118 return(0); 03119 if(value > MaxRGB) 03120 return(MaxRGB); 03121 return((unsigned int) (value+0.5)); 03122 } 03123 03124 TQImage KImageEffect::addNoise(TQImage &src, NoiseType noise_type) 03125 { 03126 int x, y; 03127 TQImage dest(src.width(), src.height(), 32); 03128 unsigned int *destData; 03129 03130 if(src.depth() > 8){ // DirectClass source image 03131 unsigned int *srcData; 03132 for(y=0; y < src.height(); ++y){ 03133 srcData = (unsigned int *)src.scanLine(y); 03134 destData = (unsigned int *)dest.scanLine(y); 03135 for(x=0; x < src.width(); ++x){ 03136 destData[x] = tqRgba(generateNoise(tqRed(srcData[x]), noise_type), 03137 generateNoise(tqGreen(srcData[x]), noise_type), 03138 generateNoise(tqBlue(srcData[x]), noise_type), 03139 tqAlpha(srcData[x])); 03140 } 03141 } 03142 } 03143 else{ // PsudeoClass source image 03144 unsigned char *srcData; 03145 unsigned int *cTable = src.tqcolorTable(); 03146 unsigned int pixel; 03147 for(y=0; y < src.height(); ++y){ 03148 srcData = (unsigned char *)src.scanLine(y); 03149 destData = (unsigned int *)dest.scanLine(y); 03150 for(x=0; x < src.width(); ++x){ 03151 pixel = *(cTable+srcData[x]); 03152 destData[x] = tqRgba(generateNoise(tqRed(pixel), noise_type), 03153 generateNoise(tqGreen(pixel), noise_type), 03154 generateNoise(tqBlue(pixel), noise_type), 03155 tqAlpha(pixel)); 03156 } 03157 } 03158 03159 } 03160 return(dest); 03161 } 03162 03163 unsigned int KImageEffect::interpolateColor(TQImage *image, double x_offset, 03164 double y_offset, 03165 unsigned int background) 03166 { 03167 double alpha, beta; 03168 unsigned int p, q, r, s; 03169 int x, y; 03170 03171 x = (int)x_offset; 03172 y = (int)y_offset; 03173 if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height())) 03174 return(background); 03175 if(image->depth() > 8){ 03176 if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) { 03177 unsigned int *t = (unsigned int *)image->scanLine(y); 03178 p = t[x]; 03179 q = t[x+1]; 03180 r = t[x+image->width()]; 03181 s = t[x+image->width()+1]; 03182 } 03183 else{ 03184 unsigned int *t = (unsigned int *)image->scanLine(y); 03185 p = background; 03186 if((x >= 0) && (y >= 0)){ 03187 p = t[x]; 03188 } 03189 q = background; 03190 if(((x+1) < image->width()) && (y >= 0)){ 03191 q = t[x+1]; 03192 } 03193 r = background; 03194 if((x >= 0) && ((y+1) < image->height())){ 03195 t = (unsigned int *)image->scanLine(y+1); 03196 r = t[x+image->width()]; 03197 } 03198 s = background; 03199 if(((x+1) < image->width()) && ((y+1) < image->height())){ 03200 t = (unsigned int *)image->scanLine(y+1); 03201 s = t[x+image->width()+1]; 03202 } 03203 03204 } 03205 } 03206 else{ 03207 unsigned int *colorTable = (unsigned int *)image->tqcolorTable(); 03208 if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) { 03209 unsigned char *t; 03210 t = (unsigned char *)image->scanLine(y); 03211 p = *(colorTable+t[x]); 03212 q = *(colorTable+t[x+1]); 03213 t = (unsigned char *)image->scanLine(y+1); 03214 r = *(colorTable+t[x]); 03215 s = *(colorTable+t[x+1]); 03216 } 03217 else{ 03218 unsigned char *t; 03219 p = background; 03220 if((x >= 0) && (y >= 0)){ 03221 t = (unsigned char *)image->scanLine(y); 03222 p = *(colorTable+t[x]); 03223 } 03224 q = background; 03225 if(((x+1) < image->width()) && (y >= 0)){ 03226 t = (unsigned char *)image->scanLine(y); 03227 q = *(colorTable+t[x+1]); 03228 } 03229 r = background; 03230 if((x >= 0) && ((y+1) < image->height())){ 03231 t = (unsigned char *)image->scanLine(y+1); 03232 r = *(colorTable+t[x]); 03233 } 03234 s = background; 03235 if(((x+1) < image->width()) && ((y+1) < image->height())){ 03236 t = (unsigned char *)image->scanLine(y+1); 03237 s = *(colorTable+t[x+1]); 03238 } 03239 03240 } 03241 03242 } 03243 x_offset -= floor(x_offset); 03244 y_offset -= floor(y_offset); 03245 alpha = 1.0-x_offset; 03246 beta = 1.0-y_offset; 03247 03248 return(tqRgba((unsigned char)(beta*(alpha*tqRed(p)+x_offset*tqRed(q))+y_offset*(alpha*tqRed(r)+x_offset*tqRed(s))), 03249 (unsigned char)(beta*(alpha*tqGreen(p)+x_offset*tqGreen(q))+y_offset*(alpha*tqGreen(r)+x_offset*tqGreen(s))), 03250 (unsigned char)(beta*(alpha*tqBlue(p)+x_offset*tqBlue(q))+y_offset*(alpha*tqBlue(r)+x_offset*tqBlue(s))), 03251 (unsigned char)(beta*(alpha*tqAlpha(p)+x_offset*tqAlpha(q))+y_offset*(alpha*tqAlpha(r)+x_offset*tqAlpha(s))))); 03252 } 03253 03254 TQImage KImageEffect::implode(TQImage &src, double factor, 03255 unsigned int background) 03256 { 03257 double amount, distance, radius; 03258 double x_center, x_distance, x_scale; 03259 double y_center, y_distance, y_scale; 03260 unsigned int *destData; 03261 int x, y; 03262 03263 TQImage dest(src.width(), src.height(), 32); 03264 03265 // compute scaling factor 03266 x_scale = 1.0; 03267 y_scale = 1.0; 03268 x_center = (double)0.5*src.width(); 03269 y_center = (double)0.5*src.height(); 03270 radius=x_center; 03271 if(src.width() > src.height()) 03272 y_scale = (double)src.width()/src.height(); 03273 else if(src.width() < src.height()){ 03274 x_scale = (double) src.height()/src.width(); 03275 radius = y_center; 03276 } 03277 amount=factor/10.0; 03278 if(amount >= 0) 03279 amount/=10.0; 03280 if(src.depth() > 8){ // DirectClass source image 03281 unsigned int *srcData; 03282 for(y=0; y < src.height(); ++y){ 03283 srcData = (unsigned int *)src.scanLine(y); 03284 destData = (unsigned int *)dest.scanLine(y); 03285 y_distance=y_scale*(y-y_center); 03286 for(x=0; x < src.width(); ++x){ 03287 destData[x] = srcData[x]; 03288 x_distance = x_scale*(x-x_center); 03289 distance= x_distance*x_distance+y_distance*y_distance; 03290 if(distance < (radius*radius)){ 03291 double factor; 03292 // Implode the pixel. 03293 factor=1.0; 03294 if(distance > 0.0) 03295 factor= 03296 pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount); 03297 destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center, 03298 factor*y_distance/y_scale+y_center, 03299 background); 03300 } 03301 } 03302 } 03303 } 03304 else{ // PsudeoClass source image 03305 unsigned char *srcData; 03306 unsigned char idx; 03307 unsigned int *cTable = src.tqcolorTable(); 03308 for(y=0; y < src.height(); ++y){ 03309 srcData = (unsigned char *)src.scanLine(y); 03310 destData = (unsigned int *)dest.scanLine(y); 03311 y_distance=y_scale*(y-y_center); 03312 for(x=0; x < src.width(); ++x){ 03313 idx = srcData[x]; 03314 destData[x] = cTable[idx]; 03315 x_distance = x_scale*(x-x_center); 03316 distance= x_distance*x_distance+y_distance*y_distance; 03317 if(distance < (radius*radius)){ 03318 double factor; 03319 // Implode the pixel. 03320 factor=1.0; 03321 if(distance > 0.0) 03322 factor= 03323 pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount); 03324 destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center, 03325 factor*y_distance/y_scale+y_center, 03326 background); 03327 } 03328 } 03329 } 03330 03331 } 03332 return(dest); 03333 } 03334 03335 TQImage KImageEffect::rotate(TQImage &img, RotateDirection r) 03336 { 03337 TQImage dest; 03338 int x, y; 03339 if(img.depth() > 8){ 03340 unsigned int *srcData, *destData; 03341 switch(r){ 03342 case Rotate90: 03343 dest.create(img.height(), img.width(), img.depth()); 03344 for(y=0; y < img.height(); ++y){ 03345 srcData = (unsigned int *)img.scanLine(y); 03346 for(x=0; x < img.width(); ++x){ 03347 destData = (unsigned int *)dest.scanLine(x); 03348 destData[img.height()-y-1] = srcData[x]; 03349 } 03350 } 03351 break; 03352 case Rotate180: 03353 dest.create(img.width(), img.height(), img.depth()); 03354 for(y=0; y < img.height(); ++y){ 03355 srcData = (unsigned int *)img.scanLine(y); 03356 destData = (unsigned int *)dest.scanLine(img.height()-y-1); 03357 for(x=0; x < img.width(); ++x) 03358 destData[img.width()-x-1] = srcData[x]; 03359 } 03360 break; 03361 case Rotate270: 03362 dest.create(img.height(), img.width(), img.depth()); 03363 for(y=0; y < img.height(); ++y){ 03364 srcData = (unsigned int *)img.scanLine(y); 03365 for(x=0; x < img.width(); ++x){ 03366 destData = (unsigned int *)dest.scanLine(img.width()-x-1); 03367 destData[y] = srcData[x]; 03368 } 03369 } 03370 break; 03371 default: 03372 dest = img; 03373 break; 03374 } 03375 } 03376 else{ 03377 unsigned char *srcData, *destData; 03378 unsigned int *srcTable, *destTable; 03379 switch(r){ 03380 case Rotate90: 03381 dest.create(img.height(), img.width(), img.depth()); 03382 dest.setNumColors(img.numColors()); 03383 srcTable = (unsigned int *)img.tqcolorTable(); 03384 destTable = (unsigned int *)dest.tqcolorTable(); 03385 for(x=0; x < img.numColors(); ++x) 03386 destTable[x] = srcTable[x]; 03387 for(y=0; y < img.height(); ++y){ 03388 srcData = (unsigned char *)img.scanLine(y); 03389 for(x=0; x < img.width(); ++x){ 03390 destData = (unsigned char *)dest.scanLine(x); 03391 destData[img.height()-y-1] = srcData[x]; 03392 } 03393 } 03394 break; 03395 case Rotate180: 03396 dest.create(img.width(), img.height(), img.depth()); 03397 dest.setNumColors(img.numColors()); 03398 srcTable = (unsigned int *)img.tqcolorTable(); 03399 destTable = (unsigned int *)dest.tqcolorTable(); 03400 for(x=0; x < img.numColors(); ++x) 03401 destTable[x] = srcTable[x]; 03402 for(y=0; y < img.height(); ++y){ 03403 srcData = (unsigned char *)img.scanLine(y); 03404 destData = (unsigned char *)dest.scanLine(img.height()-y-1); 03405 for(x=0; x < img.width(); ++x) 03406 destData[img.width()-x-1] = srcData[x]; 03407 } 03408 break; 03409 case Rotate270: 03410 dest.create(img.height(), img.width(), img.depth()); 03411 dest.setNumColors(img.numColors()); 03412 srcTable = (unsigned int *)img.tqcolorTable(); 03413 destTable = (unsigned int *)dest.tqcolorTable(); 03414 for(x=0; x < img.numColors(); ++x) 03415 destTable[x] = srcTable[x]; 03416 for(y=0; y < img.height(); ++y){ 03417 srcData = (unsigned char *)img.scanLine(y); 03418 for(x=0; x < img.width(); ++x){ 03419 destData = (unsigned char *)dest.scanLine(img.width()-x-1); 03420 destData[y] = srcData[x]; 03421 } 03422 } 03423 break; 03424 default: 03425 dest = img; 03426 break; 03427 } 03428 03429 } 03430 return(dest); 03431 } 03432 03433 void KImageEffect::solarize(TQImage &img, double factor) 03434 { 03435 int i, count; 03436 int threshold; 03437 unsigned int *data; 03438 03439 threshold = (int)(factor*(MaxRGB+1)/100.0); 03440 if(img.depth() < 32){ 03441 data = (unsigned int *)img.tqcolorTable(); 03442 count = img.numColors(); 03443 } 03444 else{ 03445 data = (unsigned int *)img.bits(); 03446 count = img.width()*img.height(); 03447 } 03448 for(i=0; i < count; ++i){ 03449 data[i] = tqRgba(tqRed(data[i]) > threshold ? MaxRGB-tqRed(data[i]) : tqRed(data[i]), 03450 tqGreen(data[i]) > threshold ? MaxRGB-tqGreen(data[i]) : tqGreen(data[i]), 03451 tqBlue(data[i]) > threshold ? MaxRGB-tqBlue(data[i]) : tqBlue(data[i]), 03452 tqAlpha(data[i])); 03453 } 03454 } 03455 03456 TQImage KImageEffect::spread(TQImage &src, unsigned int amount) 03457 { 03458 int quantum, x, y; 03459 int x_distance, y_distance; 03460 if(src.width() < 3 || src.height() < 3) 03461 return(src); 03462 TQImage dest(src); 03463 dest.detach(); 03464 quantum=(amount+1) >> 1; 03465 if(src.depth() > 8){ // DirectClass source image 03466 unsigned int *p, *q; 03467 for(y=0; y < src.height(); y++){ 03468 q = (unsigned int *)dest.scanLine(y); 03469 for(x=0; x < src.width(); x++){ 03470 x_distance = x + ((rand() & (amount+1))-quantum); 03471 y_distance = y + ((rand() & (amount+1))-quantum); 03472 x_distance = QMIN(x_distance, src.width()-1); 03473 y_distance = QMIN(y_distance, src.height()-1); 03474 if(x_distance < 0) 03475 x_distance = 0; 03476 if(y_distance < 0) 03477 y_distance = 0; 03478 p = (unsigned int *)src.scanLine(y_distance); 03479 p += x_distance; 03480 *q++=(*p); 03481 } 03482 } 03483 } 03484 else{ // PsudeoClass source image 03485 // just do colortable values 03486 unsigned char *p, *q; 03487 for(y=0; y < src.height(); y++){ 03488 q = (unsigned char *)dest.scanLine(y); 03489 for(x=0; x < src.width(); x++){ 03490 x_distance = x + ((rand() & (amount+1))-quantum); 03491 y_distance = y + ((rand() & (amount+1))-quantum); 03492 x_distance = QMIN(x_distance, src.width()-1); 03493 y_distance = QMIN(y_distance, src.height()-1); 03494 if(x_distance < 0) 03495 x_distance = 0; 03496 if(y_distance < 0) 03497 y_distance = 0; 03498 p = (unsigned char *)src.scanLine(y_distance); 03499 p += x_distance; 03500 *q++=(*p); 03501 } 03502 } 03503 } 03504 return(dest); 03505 } 03506 03507 TQImage KImageEffect::swirl(TQImage &src, double degrees, 03508 unsigned int background) 03509 { 03510 double cosine, distance, factor, radius, sine, x_center, x_distance, 03511 x_scale, y_center, y_distance, y_scale; 03512 int x, y; 03513 unsigned int *q; 03514 TQImage dest(src.width(), src.height(), 32); 03515 03516 // compute scaling factor 03517 x_center = src.width()/2.0; 03518 y_center = src.height()/2.0; 03519 radius = QMAX(x_center,y_center); 03520 x_scale=1.0; 03521 y_scale=1.0; 03522 if(src.width() > src.height()) 03523 y_scale=(double)src.width()/src.height(); 03524 else if(src.width() < src.height()) 03525 x_scale=(double)src.height()/src.width(); 03526 degrees=DegreesToRadians(degrees); 03527 // swirl each row 03528 if(src.depth() > 8){ // DirectClass source image 03529 unsigned int *p; 03530 for(y=0; y < src.height(); y++){ 03531 p = (unsigned int *)src.scanLine(y); 03532 q = (unsigned int *)dest.scanLine(y); 03533 y_distance = y_scale*(y-y_center); 03534 for(x=0; x < src.width(); x++){ 03535 // determine if the pixel is within an ellipse 03536 *q=(*p); 03537 x_distance = x_scale*(x-x_center); 03538 distance = x_distance*x_distance+y_distance*y_distance; 03539 if (distance < (radius*radius)){ 03540 // swirl 03541 factor = 1.0-sqrt(distance)/radius; 03542 sine = sin(degrees*factor*factor); 03543 cosine = cos(degrees*factor*factor); 03544 *q = interpolateColor(&src, 03545 (cosine*x_distance-sine*y_distance)/x_scale+x_center, 03546 (sine*x_distance+cosine*y_distance)/y_scale+y_center, 03547 background); 03548 } 03549 p++; 03550 q++; 03551 } 03552 } 03553 } 03554 else{ // PsudeoClass source image 03555 unsigned char *p; 03556 unsigned int *cTable = (unsigned int *)src.tqcolorTable(); 03557 for(y=0; y < src.height(); y++){ 03558 p = (unsigned char *)src.scanLine(y); 03559 q = (unsigned int *)dest.scanLine(y); 03560 y_distance = y_scale*(y-y_center); 03561 for(x=0; x < src.width(); x++){ 03562 // determine if the pixel is within an ellipse 03563 *q = *(cTable+(*p)); 03564 x_distance = x_scale*(x-x_center); 03565 distance = x_distance*x_distance+y_distance*y_distance; 03566 if (distance < (radius*radius)){ 03567 // swirl 03568 factor = 1.0-sqrt(distance)/radius; 03569 sine = sin(degrees*factor*factor); 03570 cosine = cos(degrees*factor*factor); 03571 *q = interpolateColor(&src, 03572 (cosine*x_distance-sine*y_distance)/x_scale+x_center, 03573 (sine*x_distance+cosine*y_distance)/y_scale+y_center, 03574 background); 03575 } 03576 p++; 03577 q++; 03578 } 03579 } 03580 03581 } 03582 return(dest); 03583 } 03584 03585 TQImage KImageEffect::wave(TQImage &src, double amplitude, double wavelength, 03586 unsigned int background) 03587 { 03588 double *sine_map; 03589 int x, y; 03590 unsigned int *q; 03591 03592 TQImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32); 03593 // allocate sine map 03594 sine_map = (double *)malloc(dest.width()*sizeof(double)); 03595 if(!sine_map) 03596 return(src); 03597 for(x=0; x < dest.width(); ++x) 03598 sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength); 03599 // wave image 03600 for(y=0; y < dest.height(); ++y){ 03601 q = (unsigned int *)dest.scanLine(y); 03602 for (x=0; x < dest.width(); x++){ 03603 *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background); 03604 ++q; 03605 } 03606 } 03607 free(sine_map); 03608 return(dest); 03609 } 03610 03611 // 03612 // The following methods work by computing a value from neighboring pixels 03613 // (mosfet 05/26/03) 03614 // 03615 03616 // New algorithms based on ImageMagick 5.5.6 (05/26/03) 03617 03618 TQImage KImageEffect::oilPaint(TQImage &src, int /*radius*/) 03619 { 03620 /* binary compat method - remove me when possible! */ 03621 return(oilPaintConvolve(src, 0)); 03622 } 03623 03624 TQImage KImageEffect::oilPaintConvolve(TQImage &src, double radius) 03625 { 03626 unsigned long count /*,*histogram*/; 03627 unsigned long histogram[256]; 03628 unsigned int k; 03629 int width; 03630 int x, y, mx, my, sx, sy; 03631 int mcx, mcy; 03632 unsigned int *s=0, *q; 03633 03634 if(src.depth() < 32) 03635 src.convertDepth(32); 03636 TQImage dest(src); 03637 dest.detach(); 03638 03639 width = getOptimalKernelWidth(radius, 0.5); 03640 if(src.width() < width){ 03641 qWarning("KImageEffect::oilPaintConvolve(): Image is smaller than radius!"); 03642 return(dest); 03643 } 03644 /* 03645 histogram = (unsigned long *)malloc(256*sizeof(unsigned long)); 03646 if(!histogram){ 03647 qWarning("KImageEffect::oilPaintColvolve(): Unable to allocate memory!"); 03648 return(dest); 03649 } 03650 */ 03651 unsigned int **jumpTable = (unsigned int **)src.jumpTable(); 03652 for(y=0; y < dest.height(); ++y){ 03653 sy = y-(width/2); 03654 q = (unsigned int *)dest.scanLine(y); 03655 for(x=0; x < dest.width(); ++x){ 03656 count = 0; 03657 memset(histogram, 0, 256*sizeof(unsigned long)); 03658 //memset(histogram, 0, 256); 03659 sy = y-(width/2); 03660 for(mcy=0; mcy < width; ++mcy, ++sy){ 03661 my = sy < 0 ? 0 : sy > src.height()-1 ? 03662 src.height()-1 : sy; 03663 sx = x+(-width/2); 03664 for(mcx=0; mcx < width; ++mcx, ++sx){ 03665 mx = sx < 0 ? 0 : sx > src.width()-1 ? 03666 src.width()-1 : sx; 03667 03668 k = intensityValue(jumpTable[my][mx]); 03669 if(k > 255){ 03670 qWarning("KImageEffect::oilPaintConvolve(): k is %d", 03671 k); 03672 k = 255; 03673 } 03674 histogram[k]++; 03675 if(histogram[k] > count){ 03676 count = histogram[k]; 03677 s = jumpTable[my]+mx; 03678 } 03679 } 03680 } 03681 if (s) 03682 *q++ = (*s); 03683 } 03684 } 03685 /* liberateMemory((histogram); */ 03686 return(dest); 03687 } 03688 03689 TQImage KImageEffect::charcoal(TQImage &src, double /*factor*/) 03690 { 03691 /* binary compat method - remove me when possible! */ 03692 return(charcoal(src, 0, 1)); 03693 } 03694 03695 TQImage KImageEffect::charcoal(TQImage &src, double radius, double sigma) 03696 { 03697 TQImage img(edge(src, radius)); 03698 img = blur(img, radius, sigma); 03699 normalize(img); 03700 img.invertPixels(false); 03701 KImageEffect::toGray(img); 03702 return(img); 03703 } 03704 03705 void KImageEffect::normalize(TQImage &image) 03706 { 03707 struct double_packet high, low, intensity, *histogram; 03708 struct short_packet *normalize_map; 03709 TQ_INT64 number_pixels; 03710 int x, y; 03711 unsigned int *p, *q; 03712 long i; 03713 unsigned long threshold_intensity; 03714 unsigned char r, g, b, a; 03715 03716 if(image.depth() < 32) // result will always be 32bpp 03717 image = image.convertDepth(32); 03718 03719 histogram = (struct double_packet *) 03720 malloc(256*sizeof(struct double_packet)); 03721 normalize_map = (struct short_packet *) 03722 malloc(256*sizeof(struct short_packet)); 03723 03724 if(!histogram || !normalize_map){ 03725 if(histogram) 03726 liberateMemory(&histogram); 03727 if(normalize_map) 03728 liberateMemory(&normalize_map); 03729 qWarning("KImageEffect::normalize(): Unable to allocate memory!"); 03730 return; 03731 } 03732 03733 /* 03734 Form histogram. 03735 */ 03736 memset(histogram, 0, 256*sizeof(struct double_packet)); 03737 for(y=0; y < image.height(); ++y){ 03738 p = (unsigned int *)image.scanLine(y); 03739 for(x=0; x < image.width(); ++x){ 03740 histogram[(unsigned char)(tqRed(*p))].red++; 03741 histogram[(unsigned char)(tqGreen(*p))].green++; 03742 histogram[(unsigned char)(tqBlue(*p))].blue++; 03743 histogram[(unsigned char)(tqAlpha(*p))].alpha++; 03744 p++; 03745 } 03746 } 03747 03748 /* 03749 Find the histogram boundaries by locating the 0.1 percent levels. 03750 */ 03751 number_pixels = (TQ_INT64)image.width()*image.height(); 03752 threshold_intensity = number_pixels/1000; 03753 03754 /* red */ 03755 memset(&intensity, 0, sizeof(struct double_packet)); 03756 memset(&high, 0, sizeof(struct double_packet)); 03757 memset(&low, 0, sizeof(struct double_packet)); 03758 for(high.red=255; high.red != 0; high.red--){ 03759 intensity.red+=histogram[(unsigned char)high.red].red; 03760 if(intensity.red > threshold_intensity) 03761 break; 03762 } 03763 if(low.red == high.red){ 03764 threshold_intensity = 0; 03765 memset(&intensity, 0, sizeof(struct double_packet)); 03766 for(low.red=0; low.red < 255; low.red++){ 03767 intensity.red+=histogram[(unsigned char)low.red].red; 03768 if(intensity.red > threshold_intensity) 03769 break; 03770 } 03771 memset(&intensity, 0, sizeof(struct double_packet)); 03772 for(high.red=255; high.red != 0; high.red--){ 03773 intensity.red+=histogram[(unsigned char)high.red].red; 03774 if(intensity.red > threshold_intensity) 03775 break; 03776 } 03777 } 03778 03779 /* green */ 03780 memset(&intensity, 0, sizeof(struct double_packet)); 03781 for(high.green=255; high.green != 0; high.green--){ 03782 intensity.green+=histogram[(unsigned char)high.green].green; 03783 if(intensity.green > threshold_intensity) 03784 break; 03785 } 03786 if(low.green == high.green){ 03787 threshold_intensity = 0; 03788 memset(&intensity, 0, sizeof(struct double_packet)); 03789 for(low.green=0; low.green < 255; low.green++){ 03790 intensity.green+=histogram[(unsigned char)low.green].green; 03791 if(intensity.green > threshold_intensity) 03792 break; 03793 } 03794 memset(&intensity,0,sizeof(struct double_packet)); 03795 for(high.green=255; high.green != 0; high.green--){ 03796 intensity.green+=histogram[(unsigned char)high.green].green; 03797 if(intensity.green > threshold_intensity) 03798 break; 03799 } 03800 } 03801 03802 /* blue */ 03803 memset(&intensity, 0, sizeof(struct double_packet)); 03804 for(high.blue=255; high.blue != 0; high.blue--){ 03805 intensity.blue+=histogram[(unsigned char)high.blue].blue; 03806 if(intensity.blue > threshold_intensity) 03807 break; 03808 } 03809 if(low.blue == high.blue){ 03810 threshold_intensity = 0; 03811 memset(&intensity, 0, sizeof(struct double_packet)); 03812 for(low.blue=0; low.blue < 255; low.blue++){ 03813 intensity.blue+=histogram[(unsigned char)low.blue].blue; 03814 if(intensity.blue > threshold_intensity) 03815 break; 03816 } 03817 memset(&intensity,0,sizeof(struct double_packet)); 03818 for(high.blue=255; high.blue != 0; high.blue--){ 03819 intensity.blue+=histogram[(unsigned char)high.blue].blue; 03820 if(intensity.blue > threshold_intensity) 03821 break; 03822 } 03823 } 03824 03825 /* alpha */ 03826 memset(&intensity, 0, sizeof(struct double_packet)); 03827 for(high.alpha=255; high.alpha != 0; high.alpha--){ 03828 intensity.alpha+=histogram[(unsigned char)high.alpha].alpha; 03829 if(intensity.alpha > threshold_intensity) 03830 break; 03831 } 03832 if(low.alpha == high.alpha){ 03833 threshold_intensity = 0; 03834 memset(&intensity, 0, sizeof(struct double_packet)); 03835 for(low.alpha=0; low.alpha < 255; low.alpha++){ 03836 intensity.alpha+=histogram[(unsigned char)low.alpha].alpha; 03837 if(intensity.alpha > threshold_intensity) 03838 break; 03839 } 03840 memset(&intensity,0,sizeof(struct double_packet)); 03841 for(high.alpha=255; high.alpha != 0; high.alpha--){ 03842 intensity.alpha+=histogram[(unsigned char)high.alpha].alpha; 03843 if(intensity.alpha > threshold_intensity) 03844 break; 03845 } 03846 } 03847 liberateMemory(&histogram); 03848 03849 /* 03850 Stretch the histogram to create the normalized image mapping. 03851 */ 03852 03853 // should the maxes be 65535? 03854 memset(normalize_map, 0 ,256*sizeof(struct short_packet)); 03855 for(i=0; i <= (long) 255; i++){ 03856 if(i < (long) low.red) 03857 normalize_map[i].red=0; 03858 else if (i > (long) high.red) 03859 normalize_map[i].red=65535; 03860 else if (low.red != high.red) 03861 normalize_map[i].red = 03862 (unsigned short)((65535*(i-low.red))/(high.red-low.red)); 03863 03864 if(i < (long) low.green) 03865 normalize_map[i].green=0; 03866 else if (i > (long) high.green) 03867 normalize_map[i].green=65535; 03868 else if (low.green != high.green) 03869 normalize_map[i].green = 03870 (unsigned short)((65535*(i-low.green))/(high.green-low.green)); 03871 03872 if(i < (long) low.blue) 03873 normalize_map[i].blue=0; 03874 else if (i > (long) high.blue) 03875 normalize_map[i].blue=65535; 03876 else if (low.blue != high.blue) 03877 normalize_map[i].blue = 03878 (unsigned short)((65535*(i-low.blue))/(high.blue-low.blue)); 03879 03880 if(i < (long) low.alpha) 03881 normalize_map[i].alpha=0; 03882 else if (i > (long) high.alpha) 03883 normalize_map[i].alpha=65535; 03884 else if (low.alpha != high.alpha) 03885 normalize_map[i].alpha = 03886 (unsigned short)((65535*(i-low.alpha))/(high.alpha-low.alpha)); 03887 03888 } 03889 03890 for(y=0; y < image.height(); ++y){ 03891 q = (unsigned int *)image.scanLine(y); 03892 for(x=0; x < image.width(); ++x){ 03893 if(low.red != high.red) 03894 r = (normalize_map[(unsigned short)(tqRed(q[x]))].red)/257; 03895 else 03896 r = tqRed(q[x]); 03897 if(low.green != high.green) 03898 g = (normalize_map[(unsigned short)(tqGreen(q[x]))].green)/257; 03899 else 03900 g = tqGreen(q[x]); 03901 if(low.blue != high.blue) 03902 b = (normalize_map[(unsigned short)(tqBlue(q[x]))].blue)/257; 03903 else 03904 b = tqBlue(q[x]); 03905 if(low.alpha != high.alpha) 03906 a = (normalize_map[(unsigned short)(tqAlpha(q[x]))].alpha)/257; 03907 else 03908 a = tqAlpha(q[x]); 03909 q[x] = tqRgba(r, g, b, a); 03910 } 03911 } 03912 liberateMemory(&normalize_map); 03913 } 03914 03915 void KImageEffect::equalize(TQImage &image) 03916 { 03917 struct double_packet high, low, intensity, *map, *histogram; 03918 struct short_packet *equalize_map; 03919 int x, y; 03920 unsigned int *p, *q; 03921 long i; 03922 unsigned char r, g, b, a; 03923 03924 if(image.depth() < 32) // result will always be 32bpp 03925 image = image.convertDepth(32); 03926 03927 histogram=(struct double_packet *) malloc(256*sizeof(struct double_packet)); 03928 map=(struct double_packet *) malloc(256*sizeof(struct double_packet)); 03929 equalize_map=(struct short_packet *)malloc(256*sizeof(struct short_packet)); 03930 if(!histogram || !map || !equalize_map){ 03931 if(histogram) 03932 liberateMemory(&histogram); 03933 if(map) 03934 liberateMemory(&map); 03935 if(equalize_map) 03936 liberateMemory(&equalize_map); 03937 qWarning("KImageEffect::equalize(): Unable to allocate memory!"); 03938 return; 03939 } 03940 03941 /* 03942 Form histogram. 03943 */ 03944 memset(histogram, 0, 256*sizeof(struct double_packet)); 03945 for(y=0; y < image.height(); ++y){ 03946 p = (unsigned int *)image.scanLine(y); 03947 for(x=0; x < image.width(); ++x){ 03948 histogram[(unsigned char)(tqRed(*p))].red++; 03949 histogram[(unsigned char)(tqGreen(*p))].green++; 03950 histogram[(unsigned char)(tqBlue(*p))].blue++; 03951 histogram[(unsigned char)(tqAlpha(*p))].alpha++; 03952 p++; 03953 } 03954 } 03955 /* 03956 Integrate the histogram to get the equalization map. 03957 */ 03958 memset(&intensity, 0 ,sizeof(struct double_packet)); 03959 for(i=0; i <= 255; ++i){ 03960 intensity.red += histogram[i].red; 03961 intensity.green += histogram[i].green; 03962 intensity.blue += histogram[i].blue; 03963 intensity.alpha += histogram[i].alpha; 03964 map[i]=intensity; 03965 } 03966 low=map[0]; 03967 high=map[255]; 03968 memset(equalize_map, 0, 256*sizeof(short_packet)); 03969 for(i=0; i <= 255; ++i){ 03970 if(high.red != low.red) 03971 equalize_map[i].red=(unsigned short) 03972 ((65535*(map[i].red-low.red))/(high.red-low.red)); 03973 if(high.green != low.green) 03974 equalize_map[i].green=(unsigned short) 03975 ((65535*(map[i].green-low.green))/(high.green-low.green)); 03976 if(high.blue != low.blue) 03977 equalize_map[i].blue=(unsigned short) 03978 ((65535*(map[i].blue-low.blue))/(high.blue-low.blue)); 03979 if(high.alpha != low.alpha) 03980 equalize_map[i].alpha=(unsigned short) 03981 ((65535*(map[i].alpha-low.alpha))/(high.alpha-low.alpha)); 03982 } 03983 liberateMemory(&histogram); 03984 liberateMemory(&map); 03985 03986 /* 03987 Stretch the histogram. 03988 */ 03989 for(y=0; y < image.height(); ++y){ 03990 q = (unsigned int *)image.scanLine(y); 03991 for(x=0; x < image.width(); ++x){ 03992 if(low.red != high.red) 03993 r = (equalize_map[(unsigned short)(tqRed(q[x]))].red/257); 03994 else 03995 r = tqRed(q[x]); 03996 if(low.green != high.green) 03997 g = (equalize_map[(unsigned short)(tqGreen(q[x]))].green/257); 03998 else 03999 g = tqGreen(q[x]); 04000 if(low.blue != high.blue) 04001 b = (equalize_map[(unsigned short)(tqBlue(q[x]))].blue/257); 04002 else 04003 b = tqBlue(q[x]); 04004 if(low.alpha != high.alpha) 04005 a = (equalize_map[(unsigned short)(tqAlpha(q[x]))].alpha/257); 04006 else 04007 a = tqAlpha(q[x]); 04008 q[x] = tqRgba(r, g, b, a); 04009 } 04010 } 04011 liberateMemory(&equalize_map); 04012 04013 } 04014 04015 TQImage KImageEffect::edge(TQImage &image, double radius) 04016 { 04017 double *kernel; 04018 int width; 04019 long i; 04020 TQImage dest; 04021 04022 if(radius == 50.0){ 04023 /* For binary compatability! Remove me when possible! This used to 04024 * take a different parameter, a factor, and this was the default 04025 * value */ 04026 radius = 0.0; 04027 } 04028 04029 width = getOptimalKernelWidth(radius, 0.5); 04030 if(image.width() < width || image.height() < width){ 04031 qWarning("KImageEffect::edge(): Image is smaller than radius!"); 04032 return(dest); 04033 } 04034 kernel= (double *)malloc(width*width*sizeof(double)); 04035 if(!kernel){ 04036 qWarning("KImageEffect::edge(): Unable to allocate memory!"); 04037 return(dest); 04038 } 04039 for(i=0; i < (width*width); i++) 04040 kernel[i]=(-1.0); 04041 kernel[i/2]=width*width-1.0; 04042 convolveImage(&image, &dest, width, kernel); 04043 free(kernel); 04044 return(dest); 04045 } 04046 04047 TQImage KImageEffect::emboss(TQImage &src) 04048 { 04049 /* binary compat method - remove me when possible! */ 04050 return(emboss(src, 0, 1)); 04051 } 04052 04053 TQImage KImageEffect::emboss(TQImage &image, double radius, double sigma) 04054 { 04055 double alpha, *kernel; 04056 int j, width; 04057 long i, u, v; 04058 TQImage dest; 04059 04060 if(sigma == 0.0){ 04061 qWarning("KImageEffect::emboss(): Zero sigma is not permitted!"); 04062 return(dest); 04063 } 04064 04065 width = getOptimalKernelWidth(radius, sigma); 04066 if(image.width() < width || image.height() < width){ 04067 qWarning("KImageEffect::emboss(): Image is smaller than radius!"); 04068 return(dest); 04069 } 04070 kernel= (double *)malloc(width*width*sizeof(double)); 04071 if(!kernel){ 04072 qWarning("KImageEffect::emboss(): Unable to allocate memory!"); 04073 return(dest); 04074 } 04075 if(image.depth() < 32) 04076 image = image.convertDepth(32); 04077 04078 i=0; 04079 j=width/2; 04080 for(v=(-width/2); v <= (width/2); v++){ 04081 for(u=(-width/2); u <= (width/2); u++){ 04082 alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); 04083 kernel[i]=((u < 0) || (v < 0) ? -8.0 : 8.0)*alpha/ 04084 (2.0*MagickPI*sigma*sigma); 04085 if (u == j) 04086 kernel[i]=0.0; 04087 i++; 04088 } 04089 j--; 04090 } 04091 convolveImage(&image, &dest, width, kernel); 04092 liberateMemory(&kernel); 04093 04094 equalize(dest); 04095 return(dest); 04096 } 04097 04098 void KImageEffect::blurScanLine(double *kernel, int width, 04099 unsigned int *src, unsigned int *dest, 04100 int columns) 04101 { 04102 double *p; 04103 unsigned int *q; 04104 int x; 04105 long i; 04106 double red, green, blue, alpha; 04107 double scale = 0.0; 04108 04109 if(width > columns){ 04110 for(x=0; x < columns; ++x){ 04111 scale = 0.0; 04112 red = blue = green = alpha = 0.0; 04113 p = kernel; 04114 q = src; 04115 for(i=0; i < columns; ++i){ 04116 if((i >= (x-width/2)) && (i <= (x+width/2))){ 04117 red += (*p)*(tqRed(*q)*257); 04118 green += (*p)*(tqGreen(*q)*257); 04119 blue += (*p)*(tqBlue(*q)*257); 04120 alpha += (*p)*(tqAlpha(*q)*257); 04121 } 04122 if(((i+width/2-x) >= 0) && ((i+width/2-x) < width)) 04123 scale+=kernel[i+width/2-x]; 04124 p++; 04125 q++; 04126 } 04127 scale = 1.0/scale; 04128 red = scale*(red+0.5); 04129 green = scale*(green+0.5); 04130 blue = scale*(blue+0.5); 04131 alpha = scale*(alpha+0.5); 04132 04133 red = red < 0 ? 0 : red > 65535 ? 65535 : red; 04134 green = green < 0 ? 0 : green > 65535 ? 65535 : green; 04135 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; 04136 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; 04137 04138 dest[x] = tqRgba((unsigned char)(red/257UL), 04139 (unsigned char)(green/257UL), 04140 (unsigned char)(blue/257UL), 04141 (unsigned char)(alpha/257UL)); 04142 } 04143 return; 04144 } 04145 04146 for(x=0; x < width/2; ++x){ 04147 scale = 0.0; 04148 red = blue = green = alpha = 0.0; 04149 p = kernel+width/2-x; 04150 q = src; 04151 for(i=width/2-x; i < width; ++i){ 04152 red += (*p)*(tqRed(*q)*257); 04153 green += (*p)*(tqGreen(*q)*257); 04154 blue += (*p)*(tqBlue(*q)*257); 04155 alpha += (*p)*(tqAlpha(*q)*257); 04156 scale += (*p); 04157 p++; 04158 q++; 04159 } 04160 scale=1.0/scale; 04161 04162 red = scale*(red+0.5); 04163 green = scale*(green+0.5); 04164 blue = scale*(blue+0.5); 04165 alpha = scale*(alpha+0.5); 04166 04167 red = red < 0 ? 0 : red > 65535 ? 65535 : red; 04168 green = green < 0 ? 0 : green > 65535 ? 65535 : green; 04169 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; 04170 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; 04171 04172 dest[x] = tqRgba((unsigned char)(red/257UL), 04173 (unsigned char)(green/257UL), 04174 (unsigned char)(blue/257UL), 04175 (unsigned char)(alpha/257UL)); 04176 } 04177 04178 for(; x < columns-width/2; ++x){ 04179 red = blue = green = alpha = 0.0; 04180 p = kernel; 04181 q = src+(x-width/2); 04182 for (i=0; i < (long) width; ++i){ 04183 red += (*p)*(tqRed(*q)*257); 04184 green += (*p)*(tqGreen(*q)*257); 04185 blue += (*p)*(tqBlue(*q)*257); 04186 alpha += (*p)*(tqAlpha(*q)*257); 04187 p++; 04188 q++; 04189 } 04190 red = scale*(red+0.5); 04191 green = scale*(green+0.5); 04192 blue = scale*(blue+0.5); 04193 alpha = scale*(alpha+0.5); 04194 04195 red = red < 0 ? 0 : red > 65535 ? 65535 : red; 04196 green = green < 0 ? 0 : green > 65535 ? 65535 : green; 04197 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; 04198 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; 04199 04200 dest[x] = tqRgba((unsigned char)(red/257UL), 04201 (unsigned char)(green/257UL), 04202 (unsigned char)(blue/257UL), 04203 (unsigned char)(alpha/257UL)); 04204 } 04205 04206 for(; x < columns; ++x){ 04207 red = blue = green = alpha = 0.0; 04208 scale=0; 04209 p = kernel; 04210 q = src+(x-width/2); 04211 for(i=0; i < columns-x+width/2; ++i){ 04212 red += (*p)*(tqRed(*q)*257); 04213 green += (*p)*(tqGreen(*q)*257); 04214 blue += (*p)*(tqBlue(*q)*257); 04215 alpha += (*p)*(tqAlpha(*q)*257); 04216 scale += (*p); 04217 p++; 04218 q++; 04219 } 04220 scale=1.0/scale; 04221 red = scale*(red+0.5); 04222 green = scale*(green+0.5); 04223 blue = scale*(blue+0.5); 04224 alpha = scale*(alpha+0.5); 04225 04226 red = red < 0 ? 0 : red > 65535 ? 65535 : red; 04227 green = green < 0 ? 0 : green > 65535 ? 65535 : green; 04228 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue; 04229 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha; 04230 04231 dest[x] = tqRgba((unsigned char)(red/257UL), 04232 (unsigned char)(green/257UL), 04233 (unsigned char)(blue/257UL), 04234 (unsigned char)(alpha/257UL)); 04235 } 04236 } 04237 04238 int KImageEffect::getBlurKernel(int width, double sigma, double **kernel) 04239 { 04240 #define KernelRank 3 04241 double alpha, normalize; 04242 long i; 04243 int bias; 04244 04245 assert(sigma != 0.0); 04246 if(width == 0) 04247 width = 3; 04248 *kernel=(double *)malloc(width*sizeof(double)); 04249 if(*kernel == (double *)NULL) 04250 return(0); 04251 memset(*kernel, 0, width*sizeof(double)); 04252 bias = KernelRank*width/2; 04253 for(i=(-bias); i <= bias; i++){ 04254 alpha=exp(-((double) i*i)/(2.0*KernelRank*KernelRank*sigma*sigma)); 04255 (*kernel)[(i+bias)/KernelRank]+=alpha/(MagickSQ2PI*sigma); 04256 } 04257 normalize=0; 04258 for(i=0; i < width; i++) 04259 normalize+=(*kernel)[i]; 04260 for(i=0; i < width; i++) 04261 (*kernel)[i]/=normalize; 04262 04263 return(width); 04264 } 04265 04266 TQImage KImageEffect::blur(TQImage &src, double /*factor*/) 04267 { 04268 /* binary compat method - remove me when possible! */ 04269 return(blur(src, 0, 1)); 04270 } 04271 04272 TQImage KImageEffect::blur(TQImage &src, double radius, double sigma) 04273 { 04274 double *kernel; 04275 TQImage dest; 04276 int width; 04277 int x, y; 04278 unsigned int *scanline, *temp; 04279 unsigned int *p, *q; 04280 04281 if(sigma == 0.0){ 04282 qWarning("KImageEffect::blur(): Zero sigma is not permitted!"); 04283 return(dest); 04284 } 04285 if(src.depth() < 32) 04286 src = src.convertDepth(32); 04287 04288 kernel=(double *) NULL; 04289 if(radius > 0) 04290 width=getBlurKernel((int) (2*ceil(radius)+1),sigma,&kernel); 04291 else{ 04292 double *last_kernel; 04293 last_kernel=(double *) NULL; 04294 width=getBlurKernel(3,sigma,&kernel); 04295 04296 while ((long) (MaxRGB*kernel[0]) > 0){ 04297 if(last_kernel != (double *)NULL){ 04298 liberateMemory(&last_kernel); 04299 } 04300 last_kernel=kernel; 04301 kernel = (double *)NULL; 04302 width = getBlurKernel(width+2, sigma, &kernel); 04303 } 04304 if(last_kernel != (double *) NULL){ 04305 liberateMemory(&kernel); 04306 width-=2; 04307 kernel = last_kernel; 04308 } 04309 } 04310 04311 if(width < 3){ 04312 qWarning("KImageEffect::blur(): Kernel radius is too small!"); 04313 liberateMemory(&kernel); 04314 return(dest); 04315 } 04316 04317 dest.create(src.width(), src.height(), 32); 04318 04319 // Horizontal convolution 04320 scanline = (unsigned int *)malloc(sizeof(unsigned int)*src.height()); 04321 temp = (unsigned int *)malloc(sizeof(unsigned int)*src.height()); 04322 for(y=0; y < src.height(); ++y){ 04323 p = (unsigned int *)src.scanLine(y); 04324 q = (unsigned int *)dest.scanLine(y); 04325 blurScanLine(kernel, width, p, q, src.width()); 04326 } 04327 04328 TQImage partial = dest; 04329 04330 // Vertical convolution 04331 unsigned int **srcTable = (unsigned int **)partial.jumpTable(); 04332 unsigned int **destTable = (unsigned int **)dest.jumpTable(); 04333 for(x=0; x < partial.width(); ++x){ 04334 for(y=0; y < partial.height(); ++y){ 04335 scanline[y] = srcTable[y][x]; 04336 } 04337 blurScanLine(kernel, width, scanline, temp, partial.height()); 04338 for(y=0; y < partial.height(); ++y){ 04339 destTable[y][x] = temp[y]; 04340 } 04341 } 04342 free(scanline); 04343 free(temp); 04344 free(kernel); 04345 return(dest); 04346 } 04347 04348 bool KImageEffect::convolveImage(TQImage *image, TQImage *dest, 04349 const unsigned int order, 04350 const double *kernel) 04351 { 04352 long width; 04353 double red, green, blue, alpha; 04354 double normalize, *normal_kernel; 04355 const double *k; 04356 unsigned int *q; 04357 int x, y, mx, my, sx, sy; 04358 long i; 04359 int mcx, mcy; 04360 04361 width = order; 04362 if((width % 2) == 0){ 04363 qWarning("KImageEffect: Kernel width must be an odd number!"); 04364 return(false); 04365 } 04366 normal_kernel = (double *)malloc(width*width*sizeof(double)); 04367 if(!normal_kernel){ 04368 qWarning("KImageEffect: Unable to allocate memory!"); 04369 return(false); 04370 } 04371 dest->reset(); 04372 dest->create(image->width(), image->height(), 32); 04373 if(image->depth() < 32) 04374 *image = image->convertDepth(32); 04375 04376 normalize=0.0; 04377 for(i=0; i < (width*width); i++) 04378 normalize += kernel[i]; 04379 if(fabs(normalize) <= MagickEpsilon) 04380 normalize=1.0; 04381 normalize=1.0/normalize; 04382 for(i=0; i < (width*width); i++) 04383 normal_kernel[i] = normalize*kernel[i]; 04384 04385 unsigned int **jumpTable = (unsigned int **)image->jumpTable(); 04386 for(y=0; y < dest->height(); ++y){ 04387 sy = y-(width/2); 04388 q = (unsigned int *)dest->scanLine(y); 04389 for(x=0; x < dest->width(); ++x){ 04390 k = normal_kernel; 04391 red = green = blue = alpha = 0; 04392 sy = y-(width/2); 04393 for(mcy=0; mcy < width; ++mcy, ++sy){ 04394 my = sy < 0 ? 0 : sy > image->height()-1 ? 04395 image->height()-1 : sy; 04396 sx = x+(-width/2); 04397 for(mcx=0; mcx < width; ++mcx, ++sx){ 04398 mx = sx < 0 ? 0 : sx > image->width()-1 ? 04399 image->width()-1 : sx; 04400 red += (*k)*(tqRed(jumpTable[my][mx])*257); 04401 green += (*k)*(tqGreen(jumpTable[my][mx])*257); 04402 blue += (*k)*(tqBlue(jumpTable[my][mx])*257); 04403 alpha += (*k)*(tqAlpha(jumpTable[my][mx])*257); 04404 ++k; 04405 } 04406 } 04407 04408 red = red < 0 ? 0 : red > 65535 ? 65535 : red+0.5; 04409 green = green < 0 ? 0 : green > 65535 ? 65535 : green+0.5; 04410 blue = blue < 0 ? 0 : blue > 65535 ? 65535 : blue+0.5; 04411 alpha = alpha < 0 ? 0 : alpha > 65535 ? 65535 : alpha+0.5; 04412 04413 *q++ = tqRgba((unsigned char)(red/257UL), 04414 (unsigned char)(green/257UL), 04415 (unsigned char)(blue/257UL), 04416 (unsigned char)(alpha/257UL)); 04417 } 04418 } 04419 free(normal_kernel); 04420 return(true); 04421 04422 } 04423 04424 int KImageEffect::getOptimalKernelWidth(double radius, double sigma) 04425 { 04426 double normalize, value; 04427 long width; 04428 long u; 04429 04430 assert(sigma != 0.0); 04431 if(radius > 0.0) 04432 return((int)(2.0*ceil(radius)+1.0)); 04433 for(width=5; ;){ 04434 normalize=0.0; 04435 for(u=(-width/2); u <= (width/2); u++) 04436 normalize+=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma); 04437 u=width/2; 04438 value=exp(-((double) u*u)/(2.0*sigma*sigma))/(MagickSQ2PI*sigma)/normalize; 04439 if((long)(65535*value) <= 0) 04440 break; 04441 width+=2; 04442 } 04443 return((int)width-2); 04444 } 04445 04446 TQImage KImageEffect::sharpen(TQImage &src, double /*factor*/) 04447 { 04448 /* binary compat method - remove me when possible! */ 04449 return(sharpen(src, 0, 1)); 04450 } 04451 04452 TQImage KImageEffect::sharpen(TQImage &image, double radius, double sigma) 04453 { 04454 double alpha, normalize, *kernel; 04455 int width; 04456 long i, u, v; 04457 TQImage dest; 04458 04459 if(sigma == 0.0){ 04460 qWarning("KImageEffect::sharpen(): Zero sigma is not permitted!"); 04461 return(dest); 04462 } 04463 width = getOptimalKernelWidth(radius, sigma); 04464 if(image.width() < width){ 04465 qWarning("KImageEffect::sharpen(): Image is smaller than radius!"); 04466 return(dest); 04467 } 04468 kernel = (double *)malloc(width*width*sizeof(double)); 04469 if(!kernel){ 04470 qWarning("KImageEffect::sharpen(): Unable to allocate memory!"); 04471 return(dest); 04472 } 04473 04474 i = 0; 04475 normalize=0.0; 04476 for(v=(-width/2); v <= (width/2); v++){ 04477 for(u=(-width/2); u <= (width/2); u++){ 04478 alpha=exp(-((double) u*u+v*v)/(2.0*sigma*sigma)); 04479 kernel[i]=alpha/(2.0*MagickPI*sigma*sigma); 04480 normalize+=kernel[i]; 04481 i++; 04482 } 04483 } 04484 kernel[i/2]=(-2.0)*normalize; 04485 convolveImage(&image, &dest, width, kernel); 04486 free(kernel); 04487 return(dest); 04488 } 04489 04490 // End of new algorithms 04491 04492 TQImage KImageEffect::shade(TQImage &src, bool color_shading, double azimuth, 04493 double elevation) 04494 { 04495 struct PointInfo{ 04496 double x, y, z; 04497 }; 04498 04499 double distance, normal_distance, shade; 04500 int x, y; 04501 04502 struct PointInfo light, normal; 04503 04504 unsigned int *q; 04505 04506 TQImage dest(src.width(), src.height(), 32); 04507 04508 azimuth = DegreesToRadians(azimuth); 04509 elevation = DegreesToRadians(elevation); 04510 light.x = MaxRGB*cos(azimuth)*cos(elevation); 04511 light.y = MaxRGB*sin(azimuth)*cos(elevation); 04512 light.z = MaxRGB*sin(elevation); 04513 normal.z= 2*MaxRGB; // constant Z of surface normal 04514 04515 if(src.depth() > 8){ // DirectClass source image 04516 unsigned int *p, *s0, *s1, *s2; 04517 for(y=0; y < src.height(); ++y){ 04518 p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3)); 04519 q = (unsigned int *)dest.scanLine(y); 04520 // shade this row of pixels. 04521 *q++=(*(p+src.width())); 04522 p++; 04523 s0 = p; 04524 s1 = p + src.width(); 04525 s2 = p + 2*src.width(); 04526 for(x=1; x < src.width()-1; ++x){ 04527 // determine the surface normal and compute shading. 04528 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))- 04529 (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))- 04530 (double) intensityValue(*(s2+1)); 04531 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))- 04532 (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)- 04533 (double) intensityValue(*(s0+1)); 04534 if((normal.x == 0) && (normal.y == 0)) 04535 shade=light.z; 04536 else{ 04537 shade=0.0; 04538 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z; 04539 if (distance > 0.0){ 04540 normal_distance= 04541 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z; 04542 if(fabs(normal_distance) > 0.0000001) 04543 shade=distance/sqrt(normal_distance); 04544 } 04545 } 04546 if(!color_shading){ 04547 *q = tqRgba((unsigned char)(shade), 04548 (unsigned char)(shade), 04549 (unsigned char)(shade), 04550 tqAlpha(*s1)); 04551 } 04552 else{ 04553 *q = tqRgba((unsigned char)((shade*tqRed(*s1))/(MaxRGB+1)), 04554 (unsigned char)((shade*tqGreen(*s1))/(MaxRGB+1)), 04555 (unsigned char)((shade*tqBlue(*s1))/(MaxRGB+1)), 04556 tqAlpha(*s1)); 04557 } 04558 ++s0; 04559 ++s1; 04560 ++s2; 04561 q++; 04562 } 04563 *q++=(*s1); 04564 } 04565 } 04566 else{ // PsudeoClass source image 04567 unsigned char *p, *s0, *s1, *s2; 04568 int scanLineIdx; 04569 unsigned int *cTable = (unsigned int *)src.tqcolorTable(); 04570 for(y=0; y < src.height(); ++y){ 04571 scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3); 04572 p = (unsigned char *)src.scanLine(scanLineIdx); 04573 q = (unsigned int *)dest.scanLine(y); 04574 // shade this row of pixels. 04575 s0 = p; 04576 s1 = (unsigned char *) src.scanLine(scanLineIdx+1); 04577 s2 = (unsigned char *) src.scanLine(scanLineIdx+2); 04578 *q++=(*(cTable+(*s1))); 04579 ++p; 04580 ++s0; 04581 ++s1; 04582 ++s2; 04583 for(x=1; x < src.width()-1; ++x){ 04584 // determine the surface normal and compute shading. 04585 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))- 04586 (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))- 04587 (double) intensityValue(*(cTable+(*(s2+1)))); 04588 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))- 04589 (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))- 04590 (double) intensityValue(*(cTable+(*(s0+1)))); 04591 if((normal.x == 0) && (normal.y == 0)) 04592 shade=light.z; 04593 else{ 04594 shade=0.0; 04595 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z; 04596 if (distance > 0.0){ 04597 normal_distance= 04598 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z; 04599 if(fabs(normal_distance) > 0.0000001) 04600 shade=distance/sqrt(normal_distance); 04601 } 04602 } 04603 if(!color_shading){ 04604 *q = tqRgba((unsigned char)(shade), 04605 (unsigned char)(shade), 04606 (unsigned char)(shade), 04607 tqAlpha(*(cTable+(*s1)))); 04608 } 04609 else{ 04610 *q = tqRgba((unsigned char)((shade*tqRed(*(cTable+(*s1))))/(MaxRGB+1)), 04611 (unsigned char)((shade*tqGreen(*(cTable+(*s1))))/(MaxRGB+1)), 04612 (unsigned char)((shade*tqBlue(*(cTable+(*s1))))/(MaxRGB+1)), 04613 tqAlpha(*s1)); 04614 } 04615 ++s0; 04616 ++s1; 04617 ++s2; 04618 q++; 04619 } 04620 *q++=(*(cTable+(*s1))); 04621 } 04622 } 04623 return(dest); 04624 } 04625 04626 // High quality, expensive HSV contrast. You can do a faster one by just 04627 // taking a grayscale threshold (ie: 128) and incrementing RGB color 04628 // channels above it and decrementing those below it, but this gives much 04629 // better results. (mosfet 12/28/01) 04630 void KImageEffect::contrastHSV(TQImage &img, bool sharpen) 04631 { 04632 int i, sign; 04633 unsigned int *data; 04634 int count; 04635 double brightness, scale, theta; 04636 TQColor c; 04637 int h, s, v; 04638 04639 sign = sharpen ? 1 : -1; 04640 scale=0.5000000000000001; 04641 if(img.depth() > 8){ 04642 count = img.width()*img.height(); 04643 data = (unsigned int *)img.bits(); 04644 } 04645 else{ 04646 count = img.numColors(); 04647 data = (unsigned int *)img.tqcolorTable(); 04648 } 04649 for(i=0; i < count; ++i){ 04650 c.setRgb(data[i]); 04651 c.hsv(&h, &s, &v); 04652 brightness = v/255.0; 04653 theta=(brightness-0.5)*M_PI; 04654 brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign); 04655 if (brightness > 1.0) 04656 brightness=1.0; 04657 else 04658 if (brightness < 0) 04659 brightness=0.0; 04660 v = (int)(brightness*255); 04661 c.setHsv(h, s, v); 04662 data[i] = tqRgba(c.red(), c.green(), c.blue(), tqAlpha(data[i])); 04663 } 04664 } 04665 04666 04667 struct BumpmapParams { 04668 BumpmapParams( double bm_azimuth, double bm_elevation, 04669 int bm_depth, KImageEffect::BumpmapType bm_type, 04670 bool invert ) { 04671 /* Convert to radians */ 04672 double azimuth = DegreesToRadians( bm_azimuth ); 04673 double elevation = DegreesToRadians( bm_elevation ); 04674 04675 /* Calculate the light vector */ 04676 lx = (int)( cos(azimuth) * cos(elevation) * 255.0 ); 04677 ly = (int)( sin(azimuth) * cos(elevation) * 255.0 ); 04678 int lz = (int)( sin(elevation) * 255.0 ); 04679 04680 /* Calculate constant Z component of surface normal */ 04681 int nz = (6 * 255) / bm_depth; 04682 nz2 = nz * nz; 04683 nzlz = nz * lz; 04684 04685 /* Optimize for vertical normals */ 04686 background = lz; 04687 04688 /* Calculate darkness compensation factor */ 04689 compensation = sin(elevation); 04690 04691 /* Create look-up table for map type */ 04692 for (int i = 0; i < 256; i++) 04693 { 04694 double n = 0; 04695 switch (bm_type) 04696 { 04697 case KImageEffect::Spherical: 04698 n = i / 255.0 - 1.0; 04699 lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5); 04700 break; 04701 04702 case KImageEffect::Sinuosidal: 04703 n = i / 255.0; 04704 lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) / 04705 2.0 + 0.5); 04706 break; 04707 04708 case KImageEffect::Linear: 04709 default: 04710 lut[i] = i; 04711 } 04712 04713 if (invert) 04714 lut[i] = 255 - lut[i]; 04715 } 04716 } 04717 int lx, ly; 04718 int nz2, nzlz; 04719 int background; 04720 double compensation; 04721 uchar lut[256]; 04722 }; 04723 04724 04725 static void bumpmap_convert_row( uint *row, 04726 int width, 04727 int bpp, 04728 int has_alpha, 04729 uchar *lut, 04730 int waterlevel ) 04731 { 04732 uint *p; 04733 04734 p = row; 04735 04736 has_alpha = has_alpha ? 1 : 0; 04737 04738 if (bpp >= 3) 04739 for (; width; width--) 04740 { 04741 if (has_alpha) { 04742 unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5); 04743 *p++ = lut[(unsigned int) ( waterlevel + 04744 ( ( idx - 04745 waterlevel) * tqBlue( *row )) / 255.0 )]; 04746 } else { 04747 unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5); 04748 *p++ = lut[idx]; 04749 } 04750 04751 ++row; 04752 } 04753 } 04754 04755 static void bumpmap_row( uint *src, 04756 uint *dest, 04757 int width, 04758 int bpp, 04759 int has_alpha, 04760 uint *bm_row1, 04761 uint *bm_row2, 04762 uint *bm_row3, 04763 int bm_width, 04764 int bm_xofs, 04765 bool tiled, 04766 bool row_in_bumpmap, 04767 int ambient, 04768 bool compensate, 04769 BumpmapParams *params ) 04770 { 04771 int xofs1, xofs2, xofs3; 04772 int shade; 04773 int ndotl; 04774 int nx, ny; 04775 int x; 04776 int tmp; 04777 04778 tmp = bm_xofs; 04779 xofs2 = MOD(tmp, bm_width); 04780 04781 for (x = 0; x < width; x++) 04782 { 04783 /* Calculate surface normal from bump map */ 04784 04785 if (tiled || (row_in_bumpmap && 04786 x >= - tmp && x < - tmp + bm_width)) { 04787 if (tiled) { 04788 xofs1 = MOD(xofs2 - 1, bm_width); 04789 xofs3 = MOD(xofs2 + 1, bm_width); 04790 } else { 04791 xofs1 = FXCLAMP(xofs2 - 1, 0, bm_width - 1); 04792 xofs3 = FXCLAMP(xofs2 + 1, 0, bm_width - 1); 04793 } 04794 nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] - 04795 bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]); 04796 ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] - 04797 bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]); 04798 } else { 04799 nx = ny = 0; 04800 } 04801 04802 /* Shade */ 04803 04804 if ((nx == 0) && (ny == 0)) 04805 shade = params->background; 04806 else { 04807 ndotl = nx * params->lx + ny * params->ly + params->nzlz; 04808 04809 if (ndotl < 0) 04810 shade = (int)( params->compensation * ambient ); 04811 else { 04812 shade = (int)( ndotl / sqrt(double(nx * nx + ny * ny + params->nz2)) ); 04813 04814 shade = (int)( shade + QMAX(0.0, (255 * params->compensation - shade)) * 04815 ambient / 255 ); 04816 } 04817 } 04818 04819 /* Paint */ 04820 04825 if (compensate) { 04826 int red = (int)((tqRed( *src ) * shade) / (params->compensation * 255)); 04827 int green = (int)((tqGreen( *src ) * shade) / (params->compensation * 255)); 04828 int blue = (int)((tqBlue( *src ) * shade) / (params->compensation * 255)); 04829 int alpha = (int)((tqAlpha( *src ) * shade) / (params->compensation * 255)); 04830 ++src; 04831 *dest++ = tqRgba( red, green, blue, alpha ); 04832 } else { 04833 int red = tqRed( *src ) * shade / 255; 04834 int green = tqGreen( *src ) * shade / 255; 04835 int blue = tqBlue( *src ) * shade / 255; 04836 int alpha = tqAlpha( *src ) * shade / 255; 04837 ++src; 04838 *dest++ = tqRgba( red, green, blue, alpha ); 04839 } 04840 04841 /* Next pixel */ 04842 04843 if (++xofs2 == bm_width) 04844 xofs2 = 0; 04845 } 04846 } 04847 04867 TQImage KImageEffect::bumpmap(TQImage &img, TQImage &map, double azimuth, double elevation, 04868 int depth, int xofs, int yofs, int waterlevel, 04869 int ambient, bool compensate, bool invert, 04870 BumpmapType type, bool tiled) 04871 { 04872 TQImage dst; 04873 04874 if ( img.depth() != 32 || img.depth() != 32 ) { 04875 qWarning( "Bump-mapping effect works only with 32 bit images"); 04876 return dst; 04877 } 04878 04879 dst.create( img.width(), img.height(), img.depth() ); 04880 int bm_width = map.width(); 04881 int bm_height = map.height(); 04882 int bm_bpp = map.depth(); 04883 int bm_has_alpha = map.hasAlphaBuffer(); 04884 04885 int yofs1, yofs2, yofs3; 04886 04887 if ( tiled ) { 04888 yofs2 = MOD( yofs, bm_height ); 04889 yofs1 = MOD( yofs2 - 1, bm_height); 04890 yofs3 = MOD( yofs2 + 1, bm_height); 04891 } else { 04892 yofs1 = 0; 04893 yofs2 = 0; 04894 yofs3 = FXCLAMP( yofs2+1, 0, bm_height - 1 ); 04895 } 04896 04897 BumpmapParams params( azimuth, elevation, depth, type, invert ); 04898 04899 uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 ); 04900 uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 ); 04901 uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 ); 04902 04903 bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); 04904 bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); 04905 bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel ); 04906 04907 for (int y = 0; y < img.height(); ++y) 04908 { 04909 int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height); 04910 04911 uint* src_row = (unsigned int*)img.scanLine( y ); 04912 uint* dest_row = (unsigned int*)dst.scanLine( y ); 04913 04914 bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(), 04915 bm_row1, bm_row2, bm_row3, bm_width, xofs, 04916 tiled, 04917 row_in_bumpmap, ambient, compensate, 04918 ¶ms ); 04919 04920 /* Next line */ 04921 04922 if (tiled || row_in_bumpmap) 04923 { 04924 uint* bm_tmprow = bm_row1; 04925 bm_row1 = bm_row2; 04926 bm_row2 = bm_row3; 04927 bm_row3 = bm_tmprow; 04928 04929 if (++yofs2 == bm_height) 04930 yofs2 = 0; 04931 04932 if (tiled) 04933 yofs3 = MOD(yofs2 + 1, bm_height); 04934 else 04935 yofs3 = FXCLAMP(yofs2 + 1, 0, bm_height - 1); 04936 04937 bm_row3 = (unsigned int*)map.scanLine( yofs3 ); 04938 bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, 04939 params.lut, waterlevel ); 04940 } 04941 } 04942 return dst; 04943 } 04944 04953 TQImage KImageEffect::convertToPremultipliedAlpha(TQImage input) { 04954 TQImage alphaImage = input; 04955 if (!alphaImage.isNull()) alphaImage = alphaImage.convertDepth( 32 ); 04956 04957 int w = alphaImage.width(); 04958 int h = alphaImage.height(); 04959 04960 int r; 04961 int g; 04962 int b; 04963 int a; 04964 float alpha_adjust; 04965 TQRgb l; 04966 TQRgb *ls; 04967 for (int y = 0; y < h; ++y) { 04968 ls = (TQRgb *)alphaImage.scanLine( y ); 04969 for (int x = 0; x < w; ++x) { 04970 l = ls[x]; 04971 alpha_adjust = (tqAlpha( l )/255.0); 04972 r = int( tqRed( l ) * alpha_adjust ); 04973 g = int( tqGreen( l ) * alpha_adjust ); 04974 b = int( tqBlue( l ) * alpha_adjust ); 04975 a = int( tqAlpha( l ) * 1.0 ); 04976 ls[x] = tqRgba( r, g, b, a ); 04977 } 04978 } 04979 return alphaImage; 04980 }