summaryrefslogtreecommitdiff
path: root/libopie2/opieui/oimageeffect.cpp
Unidiff
Diffstat (limited to 'libopie2/opieui/oimageeffect.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--libopie2/opieui/oimageeffect.cpp3794
1 files changed, 3794 insertions, 0 deletions
diff --git a/libopie2/opieui/oimageeffect.cpp b/libopie2/opieui/oimageeffect.cpp
new file mode 100644
index 0000000..3c28bbe
--- a/dev/null
+++ b/libopie2/opieui/oimageeffect.cpp
@@ -0,0 +1,3794 @@
1/* This file is part of the KDE libraries
2 Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley <mosfet@kde.org>
3 (C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
4 (C) 1998, 1999 Dirk A. Mueller <mueller@kde.org>
5 (C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
6
7Redistribution and use in source and binary forms, with or without
8modification, are permitted provided that the following conditions
9are met:
10
111. Redistributions of source code must retain the above copyright
12 notice, this list of conditions and the following disclaimer.
132. Redistributions in binary form must reproduce the above copyright
14 notice, this list of conditions and the following disclaimer in the
15 documentation and/or other materials provided with the distribution.
16
17THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28*/
29
30// $Id$
31
32#include <math.h>
33
34#include <qimage.h>
35#include <stdlib.h>
36#include <iostream>
37
38#include "oimageeffect.h"
39
40#define MaxRGB 255L
41#define DegreesToRadians(x) ((x)*M_PI/180.0)
42
43using namespace std;
44
45inline unsigned int intensityValue(unsigned int color)
46{
47 return((unsigned int)((0.299*qRed(color) +
48 0.587*qGreen(color) +
49 0.1140000000000001*qBlue(color))));
50}
51
52//======================================================================
53//
54// Gradient effects
55//
56//======================================================================
57
58QImage OImageEffect::gradient(const QSize &size, const QColor &ca,
59 const QColor &cb, GradientType eff, int ncols)
60{
61 int rDiff, gDiff, bDiff;
62 int rca, gca, bca, rcb, gcb, bcb;
63
64 QImage image(size, 32);
65
66 if (size.width() == 0 || size.height() == 0) {
67#ifndef NDEBUG
68 cerr << "WARNING: OImageEffect::gradient: invalid image" << endl;
69#endif
70 return image;
71 }
72
73 register int x, y;
74
75 rDiff = (rcb = cb.red()) - (rca = ca.red());
76 gDiff = (gcb = cb.green()) - (gca = ca.green());
77 bDiff = (bcb = cb.blue()) - (bca = ca.blue());
78
79 if( eff == VerticalGradient || eff == HorizontalGradient ){
80
81 uint *p;
82 uint rgb;
83
84 register int rl = rca << 16;
85 register int gl = gca << 16;
86 register int bl = bca << 16;
87
88 if( eff == VerticalGradient ) {
89
90 int rcdelta = ((1<<16) / size.height()) * rDiff;
91 int gcdelta = ((1<<16) / size.height()) * gDiff;
92 int bcdelta = ((1<<16) / size.height()) * bDiff;
93
94 for ( y = 0; y < size.height(); y++ ) {
95 p = (uint *) image.scanLine(y);
96
97 rl += rcdelta;
98 gl += gcdelta;
99 bl += bcdelta;
100
101 rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) );
102
103 for( x = 0; x < size.width(); x++ ) {
104 *p = rgb;
105 p++;
106 }
107 }
108
109 }
110 else { // must be HorizontalGradient
111
112 unsigned int *o_src = (unsigned int *)image.scanLine(0);
113 unsigned int *src = o_src;
114
115 int rcdelta = ((1<<16) / size.width()) * rDiff;
116 int gcdelta = ((1<<16) / size.width()) * gDiff;
117 int bcdelta = ((1<<16) / size.width()) * bDiff;
118
119 for( x = 0; x < size.width(); x++) {
120
121 rl += rcdelta;
122 gl += gcdelta;
123 bl += bcdelta;
124
125 *src++ = qRgb( (rl>>16), (gl>>16), (bl>>16));
126 }
127
128 src = o_src;
129
130 // Believe it or not, manually copying in a for loop is faster
131 // than calling memcpy for each scanline (on the order of ms...).
132 // I think this is due to the function call overhead (mosfet).
133
134 for (y = 1; y < size.height(); ++y) {
135
136 p = (unsigned int *)image.scanLine(y);
137 src = o_src;
138 for(x=0; x < size.width(); ++x)
139 *p++ = *src++;
140 }
141 }
142 }
143
144 else {
145
146 float rfd, gfd, bfd;
147 float rd = rca, gd = gca, bd = bca;
148
149 unsigned char *xtable[3];
150 unsigned char *ytable[3];
151
152 unsigned int w = size.width(), h = size.height();
153 xtable[0] = new unsigned char[w];
154 xtable[1] = new unsigned char[w];
155 xtable[2] = new unsigned char[w];
156 ytable[0] = new unsigned char[h];
157 ytable[1] = new unsigned char[h];
158 ytable[2] = new unsigned char[h];
159 w*=2, h*=2;
160
161 if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) {
162 // Diagonal dgradient code inspired by BlackBox (mosfet)
163 // BlackBox dgradient is (C) Brad Hughes, <bhughes@tcac.net> and
164 // Mike Cole <mike@mydot.com>.
165
166 rfd = (float)rDiff/w;
167 gfd = (float)gDiff/w;
168 bfd = (float)bDiff/w;
169
170 int dir;
171 for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) {
172 dir = eff == DiagonalGradient? x : size.width() - x - 1;
173 xtable[0][dir] = (unsigned char) rd;
174 xtable[1][dir] = (unsigned char) gd;
175 xtable[2][dir] = (unsigned char) bd;
176 }
177 rfd = (float)rDiff/h;
178 gfd = (float)gDiff/h;
179 bfd = (float)bDiff/h;
180 rd = gd = bd = 0;
181 for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) {
182 ytable[0][y] = (unsigned char) rd;
183 ytable[1][y] = (unsigned char) gd;
184 ytable[2][y] = (unsigned char) bd;
185 }
186
187 for (y = 0; y < size.height(); y++) {
188 unsigned int *scanline = (unsigned int *)image.scanLine(y);
189 for (x = 0; x < size.width(); x++) {
190 scanline[x] = qRgb(xtable[0][x] + ytable[0][y],
191 xtable[1][x] + ytable[1][y],
192 xtable[2][x] + ytable[2][y]);
193 }
194 }
195 }
196
197 else if (eff == RectangleGradient ||
198 eff == PyramidGradient ||
199 eff == PipeCrossGradient ||
200 eff == EllipticGradient)
201 {
202 int rSign = rDiff>0? 1: -1;
203 int gSign = gDiff>0? 1: -1;
204 int bSign = bDiff>0? 1: -1;
205
206 rfd = (float)rDiff / size.width();
207 gfd = (float)gDiff / size.width();
208 bfd = (float)bDiff / size.width();
209
210 rd = (float)rDiff/2;
211 gd = (float)gDiff/2;
212 bd = (float)bDiff/2;
213
214 for (x = 0; x < size.width(); x++, rd-=rfd, gd-=gfd, bd-=bfd)
215 {
216 xtable[0][x] = (unsigned char) abs((int)rd);
217 xtable[1][x] = (unsigned char) abs((int)gd);
218 xtable[2][x] = (unsigned char) abs((int)bd);
219 }
220
221 rfd = (float)rDiff/size.height();
222 gfd = (float)gDiff/size.height();
223 bfd = (float)bDiff/size.height();
224
225 rd = (float)rDiff/2;
226 gd = (float)gDiff/2;
227 bd = (float)bDiff/2;
228
229 for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd)
230 {
231 ytable[0][y] = (unsigned char) abs((int)rd);
232 ytable[1][y] = (unsigned char) abs((int)gd);
233 ytable[2][y] = (unsigned char) abs((int)bd);
234 }
235 unsigned int rgb;
236 int h = (size.height()+1)>>1;
237 for (y = 0; y < h; y++) {
238 unsigned int *sl1 = (unsigned int *)image.scanLine(y);
239 unsigned int *sl2 = (unsigned int *)image.scanLine(QMAX(size.height()-y-1, y));
240
241 int w = (size.width()+1)>>1;
242 int x2 = size.width()-1;
243
244 for (x = 0; x < w; x++, x2--) {
245 rgb = 0;
246 if (eff == PyramidGradient) {
247 rgb = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
248 gcb-gSign*(xtable[1][x]+ytable[1][y]),
249 bcb-bSign*(xtable[2][x]+ytable[2][y]));
250 }
251 if (eff == RectangleGradient) {
252 rgb = qRgb(rcb - rSign *
253 QMAX(xtable[0][x], ytable[0][y]) * 2,
254 gcb - gSign *
255 QMAX(xtable[1][x], ytable[1][y]) * 2,
256 bcb - bSign *
257 QMAX(xtable[2][x], ytable[2][y]) * 2);
258 }
259 if (eff == PipeCrossGradient) {
260 rgb = qRgb(rcb - rSign *
261 QMIN(xtable[0][x], ytable[0][y]) * 2,
262 gcb - gSign *
263 QMIN(xtable[1][x], ytable[1][y]) * 2,
264 bcb - bSign *
265 QMIN(xtable[2][x], ytable[2][y]) * 2);
266 }
267 if (eff == EllipticGradient) {
268 rgb = qRgb(rcb - rSign *
269 (int)sqrt((xtable[0][x]*xtable[0][x] +
270 ytable[0][y]*ytable[0][y])*2.0),
271 gcb - gSign *
272 (int)sqrt((xtable[1][x]*xtable[1][x] +
273 ytable[1][y]*ytable[1][y])*2.0),
274 bcb - bSign *
275 (int)sqrt((xtable[2][x]*xtable[2][x] +
276 ytable[2][y]*ytable[2][y])*2.0));
277 }
278
279 sl1[x] = sl2[x] = rgb;
280 sl1[x2] = sl2[x2] = rgb;
281 }
282 }
283 }
284
285 delete [] xtable[0];
286 delete [] xtable[1];
287 delete [] xtable[2];
288 delete [] ytable[0];
289 delete [] ytable[1];
290 delete [] ytable[2];
291 }
292
293 // dither if necessary
294 if (ncols && (QPixmap::defaultDepth() < 15 )) {
295 if ( ncols < 2 || ncols > 256 )
296 ncols = 3;
297 QColor *dPal = new QColor[ncols];
298 for (int i=0; i<ncols; i++) {
299 dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
300 gca + gDiff * i / ( ncols - 1 ),
301 bca + bDiff * i / ( ncols - 1 ) );
302 }
303 dither(image, dPal, ncols);
304 delete [] dPal;
305 }
306
307 return image;
308}
309
310
311// -----------------------------------------------------------------------------
312
313//CT this was (before Dirk A. Mueller's speedup changes)
314// merely the same code as in the above method, but it's supposedly
315// way less performant since it introduces a lot of supplementary tests
316// and simple math operations for the calculus of the balance.
317// (surprizingly, it isn't less performant, in the contrary :-)
318// Yes, I could have merged them, but then the excellent performance of
319// the balanced code would suffer with no other gain than a mere
320// source code and byte code size economy.
321
322QImage OImageEffect::unbalancedGradient(const QSize &size, const QColor &ca,
323 const QColor &cb, GradientType eff, int xfactor, int yfactor,
324 int ncols)
325{
326 int dir; // general parameter used for direction switches
327
328 bool _xanti = false , _yanti = false;
329
330 if (xfactor < 0) _xanti = true; // negative on X direction
331 if (yfactor < 0) _yanti = true; // negative on Y direction
332
333 xfactor = abs(xfactor);
334 yfactor = abs(yfactor);
335
336 if (!xfactor) xfactor = 1;
337 if (!yfactor) yfactor = 1;
338
339 if (xfactor > 200 ) xfactor = 200;
340 if (yfactor > 200 ) yfactor = 200;
341
342
343 // float xbal = xfactor/5000.;
344 // float ybal = yfactor/5000.;
345 float xbal = xfactor/30./size.width();
346 float ybal = yfactor/30./size.height();
347 float rat;
348
349 int rDiff, gDiff, bDiff;
350 int rca, gca, bca, rcb, gcb, bcb;
351
352 QImage image(size, 32);
353
354 if (size.width() == 0 || size.height() == 0) {
355#ifndef NDEBUG
356 cerr << "WARNING: OImageEffect::unbalancedGradient : invalid image\n";
357#endif
358 return image;
359 }
360
361 register int x, y;
362 unsigned int *scanline;
363
364 rDiff = (rcb = cb.red()) - (rca = ca.red());
365 gDiff = (gcb = cb.green()) - (gca = ca.green());
366 bDiff = (bcb = cb.blue()) - (bca = ca.blue());
367
368 if( eff == VerticalGradient || eff == HorizontalGradient){
369 QColor cRow;
370
371 uint *p;
372 uint rgbRow;
373
374 if( eff == VerticalGradient) {
375 for ( y = 0; y < size.height(); y++ ) {
376 dir = _yanti ? y : size.height() - 1 - y;
377 p = (uint *) image.scanLine(dir);
378 rat = 1 - exp( - (float)y * ybal );
379
380 cRow.setRgb( rcb - (int) ( rDiff * rat ),
381 gcb - (int) ( gDiff * rat ),
382 bcb - (int) ( bDiff * rat ) );
383
384 rgbRow = cRow.rgb();
385
386 for( x = 0; x < size.width(); x++ ) {
387 *p = rgbRow;
388 p++;
389 }
390 }
391 }
392 else {
393
394 unsigned int *src = (unsigned int *)image.scanLine(0);
395 for(x = 0; x < size.width(); x++ )
396 {
397 dir = _xanti ? x : size.width() - 1 - x;
398 rat = 1 - exp( - (float)x * xbal );
399
400 src[dir] = qRgb(rcb - (int) ( rDiff * rat ),
401 gcb - (int) ( gDiff * rat ),
402 bcb - (int) ( bDiff * rat ));
403 }
404
405 // Believe it or not, manually copying in a for loop is faster
406 // than calling memcpy for each scanline (on the order of ms...).
407 // I think this is due to the function call overhead (mosfet).
408
409 for(y = 1; y < size.height(); ++y)
410 {
411 scanline = (unsigned int *)image.scanLine(y);
412 for(x=0; x < size.width(); ++x)
413 scanline[x] = src[x];
414 }
415 }
416 }
417
418 else {
419 int w=size.width(), h=size.height();
420
421 unsigned char *xtable[3];
422 unsigned char *ytable[3];
423 xtable[0] = new unsigned char[w];
424 xtable[1] = new unsigned char[w];
425 xtable[2] = new unsigned char[w];
426 ytable[0] = new unsigned char[h];
427 ytable[1] = new unsigned char[h];
428 ytable[2] = new unsigned char[h];
429
430 if ( eff == DiagonalGradient || eff == CrossDiagonalGradient)
431 {
432 for (x = 0; x < w; x++) {
433 dir = _xanti ? x : w - 1 - x;
434 rat = 1 - exp( - (float)x * xbal );
435
436 xtable[0][dir] = (unsigned char) ( rDiff/2 * rat );
437 xtable[1][dir] = (unsigned char) ( gDiff/2 * rat );
438 xtable[2][dir] = (unsigned char) ( bDiff/2 * rat );
439 }
440
441 for (y = 0; y < h; y++) {
442 dir = _yanti ? y : h - 1 - y;
443 rat = 1 - exp( - (float)y * ybal );
444
445 ytable[0][dir] = (unsigned char) ( rDiff/2 * rat );
446 ytable[1][dir] = (unsigned char) ( gDiff/2 * rat );
447 ytable[2][dir] = (unsigned char) ( bDiff/2 * rat );
448 }
449
450 for (y = 0; y < h; y++) {
451 unsigned int *scanline = (unsigned int *)image.scanLine(y);
452 for (x = 0; x < w; x++) {
453 scanline[x] = qRgb(rcb - (xtable[0][x] + ytable[0][y]),
454 gcb - (xtable[1][x] + ytable[1][y]),
455 bcb - (xtable[2][x] + ytable[2][y]));
456 }
457 }
458 }
459
460 else if (eff == RectangleGradient ||
461 eff == PyramidGradient ||
462 eff == PipeCrossGradient ||
463 eff == EllipticGradient)
464 {
465 int rSign = rDiff>0? 1: -1;
466 int gSign = gDiff>0? 1: -1;
467 int bSign = bDiff>0? 1: -1;
468
469 for (x = 0; x < w; x++)
470 {
471 dir = _xanti ? x : w - 1 - x;
472 rat = 1 - exp( - (float)x * xbal );
473
474 xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
475 xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
476 xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
477 }
478
479 for (y = 0; y < h; y++)
480 {
481 dir = _yanti ? y : h - 1 - y;
482
483 rat = 1 - exp( - (float)y * ybal );
484
485 ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
486 ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
487 ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
488 }
489
490 for (y = 0; y < h; y++) {
491 unsigned int *scanline = (unsigned int *)image.scanLine(y);
492 for (x = 0; x < w; x++) {
493 if (eff == PyramidGradient)
494 {
495 scanline[x] = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
496 gcb-gSign*(xtable[1][x]+ytable[1][y]),
497 bcb-bSign*(xtable[2][x]+ytable[2][y]));
498 }
499 if (eff == RectangleGradient)
500 {
501 scanline[x] = qRgb(rcb - rSign *
502 QMAX(xtable[0][x], ytable[0][y]) * 2,
503 gcb - gSign *
504 QMAX(xtable[1][x], ytable[1][y]) * 2,
505 bcb - bSign *
506 QMAX(xtable[2][x], ytable[2][y]) * 2);
507 }
508 if (eff == PipeCrossGradient)
509 {
510 scanline[x] = qRgb(rcb - rSign *
511 QMIN(xtable[0][x], ytable[0][y]) * 2,
512 gcb - gSign *
513 QMIN(xtable[1][x], ytable[1][y]) * 2,
514 bcb - bSign *
515 QMIN(xtable[2][x], ytable[2][y]) * 2);
516 }
517 if (eff == EllipticGradient)
518 {
519 scanline[x] = qRgb(rcb - rSign *
520 (int)sqrt((xtable[0][x]*xtable[0][x] +
521 ytable[0][y]*ytable[0][y])*2.0),
522 gcb - gSign *
523 (int)sqrt((xtable[1][x]*xtable[1][x] +
524 ytable[1][y]*ytable[1][y])*2.0),
525 bcb - bSign *
526 (int)sqrt((xtable[2][x]*xtable[2][x] +
527 ytable[2][y]*ytable[2][y])*2.0));
528 }
529 }
530 }
531 }
532
533 if (ncols && (QPixmap::defaultDepth() < 15 )) {
534 if ( ncols < 2 || ncols > 256 )
535 ncols = 3;
536 QColor *dPal = new QColor[ncols];
537 for (int i=0; i<ncols; i++) {
538 dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
539 gca + gDiff * i / ( ncols - 1 ),
540 bca + bDiff * i / ( ncols - 1 ) );
541 }
542 dither(image, dPal, ncols);
543 delete [] dPal;
544 }
545
546 delete [] xtable[0];
547 delete [] xtable[1];
548 delete [] xtable[2];
549 delete [] ytable[0];
550 delete [] ytable[1];
551 delete [] ytable[2];
552
553 }
554
555 return image;
556}
557
558
559//======================================================================
560//
561// Intensity effects
562//
563//======================================================================
564
565
566/* This builds a 256 byte unsigned char lookup table with all
567 * the possible percent values prior to applying the effect, then uses
568 * integer math for the pixels. For any image larger than 9x9 this will be
569 * less expensive than doing a float operation on the 3 color components of
570 * each pixel. (mosfet)
571 */
572
573QImage& OImageEffect::intensity(QImage &image, float percent)
574{
575 if (image.width() == 0 || image.height() == 0) {
576#ifndef NDEBUG
577 cerr << "WARNING: OImageEffect::intensity : invalid image\n";
578#endif
579 return image;
580 }
581
582 int segColors = image.depth() > 8 ? 256 : image.numColors();
583 unsigned char *segTbl = new unsigned char[segColors];
584 int pixels = image.depth() > 8 ? image.width()*image.height() :
585 image.numColors();
586 unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
587 (unsigned int *)image.colorTable();
588
589 bool brighten = (percent >= 0);
590 if(percent < 0)
591 percent = -percent;
592
593 if(brighten){ // keep overflow check out of loops
594 for(int i=0; i < segColors; ++i){
595 int tmp = (int)(i*percent);
596 if(tmp > 255)
597 tmp = 255;
598 segTbl[i] = tmp;
599 }
600 }
601 else{
602 for(int i=0; i < segColors; ++i){
603 int tmp = (int)(i*percent);
604 if(tmp < 0)
605 tmp = 0;
606 segTbl[i] = tmp;
607 }
608 }
609
610 if(brighten){ // same here
611 for(int i=0; i < pixels; ++i){
612 int r = qRed(data[i]);
613 int g = qGreen(data[i]);
614 int b = qBlue(data[i]);
615 int a = qAlpha(data[i]);
616 r = r + segTbl[r] > 255 ? 255 : r + segTbl[r];
617 g = g + segTbl[g] > 255 ? 255 : g + segTbl[g];
618 b = b + segTbl[b] > 255 ? 255 : b + segTbl[b];
619 data[i] = qRgba(r, g, b,a);
620 }
621 }
622 else{
623 for(int i=0; i < pixels; ++i){
624 int r = qRed(data[i]);
625 int g = qGreen(data[i]);
626 int b = qBlue(data[i]);
627 int a = qAlpha(data[i]);
628 r = r - segTbl[r] < 0 ? 0 : r - segTbl[r];
629 g = g - segTbl[g] < 0 ? 0 : g - segTbl[g];
630 b = b - segTbl[b] < 0 ? 0 : b - segTbl[b];
631 data[i] = qRgba(r, g, b, a);
632 }
633 }
634 delete [] segTbl;
635
636 return image;
637}
638
639QImage& OImageEffect::channelIntensity(QImage &image, float percent,
640 RGBComponent channel)
641{
642 if (image.width() == 0 || image.height() == 0) {
643#ifndef NDEBUG
644 cerr << "WARNING: OImageEffect::channelIntensity : invalid image\n";
645#endif
646 return image;
647 }
648
649 int segColors = image.depth() > 8 ? 256 : image.numColors();
650 unsigned char *segTbl = new unsigned char[segColors];
651 int pixels = image.depth() > 8 ? image.width()*image.height() :
652 image.numColors();
653 unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
654 (unsigned int *)image.colorTable();
655 bool brighten = (percent >= 0);
656 if(percent < 0)
657 percent = -percent;
658
659 if(brighten){ // keep overflow check out of loops
660 for(int i=0; i < segColors; ++i){
661 int tmp = (int)(i*percent);
662 if(tmp > 255)
663 tmp = 255;
664 segTbl[i] = tmp;
665 }
666 }
667 else{
668 for(int i=0; i < segColors; ++i){
669 int tmp = (int)(i*percent);
670 if(tmp < 0)
671 tmp = 0;
672 segTbl[i] = tmp;
673 }
674 }
675
676 if(brighten){ // same here
677 if(channel == Red){ // and here ;-)
678 for(int i=0; i < pixels; ++i){
679 int c = qRed(data[i]);
680 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
681 data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
682 }
683 }
684 if(channel == Green){
685 for(int i=0; i < pixels; ++i){
686 int c = qGreen(data[i]);
687 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
688 data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
689 }
690 }
691 else{
692 for(int i=0; i < pixels; ++i){
693 int c = qBlue(data[i]);
694 c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
695 data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
696 }
697 }
698
699 }
700 else{
701 if(channel == Red){
702 for(int i=0; i < pixels; ++i){
703 int c = qRed(data[i]);
704 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
705 data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
706 }
707 }
708 if(channel == Green){
709 for(int i=0; i < pixels; ++i){
710 int c = qGreen(data[i]);
711 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
712 data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
713 }
714 }
715 else{
716 for(int i=0; i < pixels; ++i){
717 int c = qBlue(data[i]);
718 c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
719 data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
720 }
721 }
722 }
723 delete [] segTbl;
724
725 return image;
726}
727
728// Modulate an image with an RBG channel of another image
729//
730QImage& OImageEffect::modulate(QImage &image, QImage &modImage, bool reverse,
731 ModulationType type, int factor, RGBComponent channel)
732{
733 if (image.width() == 0 || image.height() == 0 ||
734 modImage.width() == 0 || modImage.height() == 0) {
735#ifndef NDEBUG
736 cerr << "WARNING: OImageEffect::modulate : invalid image\n";
737#endif
738 return image;
739 }
740
741 int r, g, b, h, s, v, a;
742 QColor clr;
743 int mod=0;
744 unsigned int x1, x2, y1, y2;
745 register int x, y;
746
747 // for image, we handle only depth 32
748 if (image.depth()<32) image = image.convertDepth(32);
749
750 // for modImage, we handle depth 8 and 32
751 if (modImage.depth()<8) modImage = modImage.convertDepth(8);
752
753 unsigned int *colorTable2 = (modImage.depth()==8) ?
754 modImage.colorTable():0;
755 unsigned int *data1, *data2;
756 unsigned char *data2b;
757 unsigned int color1, color2;
758
759 x1 = image.width(); y1 = image.height();
760 x2 = modImage.width(); y2 = modImage.height();
761
762 for (y = 0; y < (int)y1; y++) {
763 data1 = (unsigned int *) image.scanLine(y);
764 data2 = (unsigned int *) modImage.scanLine( y%y2 );
765 data2b = (unsigned char *) modImage.scanLine( y%y2 );
766
767 x=0;
768 while(x < (int)x1) {
769 color2 = (colorTable2) ? colorTable2[*data2b] : *data2;
770 if (reverse) {
771 color1 = color2;
772 color2 = *data1;
773 }
774 else
775 color1 = *data1;
776
777 if (type == Intensity || type == Contrast) {
778 r = qRed(color1);
779 g = qGreen(color1);
780 b = qBlue(color1);
781 if (channel != All) {
782 mod = (channel == Red) ? qRed(color2) :
783 (channel == Green) ? qGreen(color2) :
784 (channel == Blue) ? qBlue(color2) :
785 (channel == Gray) ? qGray(color2) : 0;
786 mod = mod*factor/50;
787 }
788
789 if (type == Intensity) {
790 if (channel == All) {
791 r += r * factor/50 * qRed(color2)/256;
792 g += g * factor/50 * qGreen(color2)/256;
793 b += b * factor/50 * qBlue(color2)/256;
794 }
795 else {
796 r += r * mod/256;
797 g += g * mod/256;
798 b += b * mod/256;
799 }
800 }
801 else { // Contrast
802 if (channel == All) {
803 r += (r-128) * factor/50 * qRed(color2)/128;
804 g += (g-128) * factor/50 * qGreen(color2)/128;
805 b += (b-128) * factor/50 * qBlue(color2)/128;
806 }
807 else {
808 r += (r-128) * mod/128;
809 g += (g-128) * mod/128;
810 b += (b-128) * mod/128;
811 }
812 }
813
814 if (r<0) r=0; if (r>255) r=255;
815 if (g<0) g=0; if (g>255) g=255;
816 if (b<0) b=0; if (b>255) b=255;
817 a = qAlpha(*data1);
818 *data1 = qRgba(r, g, b, a);
819 }
820 else if (type == Saturation || type == HueShift) {
821 clr.setRgb(color1);
822 clr.hsv(&h, &s, &v);
823 mod = (channel == Red) ? qRed(color2) :
824 (channel == Green) ? qGreen(color2) :
825 (channel == Blue) ? qBlue(color2) :
826 (channel == Gray) ? qGray(color2) : 0;
827 mod = mod*factor/50;
828
829 if (type == Saturation) {
830 s -= s * mod/256;
831 if (s<0) s=0; if (s>255) s=255;
832 }
833 else { // HueShift
834 h += mod;
835 while(h<0) h+=360;
836 h %= 360;
837 }
838
839 clr.setHsv(h, s, v);
840 a = qAlpha(*data1);
841 *data1 = clr.rgb() | ((uint)(a & 0xff) << 24);
842 }
843 data1++; data2++; data2b++; x++;
844 if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; }
845 }
846 }
847 return image;
848}
849
850
851
852//======================================================================
853//
854// Blend effects
855//
856//======================================================================
857
858
859// Nice and fast direct pixel manipulation
860QImage& OImageEffect::blend(const QColor& clr, QImage& dst, float opacity)
861{
862 if (dst.width() <= 0 || dst.height() <= 0)
863 return dst;
864
865 if (opacity < 0.0 || opacity > 1.0) {
866#ifndef NDEBUG
867 cerr << "WARNING: OImageEffect::blend : invalid opacity. Range [0, 1]\n";
868#endif
869 return dst;
870 }
871
872 int depth = dst.depth();
873 if (depth != 32)
874 dst = dst.convertDepth(32);
875
876 int pixels = dst.width() * dst.height();
877 int rcol, gcol, bcol;
878 clr.rgb(&rcol, &gcol, &bcol);
879
880#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
881 register unsigned char *data = (unsigned char *)dst.bits() + 1;
882#else // BGRA
883 register unsigned char *data = (unsigned char *)dst.bits();
884#endif
885
886 for (register int i=0; i<pixels; i++)
887 {
888#ifdef WORDS_BIGENDIAN
889 *(data++) += (unsigned char)((rcol - *data) * opacity);
890 *(data++) += (unsigned char)((gcol - *data) * opacity);
891 *(data++) += (unsigned char)((bcol - *data) * opacity);
892#else
893 *(data++) += (unsigned char)((bcol - *data) * opacity);
894 *(data++) += (unsigned char)((gcol - *data) * opacity);
895 *(data++) += (unsigned char)((rcol - *data) * opacity);
896#endif
897 data++; // skip alpha
898 }
899 return dst;
900}
901
902// Nice and fast direct pixel manipulation
903QImage& OImageEffect::blend(QImage& src, QImage& dst, float opacity)
904{
905 if (src.width() <= 0 || src.height() <= 0)
906 return dst;
907 if (dst.width() <= 0 || dst.height() <= 0)
908 return dst;
909
910 if (src.width() != dst.width() || src.height() != dst.height()) {
911#ifndef NDEBUG
912 cerr << "WARNING: OImageEffect::blend : src and destination images are not the same size\n";
913#endif
914 return dst;
915 }
916
917 if (opacity < 0.0 || opacity > 1.0) {
918#ifndef NDEBUG
919 cerr << "WARNING: OImageEffect::blend : invalid opacity. Range [0, 1]\n";
920#endif
921 return dst;
922 }
923
924 if (src.depth() != 32) src = src.convertDepth(32);
925 if (dst.depth() != 32) dst = dst.convertDepth(32);
926
927 int pixels = src.width() * src.height();
928#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
929 register unsigned char *data1 = (unsigned char *)dst.bits() + 1;
930 register unsigned char *data2 = (unsigned char *)src.bits() + 1;
931#else // BGRA
932 register unsigned char *data1 = (unsigned char *)dst.bits();
933 register unsigned char *data2 = (unsigned char *)src.bits();
934#endif
935
936 for (register int i=0; i<pixels; i++)
937 {
938#ifdef WORDS_BIGENDIAN
939 *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
940 *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
941 *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
942#else
943 *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
944 *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
945 *(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
946#endif
947 data1++; // skip alpha
948 data2++;
949 }
950
951 return dst;
952}
953
954
955QImage& OImageEffect::blend(QImage &image, float initial_intensity,
956 const QColor &bgnd, GradientType eff,
957 bool anti_dir)
958{
959 if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
960#ifndef NDEBUG
961 cerr << "WARNING: OImageEffect::blend : invalid image\n";
962#endif
963 return image;
964 }
965
966 int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
967 int r, g, b;
968 int ind;
969
970 unsigned int xi, xf, yi, yf;
971 unsigned int a;
972
973 // check the boundaries of the initial intesity param
974 float unaffected = 1;
975 if (initial_intensity > 1) initial_intensity = 1;
976 if (initial_intensity < -1) initial_intensity = -1;
977 if (initial_intensity < 0) {
978 unaffected = 1. + initial_intensity;
979 initial_intensity = 0;
980 }
981
982
983 float intensity = initial_intensity;
984 float var = 1. - initial_intensity;
985
986 if (anti_dir) {
987 initial_intensity = intensity = 1.;
988 var = -var;
989 }
990
991 register int x, y;
992
993 unsigned int *data = (unsigned int *)image.bits();
994
995 int image_width = image.width(); //Those can't change
996 int image_height = image.height();
997
998
999 if( eff == VerticalGradient || eff == HorizontalGradient ) {
1000
1001 // set the image domain to apply the effect to
1002 xi = 0, xf = image_width;
1003 yi = 0, yf = image_height;
1004 if (eff == VerticalGradient) {
1005 if (anti_dir) yf = (int)(image_height * unaffected);
1006 else yi = (int)(image_height * (1 - unaffected));
1007 }
1008 else {
1009 if (anti_dir) xf = (int)(image_width * unaffected);
1010 else xi = (int)(image_height * (1 - unaffected));
1011 }
1012
1013 var /= (eff == VerticalGradient?yf-yi:xf-xi);
1014
1015 int ind_base;
1016 for (y = yi; y < (int)yf; y++) {
1017 intensity = eff == VerticalGradient? intensity + var :
1018 initial_intensity;
1019 ind_base = image_width * y ;
1020 for (x = xi; x < (int)xf ; x++) {
1021 if (eff == HorizontalGradient) intensity += var;
1022 ind = x + ind_base;
1023 r = qRed (data[ind]) + (int)(intensity *
1024 (r_bgnd - qRed (data[ind])));
1025 g = qGreen(data[ind]) + (int)(intensity *
1026 (g_bgnd - qGreen(data[ind])));
1027 b = qBlue (data[ind]) + (int)(intensity *
1028 (b_bgnd - qBlue (data[ind])));
1029 if (r > 255) r = 255; if (r < 0 ) r = 0;
1030 if (g > 255) g = 255; if (g < 0 ) g = 0;
1031 if (b > 255) b = 255; if (b < 0 ) b = 0;
1032 a = qAlpha(data[ind]);
1033 data[ind] = qRgba(r, g, b, a);
1034 }
1035 }
1036 }
1037 else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) {
1038 float xvar = var / 2 / image_width; // / unaffected;
1039 float yvar = var / 2 / image_height; // / unaffected;
1040 float tmp;
1041
1042 for (x = 0; x < image_width ; x++) {
1043 tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1);
1044 ind = x;
1045 for (y = 0; y < image_height ; y++) {
1046 intensity = initial_intensity + tmp + yvar * y;
1047
1048 r = qRed (data[ind]) + (int)(intensity *
1049 (r_bgnd - qRed (data[ind])));
1050 g = qGreen(data[ind]) + (int)(intensity *
1051 (g_bgnd - qGreen(data[ind])));
1052 b = qBlue (data[ind]) + (int)(intensity *
1053 (b_bgnd - qBlue (data[ind])));
1054 if (r > 255) r = 255; if (r < 0 ) r = 0;
1055 if (g > 255) g = 255; if (g < 0 ) g = 0;
1056 if (b > 255) b = 255; if (b < 0 ) b = 0;
1057 a = qAlpha(data[ind]);
1058 data[ind] = qRgba(r, g, b, a);
1059
1060 ind += image_width;
1061 }
1062 }
1063 }
1064
1065 else if (eff == RectangleGradient || eff == EllipticGradient) {
1066 float xvar;
1067 float yvar;
1068
1069 for (x = 0; x < image_width / 2 + image_width % 2; x++) {
1070 xvar = var / image_width * (image_width - x*2/unaffected-1);
1071 for (y = 0; y < image_height / 2 + image_height % 2; y++) {
1072 yvar = var / image_height * (image_height - y*2/unaffected -1);
1073
1074 if (eff == RectangleGradient)
1075 intensity = initial_intensity + QMAX(xvar, yvar);
1076 else
1077 intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
1078 if (intensity > 1) intensity = 1;
1079 if (intensity < 0) intensity = 0;
1080
1081 //NW
1082 ind = x + image_width * y ;
1083 r = qRed (data[ind]) + (int)(intensity *
1084 (r_bgnd - qRed (data[ind])));
1085 g = qGreen(data[ind]) + (int)(intensity *
1086 (g_bgnd - qGreen(data[ind])));
1087 b = qBlue (data[ind]) + (int)(intensity *
1088 (b_bgnd - qBlue (data[ind])));
1089 if (r > 255) r = 255; if (r < 0 ) r = 0;
1090 if (g > 255) g = 255; if (g < 0 ) g = 0;
1091 if (b > 255) b = 255; if (b < 0 ) b = 0;
1092 a = qAlpha(data[ind]);
1093 data[ind] = qRgba(r, g, b, a);
1094
1095 //NE
1096 ind = image_width - x - 1 + image_width * y ;
1097 r = qRed (data[ind]) + (int)(intensity *
1098 (r_bgnd - qRed (data[ind])));
1099 g = qGreen(data[ind]) + (int)(intensity *
1100 (g_bgnd - qGreen(data[ind])));
1101 b = qBlue (data[ind]) + (int)(intensity *
1102 (b_bgnd - qBlue (data[ind])));
1103 if (r > 255) r = 255; if (r < 0 ) r = 0;
1104 if (g > 255) g = 255; if (g < 0 ) g = 0;
1105 if (b > 255) b = 255; if (b < 0 ) b = 0;
1106 a = qAlpha(data[ind]);
1107 data[ind] = qRgba(r, g, b, a);
1108 }
1109 }
1110
1111 //CT loop is doubled because of stupid central row/column issue.
1112 // other solution?
1113 for (x = 0; x < image_width / 2; x++) {
1114 xvar = var / image_width * (image_width - x*2/unaffected-1);
1115 for (y = 0; y < image_height / 2; y++) {
1116 yvar = var / image_height * (image_height - y*2/unaffected -1);
1117
1118 if (eff == RectangleGradient)
1119 intensity = initial_intensity + QMAX(xvar, yvar);
1120 else
1121 intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
1122 if (intensity > 1) intensity = 1;
1123 if (intensity < 0) intensity = 0;
1124
1125 //SW
1126 ind = x + image_width * (image_height - y -1) ;
1127 r = qRed (data[ind]) + (int)(intensity *
1128 (r_bgnd - qRed (data[ind])));
1129 g = qGreen(data[ind]) + (int)(intensity *
1130 (g_bgnd - qGreen(data[ind])));
1131 b = qBlue (data[ind]) + (int)(intensity *
1132 (b_bgnd - qBlue (data[ind])));
1133 if (r > 255) r = 255; if (r < 0 ) r = 0;
1134 if (g > 255) g = 255; if (g < 0 ) g = 0;
1135 if (b > 255) b = 255; if (b < 0 ) b = 0;
1136 a = qAlpha(data[ind]);
1137 data[ind] = qRgba(r, g, b, a);
1138
1139 //SE
1140 ind = image_width-x-1 + image_width * (image_height - y - 1) ;
1141 r = qRed (data[ind]) + (int)(intensity *
1142 (r_bgnd - qRed (data[ind])));
1143 g = qGreen(data[ind]) + (int)(intensity *
1144 (g_bgnd - qGreen(data[ind])));
1145 b = qBlue (data[ind]) + (int)(intensity *
1146 (b_bgnd - qBlue (data[ind])));
1147 if (r > 255) r = 255; if (r < 0 ) r = 0;
1148 if (g > 255) g = 255; if (g < 0 ) g = 0;
1149 if (b > 255) b = 255; if (b < 0 ) b = 0;
1150 a = qAlpha(data[ind]);
1151 data[ind] = qRgba(r, g, b, a);
1152 }
1153 }
1154 }
1155#ifndef NDEBUG
1156 else cerr << "OImageEffect::blend effect not implemented" << endl;
1157#endif
1158 return image;
1159}
1160
1161// Not very efficient as we create a third big image...
1162//
1163QImage& OImageEffect::blend(QImage &image1, QImage &image2,
1164 GradientType gt, int xf, int yf)
1165{
1166 if (image1.width() == 0 || image1.height() == 0 ||
1167 image2.width() == 0 || image2.height() == 0)
1168 return image1;
1169
1170 QImage image3;
1171
1172 image3 = OImageEffect::unbalancedGradient(image1.size(),
1173 QColor(0,0,0), QColor(255,255,255),
1174 gt, xf, yf, 0);
1175
1176 return blend(image1,image2,image3, Red); // Channel to use is arbitrary
1177}
1178
1179// Blend image2 into image1, using an RBG channel of blendImage
1180//
1181QImage& OImageEffect::blend(QImage &image1, QImage &image2,
1182 QImage &blendImage, RGBComponent channel)
1183{
1184 if (image1.width() == 0 || image1.height() == 0 ||
1185 image2.width() == 0 || image2.height() == 0 ||
1186 blendImage.width() == 0 || blendImage.height() == 0) {
1187#ifndef NDEBUG
1188 cerr << "OImageEffect::blend effect invalid image" << endl;
1189#endif
1190 return image1;
1191 }
1192
1193 int r, g, b;
1194 int ind1, ind2, ind3;
1195
1196 unsigned int x1, x2, x3, y1, y2, y3;
1197 unsigned int a;
1198
1199 register int x, y;
1200
1201 // for image1 and image2, we only handle depth 32
1202 if (image1.depth()<32) image1 = image1.convertDepth(32);
1203 if (image2.depth()<32) image2 = image2.convertDepth(32);
1204
1205 // for blendImage, we handle depth 8 and 32
1206 if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
1207
1208 unsigned int *colorTable3 = (blendImage.depth()==8) ?
1209 blendImage.colorTable():0;
1210
1211 unsigned int *data1 = (unsigned int *)image1.bits();
1212 unsigned int *data2 = (unsigned int *)image2.bits();
1213 unsigned int *data3 = (unsigned int *)blendImage.bits();
1214 unsigned char *data3b = (unsigned char *)blendImage.bits();
1215 unsigned int color3;
1216
1217 x1 = image1.width(); y1 = image1.height();
1218 x2 = image2.width(); y2 = image2.height();
1219 x3 = blendImage.width(); y3 = blendImage.height();
1220
1221 for (y = 0; y < (int)y1; y++) {
1222 ind1 = x1*y;
1223 ind2 = x2*(y%y2);
1224 ind3 = x3*(y%y3);
1225
1226 x=0;
1227 while(x < (int)x1) {
1228 color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
1229
1230 a = (channel == Red) ? qRed(color3) :
1231 (channel == Green) ? qGreen(color3) :
1232 (channel == Blue) ? qBlue(color3) : qGray(color3);
1233
1234 r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256;
1235 g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256;
1236 b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256;
1237
1238 a = qAlpha(data1[ind1]);
1239 data1[ind1] = qRgba(r, g, b, a);
1240
1241 ind1++; ind2++; ind3++; x++;
1242 if ( (x%x2) ==0) ind2 -= x2;
1243 if ( (x%x3) ==0) ind3 -= x3;
1244 }
1245 }
1246 return image1;
1247}
1248
1249
1250//======================================================================
1251//
1252// Hash effects
1253//
1254//======================================================================
1255
1256unsigned int OImageEffect::lHash(unsigned int c)
1257{
1258 unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
1259 unsigned char nr, ng, nb;
1260 nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
1261 ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
1262 nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
1263
1264 return qRgba(nr, ng, nb, a);
1265}
1266
1267
1268// -----------------------------------------------------------------------------
1269
1270unsigned int OImageEffect::uHash(unsigned int c)
1271{
1272 unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
1273 unsigned char nr, ng, nb;
1274 nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
1275 ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
1276 nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
1277
1278 return qRgba(nr, ng, nb, a);
1279}
1280
1281
1282// -----------------------------------------------------------------------------
1283
1284QImage& OImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing)
1285{
1286 if (image.width() == 0 || image.height() == 0) {
1287#ifndef NDEBUG
1288 cerr << "OImageEffect::hash effect invalid image" << endl;
1289#endif
1290 return image;
1291 }
1292
1293 register int x, y;
1294 unsigned int *data = (unsigned int *)image.bits();
1295 unsigned int ind;
1296
1297 //CT no need to do it if not enough space
1298 if ((lite == NorthLite ||
1299 lite == SouthLite)&&
1300 (unsigned)image.height() < 2+spacing) return image;
1301 if ((lite == EastLite ||
1302 lite == WestLite)&&
1303 (unsigned)image.height() < 2+spacing) return image;
1304
1305 if (lite == NorthLite || lite == SouthLite) {
1306 for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
1307 for (x = 0; x < image.width(); x++) {
1308 ind = x + image.width() * y;
1309 data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
1310
1311 ind = ind + image.width();
1312 data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
1313 }
1314 }
1315 }
1316
1317 else if (lite == EastLite || lite == WestLite) {
1318 for (y = 0 ; y < image.height(); y++) {
1319 for (x = 0; x < image.width(); x = x + 2 + spacing) {
1320 ind = x + image.width() * y;
1321 data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
1322
1323 ind++;
1324 data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
1325 }
1326 }
1327 }
1328
1329 else if (lite == NWLite || lite == SELite) {
1330 for (y = 0 ; y < image.height(); y++) {
1331 for (x = 0;
1332 x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
1333 x = x + 2 + spacing) {
1334 ind = x + image.width() * y + ((y & 1)? 1 : 0);
1335 data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
1336
1337 ind++;
1338 data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
1339 }
1340 }
1341 }
1342
1343 else if (lite == SWLite || lite == NELite) {
1344 for (y = 0 ; y < image.height(); y++) {
1345 for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
1346 ind = x + image.width() * y - ((y & 1)? 1 : 0);
1347 data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
1348
1349 ind++;
1350 data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
1351 }
1352 }
1353 }
1354
1355 return image;
1356}
1357
1358
1359//======================================================================
1360//
1361// Flatten effects
1362//
1363//======================================================================
1364
1365QImage& OImageEffect::flatten(QImage &img, const QColor &ca,
1366 const QColor &cb, int ncols)
1367{
1368 if (img.width() == 0 || img.height() == 0)
1369 return img;
1370
1371 // a bitmap is easy...
1372 if (img.depth() == 1) {
1373 img.setColor(0, ca.rgb());
1374 img.setColor(1, cb.rgb());
1375 return img;
1376 }
1377
1378 int r1 = ca.red(); int r2 = cb.red();
1379 int g1 = ca.green(); int g2 = cb.green();
1380 int b1 = ca.blue(); int b2 = cb.blue();
1381 int min = 0, max = 255;
1382
1383 QRgb col;
1384
1385 // Get minimum and maximum greylevel.
1386 if (img.numColors()) {
1387 // pseudocolor
1388 for (int i = 0; i < img.numColors(); i++) {
1389 col = img.color(i);
1390 int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
1391 min = QMIN(min, mean);
1392 max = QMAX(max, mean);
1393 }
1394 } else {
1395 // truecolor
1396 for (int y=0; y < img.height(); y++)
1397 for (int x=0; x < img.width(); x++) {
1398 col = img.pixel(x, y);
1399 int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
1400 min = QMIN(min, mean);
1401 max = QMAX(max, mean);
1402 }
1403 }
1404
1405 // Conversion factors
1406 float sr = ((float) r2 - r1) / (max - min);
1407 float sg = ((float) g2 - g1) / (max - min);
1408 float sb = ((float) b2 - b1) / (max - min);
1409
1410
1411 // Repaint the image
1412 if (img.numColors()) {
1413 for (int i=0; i < img.numColors(); i++) {
1414 col = img.color(i);
1415 int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
1416 int r = (int) (sr * (mean - min) + r1 + 0.5);
1417 int g = (int) (sg * (mean - min) + g1 + 0.5);
1418 int b = (int) (sb * (mean - min) + b1 + 0.5);
1419 img.setColor(i, qRgba(r, g, b, qAlpha(col)));
1420 }
1421 } else {
1422 for (int y=0; y < img.height(); y++)
1423 for (int x=0; x < img.width(); x++) {
1424 col = img.pixel(x, y);
1425 int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
1426 int r = (int) (sr * (mean - min) + r1 + 0.5);
1427 int g = (int) (sg * (mean - min) + g1 + 0.5);
1428 int b = (int) (sb * (mean - min) + b1 + 0.5);
1429 img.setPixel(x, y, qRgba(r, g, b, qAlpha(col)));
1430 }
1431 }
1432
1433
1434 // Dither if necessary
1435 if ( (ncols <= 0) || ((img.numColors() != 0) && (img.numColors() <= ncols)))
1436 return img;
1437
1438 if (ncols == 1) ncols++;
1439 if (ncols > 256) ncols = 256;
1440
1441 QColor *pal = new QColor[ncols];
1442 sr = ((float) r2 - r1) / (ncols - 1);
1443 sg = ((float) g2 - g1) / (ncols - 1);
1444 sb = ((float) b2 - b1) / (ncols - 1);
1445
1446 for (int i=0; i<ncols; i++)
1447 pal[i] = QColor(r1 + int(sr*i), g1 + int(sg*i), b1 + int(sb*i));
1448
1449 dither(img, pal, ncols);
1450
1451 delete[] pal;
1452 return img;
1453}
1454
1455
1456//======================================================================
1457//
1458// Fade effects
1459//
1460//======================================================================
1461
1462QImage& OImageEffect::fade(QImage &img, float val, const QColor &color)
1463{
1464 if (img.width() == 0 || img.height() == 0)
1465 return img;
1466
1467 // We don't handle bitmaps
1468 if (img.depth() == 1)
1469 return img;
1470
1471 unsigned char tbl[256];
1472 for (int i=0; i<256; i++)
1473 tbl[i] = (int) (val * i + 0.5);
1474
1475 int red = color.red();
1476 int green = color.green();
1477 int blue = color.blue();
1478
1479 QRgb col;
1480 int r, g, b, cr, cg, cb;
1481
1482 if (img.depth() <= 8) {
1483 // pseudo color
1484 for (int i=0; i<img.numColors(); i++) {
1485 col = img.color(i);
1486 cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
1487 if (cr > red)
1488 r = cr - tbl[cr - red];
1489 else
1490 r = cr + tbl[red - cr];
1491 if (cg > green)
1492 g = cg - tbl[cg - green];
1493 else
1494 g = cg + tbl[green - cg];
1495 if (cb > blue)
1496 b = cb - tbl[cb - blue];
1497 else
1498 b = cb + tbl[blue - cb];
1499 img.setColor(i, qRgba(r, g, b, qAlpha(col)));
1500 }
1501
1502 } else {
1503 // truecolor
1504 for (int y=0; y<img.height(); y++) {
1505 QRgb *data = (QRgb *) img.scanLine(y);
1506 for (int x=0; x<img.width(); x++) {
1507 col = *data;
1508 cr = qRed(col); cg = qGreen(col); cb = qBlue(col);
1509 if (cr > red)
1510 r = cr - tbl[cr - red];
1511 else
1512 r = cr + tbl[red - cr];
1513 if (cg > green)
1514 g = cg - tbl[cg - green];
1515 else
1516 g = cg + tbl[green - cg];
1517 if (cb > blue)
1518 b = cb - tbl[cb - blue];
1519 else
1520 b = cb + tbl[blue - cb];
1521 *data++ = qRgba(r, g, b, qAlpha(col));
1522 }
1523 }
1524 }
1525
1526 return img;
1527}
1528
1529//======================================================================
1530//
1531// Color effects
1532//
1533//======================================================================
1534
1535// This code is adapted from code (C) Rik Hemsley <rik@kde.org>
1536//
1537// The formula used (r + b + g) /3 is different from the qGray formula
1538// used by Qt. This is because our formula is much much faster. If,
1539// however, it turns out that this is producing sub-optimal images,
1540// then it will have to change (kurt)
1541//
1542// It does produce lower quality grayscale ;-) Use fast == true for the fast
1543// algorithm, false for the higher quality one (mosfet).
1544QImage& OImageEffect::toGray(QImage &img, bool fast)
1545{
1546 if (img.width() == 0 || img.height() == 0)
1547 return img;
1548
1549 if(fast){
1550 if (img.depth() == 32) {
1551 register uchar * r(img.bits());
1552 register uchar * g(img.bits() + 1);
1553 register uchar * b(img.bits() + 2);
1554
1555 uchar * end(img.bits() + img.numBytes());
1556
1557 while (r != end) {
1558
1559 *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3
1560
1561 r += 4;
1562 g += 4;
1563 b += 4;
1564 }
1565 }
1566 else
1567 {
1568 for (int i = 0; i < img.numColors(); i++)
1569 {
1570 register uint r = qRed(img.color(i));
1571 register uint g = qGreen(img.color(i));
1572 register uint b = qBlue(img.color(i));
1573
1574 register uint gray = (((r + g) >> 1) + b) >> 1;
1575 img.setColor(i, qRgba(gray, gray, gray, qAlpha(img.color(i))));
1576 }
1577 }
1578 }
1579 else{
1580 int pixels = img.depth() > 8 ? img.width()*img.height() :
1581 img.numColors();
1582 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
1583 (unsigned int *)img.colorTable();
1584 int val, i;
1585 for(i=0; i < pixels; ++i){
1586 val = qGray(data[i]);
1587 data[i] = qRgba(val, val, val, qAlpha(data[i]));
1588 }
1589 }
1590 return img;
1591}
1592
1593// CT 29Jan2000 - desaturation algorithms
1594QImage& OImageEffect::desaturate(QImage &img, float desat)
1595{
1596 if (img.width() == 0 || img.height() == 0)
1597 return img;
1598
1599 if (desat < 0) desat = 0.;
1600 if (desat > 1) desat = 1.;
1601 int pixels = img.depth() > 8 ? img.width()*img.height() :
1602 img.numColors();
1603 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
1604 (unsigned int *)img.colorTable();
1605 int h, s, v, i;
1606 QColor clr; // keep constructor out of loop (mosfet)
1607 for(i=0; i < pixels; ++i){
1608 clr.setRgb(data[i]);
1609 clr.hsv(&h, &s, &v);
1610 clr.setHsv(h, (int)(s * (1. - desat)), v);
1611 data[i] = clr.rgb();
1612 }
1613 return img;
1614}
1615
1616// Contrast stuff (mosfet)
1617QImage& OImageEffect::contrast(QImage &img, int c)
1618{
1619 if (img.width() == 0 || img.height() == 0)
1620 return img;
1621
1622 if(c > 255)
1623 c = 255;
1624 if(c < -255)
1625 c = -255;
1626 int pixels = img.depth() > 8 ? img.width()*img.height() :
1627 img.numColors();
1628 unsigned int *data = img.depth() > 8 ? (unsigned int *)img.bits() :
1629 (unsigned int *)img.colorTable();
1630 int i, r, g, b;
1631 for(i=0; i < pixels; ++i){
1632 r = qRed(data[i]);
1633 g = qGreen(data[i]);
1634 b = qBlue(data[i]);
1635 if(qGray(data[i]) <= 127){
1636 if(r - c <= 255)
1637 r -= c;
1638 if(g - c <= 255)
1639 g -= c;
1640 if(b - c <= 255)
1641 b -= c;
1642 }
1643 else{
1644 if(r + c <= 255)
1645 r += c;
1646 if(g + c <= 255)
1647 g += c;
1648 if(b + c <= 255)
1649 b += c;
1650 }
1651 data[i] = qRgba(r, g, b, qAlpha(data[i]));
1652 }
1653 return(img);
1654}
1655
1656//======================================================================
1657//
1658// Dithering effects
1659//
1660//======================================================================
1661
1662// adapted from kFSDither (C) 1997 Martin Jones (mjones@kde.org)
1663//
1664// Floyd-Steinberg dithering
1665// Ref: Bitmapped Graphics Programming in C++
1666// Marv Luse, Addison-Wesley Publishing, 1993.
1667QImage& OImageEffect::dither(QImage &img, const QColor *palette, int size)
1668{
1669 if (img.width() == 0 || img.height() == 0 ||
1670 palette == 0 || img.depth() <= 8)
1671 return img;
1672
1673 QImage dImage( img.width(), img.height(), 8, size );
1674 int i;
1675
1676 dImage.setNumColors( size );
1677 for ( i = 0; i < size; i++ )
1678 dImage.setColor( i, palette[ i ].rgb() );
1679
1680 int *rerr1 = new int [ img.width() * 2 ];
1681 int *gerr1 = new int [ img.width() * 2 ];
1682 int *berr1 = new int [ img.width() * 2 ];
1683
1684 memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
1685 memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
1686 memset( berr1, 0, sizeof( int ) * img.width() * 2 );
1687
1688 int *rerr2 = rerr1 + img.width();
1689 int *gerr2 = gerr1 + img.width();
1690 int *berr2 = berr1 + img.width();
1691
1692 for ( int j = 0; j < img.height(); j++ )
1693 {
1694 uint *ip = (uint * )img.scanLine( j );
1695 uchar *dp = dImage.scanLine( j );
1696
1697 for ( i = 0; i < img.width(); i++ )
1698 {
1699 rerr1[i] = rerr2[i] + qRed( *ip );
1700 rerr2[i] = 0;
1701 gerr1[i] = gerr2[i] + qGreen( *ip );
1702 gerr2[i] = 0;
1703 berr1[i] = berr2[i] + qBlue( *ip );
1704 berr2[i] = 0;
1705 ip++;
1706 }
1707
1708 *dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
1709
1710 for ( i = 1; i < img.width()-1; i++ )
1711 {
1712 int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
1713 *dp = indx;
1714
1715 int rerr = rerr1[i];
1716 rerr -= palette[indx].red();
1717 int gerr = gerr1[i];
1718 gerr -= palette[indx].green();
1719 int berr = berr1[i];
1720 berr -= palette[indx].blue();
1721
1722 // diffuse red error
1723 rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
1724 rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
1725 rerr2[ i ] += ( rerr * 5 ) >> 4;
1726 rerr2[ i+1 ] += ( rerr ) >> 4;
1727
1728 // diffuse green error
1729 gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
1730 gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
1731 gerr2[ i ] += ( gerr * 5 ) >> 4;
1732 gerr2[ i+1 ] += ( gerr ) >> 4;
1733
1734 // diffuse red error
1735 berr1[ i+1 ] += ( berr * 7 ) >> 4;
1736 berr2[ i-1 ] += ( berr * 3 ) >> 4;
1737 berr2[ i ] += ( berr * 5 ) >> 4;
1738 berr2[ i+1 ] += ( berr ) >> 4;
1739
1740 dp++;
1741 }
1742
1743 *dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
1744 }
1745
1746 delete [] rerr1;
1747 delete [] gerr1;
1748 delete [] berr1;
1749
1750 img = dImage;
1751 return img;
1752}
1753
1754int OImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size )
1755{
1756 if (palette == 0)
1757 return 0;
1758
1759 int dr = palette[0].red() - r;
1760 int dg = palette[0].green() - g;
1761 int db = palette[0].blue() - b;
1762
1763 int minDist = dr*dr + dg*dg + db*db;
1764 int nearest = 0;
1765
1766 for (int i = 1; i < size; i++ )
1767 {
1768 dr = palette[i].red() - r;
1769 dg = palette[i].green() - g;
1770 db = palette[i].blue() - b;
1771
1772 int dist = dr*dr + dg*dg + db*db;
1773
1774 if ( dist < minDist )
1775 {
1776 minDist = dist;
1777 nearest = i;
1778 }
1779 }
1780
1781 return nearest;
1782}
1783
1784bool OImageEffect::blend(
1785 const QImage & upper,
1786 const QImage & lower,
1787 QImage & output
1788)
1789{
1790 if (
1791 upper.width() > lower.width() ||
1792 upper.height() > lower.height() ||
1793 upper.depth() != 32 ||
1794 lower.depth() != 32
1795 )
1796 {
1797#ifndef NDEBUG
1798 cerr << "OImageEffect::blend : Sizes not correct\n" ;
1799#endif
1800 return false;
1801 }
1802
1803 output = lower.copy();
1804
1805 register uchar *i, *o;
1806 register int a;
1807 register int col;
1808 register int w = upper.width();
1809 int row(upper.height() - 1);
1810
1811 do {
1812
1813 i = upper.scanLine(row);
1814 o = output.scanLine(row);
1815
1816 col = w << 2;
1817 --col;
1818
1819 do {
1820
1821 while (!(a = i[col]) && (col != 3)) {
1822 --col; --col; --col; --col;
1823 }
1824
1825 --col;
1826 o[col] += ((i[col] - o[col]) * a) >> 8;
1827
1828 --col;
1829 o[col] += ((i[col] - o[col]) * a) >> 8;
1830
1831 --col;
1832 o[col] += ((i[col] - o[col]) * a) >> 8;
1833
1834 } while (col--);
1835
1836 } while (row--);
1837
1838 return true;
1839}
1840
1841#if 0
1842// Not yet...
1843bool OImageEffect::blend(
1844 const QImage & upper,
1845 const QImage & lower,
1846 QImage & output,
1847 const QRect & destRect
1848)
1849{
1850 output = lower.copy();
1851 return output;
1852}
1853
1854#endif
1855
1856bool OImageEffect::blend(
1857 int &x, int &y,
1858 const QImage & upper,
1859 const QImage & lower,
1860 QImage & output
1861)
1862{
1863 int cx=0, cy=0, cw=upper.width(), ch=upper.height();
1864
1865 if ( upper.width() + x > lower.width() ||
1866 upper.height() + y > lower.height() ||
1867 x < 0 || y < 0 ||
1868 upper.depth() != 32 || lower.depth() != 32 )
1869 {
1870 if ( x > lower.width() || y > lower.height() ) return false;
1871 if ( upper.width()<=0 || upper.height() <= 0 ) return false;
1872 if ( lower.width()<=0 || lower.height() <= 0 ) return false;
1873
1874 if (x<0) {cx=-x; cw+=x; x=0; };
1875 if (cw + x > lower.width()) { cw=lower.width()-x; };
1876 if (y<0) {cy=-y; ch+=y; y=0; };
1877 if (ch + y > lower.height()) { ch=lower.height()-y; };
1878
1879 if ( cx >= upper.width() || cy >= upper.height() ) return true;
1880 if ( cw <= 0 || ch <= 0 ) return true;
1881 }
1882
1883 output.create(cw,ch,32);
1884// output.setAlphaBuffer(true); // I should do some benchmarks to see if
1885 // this is worth the effort
1886
1887 register QRgb *i, *o, *b;
1888
1889 register int a;
1890 register int j,k;
1891 for (j=0; j<ch; j++)
1892 {
1893 b=reinterpret_cast<QRgb *>(&lower.scanLine(y+j) [ (x+cw) << 2 ]);
1894 i=reinterpret_cast<QRgb *>(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]);
1895 o=reinterpret_cast<QRgb *>(&output.scanLine(j) [ cw << 2 ]);
1896
1897 k=cw-1;
1898 --b; --i; --o;
1899 do
1900 {
1901 while ( !(a=qAlpha(*i)) && k>0 )
1902 {
1903 i--;
1904 //*o=0;
1905 *o=*b;
1906 --o; --b;
1907 k--;
1908 };
1909// *o=0xFF;
1910 *o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8),
1911 qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8),
1912 qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8));
1913 --i; --o; --b;
1914 } while (k--);
1915 }
1916
1917 return true;
1918}
1919
1920bool OImageEffect::blendOnLower(
1921 int x, int y,
1922 const QImage & upper,
1923 const QImage & lower
1924)
1925{
1926 int cx=0, cy=0, cw=upper.width(), ch=upper.height();
1927
1928 if ( upper.depth() != 32 || lower.depth() != 32 ) return false;
1929 if ( x + cw > lower.width() ||
1930 y + ch > lower.height() ||
1931 x < 0 || y < 0 )
1932 {
1933 if ( x > lower.width() || y > lower.height() ) return true;
1934 if ( upper.width()<=0 || upper.height() <= 0 ) return true;
1935 if ( lower.width()<=0 || lower.height() <= 0 ) return true;
1936
1937 if (x<0) {cx=-x; cw+=x; x=0; };
1938 if (cw + x > lower.width()) { cw=lower.width()-x; };
1939 if (y<0) {cy=-y; ch+=y; y=0; };
1940 if (ch + y > lower.height()) { ch=lower.height()-y; };
1941
1942 if ( cx >= upper.width() || cy >= upper.height() ) return true;
1943 if ( cw <= 0 || ch <= 0 ) return true;
1944 }
1945
1946 register uchar *i, *b;
1947 register int a;
1948 register int k;
1949
1950 for (int j=0; j<ch; j++)
1951 {
1952 b=&lower.scanLine(y+j) [ (x+cw) << 2 ];
1953 i=&upper.scanLine(cy+j)[ (cx+cw) << 2 ];
1954
1955 k=cw-1;
1956 --b; --i;
1957 do
1958 {
1959#ifndef WORDS_BIGENDIAN
1960 while ( !(a=*i) && k>0 )
1961#else
1962 while ( !(a=*(i-3)) && k>0 )
1963#endif
1964 {
1965 i-=4; b-=4; k--;
1966 };
1967
1968#ifndef WORDS_BIGENDIAN
1969 --i; --b;
1970 *b += ( ((*i - *b) * a) >> 8 );
1971 --i; --b;
1972 *b += ( ((*i - *b) * a) >> 8 );
1973 --i; --b;
1974 *b += ( ((*i - *b) * a) >> 8 );
1975 --i; --b;
1976#else
1977 *b += ( ((*i - *b) * a) >> 8 );
1978 --i; --b;
1979 *b += ( ((*i - *b) * a) >> 8 );
1980 --i; --b;
1981 *b += ( ((*i - *b) * a) >> 8 );
1982 i -= 2; b -= 2;
1983#endif
1984 } while (k--);
1985 }
1986
1987 return true;
1988}
1989
1990// For selected icons
1991QImage& OImageEffect::selectedImage( QImage &img, const QColor &col )
1992{
1993 return blend( col, img, 0.5);
1994}
1995
1996//
1997// ===================================================================
1998// Effects originally ported from ImageMagick for PixiePlus, plus a few
1999// new ones. (mosfet 12/29/01)
2000// ===================================================================
2001//
2002
2003void OImageEffect::normalize(QImage &img)
2004{
2005 int *histogram, threshold_intensity, intense;
2006 int x, y, i;
2007
2008 unsigned int gray_value;
2009 unsigned int *normalize_map;
2010 unsigned int high, low;
2011
2012 // allocate histogram and normalize map
2013 histogram = (int *)calloc(MaxRGB+1, sizeof(int));
2014 normalize_map = (unsigned int *)malloc((MaxRGB+1)*sizeof(unsigned int));
2015 if(!normalize_map || !histogram){
2016 qWarning("Unable to allocate normalize histogram and map");
2017 free(normalize_map);
2018 free(histogram);
2019 return;
2020 }
2021
2022 // form histogram
2023 if(img.depth() > 8){ // DirectClass
2024 unsigned int *data;
2025 for(y=0; y < img.height(); ++y){
2026 data = (unsigned int *)img.scanLine(y);
2027 for(x=0; x < img.width(); ++x){
2028 gray_value = intensityValue(data[x]);
2029 histogram[gray_value]++;
2030 }
2031 }
2032 }
2033 else{ // PsudeoClass
2034 unsigned char *data;
2035 unsigned int *cTable = img.colorTable();
2036 for(y=0; y < img.height(); ++y){
2037 data = (unsigned char *)img.scanLine(y);
2038 for(x=0; x < img.width(); ++x){
2039 gray_value = intensityValue(*(cTable+data[x]));
2040 histogram[gray_value]++;
2041 }
2042 }
2043 }
2044
2045 // find histogram boundaries by locating the 1 percent levels
2046 threshold_intensity = (img.width()*img.height())/100;
2047 intense = 0;
2048 for(low=0; low < MaxRGB; ++low){
2049 intense+=histogram[low];
2050 if(intense > threshold_intensity)
2051 break;
2052 }
2053 intense=0;
2054 for(high=MaxRGB; high != 0; --high){
2055 intense+=histogram[high];
2056 if(intense > threshold_intensity)
2057 break;
2058 }
2059
2060 if (low == high){
2061 // Unreasonable contrast; use zero threshold to determine boundaries.
2062 threshold_intensity=0;
2063 intense=0;
2064 for(low=0; low < MaxRGB; ++low){
2065 intense+=histogram[low];
2066 if(intense > threshold_intensity)
2067 break;
2068 }
2069 intense=0;
2070 for(high=MaxRGB; high != 0; --high)
2071 {
2072 intense+=histogram[high];
2073 if(intense > threshold_intensity)
2074 break;
2075 }
2076 if(low == high)
2077 return; // zero span bound
2078 }
2079
2080 // Stretch the histogram to create the normalized image mapping.
2081 for(i=0; i <= MaxRGB; i++){
2082 if (i < (int) low)
2083 normalize_map[i]=0;
2084 else{
2085 if(i > (int) high)
2086 normalize_map[i]=MaxRGB;
2087 else
2088 normalize_map[i]=(MaxRGB-1)*(i-low)/(high-low);
2089 }
2090 }
2091 // Normalize
2092 if(img.depth() > 8){ // DirectClass
2093 unsigned int *data;
2094 for(y=0; y < img.height(); ++y){
2095 data = (unsigned int *)img.scanLine(y);
2096 for(x=0; x < img.width(); ++x){
2097 data[x] = qRgba(normalize_map[qRed(data[x])],
2098 normalize_map[qGreen(data[x])],
2099 normalize_map[qBlue(data[x])],
2100 qAlpha(data[x]));
2101 }
2102 }
2103 }
2104 else{ // PsudeoClass
2105 int colors = img.numColors();
2106 unsigned int *cTable = img.colorTable();
2107 for(i=0; i < colors; ++i){
2108 cTable[i] = qRgba(normalize_map[qRed(cTable[i])],
2109 normalize_map[qGreen(cTable[i])],
2110 normalize_map[qBlue(cTable[i])],
2111 qAlpha(cTable[i]));
2112 }
2113 }
2114 free(histogram);
2115 free(normalize_map);
2116}
2117
2118
2119void OImageEffect::equalize(QImage &img)
2120{
2121 int *histogram, *map, *equalize_map;
2122 int x, y, i, j;
2123
2124 unsigned int high, low;
2125
2126 // allocate histogram and maps
2127 histogram = (int *)calloc(MaxRGB+1, sizeof(int));
2128 map = (int *)malloc((MaxRGB+1)*sizeof(unsigned int));
2129 equalize_map = (int *)malloc((MaxRGB+1)*sizeof(unsigned int));
2130
2131 if(!histogram || !map || !equalize_map){
2132 qWarning("Unable to allocate equalize histogram and maps");
2133 free(histogram);
2134 free(map);
2135 free(equalize_map);
2136 return;
2137 }
2138 // form histogram
2139 if(img.depth() > 8){ // DirectClass
2140 unsigned int *data;
2141 for(y=0; y < img.height(); ++y){
2142 data = (unsigned int *)img.scanLine(y);
2143 for(x=0; x < img.width(); ++x){
2144 histogram[intensityValue(data[x])]++;
2145 }
2146 }
2147 }
2148 else{ // PsudeoClass
2149 unsigned char *data;
2150 unsigned int *cTable = img.colorTable();
2151 for(y=0; y < img.height(); ++y){
2152 data = (unsigned char *)img.scanLine(y);
2153 for(x=0; x < img.width(); ++x){
2154 histogram[intensityValue(*(cTable+data[x]))]++;
2155 }
2156 }
2157 }
2158
2159 // integrate the histogram to get the equalization map.
2160 j=0;
2161 for(i=0; i <= MaxRGB; i++){
2162 j+=histogram[i];
2163 map[i]=j;
2164 }
2165 free(histogram);
2166 if(map[MaxRGB] == 0){
2167 free(equalize_map);
2168 free(map);
2169 return;
2170 }
2171 // equalize
2172 low=map[0];
2173 high=map[MaxRGB];
2174 for(i=0; i <= MaxRGB; i++)
2175 equalize_map[i]=(unsigned int)
2176 ((((double) (map[i]-low))*MaxRGB)/QMAX(high-low,1));
2177 free(map);
2178 // stretch the histogram
2179 if(img.depth() > 8){ // DirectClass
2180 unsigned int *data;
2181 for(y=0; y < img.height(); ++y){
2182 data = (unsigned int *)img.scanLine(y);
2183 for(x=0; x < img.width(); ++x){
2184 data[x] = qRgba(equalize_map[qRed(data[x])],
2185 equalize_map[qGreen(data[x])],
2186 equalize_map[qBlue(data[x])],
2187 qAlpha(data[x]));
2188 }
2189 }
2190 }
2191 else{ // PsudeoClass
2192 int colors = img.numColors();
2193 unsigned int *cTable = img.colorTable();
2194 for(i=0; i < colors; ++i){
2195 cTable[i] = qRgba(equalize_map[qRed(cTable[i])],
2196 equalize_map[qGreen(cTable[i])],
2197 equalize_map[qBlue(cTable[i])],
2198 qAlpha(cTable[i]));
2199 }
2200 }
2201 free(equalize_map);
2202}
2203
2204QImage OImageEffect::sample(QImage &src, int w, int h)
2205{
2206 if(w == src.width() && h == src.height())
2207 return(src);
2208
2209 double *x_offset, *y_offset;
2210 int j, k, y;
2211 register int x;
2212 QImage dest(w, h, src.depth());
2213
2214 x_offset = (double *)malloc(w*sizeof(double));
2215 y_offset = (double *)malloc(h*sizeof(double));
2216 if(!x_offset || !y_offset){
2217 qWarning("Unable to allocate pixels buffer");
2218 free(x_offset);
2219 free(y_offset);
2220 return(src);
2221 }
2222
2223 // init pixel offsets
2224 for(x=0; x < w; ++x)
2225 x_offset[x] = x*src.width()/((double)w);
2226 for(y=0; y < h; ++y)
2227 y_offset[y] = y*src.height()/((double)h);
2228
2229 // sample each row
2230 if(src.depth() > 8){ // DirectClass source image
2231 unsigned int *srcData, *destData;
2232 unsigned int *pixels;
2233 pixels = (unsigned int *)malloc(src.width()*sizeof(unsigned int));
2234 if(!pixels){
2235 qWarning("Unable to allocate pixels buffer");
2236 free(pixels);
2237 free(x_offset);
2238 free(y_offset);
2239 return(src);
2240 }
2241 j = (-1);
2242 for(y=0; y < h; ++y){
2243 destData = (unsigned int *)dest.scanLine(y);
2244 if(j != y_offset[y]){
2245 // read a scan line
2246 j = (int)(y_offset[y]);
2247 srcData = (unsigned int *)src.scanLine(j);
2248 (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned int));
2249 }
2250 // sample each column
2251 for(x=0; x < w; ++x){
2252 k = (int)(x_offset[x]);
2253 destData[x] = pixels[k];
2254 }
2255 }
2256 free(pixels);
2257 }
2258 else{ // PsudeoClass source image
2259 unsigned char *srcData, *destData;
2260 unsigned char *pixels;
2261 pixels = (unsigned char *)malloc(src.width()*sizeof(unsigned char));
2262 if(!pixels){
2263 qWarning("Unable to allocate pixels buffer");
2264 free(pixels);
2265 free(x_offset);
2266 free(y_offset);
2267 return(src);
2268 }
2269 // copy colortable
2270 dest.setNumColors(src.numColors());
2271 (void)memcpy(dest.colorTable(), src.colorTable(),
2272 src.numColors()*sizeof(unsigned int));
2273
2274 // sample image
2275 j = (-1);
2276 for(y=0; y < h; ++y){
2277 destData = (unsigned char *)dest.scanLine(y);
2278 if(j != y_offset[y]){
2279 // read a scan line
2280 j = (int)(y_offset[y]);
2281 srcData = (unsigned char *)src.scanLine(j);
2282 (void)memcpy(pixels, srcData, src.width()*sizeof(unsigned char));
2283 }
2284 // sample each column
2285 for(x=0; x < w; ++x){
2286 k = (int)(x_offset[x]);
2287 destData[x] = pixels[k];
2288 }
2289 }
2290 free(pixels);
2291 }
2292 free(x_offset);
2293 free(y_offset);
2294 return(dest);
2295}
2296
2297void OImageEffect::threshold(QImage &img, unsigned int threshold)
2298{
2299 int i, count;
2300 unsigned int *data;
2301 if(img.depth() > 8){ // DirectClass
2302 count = img.width()*img.height();
2303 data = (unsigned int *)img.bits();
2304 }
2305 else{ // PsudeoClass
2306 count = img.numColors();
2307 data = (unsigned int *)img.colorTable();
2308 }
2309 for(i=0; i < count; ++i)
2310 data[i] = intensityValue(data[i]) < threshold ? Qt::black.rgb() : Qt::white.rgb();
2311}
2312
2313QImage OImageEffect::charcoal(QImage &src, double factor)
2314{
2315 QImage dest(src);
2316 dest.detach();
2317 toGray(dest);
2318 dest = edge(dest, factor);
2319 dest = blur(dest, factor);
2320 normalize(dest);
2321 dest.invertPixels(false);
2322 return(dest);
2323}
2324
2325void OImageEffect::hull(const int x_offset, const int y_offset,
2326 const int polarity, const int columns,
2327 const int rows,
2328 unsigned int *f, unsigned int *g)
2329{
2330 int x, y;
2331
2332 unsigned int *p, *q, *r, *s;
2333 unsigned int v;
2334 if(f == NULL || g == NULL)
2335 return;
2336 p=f+(columns+2);
2337 q=g+(columns+2);
2338 r=p+(y_offset*(columns+2)+x_offset);
2339 for (y=0; y < rows; y++){
2340 p++;
2341 q++;
2342 r++;
2343 if(polarity > 0)
2344 for (x=0; x < columns; x++){
2345 v=(*p);
2346 if (*r > v)
2347 v++;
2348 *q=v;
2349 p++;
2350 q++;
2351 r++;
2352 }
2353 else
2354 for(x=0; x < columns; x++){
2355 v=(*p);
2356 if (v > (unsigned int) (*r+1))
2357 v--;
2358 *q=v;
2359 p++;
2360 q++;
2361 r++;
2362 }
2363 p++;
2364 q++;
2365 r++;
2366 }
2367 p=f+(columns+2);
2368 q=g+(columns+2);
2369 r=q+(y_offset*(columns+2)+x_offset);
2370 s=q-(y_offset*(columns+2)+x_offset);
2371 for(y=0; y < rows; y++){
2372 p++;
2373 q++;
2374 r++;
2375 s++;
2376 if(polarity > 0)
2377 for(x=0; x < (int) columns; x++){
2378 v=(*q);
2379 if (((unsigned int) (*s+1) > v) && (*r > v))
2380 v++;
2381 *p=v;
2382 p++;
2383 q++;
2384 r++;
2385 s++;
2386 }
2387 else
2388 for (x=0; x < columns; x++){
2389 v=(*q);
2390 if (((unsigned int) (*s+1) < v) && (*r < v))
2391 v--;
2392 *p=v;
2393 p++;
2394 q++;
2395 r++;
2396 s++;
2397 }
2398 p++;
2399 q++;
2400 r++;
2401 s++;
2402 }
2403}
2404
2405QImage OImageEffect::despeckle(QImage &src)
2406{
2407 int i, j, x, y;
2408 unsigned int *blue_channel, *red_channel, *green_channel, *buffer,
2409 *alpha_channel;
2410 int packets;
2411 static const int
2412 X[4]= {0, 1, 1,-1},
2413 Y[4]= {1, 0, 1, 1};
2414
2415 unsigned int *destData;
2416 QImage dest(src.width(), src.height(), 32);
2417
2418 packets = (src.width()+2)*(src.height()+2);
2419 red_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2420 green_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2421 blue_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2422 alpha_channel = (unsigned int *)calloc(packets, sizeof(unsigned int));
2423 buffer = (unsigned int *)calloc(packets, sizeof(unsigned int));
2424 if(!red_channel || ! green_channel || ! blue_channel || ! alpha_channel ||
2425 !buffer){
2426 free(red_channel);
2427 free(green_channel);
2428 free(blue_channel);
2429 free(alpha_channel);
2430 free(buffer);
2431 return(src);
2432 }
2433
2434 // copy image pixels to color component buffers
2435 j = src.width()+2;
2436 if(src.depth() > 8){ // DirectClass source image
2437 unsigned int *srcData;
2438 for(y=0; y < src.height(); ++y){
2439 srcData = (unsigned int *)src.scanLine(y);
2440 ++j;
2441 for(x=0; x < src.width(); ++x){
2442 red_channel[j] = qRed(srcData[x]);
2443 green_channel[j] = qGreen(srcData[x]);
2444 blue_channel[j] = qBlue(srcData[x]);
2445 alpha_channel[j] = qAlpha(srcData[x]);
2446 ++j;
2447 }
2448 ++j;
2449 }
2450 }
2451 else{ // PsudeoClass source image
2452 unsigned char *srcData;
2453 unsigned int *cTable = src.colorTable();
2454 unsigned int pixel;
2455 for(y=0; y < src.height(); ++y){
2456 srcData = (unsigned char *)src.scanLine(y);
2457 ++j;
2458 for(x=0; x < src.width(); ++x){
2459 pixel = *(cTable+srcData[x]);
2460 red_channel[j] = qRed(pixel);
2461 green_channel[j] = qGreen(pixel);
2462 blue_channel[j] = qBlue(pixel);
2463 alpha_channel[j] = qAlpha(pixel);
2464 ++j;
2465 }
2466 ++j;
2467 }
2468 }
2469 // reduce speckle in red channel
2470 for(i=0; i < 4; i++){
2471 hull(X[i],Y[i],1,src.width(),src.height(),red_channel,buffer);
2472 hull(-X[i],-Y[i],1,src.width(),src.height(),red_channel,buffer);
2473 hull(-X[i],-Y[i],-1,src.width(),src.height(),red_channel,buffer);
2474 hull(X[i],Y[i],-1,src.width(),src.height(),red_channel,buffer);
2475 }
2476 // reduce speckle in green channel
2477 for (i=0; i < packets; i++)
2478 buffer[i]=0;
2479 for (i=0; i < 4; i++){
2480 hull(X[i],Y[i],1,src.width(),src.height(),green_channel,buffer);
2481 hull(-X[i],-Y[i],1,src.width(),src.height(),green_channel,buffer);
2482 hull(-X[i],-Y[i],-1,src.width(),src.height(),green_channel,buffer);
2483 hull(X[i],Y[i],-1,src.width(),src.height(),green_channel,buffer);
2484 }
2485 // reduce speckle in blue channel
2486 for (i=0; i < packets; i++)
2487 buffer[i]=0;
2488 for (i=0; i < 4; i++){
2489 hull(X[i],Y[i],1,src.width(),src.height(),blue_channel,buffer);
2490 hull(-X[i],-Y[i],1,src.width(),src.height(),blue_channel,buffer);
2491 hull(-X[i],-Y[i],-1,src.width(),src.height(),blue_channel,buffer);
2492 hull(X[i],Y[i],-1,src.width(),src.height(),blue_channel,buffer);
2493 }
2494 // copy color component buffers to despeckled image
2495 j = dest.width()+2;
2496 for(y=0; y < dest.height(); ++y)
2497 {
2498 destData = (unsigned int *)dest.scanLine(y);
2499 ++j;
2500 for (x=0; x < dest.width(); ++x)
2501 {
2502 destData[x] = qRgba(red_channel[j], green_channel[j],
2503 blue_channel[j], alpha_channel[j]);
2504 ++j;
2505 }
2506 ++j;
2507 }
2508 free(buffer);
2509 free(red_channel);
2510 free(green_channel);
2511 free(blue_channel);
2512 free(alpha_channel);
2513 return(dest);
2514}
2515
2516unsigned int OImageEffect::generateNoise(unsigned int pixel,
2517 NoiseType noise_type)
2518{
2519#define NoiseEpsilon 1.0e-5
2520#define NoiseMask 0x7fff
2521#define SigmaUniform 4.0
2522#define SigmaGaussian 4.0
2523#define SigmaImpulse 0.10
2524#define SigmaLaplacian 10.0
2525#define SigmaMultiplicativeGaussian 0.5
2526#define SigmaPoisson 0.05
2527#define TauGaussian 20.0
2528
2529 double alpha, beta, sigma, value;
2530 alpha=(double) (rand() & NoiseMask)/NoiseMask;
2531 if (alpha == 0.0)
2532 alpha=1.0;
2533 switch(noise_type){
2534 case UniformNoise:
2535 default:
2536 {
2537 value=(double) pixel+SigmaUniform*(alpha-0.5);
2538 break;
2539 }
2540 case GaussianNoise:
2541 {
2542 double tau;
2543
2544 beta=(double) (rand() & NoiseMask)/NoiseMask;
2545 sigma=sqrt(-2.0*log(alpha))*cos(2.0*M_PI*beta);
2546 tau=sqrt(-2.0*log(alpha))*sin(2.0*M_PI*beta);
2547 value=(double) pixel+
2548 (sqrt((double) pixel)*SigmaGaussian*sigma)+(TauGaussian*tau);
2549 break;
2550 }
2551 case MultiplicativeGaussianNoise:
2552 {
2553 if (alpha <= NoiseEpsilon)
2554 sigma=MaxRGB;
2555 else
2556 sigma=sqrt(-2.0*log(alpha));
2557 beta=(rand() & NoiseMask)/NoiseMask;
2558 value=(double) pixel+
2559 pixel*SigmaMultiplicativeGaussian*sigma*cos(2.0*M_PI*beta);
2560 break;
2561 }
2562 case ImpulseNoise:
2563 {
2564 if (alpha < (SigmaImpulse/2.0))
2565 value=0;
2566 else
2567 if (alpha >= (1.0-(SigmaImpulse/2.0)))
2568 value=MaxRGB;
2569 else
2570 value=pixel;
2571 break;
2572 }
2573 case LaplacianNoise:
2574 {
2575 if (alpha <= 0.5)
2576 {
2577 if (alpha <= NoiseEpsilon)
2578 value=(double) pixel-MaxRGB;
2579 else
2580 value=(double) pixel+SigmaLaplacian*log(2.0*alpha);
2581 break;
2582 }
2583 beta=1.0-alpha;
2584 if (beta <= (0.5*NoiseEpsilon))
2585 value=(double) pixel+MaxRGB;
2586 else
2587 value=(double) pixel-SigmaLaplacian*log(2.0*beta);
2588 break;
2589 }
2590 case PoissonNoise:
2591 {
2592 register int
2593 i;
2594
2595 for (i=0; alpha > exp(-SigmaPoisson*pixel); i++)
2596 {
2597 beta=(double) (rand() & NoiseMask)/NoiseMask;
2598 alpha=alpha*beta;
2599 }
2600 value=i/SigmaPoisson;
2601 break;
2602 }
2603 }
2604 if(value < 0.0)
2605 return(0);
2606 if(value > MaxRGB)
2607 return(MaxRGB);
2608 return((unsigned int) (value+0.5));
2609}
2610
2611QImage OImageEffect::addNoise(QImage &src, NoiseType noise_type)
2612{
2613 int x, y;
2614 QImage dest(src.width(), src.height(), 32);
2615 unsigned int *destData;
2616
2617 if(src.depth() > 8){ // DirectClass source image
2618 unsigned int *srcData;
2619 for(y=0; y < src.height(); ++y){
2620 srcData = (unsigned int *)src.scanLine(y);
2621 destData = (unsigned int *)dest.scanLine(y);
2622 for(x=0; x < src.width(); ++x){
2623 destData[x] = qRgba(generateNoise(qRed(srcData[x]), noise_type),
2624 generateNoise(qGreen(srcData[x]), noise_type),
2625 generateNoise(qBlue(srcData[x]), noise_type),
2626 qAlpha(srcData[x]));
2627 }
2628 }
2629 }
2630 else{ // PsudeoClass source image
2631 unsigned char *srcData;
2632 unsigned int *cTable = src.colorTable();
2633 unsigned int pixel;
2634 for(y=0; y < src.height(); ++y){
2635 srcData = (unsigned char *)src.scanLine(y);
2636 destData = (unsigned int *)dest.scanLine(y);
2637 for(x=0; x < src.width(); ++x){
2638 pixel = *(cTable+srcData[x]);
2639 destData[x] = qRgba(generateNoise(qRed(pixel), noise_type),
2640 generateNoise(qGreen(pixel), noise_type),
2641 generateNoise(qBlue(pixel), noise_type),
2642 qAlpha(pixel));
2643 }
2644 }
2645
2646 }
2647 return(dest);
2648}
2649
2650unsigned int OImageEffect::interpolateColor(QImage *image, double x_offset,
2651 double y_offset,
2652 unsigned int background)
2653{
2654 double alpha, beta;
2655 unsigned int p, q, r, s;
2656 int x, y;
2657
2658 x = (int)x_offset;
2659 y = (int)y_offset;
2660 if((x < -1) || (x >= image->width()) || (y < -1) || (y >= image->height()))
2661 return(background);
2662 if(image->depth() > 8){
2663 if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
2664 unsigned int *t = (unsigned int *)image->scanLine(y);
2665 p = t[x];
2666 q = t[x+1];
2667 r = t[x+image->width()];
2668 s = t[x+image->width()+1];
2669 }
2670 else{
2671 unsigned int *t = (unsigned int *)image->scanLine(y);
2672 p = background;
2673 if((x >= 0) && (y >= 0)){
2674 p = t[x];
2675 }
2676 q = background;
2677 if(((x+1) < image->width()) && (y >= 0)){
2678 q = t[x+1];
2679 }
2680 r = background;
2681 if((x >= 0) && ((y+1) < image->height())){
2682 t = (unsigned int *)image->scanLine(y+1);
2683 r = t[x+image->width()];
2684 }
2685 s = background;
2686 if(((x+1) < image->width()) && ((y+1) < image->height())){
2687 t = (unsigned int *)image->scanLine(y+1);
2688 s = t[x+image->width()+1];
2689 }
2690
2691 }
2692 }
2693 else{
2694 unsigned int *colorTable = (unsigned int *)image->colorTable();
2695 if((x >= 0) && (y >= 0) && (x < (image->width()-1)) && (y < (image->height()-1))) {
2696 unsigned char *t;
2697 t = (unsigned char *)image->scanLine(y);
2698 p = *(colorTable+t[x]);
2699 q = *(colorTable+t[x+1]);
2700 t = (unsigned char *)image->scanLine(y+1);
2701 r = *(colorTable+t[x]);
2702 s = *(colorTable+t[x+1]);
2703 }
2704 else{
2705 unsigned char *t;
2706 p = background;
2707 if((x >= 0) && (y >= 0)){
2708 t = (unsigned char *)image->scanLine(y);
2709 p = *(colorTable+t[x]);
2710 }
2711 q = background;
2712 if(((x+1) < image->width()) && (y >= 0)){
2713 t = (unsigned char *)image->scanLine(y);
2714 q = *(colorTable+t[x+1]);
2715 }
2716 r = background;
2717 if((x >= 0) && ((y+1) < image->height())){
2718 t = (unsigned char *)image->scanLine(y+1);
2719 r = *(colorTable+t[x]);
2720 }
2721 s = background;
2722 if(((x+1) < image->width()) && ((y+1) < image->height())){
2723 t = (unsigned char *)image->scanLine(y+1);
2724 s = *(colorTable+t[x+1]);
2725 }
2726
2727 }
2728
2729 }
2730 x_offset -= floor(x_offset);
2731 y_offset -= floor(y_offset);
2732 alpha = 1.0-x_offset;
2733 beta = 1.0-y_offset;
2734
2735 return(qRgba((unsigned char)(beta*(alpha*qRed(p)+x_offset*qRed(q))+y_offset*(alpha*qRed(r)+x_offset*qRed(s))),
2736 (unsigned char)(beta*(alpha*qGreen(p)+x_offset*qGreen(q))+y_offset*(alpha*qGreen(r)+x_offset*qGreen(s))),
2737 (unsigned char)(beta*(alpha*qBlue(p)+x_offset*qBlue(q))+y_offset*(alpha*qBlue(r)+x_offset*qBlue(s))),
2738 (unsigned char)(beta*(alpha*qAlpha(p)+x_offset*qAlpha(q))+y_offset*(alpha*qAlpha(r)+x_offset*qAlpha(s)))));
2739}
2740
2741QImage OImageEffect::implode(QImage &src, double factor,
2742 unsigned int background)
2743{
2744 double amount, distance, radius;
2745 double x_center, x_distance, x_scale;
2746 double y_center, y_distance, y_scale;
2747 unsigned int *destData;
2748 int x, y;
2749
2750 QImage dest(src.width(), src.height(), 32);
2751
2752 // compute scaling factor
2753 x_scale = 1.0;
2754 y_scale = 1.0;
2755 x_center = (double)0.5*src.width();
2756 y_center = (double)0.5*src.height();
2757 radius=x_center;
2758 if(src.width() > src.height())
2759 y_scale = (double)src.width()/src.height();
2760 else if(src.width() < src.height()){
2761 x_scale = (double) src.height()/src.width();
2762 radius = y_center;
2763 }
2764 amount=factor/10.0;
2765 if(amount >= 0)
2766 amount/=10.0;
2767 if(src.depth() > 8){ // DirectClass source image
2768 unsigned int *srcData;
2769 for(y=0; y < src.height(); ++y){
2770 srcData = (unsigned int *)src.scanLine(y);
2771 destData = (unsigned int *)dest.scanLine(y);
2772 y_distance=y_scale*(y-y_center);
2773 for(x=0; x < src.width(); ++x){
2774 destData[x] = srcData[x];
2775 x_distance = x_scale*(x-x_center);
2776 distance= x_distance*x_distance+y_distance*y_distance;
2777 if(distance < (radius*radius)){
2778 double factor;
2779 // Implode the pixel.
2780 factor=1.0;
2781 if(distance > 0.0)
2782 factor=
2783 pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
2784 destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
2785 factor*y_distance/y_scale+y_center,
2786 background);
2787 }
2788 }
2789 }
2790 }
2791 else{ // PsudeoClass source image
2792 unsigned char *srcData;
2793 unsigned char idx;
2794 unsigned int *cTable = src.colorTable();
2795 for(y=0; y < src.height(); ++y){
2796 srcData = (unsigned char *)src.scanLine(y);
2797 destData = (unsigned int *)dest.scanLine(y);
2798 y_distance=y_scale*(y-y_center);
2799 for(x=0; x < src.width(); ++x){
2800 idx = srcData[x];
2801 destData[x] = cTable[idx];
2802 x_distance = x_scale*(x-x_center);
2803 distance= x_distance*x_distance+y_distance*y_distance;
2804 if(distance < (radius*radius)){
2805 double factor;
2806 // Implode the pixel.
2807 factor=1.0;
2808 if(distance > 0.0)
2809 factor=
2810 pow(sin(0.5000000000000001*M_PI*sqrt(distance)/radius),-amount);
2811 destData[x] = interpolateColor(&src, factor*x_distance/x_scale+x_center,
2812 factor*y_distance/y_scale+y_center,
2813 background);
2814 }
2815 }
2816 }
2817
2818 }
2819 return(dest);
2820}
2821
2822QImage OImageEffect::rotate(QImage &img, RotateDirection r)
2823{
2824 QImage dest;
2825 int x, y;
2826 if(img.depth() > 8){
2827 unsigned int *srcData, *destData;
2828 switch(r){
2829 case Rotate90:
2830 dest.create(img.height(), img.width(), img.depth());
2831 for(y=0; y < img.height(); ++y){
2832 srcData = (unsigned int *)img.scanLine(y);
2833 for(x=0; x < img.width(); ++x){
2834 destData = (unsigned int *)dest.scanLine(x);
2835 destData[img.height()-y-1] = srcData[x];
2836 }
2837 }
2838 break;
2839 case Rotate180:
2840 dest.create(img.width(), img.height(), img.depth());
2841 for(y=0; y < img.height(); ++y){
2842 srcData = (unsigned int *)img.scanLine(y);
2843 destData = (unsigned int *)dest.scanLine(img.height()-y-1);
2844 for(x=0; x < img.width(); ++x)
2845 destData[img.width()-x-1] = srcData[x];
2846 }
2847 break;
2848 case Rotate270:
2849 dest.create(img.height(), img.width(), img.depth());
2850 for(y=0; y < img.height(); ++y){
2851 srcData = (unsigned int *)img.scanLine(y);
2852 for(x=0; x < img.width(); ++x){
2853 destData = (unsigned int *)dest.scanLine(img.width()-x-1);
2854 destData[y] = srcData[x];
2855 }
2856 }
2857 break;
2858 default:
2859 dest = img;
2860 break;
2861 }
2862 }
2863 else{
2864 unsigned char *srcData, *destData;
2865 unsigned int *srcTable, *destTable;
2866 switch(r){
2867 case Rotate90:
2868 dest.create(img.height(), img.width(), img.depth());
2869 dest.setNumColors(img.numColors());
2870 srcTable = (unsigned int *)img.colorTable();
2871 destTable = (unsigned int *)dest.colorTable();
2872 for(x=0; x < img.numColors(); ++x)
2873 destTable[x] = srcTable[x];
2874 for(y=0; y < img.height(); ++y){
2875 srcData = (unsigned char *)img.scanLine(y);
2876 for(x=0; x < img.width(); ++x){
2877 destData = (unsigned char *)dest.scanLine(x);
2878 destData[img.height()-y-1] = srcData[x];
2879 }
2880 }
2881 break;
2882 case Rotate180:
2883 dest.create(img.width(), img.height(), img.depth());
2884 dest.setNumColors(img.numColors());
2885 srcTable = (unsigned int *)img.colorTable();
2886 destTable = (unsigned int *)dest.colorTable();
2887 for(x=0; x < img.numColors(); ++x)
2888 destTable[x] = srcTable[x];
2889 for(y=0; y < img.height(); ++y){
2890 srcData = (unsigned char *)img.scanLine(y);
2891 destData = (unsigned char *)dest.scanLine(img.height()-y-1);
2892 for(x=0; x < img.width(); ++x)
2893 destData[img.width()-x-1] = srcData[x];
2894 }
2895 break;
2896 case Rotate270:
2897 dest.create(img.height(), img.width(), img.depth());
2898 dest.setNumColors(img.numColors());
2899 srcTable = (unsigned int *)img.colorTable();
2900 destTable = (unsigned int *)dest.colorTable();
2901 for(x=0; x < img.numColors(); ++x)
2902 destTable[x] = srcTable[x];
2903 for(y=0; y < img.height(); ++y){
2904 srcData = (unsigned char *)img.scanLine(y);
2905 for(x=0; x < img.width(); ++x){
2906 destData = (unsigned char *)dest.scanLine(img.width()-x-1);
2907 destData[y] = srcData[x];
2908 }
2909 }
2910 break;
2911 default:
2912 dest = img;
2913 break;
2914 }
2915
2916 }
2917 return(dest);
2918}
2919
2920void OImageEffect::solarize(QImage &img, double factor)
2921{
2922 int i, count;
2923 int threshold;
2924 unsigned int *data;
2925
2926 threshold = (int)(factor*(MaxRGB+1)/100.0);
2927 if(img.depth() < 32){
2928 data = (unsigned int *)img.colorTable();
2929 count = img.numColors();
2930 }
2931 else{
2932 data = (unsigned int *)img.bits();
2933 count = img.width()*img.height();
2934 }
2935 for(i=0; i < count; ++i){
2936 data[i] = qRgba(qRed(data[i]) > threshold ? MaxRGB-qRed(data[i]) : qRed(data[i]),
2937 qGreen(data[i]) > threshold ? MaxRGB-qGreen(data[i]) : qGreen(data[i]),
2938 qBlue(data[i]) > threshold ? MaxRGB-qBlue(data[i]) : qBlue(data[i]),
2939 qAlpha(data[i]));
2940 }
2941}
2942
2943QImage OImageEffect::spread(QImage &src, unsigned int amount)
2944{
2945 int quantum, x, y;
2946 int x_distance, y_distance;
2947 if(src.width() < 3 || src.height() < 3)
2948 return(src);
2949 QImage dest(src);
2950 dest.detach();
2951 quantum=(amount+1) >> 1;
2952 if(src.depth() > 8){ // DirectClass source image
2953 unsigned int *p, *q;
2954 for(y=0; y < src.height(); y++){
2955 q = (unsigned int *)dest.scanLine(y);
2956 for(x=0; x < src.width(); x++){
2957 x_distance = x + ((rand() & (amount+1))-quantum);
2958 y_distance = y + ((rand() & (amount+1))-quantum);
2959 x_distance = QMIN(x_distance, src.width()-1);
2960 y_distance = QMIN(y_distance, src.height()-1);
2961 if(x_distance < 0)
2962 x_distance = 0;
2963 if(y_distance < 0)
2964 y_distance = 0;
2965 p = (unsigned int *)src.scanLine(y_distance);
2966 p += x_distance;
2967 *q++=(*p);
2968 }
2969 }
2970 }
2971 else{ // PsudeoClass source image
2972 // just do colortable values
2973 unsigned char *p, *q;
2974 for(y=0; y < src.height(); y++){
2975 q = (unsigned char *)dest.scanLine(y);
2976 for(x=0; x < src.width(); x++){
2977 x_distance = x + ((rand() & (amount+1))-quantum);
2978 y_distance = y + ((rand() & (amount+1))-quantum);
2979 x_distance = QMIN(x_distance, src.width()-1);
2980 y_distance = QMIN(y_distance, src.height()-1);
2981 if(x_distance < 0)
2982 x_distance = 0;
2983 if(y_distance < 0)
2984 y_distance = 0;
2985 p = (unsigned char *)src.scanLine(y_distance);
2986 p += x_distance;
2987 *q++=(*p);
2988 }
2989 }
2990 }
2991 return(dest);
2992}
2993
2994QImage OImageEffect::swirl(QImage &src, double degrees,
2995 unsigned int background)
2996{
2997 double cosine, distance, factor, radius, sine, x_center, x_distance,
2998 x_scale, y_center, y_distance, y_scale;
2999 int x, y;
3000 unsigned int *q;
3001 QImage dest(src.width(), src.height(), 32);
3002
3003 // compute scaling factor
3004 x_center = src.width()/2.0;
3005 y_center = src.height()/2.0;
3006 radius = QMAX(x_center,y_center);
3007 x_scale=1.0;
3008 y_scale=1.0;
3009 if(src.width() > src.height())
3010 y_scale=(double)src.width()/src.height();
3011 else if(src.width() < src.height())
3012 x_scale=(double)src.height()/src.width();
3013 degrees=DegreesToRadians(degrees);
3014 // swirl each row
3015 if(src.depth() > 8){ // DirectClass source image
3016 unsigned int *p;
3017 for(y=0; y < src.height(); y++){
3018 p = (unsigned int *)src.scanLine(y);
3019 q = (unsigned int *)dest.scanLine(y);
3020 y_distance = y_scale*(y-y_center);
3021 for(x=0; x < src.width(); x++){
3022 // determine if the pixel is within an ellipse
3023 *q=(*p);
3024 x_distance = x_scale*(x-x_center);
3025 distance = x_distance*x_distance+y_distance*y_distance;
3026 if (distance < (radius*radius)){
3027 // swirl
3028 factor = 1.0-sqrt(distance)/radius;
3029 sine = sin(degrees*factor*factor);
3030 cosine = cos(degrees*factor*factor);
3031 *q = interpolateColor(&src,
3032 (cosine*x_distance-sine*y_distance)/x_scale+x_center,
3033 (sine*x_distance+cosine*y_distance)/y_scale+y_center,
3034 background);
3035 }
3036 p++;
3037 q++;
3038 }
3039 }
3040 }
3041 else{ // PsudeoClass source image
3042 unsigned char *p;
3043 unsigned int *cTable = (unsigned int *)src.colorTable();
3044 for(y=0; y < src.height(); y++){
3045 p = (unsigned char *)src.scanLine(y);
3046 q = (unsigned int *)dest.scanLine(y);
3047 y_distance = y_scale*(y-y_center);
3048 for(x=0; x < src.width(); x++){
3049 // determine if the pixel is within an ellipse
3050 *q = *(cTable+(*p));
3051 x_distance = x_scale*(x-x_center);
3052 distance = x_distance*x_distance+y_distance*y_distance;
3053 if (distance < (radius*radius)){
3054 // swirl
3055 factor = 1.0-sqrt(distance)/radius;
3056 sine = sin(degrees*factor*factor);
3057 cosine = cos(degrees*factor*factor);
3058 *q = interpolateColor(&src,
3059 (cosine*x_distance-sine*y_distance)/x_scale+x_center,
3060 (sine*x_distance+cosine*y_distance)/y_scale+y_center,
3061 background);
3062 }
3063 p++;
3064 q++;
3065 }
3066 }
3067
3068 }
3069 return(dest);
3070}
3071
3072QImage OImageEffect::wave(QImage &src, double amplitude, double wavelength,
3073 unsigned int background)
3074{
3075 double *sine_map;
3076 int x, y;
3077 unsigned int *q;
3078
3079 QImage dest(src.width(), src.height() + (int)(2*fabs(amplitude)), 32);
3080 // allocate sine map
3081 sine_map = (double *)malloc(dest.width()*sizeof(double));
3082 if(!sine_map)
3083 return(src);
3084 for(x=0; x < dest.width(); ++x)
3085 sine_map[x]=fabs(amplitude)+amplitude*sin((2*M_PI*x)/wavelength);
3086 // wave image
3087 for(y=0; y < dest.height(); ++y){
3088 q = (unsigned int *)dest.scanLine(y);
3089 for (x=0; x < dest.width(); x++){
3090 *q=interpolateColor(&src, x, (int)(y-sine_map[x]), background);
3091 ++q;
3092 }
3093 }
3094 free(sine_map);
3095 return(dest);
3096}
3097
3098QImage OImageEffect::oilPaint(QImage &src, int radius)
3099{
3100 // TODO 8bpp src!
3101 if(src.depth() < 32){
3102 qWarning("Oil Paint source image < 32bpp. Convert before using!");
3103 return(src);
3104 }
3105 int j, k, i, x, y;
3106 unsigned int *histogram;
3107 unsigned int *s;
3108 unsigned int count;
3109
3110 unsigned int *srcData, *destData;
3111
3112 QImage dest(src);
3113 dest.detach();
3114 histogram = (unsigned int *) malloc((MaxRGB+1)*sizeof(unsigned int));
3115 if(!histogram)
3116 return(src);
3117 // paint each row
3118 k=0;
3119 for(y = radius; y < src.height(); ++y){
3120 srcData = (unsigned int *)src.scanLine(y-radius);
3121 destData = (unsigned int *)dest.scanLine(y);
3122 srcData += radius*src.width()+radius;
3123 destData += radius;
3124 for(x=radius; x < src.width()-radius; ++x){
3125 // determine most frequent color
3126 count = 0;
3127 for(i=0; i < MaxRGB+1; ++i)
3128 histogram[i] = 0;
3129 for(i=0; i < radius; ++i){
3130 s = srcData-(radius-1)*src.width()-i-1;
3131 for(j =0; j < (2*i+1); ++j){
3132 k = intensityValue(*s);
3133 histogram[k]++;
3134 if(histogram[k] > count){
3135 *destData = *s;
3136 count = histogram[k];
3137 }
3138 ++s;
3139 }
3140 s = srcData+(radius-i)*src.width()-i-1;
3141 for(j =0; j < (2*i+1); ++j){
3142 k = intensityValue(*s);
3143 histogram[k]++;
3144 if(histogram[k] > count){
3145 *destData = *s;
3146 count = histogram[k];
3147 }
3148 ++s;
3149 }
3150 }
3151 s = srcData-radius;
3152 for(j =0; j < (2*i+1); ++j){
3153 k = intensityValue(*s);
3154 histogram[k]++;
3155 if(histogram[k] > count){
3156 *destData = *s;
3157 count = histogram[k];
3158 }
3159 ++s;
3160 }
3161 ++srcData;
3162 ++destData;
3163 }
3164 }
3165 free(histogram);
3166 return(dest);
3167}
3168
3169//
3170// The following methods work by computing a value from neighboring pixels
3171// (mosfet 12/28/01)
3172//
3173
3174QImage OImageEffect::edge(QImage &src, double factor)
3175{
3176#define Edge(weight) \
3177 total_red+=(weight)*qRed(*s); \
3178 total_green+=(weight)*qGreen(*s); \
3179 total_blue+=(weight)*qBlue(*s); \
3180 total_opacity+=(weight)*qAlpha(*s); \
3181 s++;
3182
3183#define Edge256(weight) \
3184 total_red+=(weight)*qRed(*(cTable+(*s))); \
3185 total_green+=(weight)*qGreen(*(cTable+(*s))); \
3186 total_blue+=(weight)*qBlue(*(cTable+(*s))); \
3187 total_opacity+=(weight)*qAlpha(*(cTable+(*s))); \
3188 s++;
3189
3190 if(src.width() < 3 || src.height() < 3)
3191 return(src);
3192
3193 double total_blue, total_green, total_opacity, total_red, weight;
3194
3195 int x, y;
3196
3197 unsigned int *q;
3198
3199 QImage dest(src.width(), src.height(), 32);
3200 weight=factor/8.0;
3201 if(src.depth() > 8){ // DirectClass source image
3202 unsigned int *p, *s;
3203 for(y=0; y < src.height(); ++y){
3204 p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
3205 q = (unsigned int *)dest.scanLine(y);
3206 // edge detect this row of pixels.
3207 *q++=(*(p+src.width()));
3208 for(x=1; x < src.width()-1; ++x){
3209 // compute weighted average of target pixel color components.
3210 total_red=0.0;
3211 total_green=0.0;
3212 total_blue=0.0;
3213 total_opacity=0.0;
3214 s=p;
3215 Edge(-weight/8); Edge(-weight/8) Edge(-weight/8);
3216 s=p+src.width();
3217 Edge(-weight/8); Edge(weight); Edge(-weight/8);
3218 s=p+2*src.width();
3219 Edge(-weight/8); Edge(-weight/8); Edge(-weight/8);
3220 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
3221 (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
3222 (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
3223 (unsigned char)((total_opacity < 0) ? 0 : (total_opacity > MaxRGB) ? MaxRGB : total_opacity));
3224 p++;
3225 q++;
3226 }
3227 p++;
3228 *q++=(*p);
3229 }
3230 }
3231 else{ // PsudeoClass source image
3232 unsigned char *p, *p2, *p3, *s;
3233 unsigned int *cTable = src.colorTable();
3234 int scanLineIdx;
3235 for(y=0; y < src.height(); ++y){
3236 scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
3237 p = (unsigned char *)src.scanLine(scanLineIdx);
3238 p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
3239 p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
3240 q = (unsigned int *)dest.scanLine(y);
3241 // edge detect this row of pixels.
3242 *q++=(*(cTable+(*p2)));
3243 for(x=1; x < src.width()-1; ++x){
3244 // compute weighted average of target pixel color components.
3245 total_red=0.0;
3246 total_green=0.0;
3247 total_blue=0.0;
3248 total_opacity=0.0;
3249 s=p;
3250 Edge256(-weight/8); Edge256(-weight/8) Edge256(-weight/8);
3251 s=p2;
3252 Edge256(-weight/8); Edge256(weight); Edge256(-weight/8);
3253 s=p3;
3254 Edge256(-weight/8); Edge256(-weight/8); Edge256(-weight/8);
3255 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
3256 (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
3257 (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
3258 (unsigned char)((total_opacity < 0) ? 0 : (total_opacity > MaxRGB) ? MaxRGB : total_opacity));
3259 p++;
3260 p2++;
3261 p3++;
3262 q++;
3263 }
3264 p++;
3265 *q++=(*(cTable+(*p)));
3266 }
3267 }
3268 return(dest);
3269}
3270
3271QImage OImageEffect::sharpen(QImage &src, double factor)
3272{
3273#define Sharpen(weight) \
3274 total_red+=(weight)*qRed(*s); \
3275 total_green+=(weight)*qGreen(*s); \
3276 total_blue+=(weight)*qBlue(*s); \
3277 total_opacity+=(weight)*qAlpha(*s); \
3278 s++;
3279
3280#define Sharpen256(weight) \
3281 total_red+=(weight)*qRed(*(cTable+(*s))); \
3282 total_green+=(weight)*qGreen(*(cTable+(*s))); \
3283 total_blue+=(weight)*qBlue(*(cTable+(*s))); \
3284 total_opacity+=(weight)*qAlpha(*(cTable+(*s))); \
3285 s++;
3286
3287 if(src.width() < 3 || src.height() < 3)
3288 return(src);
3289
3290 double total_blue, total_green, total_opacity, total_red;
3291 double quantum, weight;
3292 unsigned char r, g, b, a;
3293
3294 int x, y;
3295 unsigned int *q;
3296
3297 QImage dest(src.width(), src.height(), 32);
3298 weight = ((100.0-factor)/2.0+13.0);
3299 quantum = QMAX(weight-12.0, 1.0);
3300 if(src.depth() > 8){ // DirectClass source image
3301 unsigned int *p, *s;
3302 for(y=0; y < src.height(); ++y){
3303 p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
3304 q = (unsigned int *)dest.scanLine(y);
3305 // sharpen this row of pixels.
3306 *q++=(*(p+src.width()));
3307 for(x=1; x < src.width()-1; ++x){
3308 // compute weighted average of target pixel color components.
3309 total_red=0.0;
3310 total_green=0.0;
3311 total_blue=0.0;
3312 total_opacity=0.0;
3313 s=p;
3314 Sharpen(-1); Sharpen(-2); Sharpen(-1);
3315 s=p+src.width();
3316 Sharpen(-2); Sharpen(weight); Sharpen(-2);
3317 s=p+2*src.width();
3318 Sharpen(-1); Sharpen(-2); Sharpen(-1);
3319 if(total_red < 0)
3320 r=0;
3321 else if(total_red > (int)(MaxRGB*quantum))
3322 r = (unsigned char)MaxRGB;
3323 else
3324 r = (unsigned char)((total_red+(quantum/2.0))/quantum);
3325
3326 if(total_green < 0)
3327 g = 0;
3328 else if(total_green > (int)(MaxRGB*quantum))
3329 g = (unsigned char)MaxRGB;
3330 else
3331 g = (unsigned char)((total_green+(quantum/2.0))/quantum);
3332
3333 if(total_blue < 0)
3334 b = 0;
3335 else if(total_blue > (int)(MaxRGB*quantum))
3336 b = (unsigned char)MaxRGB;
3337 else
3338 b = (unsigned char)((total_blue+(quantum/2.0))/quantum);
3339
3340 if(total_opacity < 0)
3341 a = 0;
3342 else if(total_opacity > (int)(MaxRGB*quantum))
3343 a = (unsigned char)MaxRGB;
3344 else
3345 a= (unsigned char)((total_opacity+(quantum/2.0))/quantum);
3346
3347 *q = qRgba(r, g, b, a);
3348
3349 p++;
3350 q++;
3351 }
3352 p++;
3353 *q++=(*p);
3354 }
3355 }
3356 else{ // PsudeoClass source image
3357 unsigned char *p, *p2, *p3, *s;
3358 unsigned int *cTable = src.colorTable();
3359 int scanLineIdx;
3360 for(y=0; y < src.height(); ++y){
3361 scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
3362 p = (unsigned char *)src.scanLine(scanLineIdx);
3363 p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
3364 p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
3365 q = (unsigned int *)dest.scanLine(y);
3366 // sharpen this row of pixels.
3367 *q++=(*(cTable+(*p2)));
3368 for(x=1; x < src.width()-1; ++x){
3369 // compute weighted average of target pixel color components.
3370 total_red=0.0;
3371 total_green=0.0;
3372 total_blue=0.0;
3373 total_opacity=0.0;
3374 s=p;
3375 Sharpen256(-1); Sharpen256(-2); Sharpen256(-1);
3376 s=p2;
3377 Sharpen256(-2); Sharpen256(weight); Sharpen256(-2);
3378 s=p3;
3379 Sharpen256(-1); Sharpen256(-2); Sharpen256(-1);
3380 if(total_red < 0)
3381 r=0;
3382 else if(total_red > (int)(MaxRGB*quantum))
3383 r = (unsigned char)MaxRGB;
3384 else
3385 r = (unsigned char)((total_red+(quantum/2.0))/quantum);
3386
3387 if(total_green < 0)
3388 g = 0;
3389 else if(total_green > (int)(MaxRGB*quantum))
3390 g = (unsigned char)MaxRGB;
3391 else
3392 g = (unsigned char)((total_green+(quantum/2.0))/quantum);
3393
3394 if(total_blue < 0)
3395 b = 0;
3396 else if(total_blue > (int)(MaxRGB*quantum))
3397 b = (unsigned char)MaxRGB;
3398 else
3399 b = (unsigned char)((total_blue+(quantum/2.0))/quantum);
3400
3401 if(total_opacity < 0)
3402 a = 0;
3403 else if(total_opacity > (int)(MaxRGB*quantum))
3404 a = (unsigned char)MaxRGB;
3405 else
3406 a = (unsigned char)((total_opacity+(quantum/2.0))/quantum);
3407
3408 *q = qRgba(r, g, b, a);
3409
3410 p++;
3411 p2++;
3412 p3++;
3413 q++;
3414 }
3415 p++;
3416 *q++=(*(cTable+(*p)));
3417 }
3418 }
3419 return(dest);
3420}
3421
3422QImage OImageEffect::emboss(QImage &src)
3423{
3424#define Emboss(weight) \
3425 total_red+=(weight)*qRed(*s); \
3426 total_green+=(weight)*qGreen(*s); \
3427 total_blue+=(weight)*qBlue(*s); \
3428 s++;
3429
3430#define Emboss256(weight) \
3431 total_red+=(weight)*qRed(*(cTable+(*s))); \
3432 total_green+=(weight)*qGreen(*(cTable+(*s))); \
3433 total_blue+=(weight)*qBlue(*(cTable+(*s))); \
3434 s++;
3435
3436 if(src.width() < 3 || src.height() < 3)
3437 return(src);
3438
3439 double total_blue, total_green, total_red;
3440 int x, y;
3441 unsigned int *q;
3442
3443 QImage dest(src.width(), src.height(), 32);
3444 if(src.depth() > 8){ // DirectClass source image
3445 unsigned int *p, *s;
3446 for(y=0; y < src.height(); ++y){
3447 p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
3448 q = (unsigned int *)dest.scanLine(y);
3449 // emboss this row of pixels.
3450 *q++=(*(p+src.width()));
3451 for(x=1; x < src.width()-1; ++x){
3452 // compute weighted average of target pixel color components.
3453 total_red=0.0;
3454 total_green=0.0;
3455 total_blue=0.0;
3456 s=p;
3457 Emboss(-1); Emboss(-2); Emboss( 0);
3458 s=p+src.width();
3459 Emboss(-2); Emboss( 0); Emboss( 2);
3460 s=p+2*src.width();
3461 Emboss( 0); Emboss( 2); Emboss( 1);
3462 total_red += (MaxRGB+1)/2;
3463 total_green += (MaxRGB+1)/2;
3464 total_blue += (MaxRGB+1)/2;
3465 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
3466 (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
3467 (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
3468 255);
3469 p++;
3470 q++;
3471 }
3472 p++;
3473 *q++=(*p);
3474 }
3475 }
3476 else{ // PsudeoClass source image
3477 unsigned char *p, *p2, *p3, *s;
3478 unsigned int *cTable = src.colorTable();
3479 int scanLineIdx;
3480 for(y=0; y < src.height(); ++y){
3481 scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
3482 p = (unsigned char *)src.scanLine(scanLineIdx);
3483 p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
3484 p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
3485 q = (unsigned int *)dest.scanLine(y);
3486 // emboss this row of pixels.
3487 *q++=(*(cTable+(*p2)));
3488 for(x=1; x < src.width()-1; ++x){
3489 // compute weighted average of target pixel color components.
3490 total_red=0.0;
3491 total_green=0.0;
3492 total_blue=0.0;
3493 s=p;
3494 Emboss256(-1); Emboss256(-2); Emboss256(0);
3495 s=p2;
3496 Emboss256(-2); Emboss256(0); Emboss256(2);
3497 s=p3;
3498 Emboss256(0); Emboss256(2); Emboss256(1);
3499 total_red += (MaxRGB+1)/2;
3500 total_green += (MaxRGB+1)/2;
3501 total_blue += (MaxRGB+1)/2;
3502 *q = qRgba((unsigned char)((total_red < 0) ? 0 : (total_red > MaxRGB) ? MaxRGB : total_red),
3503 (unsigned char)((total_green < 0) ? 0 : (total_green > MaxRGB) ? MaxRGB : total_green),
3504 (unsigned char)((total_blue < 0) ? 0 : (total_blue > MaxRGB) ? MaxRGB : total_blue),
3505 255);
3506 p++;
3507 p2++;
3508 p3++;
3509 q++;
3510 }
3511 p++;
3512 *q++=(*(cTable+(*p)));
3513 }
3514 }
3515 toGray(dest);
3516 normalize(dest);
3517 return(dest);
3518}
3519
3520QImage OImageEffect::shade(QImage &src, bool color_shading, double azimuth,
3521 double elevation)
3522{
3523 struct PointInfo{
3524 double x, y, z;
3525 };
3526
3527 double distance, normal_distance, shade;
3528 int x, y;
3529
3530 struct PointInfo light, normal;
3531
3532 unsigned int *q;
3533
3534 QImage dest(src.width(), src.height(), 32);
3535
3536 azimuth = DegreesToRadians(azimuth);
3537 elevation = DegreesToRadians(elevation);
3538 light.x = MaxRGB*cos(azimuth)*cos(elevation);
3539 light.y = MaxRGB*sin(azimuth)*cos(elevation);
3540 light.z = MaxRGB*sin(elevation);
3541 normal.z= 2*MaxRGB; // constant Z of surface normal
3542
3543 if(src.depth() > 8){ // DirectClass source image
3544 unsigned int *p, *s0, *s1, *s2;
3545 for(y=0; y < src.height(); ++y){
3546 p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
3547 q = (unsigned int *)dest.scanLine(y);
3548 // shade this row of pixels.
3549 *q++=(*(p+src.width()));
3550 p++;
3551 s0 = p;
3552 s1 = p + src.width();
3553 s2 = p + 2*src.width();
3554 for(x=1; x < src.width()-1; ++x){
3555 // determine the surface normal and compute shading.
3556 normal.x=intensityValue(*(s0-1))+intensityValue(*(s1-1))+intensityValue(*(s2-1))-
3557 (double) intensityValue(*(s0+1))-(double) intensityValue(*(s1+1))-
3558 (double) intensityValue(*(s2+1));
3559 normal.y=intensityValue(*(s2-1))+intensityValue(*s2)+intensityValue(*(s2+1))-
3560 (double) intensityValue(*(s0-1))-(double) intensityValue(*s0)-
3561 (double) intensityValue(*(s0+1));
3562 if((normal.x == 0) && (normal.y == 0))
3563 shade=light.z;
3564 else{
3565 shade=0.0;
3566 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3567 if (distance > 0.0){
3568 normal_distance=
3569 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3570 if(fabs(normal_distance) > 0.0000001)
3571 shade=distance/sqrt(normal_distance);
3572 }
3573 }
3574 if(!color_shading){
3575 *q = qRgba((unsigned char)(shade),
3576 (unsigned char)(shade),
3577 (unsigned char)(shade),
3578 qAlpha(*s1));
3579 }
3580 else{
3581 *q = qRgba((unsigned char)((shade*qRed(*s1))/(MaxRGB+1)),
3582 (unsigned char)((shade*qGreen(*s1))/(MaxRGB+1)),
3583 (unsigned char)((shade*qBlue(*s1))/(MaxRGB+1)),
3584 qAlpha(*s1));
3585 }
3586 ++s0;
3587 ++s1;
3588 ++s2;
3589 q++;
3590 }
3591 *q++=(*s1);
3592 }
3593 }
3594 else{ // PsudeoClass source image
3595 unsigned char *p, *s0, *s1, *s2;
3596 int scanLineIdx;
3597 unsigned int *cTable = (unsigned int *)src.colorTable();
3598 for(y=0; y < src.height(); ++y){
3599 scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
3600 p = (unsigned char *)src.scanLine(scanLineIdx);
3601 q = (unsigned int *)dest.scanLine(y);
3602 // shade this row of pixels.
3603 s0 = p;
3604 s1 = (unsigned char *) src.scanLine(scanLineIdx+1);
3605 s2 = (unsigned char *) src.scanLine(scanLineIdx+2);
3606 *q++=(*(cTable+(*s1)));
3607 ++p;
3608 ++s0;
3609 ++s1;
3610 ++s2;
3611 for(x=1; x < src.width()-1; ++x){
3612 // determine the surface normal and compute shading.
3613 normal.x=intensityValue(*(cTable+(*(s0-1))))+intensityValue(*(cTable+(*(s1-1))))+intensityValue(*(cTable+(*(s2-1))))-
3614 (double) intensityValue(*(cTable+(*(s0+1))))-(double) intensityValue(*(cTable+(*(s1+1))))-
3615 (double) intensityValue(*(cTable+(*(s2+1))));
3616 normal.y=intensityValue(*(cTable+(*(s2-1))))+intensityValue(*(cTable+(*s2)))+intensityValue(*(cTable+(*(s2+1))))-
3617 (double) intensityValue(*(cTable+(*(s0-1))))-(double) intensityValue(*(cTable+(*s0)))-
3618 (double) intensityValue(*(cTable+(*(s0+1))));
3619 if((normal.x == 0) && (normal.y == 0))
3620 shade=light.z;
3621 else{
3622 shade=0.0;
3623 distance=normal.x*light.x+normal.y*light.y+normal.z*light.z;
3624 if (distance > 0.0){
3625 normal_distance=
3626 normal.x*normal.x+normal.y*normal.y+normal.z*normal.z;
3627 if(fabs(normal_distance) > 0.0000001)
3628 shade=distance/sqrt(normal_distance);
3629 }
3630 }
3631 if(!color_shading){
3632 *q = qRgba((unsigned char)(shade),
3633 (unsigned char)(shade),
3634 (unsigned char)(shade),
3635 qAlpha(*(cTable+(*s1))));
3636 }
3637 else{
3638 *q = qRgba((unsigned char)((shade*qRed(*(cTable+(*s1))))/(MaxRGB+1)),
3639 (unsigned char)((shade*qGreen(*(cTable+(*s1))))/(MaxRGB+1)),
3640 (unsigned char)((shade*qBlue(*(cTable+(*s1))))/(MaxRGB+1)),
3641 qAlpha(*s1));
3642 }
3643 ++s0;
3644 ++s1;
3645 ++s2;
3646 q++;
3647 }
3648 *q++=(*(cTable+(*s1)));
3649 }
3650 }
3651 return(dest);
3652}
3653
3654QImage OImageEffect::blur(QImage &src, double factor)
3655{
3656
3657#define Blur(weight) \
3658 total_red+=(weight)*qRed(*s); \
3659 total_green+=(weight)*qGreen(*s); \
3660 total_blue+=(weight)*qBlue(*s); \
3661 total_opacity+=(weight)*qAlpha(*s); \
3662 s++;
3663
3664#define Blur256(weight) \
3665 total_red+=(weight)*qRed(*(cTable+(*s))); \
3666 total_green+=(weight)*qGreen(*(cTable+(*s))); \
3667 total_blue+=(weight)*qBlue(*(cTable+(*s))); \
3668 total_opacity+=(weight)*qAlpha(*(cTable+(*s))); \
3669 s++;
3670
3671 if(src.width() < 3 || src.height() < 3)
3672 return(src);
3673
3674 double quantum, total_blue, total_green, total_opacity, total_red, weight;
3675
3676 int x, y;
3677 unsigned int *q;
3678
3679 QImage dest(src.width(), src.height(), 32);
3680 weight=((100.0-factor)/2)+1;
3681 quantum = QMAX(weight+12.0, 1.0);
3682 if(src.depth() > 8){ // DirectClass source image
3683 unsigned int *p, *s;
3684 for(y=0; y < src.height(); ++y){
3685 p = (unsigned int *)src.scanLine(QMIN(QMAX(y-1,0),src.height()-3));
3686 q = (unsigned int *)dest.scanLine(y);
3687 // blur this row of pixels.
3688 *q++=(*(p+src.width()));
3689 for(x=1; x < src.width()-1; ++x){
3690 // compute weighted average of target pixel color components.
3691 total_red=0.0;
3692 total_green=0.0;
3693 total_blue=0.0;
3694 total_opacity=0.0;
3695 s=p;
3696 Blur(1); Blur(2); Blur(1);
3697 s=p+src.width();
3698 Blur(2); Blur(weight); Blur(2);
3699 s=p+2*src.width();
3700 Blur(1); Blur(2); Blur(1);
3701 *q = qRgba((unsigned char)((total_red+(quantum/2))/quantum),
3702 (unsigned char)((total_green+(quantum/2))/quantum),
3703 (unsigned char)((total_blue+(quantum/2))/quantum),
3704 (unsigned char)((total_opacity+(quantum/2))/quantum));
3705 p++;
3706 q++;
3707 }
3708 p++;
3709 *q++=(*p);
3710 }
3711 }
3712 else{ // PsudeoClass source image
3713 unsigned char *p, *p2, *p3, *s;
3714 unsigned int *cTable = src.colorTable();
3715 int scanLineIdx;
3716 for(y=0; y < src.height(); ++y){
3717 scanLineIdx = QMIN(QMAX(y-1,0),src.height()-3);
3718 p = (unsigned char *)src.scanLine(scanLineIdx);
3719 p2 = (unsigned char *)src.scanLine(scanLineIdx+1);
3720 p3 = (unsigned char *)src.scanLine(scanLineIdx+2);
3721 q = (unsigned int *)dest.scanLine(y);
3722 // blur this row of pixels.
3723 *q++=(*(cTable+(*p2)));
3724 for(x=1; x < src.width()-1; ++x){
3725 // compute weighted average of target pixel color components.
3726 total_red=0.0;
3727 total_green=0.0;
3728 total_blue=0.0;
3729 total_opacity=0.0;
3730 s=p;
3731 Blur256(1); Blur256(2); Blur256(1);
3732 s=p2;
3733 Blur256(2); Blur256(weight); Blur256(2);
3734 s=p3;
3735 Blur256(1); Blur256(2); Blur256(1);
3736 *q = qRgba((unsigned char)((total_red+(quantum/2))/quantum),
3737 (unsigned char)((total_green+(quantum/2))/quantum),
3738 (unsigned char)((total_blue+(quantum/2))/quantum),
3739 (unsigned char)((total_opacity+(quantum/2))/quantum));
3740 p++;
3741 p2++;
3742 p3++;
3743 q++;
3744 }
3745 p++;
3746 *q++=(*(cTable+(*p)));
3747 }
3748 }
3749 return(dest);
3750}
3751
3752// High quality, expensive HSV contrast. You can do a faster one by just
3753// taking a grayscale threshold (ie: 128) and incrementing RGB color
3754// channels above it and decrementing those below it, but this gives much
3755// better results. (mosfet 12/28/01)
3756void OImageEffect::contrastHSV(QImage &img, bool sharpen)
3757{
3758 int i, sign;
3759 unsigned int *data;
3760 int count;
3761 double brightness, scale, theta;
3762 QColor c;
3763 int h, s, v;
3764
3765 sign = sharpen ? 1 : -1;
3766 scale=0.5000000000000001;
3767 if(img.depth() > 8){
3768 count = img.width()*img.height();
3769 data = (unsigned int *)img.bits();
3770 }
3771 else{
3772 count = img.numColors();
3773 data = (unsigned int *)img.colorTable();
3774 }
3775 for(i=0; i < count; ++i){
3776 c.setRgb(data[i]);
3777 c.hsv(&h, &s, &v);
3778 brightness = v/255.0;
3779 theta=(brightness-0.5)*M_PI;
3780 brightness+=scale*(((scale*((sin(theta)+1.0)))-brightness)*sign);
3781 if (brightness > 1.0)
3782 brightness=1.0;
3783 else
3784 if (brightness < 0)
3785 brightness=0.0;
3786 v = (int)(brightness*255);
3787 c.setHsv(h, s, v);
3788 data[i] = qRgba(c.red(), c.green(), c.blue(), qAlpha(data[i]));
3789 }
3790}
3791
3792
3793
3794