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