kdefx Library API Documentation

kimageeffect.cpp

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