summaryrefslogtreecommitdiff
Side-by-side diff
Diffstat (more/less context) (show whitespace changes)
-rw-r--r--libopie2/opieui/oimageeffect.cpp52
1 files changed, 13 insertions, 39 deletions
diff --git a/libopie2/opieui/oimageeffect.cpp b/libopie2/opieui/oimageeffect.cpp
index 3c28bbe..2855da6 100644
--- a/libopie2/opieui/oimageeffect.cpp
+++ b/libopie2/opieui/oimageeffect.cpp
@@ -1,197 +1,195 @@
/* This file is part of the KDE libraries
Copyright (C) 1998, 1999, 2001, 2002 Daniel M. Duley <mosfet@kde.org>
(C) 1998, 1999 Christian Tibirna <ctibirna@total.net>
(C) 1998, 1999 Dirk A. Mueller <mueller@kde.org>
(C) 2000 Josef Weidendorfer <weidendo@in.tum.de>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// $Id$
#include <math.h>
#include <qimage.h>
#include <stdlib.h>
#include <iostream>
#include "oimageeffect.h"
#define MaxRGB 255L
#define DegreesToRadians(x) ((x)*M_PI/180.0)
using namespace std;
inline unsigned int intensityValue(unsigned int color)
{
return((unsigned int)((0.299*qRed(color) +
0.587*qGreen(color) +
0.1140000000000001*qBlue(color))));
}
//======================================================================
//
// Gradient effects
//
//======================================================================
QImage OImageEffect::gradient(const QSize &size, const QColor &ca,
const QColor &cb, GradientType eff, int ncols)
{
int rDiff, gDiff, bDiff;
int rca, gca, bca, rcb, gcb, bcb;
QImage image(size, 32);
if (size.width() == 0 || size.height() == 0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::gradient: invalid image" << endl;
-#endif
+ qDebug( "WARNING: OImageEffect::gradient: invalid image" );
return image;
}
register int x, y;
rDiff = (rcb = cb.red()) - (rca = ca.red());
gDiff = (gcb = cb.green()) - (gca = ca.green());
bDiff = (bcb = cb.blue()) - (bca = ca.blue());
if( eff == VerticalGradient || eff == HorizontalGradient ){
uint *p;
uint rgb;
register int rl = rca << 16;
register int gl = gca << 16;
register int bl = bca << 16;
if( eff == VerticalGradient ) {
int rcdelta = ((1<<16) / size.height()) * rDiff;
int gcdelta = ((1<<16) / size.height()) * gDiff;
int bcdelta = ((1<<16) / size.height()) * bDiff;
for ( y = 0; y < size.height(); y++ ) {
p = (uint *) image.scanLine(y);
rl += rcdelta;
gl += gcdelta;
bl += bcdelta;
rgb = qRgb( (rl>>16), (gl>>16), (bl>>16) );
for( x = 0; x < size.width(); x++ ) {
*p = rgb;
p++;
}
}
}
else { // must be HorizontalGradient
unsigned int *o_src = (unsigned int *)image.scanLine(0);
unsigned int *src = o_src;
int rcdelta = ((1<<16) / size.width()) * rDiff;
int gcdelta = ((1<<16) / size.width()) * gDiff;
int bcdelta = ((1<<16) / size.width()) * bDiff;
for( x = 0; x < size.width(); x++) {
rl += rcdelta;
gl += gcdelta;
bl += bcdelta;
*src++ = qRgb( (rl>>16), (gl>>16), (bl>>16));
}
src = o_src;
// Believe it or not, manually copying in a for loop is faster
// than calling memcpy for each scanline (on the order of ms...).
// I think this is due to the function call overhead (mosfet).
for (y = 1; y < size.height(); ++y) {
p = (unsigned int *)image.scanLine(y);
src = o_src;
for(x=0; x < size.width(); ++x)
*p++ = *src++;
}
}
}
else {
float rfd, gfd, bfd;
float rd = rca, gd = gca, bd = bca;
unsigned char *xtable[3];
unsigned char *ytable[3];
unsigned int w = size.width(), h = size.height();
xtable[0] = new unsigned char[w];
xtable[1] = new unsigned char[w];
xtable[2] = new unsigned char[w];
ytable[0] = new unsigned char[h];
ytable[1] = new unsigned char[h];
ytable[2] = new unsigned char[h];
w*=2, h*=2;
if ( eff == DiagonalGradient || eff == CrossDiagonalGradient) {
// Diagonal dgradient code inspired by BlackBox (mosfet)
// BlackBox dgradient is (C) Brad Hughes, <bhughes@tcac.net> and
// Mike Cole <mike@mydot.com>.
rfd = (float)rDiff/w;
gfd = (float)gDiff/w;
bfd = (float)bDiff/w;
int dir;
for (x = 0; x < size.width(); x++, rd+=rfd, gd+=gfd, bd+=bfd) {
dir = eff == DiagonalGradient? x : size.width() - x - 1;
xtable[0][dir] = (unsigned char) rd;
xtable[1][dir] = (unsigned char) gd;
xtable[2][dir] = (unsigned char) bd;
}
rfd = (float)rDiff/h;
gfd = (float)gDiff/h;
bfd = (float)bDiff/h;
rd = gd = bd = 0;
for (y = 0; y < size.height(); y++, rd+=rfd, gd+=gfd, bd+=bfd) {
ytable[0][y] = (unsigned char) rd;
ytable[1][y] = (unsigned char) gd;
ytable[2][y] = (unsigned char) bd;
}
for (y = 0; y < size.height(); y++) {
unsigned int *scanline = (unsigned int *)image.scanLine(y);
for (x = 0; x < size.width(); x++) {
scanline[x] = qRgb(xtable[0][x] + ytable[0][y],
xtable[1][x] + ytable[1][y],
xtable[2][x] + ytable[2][y]);
}
}
}
else if (eff == RectangleGradient ||
@@ -227,1191 +225,1169 @@ QImage OImageEffect::gradient(const QSize &size, const QColor &ca,
bd = (float)bDiff/2;
for (y = 0; y < size.height(); y++, rd-=rfd, gd-=gfd, bd-=bfd)
{
ytable[0][y] = (unsigned char) abs((int)rd);
ytable[1][y] = (unsigned char) abs((int)gd);
ytable[2][y] = (unsigned char) abs((int)bd);
}
unsigned int rgb;
int h = (size.height()+1)>>1;
for (y = 0; y < h; y++) {
unsigned int *sl1 = (unsigned int *)image.scanLine(y);
unsigned int *sl2 = (unsigned int *)image.scanLine(QMAX(size.height()-y-1, y));
int w = (size.width()+1)>>1;
int x2 = size.width()-1;
for (x = 0; x < w; x++, x2--) {
rgb = 0;
if (eff == PyramidGradient) {
rgb = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
gcb-gSign*(xtable[1][x]+ytable[1][y]),
bcb-bSign*(xtable[2][x]+ytable[2][y]));
}
if (eff == RectangleGradient) {
rgb = qRgb(rcb - rSign *
QMAX(xtable[0][x], ytable[0][y]) * 2,
gcb - gSign *
QMAX(xtable[1][x], ytable[1][y]) * 2,
bcb - bSign *
QMAX(xtable[2][x], ytable[2][y]) * 2);
}
if (eff == PipeCrossGradient) {
rgb = qRgb(rcb - rSign *
QMIN(xtable[0][x], ytable[0][y]) * 2,
gcb - gSign *
QMIN(xtable[1][x], ytable[1][y]) * 2,
bcb - bSign *
QMIN(xtable[2][x], ytable[2][y]) * 2);
}
if (eff == EllipticGradient) {
rgb = qRgb(rcb - rSign *
(int)sqrt((xtable[0][x]*xtable[0][x] +
ytable[0][y]*ytable[0][y])*2.0),
gcb - gSign *
(int)sqrt((xtable[1][x]*xtable[1][x] +
ytable[1][y]*ytable[1][y])*2.0),
bcb - bSign *
(int)sqrt((xtable[2][x]*xtable[2][x] +
ytable[2][y]*ytable[2][y])*2.0));
}
sl1[x] = sl2[x] = rgb;
sl1[x2] = sl2[x2] = rgb;
}
}
}
delete [] xtable[0];
delete [] xtable[1];
delete [] xtable[2];
delete [] ytable[0];
delete [] ytable[1];
delete [] ytable[2];
}
// dither if necessary
if (ncols && (QPixmap::defaultDepth() < 15 )) {
if ( ncols < 2 || ncols > 256 )
ncols = 3;
QColor *dPal = new QColor[ncols];
for (int i=0; i<ncols; i++) {
dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
gca + gDiff * i / ( ncols - 1 ),
bca + bDiff * i / ( ncols - 1 ) );
}
dither(image, dPal, ncols);
delete [] dPal;
}
return image;
}
// -----------------------------------------------------------------------------
//CT this was (before Dirk A. Mueller's speedup changes)
// merely the same code as in the above method, but it's supposedly
// way less performant since it introduces a lot of supplementary tests
// and simple math operations for the calculus of the balance.
// (surprizingly, it isn't less performant, in the contrary :-)
// Yes, I could have merged them, but then the excellent performance of
// the balanced code would suffer with no other gain than a mere
// source code and byte code size economy.
QImage OImageEffect::unbalancedGradient(const QSize &size, const QColor &ca,
const QColor &cb, GradientType eff, int xfactor, int yfactor,
int ncols)
{
int dir; // general parameter used for direction switches
bool _xanti = false , _yanti = false;
if (xfactor < 0) _xanti = true; // negative on X direction
if (yfactor < 0) _yanti = true; // negative on Y direction
xfactor = abs(xfactor);
yfactor = abs(yfactor);
if (!xfactor) xfactor = 1;
if (!yfactor) yfactor = 1;
if (xfactor > 200 ) xfactor = 200;
if (yfactor > 200 ) yfactor = 200;
// float xbal = xfactor/5000.;
// float ybal = yfactor/5000.;
float xbal = xfactor/30./size.width();
float ybal = yfactor/30./size.height();
float rat;
int rDiff, gDiff, bDiff;
int rca, gca, bca, rcb, gcb, bcb;
QImage image(size, 32);
if (size.width() == 0 || size.height() == 0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::unbalancedGradient : invalid image\n";
-#endif
+ qDebug( "WARNING: OImageEffect::unbalancedGradient : invalid image" );
return image;
}
register int x, y;
unsigned int *scanline;
rDiff = (rcb = cb.red()) - (rca = ca.red());
gDiff = (gcb = cb.green()) - (gca = ca.green());
bDiff = (bcb = cb.blue()) - (bca = ca.blue());
if( eff == VerticalGradient || eff == HorizontalGradient){
QColor cRow;
uint *p;
uint rgbRow;
if( eff == VerticalGradient) {
for ( y = 0; y < size.height(); y++ ) {
dir = _yanti ? y : size.height() - 1 - y;
p = (uint *) image.scanLine(dir);
rat = 1 - exp( - (float)y * ybal );
cRow.setRgb( rcb - (int) ( rDiff * rat ),
gcb - (int) ( gDiff * rat ),
bcb - (int) ( bDiff * rat ) );
rgbRow = cRow.rgb();
for( x = 0; x < size.width(); x++ ) {
*p = rgbRow;
p++;
}
}
}
else {
unsigned int *src = (unsigned int *)image.scanLine(0);
for(x = 0; x < size.width(); x++ )
{
dir = _xanti ? x : size.width() - 1 - x;
rat = 1 - exp( - (float)x * xbal );
src[dir] = qRgb(rcb - (int) ( rDiff * rat ),
gcb - (int) ( gDiff * rat ),
bcb - (int) ( bDiff * rat ));
}
// Believe it or not, manually copying in a for loop is faster
// than calling memcpy for each scanline (on the order of ms...).
// I think this is due to the function call overhead (mosfet).
for(y = 1; y < size.height(); ++y)
{
scanline = (unsigned int *)image.scanLine(y);
for(x=0; x < size.width(); ++x)
scanline[x] = src[x];
}
}
}
else {
int w=size.width(), h=size.height();
unsigned char *xtable[3];
unsigned char *ytable[3];
xtable[0] = new unsigned char[w];
xtable[1] = new unsigned char[w];
xtable[2] = new unsigned char[w];
ytable[0] = new unsigned char[h];
ytable[1] = new unsigned char[h];
ytable[2] = new unsigned char[h];
if ( eff == DiagonalGradient || eff == CrossDiagonalGradient)
{
for (x = 0; x < w; x++) {
dir = _xanti ? x : w - 1 - x;
rat = 1 - exp( - (float)x * xbal );
xtable[0][dir] = (unsigned char) ( rDiff/2 * rat );
xtable[1][dir] = (unsigned char) ( gDiff/2 * rat );
xtable[2][dir] = (unsigned char) ( bDiff/2 * rat );
}
for (y = 0; y < h; y++) {
dir = _yanti ? y : h - 1 - y;
rat = 1 - exp( - (float)y * ybal );
ytable[0][dir] = (unsigned char) ( rDiff/2 * rat );
ytable[1][dir] = (unsigned char) ( gDiff/2 * rat );
ytable[2][dir] = (unsigned char) ( bDiff/2 * rat );
}
for (y = 0; y < h; y++) {
unsigned int *scanline = (unsigned int *)image.scanLine(y);
for (x = 0; x < w; x++) {
scanline[x] = qRgb(rcb - (xtable[0][x] + ytable[0][y]),
gcb - (xtable[1][x] + ytable[1][y]),
bcb - (xtable[2][x] + ytable[2][y]));
}
}
}
else if (eff == RectangleGradient ||
eff == PyramidGradient ||
eff == PipeCrossGradient ||
eff == EllipticGradient)
{
int rSign = rDiff>0? 1: -1;
int gSign = gDiff>0? 1: -1;
int bSign = bDiff>0? 1: -1;
for (x = 0; x < w; x++)
{
dir = _xanti ? x : w - 1 - x;
rat = 1 - exp( - (float)x * xbal );
xtable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
xtable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
xtable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
}
for (y = 0; y < h; y++)
{
dir = _yanti ? y : h - 1 - y;
rat = 1 - exp( - (float)y * ybal );
ytable[0][dir] = (unsigned char) abs((int)(rDiff*(0.5-rat)));
ytable[1][dir] = (unsigned char) abs((int)(gDiff*(0.5-rat)));
ytable[2][dir] = (unsigned char) abs((int)(bDiff*(0.5-rat)));
}
for (y = 0; y < h; y++) {
unsigned int *scanline = (unsigned int *)image.scanLine(y);
for (x = 0; x < w; x++) {
if (eff == PyramidGradient)
{
scanline[x] = qRgb(rcb-rSign*(xtable[0][x]+ytable[0][y]),
gcb-gSign*(xtable[1][x]+ytable[1][y]),
bcb-bSign*(xtable[2][x]+ytable[2][y]));
}
if (eff == RectangleGradient)
{
scanline[x] = qRgb(rcb - rSign *
QMAX(xtable[0][x], ytable[0][y]) * 2,
gcb - gSign *
QMAX(xtable[1][x], ytable[1][y]) * 2,
bcb - bSign *
QMAX(xtable[2][x], ytable[2][y]) * 2);
}
if (eff == PipeCrossGradient)
{
scanline[x] = qRgb(rcb - rSign *
QMIN(xtable[0][x], ytable[0][y]) * 2,
gcb - gSign *
QMIN(xtable[1][x], ytable[1][y]) * 2,
bcb - bSign *
QMIN(xtable[2][x], ytable[2][y]) * 2);
}
if (eff == EllipticGradient)
{
scanline[x] = qRgb(rcb - rSign *
(int)sqrt((xtable[0][x]*xtable[0][x] +
ytable[0][y]*ytable[0][y])*2.0),
gcb - gSign *
(int)sqrt((xtable[1][x]*xtable[1][x] +
ytable[1][y]*ytable[1][y])*2.0),
bcb - bSign *
(int)sqrt((xtable[2][x]*xtable[2][x] +
ytable[2][y]*ytable[2][y])*2.0));
}
}
}
}
if (ncols && (QPixmap::defaultDepth() < 15 )) {
if ( ncols < 2 || ncols > 256 )
ncols = 3;
QColor *dPal = new QColor[ncols];
for (int i=0; i<ncols; i++) {
dPal[i].setRgb ( rca + rDiff * i / ( ncols - 1 ),
gca + gDiff * i / ( ncols - 1 ),
bca + bDiff * i / ( ncols - 1 ) );
}
dither(image, dPal, ncols);
delete [] dPal;
}
delete [] xtable[0];
delete [] xtable[1];
delete [] xtable[2];
delete [] ytable[0];
delete [] ytable[1];
delete [] ytable[2];
}
return image;
}
//======================================================================
//
// Intensity effects
//
//======================================================================
/* This builds a 256 byte unsigned char lookup table with all
* the possible percent values prior to applying the effect, then uses
* integer math for the pixels. For any image larger than 9x9 this will be
* less expensive than doing a float operation on the 3 color components of
* each pixel. (mosfet)
*/
QImage& OImageEffect::intensity(QImage &image, float percent)
{
if (image.width() == 0 || image.height() == 0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::intensity : invalid image\n";
-#endif
+ qDebug( "WARNING: OImageEffect::intensity : invalid image" );
return image;
}
int segColors = image.depth() > 8 ? 256 : image.numColors();
unsigned char *segTbl = new unsigned char[segColors];
int pixels = image.depth() > 8 ? image.width()*image.height() :
image.numColors();
unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
(unsigned int *)image.colorTable();
bool brighten = (percent >= 0);
if(percent < 0)
percent = -percent;
if(brighten){ // keep overflow check out of loops
for(int i=0; i < segColors; ++i){
int tmp = (int)(i*percent);
if(tmp > 255)
tmp = 255;
segTbl[i] = tmp;
}
}
else{
for(int i=0; i < segColors; ++i){
int tmp = (int)(i*percent);
if(tmp < 0)
tmp = 0;
segTbl[i] = tmp;
}
}
if(brighten){ // same here
for(int i=0; i < pixels; ++i){
int r = qRed(data[i]);
int g = qGreen(data[i]);
int b = qBlue(data[i]);
int a = qAlpha(data[i]);
r = r + segTbl[r] > 255 ? 255 : r + segTbl[r];
g = g + segTbl[g] > 255 ? 255 : g + segTbl[g];
b = b + segTbl[b] > 255 ? 255 : b + segTbl[b];
data[i] = qRgba(r, g, b,a);
}
}
else{
for(int i=0; i < pixels; ++i){
int r = qRed(data[i]);
int g = qGreen(data[i]);
int b = qBlue(data[i]);
int a = qAlpha(data[i]);
r = r - segTbl[r] < 0 ? 0 : r - segTbl[r];
g = g - segTbl[g] < 0 ? 0 : g - segTbl[g];
b = b - segTbl[b] < 0 ? 0 : b - segTbl[b];
data[i] = qRgba(r, g, b, a);
}
}
delete [] segTbl;
return image;
}
QImage& OImageEffect::channelIntensity(QImage &image, float percent,
RGBComponent channel)
{
if (image.width() == 0 || image.height() == 0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::channelIntensity : invalid image\n";
-#endif
+ qDebug( "WARNING: OImageEffect::channelIntensity : invalid image" );
return image;
}
int segColors = image.depth() > 8 ? 256 : image.numColors();
unsigned char *segTbl = new unsigned char[segColors];
int pixels = image.depth() > 8 ? image.width()*image.height() :
image.numColors();
unsigned int *data = image.depth() > 8 ? (unsigned int *)image.bits() :
(unsigned int *)image.colorTable();
bool brighten = (percent >= 0);
if(percent < 0)
percent = -percent;
if(brighten){ // keep overflow check out of loops
for(int i=0; i < segColors; ++i){
int tmp = (int)(i*percent);
if(tmp > 255)
tmp = 255;
segTbl[i] = tmp;
}
}
else{
for(int i=0; i < segColors; ++i){
int tmp = (int)(i*percent);
if(tmp < 0)
tmp = 0;
segTbl[i] = tmp;
}
}
if(brighten){ // same here
if(channel == Red){ // and here ;-)
for(int i=0; i < pixels; ++i){
int c = qRed(data[i]);
c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
}
}
if(channel == Green){
for(int i=0; i < pixels; ++i){
int c = qGreen(data[i]);
c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
}
}
else{
for(int i=0; i < pixels; ++i){
int c = qBlue(data[i]);
c = c + segTbl[c] > 255 ? 255 : c + segTbl[c];
data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
}
}
}
else{
if(channel == Red){
for(int i=0; i < pixels; ++i){
int c = qRed(data[i]);
c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
data[i] = qRgba(c, qGreen(data[i]), qBlue(data[i]), qAlpha(data[i]));
}
}
if(channel == Green){
for(int i=0; i < pixels; ++i){
int c = qGreen(data[i]);
c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
data[i] = qRgba(qRed(data[i]), c, qBlue(data[i]), qAlpha(data[i]));
}
}
else{
for(int i=0; i < pixels; ++i){
int c = qBlue(data[i]);
c = c - segTbl[c] < 0 ? 0 : c - segTbl[c];
data[i] = qRgba(qRed(data[i]), qGreen(data[i]), c, qAlpha(data[i]));
}
}
}
delete [] segTbl;
return image;
}
// Modulate an image with an RBG channel of another image
//
QImage& OImageEffect::modulate(QImage &image, QImage &modImage, bool reverse,
ModulationType type, int factor, RGBComponent channel)
{
if (image.width() == 0 || image.height() == 0 ||
modImage.width() == 0 || modImage.height() == 0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::modulate : invalid image\n";
-#endif
+ qDebug( "WARNING: OImageEffect::modulate : invalid image" );
return image;
}
int r, g, b, h, s, v, a;
QColor clr;
int mod=0;
unsigned int x1, x2, y1, y2;
register int x, y;
// for image, we handle only depth 32
if (image.depth()<32) image = image.convertDepth(32);
// for modImage, we handle depth 8 and 32
if (modImage.depth()<8) modImage = modImage.convertDepth(8);
unsigned int *colorTable2 = (modImage.depth()==8) ?
modImage.colorTable():0;
unsigned int *data1, *data2;
unsigned char *data2b;
unsigned int color1, color2;
x1 = image.width(); y1 = image.height();
x2 = modImage.width(); y2 = modImage.height();
for (y = 0; y < (int)y1; y++) {
data1 = (unsigned int *) image.scanLine(y);
data2 = (unsigned int *) modImage.scanLine( y%y2 );
data2b = (unsigned char *) modImage.scanLine( y%y2 );
x=0;
while(x < (int)x1) {
color2 = (colorTable2) ? colorTable2[*data2b] : *data2;
if (reverse) {
color1 = color2;
color2 = *data1;
}
else
color1 = *data1;
if (type == Intensity || type == Contrast) {
r = qRed(color1);
g = qGreen(color1);
b = qBlue(color1);
if (channel != All) {
mod = (channel == Red) ? qRed(color2) :
(channel == Green) ? qGreen(color2) :
(channel == Blue) ? qBlue(color2) :
(channel == Gray) ? qGray(color2) : 0;
mod = mod*factor/50;
}
if (type == Intensity) {
if (channel == All) {
r += r * factor/50 * qRed(color2)/256;
g += g * factor/50 * qGreen(color2)/256;
b += b * factor/50 * qBlue(color2)/256;
}
else {
r += r * mod/256;
g += g * mod/256;
b += b * mod/256;
}
}
else { // Contrast
if (channel == All) {
r += (r-128) * factor/50 * qRed(color2)/128;
g += (g-128) * factor/50 * qGreen(color2)/128;
b += (b-128) * factor/50 * qBlue(color2)/128;
}
else {
r += (r-128) * mod/128;
g += (g-128) * mod/128;
b += (b-128) * mod/128;
}
}
if (r<0) r=0; if (r>255) r=255;
if (g<0) g=0; if (g>255) g=255;
if (b<0) b=0; if (b>255) b=255;
a = qAlpha(*data1);
*data1 = qRgba(r, g, b, a);
}
else if (type == Saturation || type == HueShift) {
clr.setRgb(color1);
clr.hsv(&h, &s, &v);
mod = (channel == Red) ? qRed(color2) :
(channel == Green) ? qGreen(color2) :
(channel == Blue) ? qBlue(color2) :
(channel == Gray) ? qGray(color2) : 0;
mod = mod*factor/50;
if (type == Saturation) {
s -= s * mod/256;
if (s<0) s=0; if (s>255) s=255;
}
else { // HueShift
h += mod;
while(h<0) h+=360;
h %= 360;
}
clr.setHsv(h, s, v);
a = qAlpha(*data1);
*data1 = clr.rgb() | ((uint)(a & 0xff) << 24);
}
data1++; data2++; data2b++; x++;
if ( (x%x2) ==0) { data2 -= x2; data2b -= x2; }
}
}
return image;
}
//======================================================================
//
// Blend effects
//
//======================================================================
// Nice and fast direct pixel manipulation
QImage& OImageEffect::blend(const QColor& clr, QImage& dst, float opacity)
{
if (dst.width() <= 0 || dst.height() <= 0)
return dst;
if (opacity < 0.0 || opacity > 1.0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::blend : invalid opacity. Range [0, 1]\n";
-#endif
+ qDebug( "WARNING: OImageEffect::blend : invalid opacity. Range [0, 1] ");
return dst;
}
int depth = dst.depth();
if (depth != 32)
dst = dst.convertDepth(32);
int pixels = dst.width() * dst.height();
int rcol, gcol, bcol;
clr.rgb(&rcol, &gcol, &bcol);
#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
register unsigned char *data = (unsigned char *)dst.bits() + 1;
#else // BGRA
register unsigned char *data = (unsigned char *)dst.bits();
#endif
for (register int i=0; i<pixels; i++)
{
#ifdef WORDS_BIGENDIAN
*(data++) += (unsigned char)((rcol - *data) * opacity);
*(data++) += (unsigned char)((gcol - *data) * opacity);
*(data++) += (unsigned char)((bcol - *data) * opacity);
#else
*(data++) += (unsigned char)((bcol - *data) * opacity);
*(data++) += (unsigned char)((gcol - *data) * opacity);
*(data++) += (unsigned char)((rcol - *data) * opacity);
#endif
data++; // skip alpha
}
return dst;
}
// Nice and fast direct pixel manipulation
QImage& OImageEffect::blend(QImage& src, QImage& dst, float opacity)
{
if (src.width() <= 0 || src.height() <= 0)
return dst;
if (dst.width() <= 0 || dst.height() <= 0)
return dst;
if (src.width() != dst.width() || src.height() != dst.height()) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::blend : src and destination images are not the same size\n";
-#endif
+ qDebug( "WARNING: OImageEffect::blend : src and destination images are not the same size" );
return dst;
}
if (opacity < 0.0 || opacity > 1.0) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::blend : invalid opacity. Range [0, 1]\n";
-#endif
+ qDebug( "WARNING: OImageEffect::blend : invalid opacity. Range [0, 1]" );
return dst;
}
if (src.depth() != 32) src = src.convertDepth(32);
if (dst.depth() != 32) dst = dst.convertDepth(32);
int pixels = src.width() * src.height();
#ifdef WORDS_BIGENDIAN // ARGB (skip alpha)
register unsigned char *data1 = (unsigned char *)dst.bits() + 1;
register unsigned char *data2 = (unsigned char *)src.bits() + 1;
#else // BGRA
register unsigned char *data1 = (unsigned char *)dst.bits();
register unsigned char *data2 = (unsigned char *)src.bits();
#endif
for (register int i=0; i<pixels; i++)
{
#ifdef WORDS_BIGENDIAN
*(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
*(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
*(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
#else
*(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
*(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
*(data1++) += (unsigned char)((*(data2++) - *data1) * opacity);
#endif
data1++; // skip alpha
data2++;
}
return dst;
}
QImage& OImageEffect::blend(QImage &image, float initial_intensity,
const QColor &bgnd, GradientType eff,
bool anti_dir)
{
if (image.width() == 0 || image.height() == 0 || image.depth()!=32 ) {
-#ifndef NDEBUG
- cerr << "WARNING: OImageEffect::blend : invalid image\n";
-#endif
+ qDebug( "WARNING: OImageEffect::blend : invalid image" );
return image;
}
int r_bgnd = bgnd.red(), g_bgnd = bgnd.green(), b_bgnd = bgnd.blue();
int r, g, b;
int ind;
unsigned int xi, xf, yi, yf;
unsigned int a;
// check the boundaries of the initial intesity param
float unaffected = 1;
if (initial_intensity > 1) initial_intensity = 1;
if (initial_intensity < -1) initial_intensity = -1;
if (initial_intensity < 0) {
unaffected = 1. + initial_intensity;
initial_intensity = 0;
}
float intensity = initial_intensity;
float var = 1. - initial_intensity;
if (anti_dir) {
initial_intensity = intensity = 1.;
var = -var;
}
register int x, y;
unsigned int *data = (unsigned int *)image.bits();
int image_width = image.width(); //Those can't change
int image_height = image.height();
if( eff == VerticalGradient || eff == HorizontalGradient ) {
// set the image domain to apply the effect to
xi = 0, xf = image_width;
yi = 0, yf = image_height;
if (eff == VerticalGradient) {
if (anti_dir) yf = (int)(image_height * unaffected);
else yi = (int)(image_height * (1 - unaffected));
}
else {
if (anti_dir) xf = (int)(image_width * unaffected);
else xi = (int)(image_height * (1 - unaffected));
}
var /= (eff == VerticalGradient?yf-yi:xf-xi);
int ind_base;
for (y = yi; y < (int)yf; y++) {
intensity = eff == VerticalGradient? intensity + var :
initial_intensity;
ind_base = image_width * y ;
for (x = xi; x < (int)xf ; x++) {
if (eff == HorizontalGradient) intensity += var;
ind = x + ind_base;
r = qRed (data[ind]) + (int)(intensity *
(r_bgnd - qRed (data[ind])));
g = qGreen(data[ind]) + (int)(intensity *
(g_bgnd - qGreen(data[ind])));
b = qBlue (data[ind]) + (int)(intensity *
(b_bgnd - qBlue (data[ind])));
if (r > 255) r = 255; if (r < 0 ) r = 0;
if (g > 255) g = 255; if (g < 0 ) g = 0;
if (b > 255) b = 255; if (b < 0 ) b = 0;
a = qAlpha(data[ind]);
data[ind] = qRgba(r, g, b, a);
}
}
}
else if (eff == DiagonalGradient || eff == CrossDiagonalGradient) {
float xvar = var / 2 / image_width; // / unaffected;
float yvar = var / 2 / image_height; // / unaffected;
float tmp;
for (x = 0; x < image_width ; x++) {
tmp = xvar * (eff == DiagonalGradient? x : image.width()-x-1);
ind = x;
for (y = 0; y < image_height ; y++) {
intensity = initial_intensity + tmp + yvar * y;
r = qRed (data[ind]) + (int)(intensity *
(r_bgnd - qRed (data[ind])));
g = qGreen(data[ind]) + (int)(intensity *
(g_bgnd - qGreen(data[ind])));
b = qBlue (data[ind]) + (int)(intensity *
(b_bgnd - qBlue (data[ind])));
if (r > 255) r = 255; if (r < 0 ) r = 0;
if (g > 255) g = 255; if (g < 0 ) g = 0;
if (b > 255) b = 255; if (b < 0 ) b = 0;
a = qAlpha(data[ind]);
data[ind] = qRgba(r, g, b, a);
ind += image_width;
}
}
}
else if (eff == RectangleGradient || eff == EllipticGradient) {
float xvar;
float yvar;
for (x = 0; x < image_width / 2 + image_width % 2; x++) {
xvar = var / image_width * (image_width - x*2/unaffected-1);
for (y = 0; y < image_height / 2 + image_height % 2; y++) {
yvar = var / image_height * (image_height - y*2/unaffected -1);
if (eff == RectangleGradient)
intensity = initial_intensity + QMAX(xvar, yvar);
else
intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
if (intensity > 1) intensity = 1;
if (intensity < 0) intensity = 0;
//NW
ind = x + image_width * y ;
r = qRed (data[ind]) + (int)(intensity *
(r_bgnd - qRed (data[ind])));
g = qGreen(data[ind]) + (int)(intensity *
(g_bgnd - qGreen(data[ind])));
b = qBlue (data[ind]) + (int)(intensity *
(b_bgnd - qBlue (data[ind])));
if (r > 255) r = 255; if (r < 0 ) r = 0;
if (g > 255) g = 255; if (g < 0 ) g = 0;
if (b > 255) b = 255; if (b < 0 ) b = 0;
a = qAlpha(data[ind]);
data[ind] = qRgba(r, g, b, a);
//NE
ind = image_width - x - 1 + image_width * y ;
r = qRed (data[ind]) + (int)(intensity *
(r_bgnd - qRed (data[ind])));
g = qGreen(data[ind]) + (int)(intensity *
(g_bgnd - qGreen(data[ind])));
b = qBlue (data[ind]) + (int)(intensity *
(b_bgnd - qBlue (data[ind])));
if (r > 255) r = 255; if (r < 0 ) r = 0;
if (g > 255) g = 255; if (g < 0 ) g = 0;
if (b > 255) b = 255; if (b < 0 ) b = 0;
a = qAlpha(data[ind]);
data[ind] = qRgba(r, g, b, a);
}
}
//CT loop is doubled because of stupid central row/column issue.
// other solution?
for (x = 0; x < image_width / 2; x++) {
xvar = var / image_width * (image_width - x*2/unaffected-1);
for (y = 0; y < image_height / 2; y++) {
yvar = var / image_height * (image_height - y*2/unaffected -1);
if (eff == RectangleGradient)
intensity = initial_intensity + QMAX(xvar, yvar);
else
intensity = initial_intensity + sqrt(xvar * xvar + yvar * yvar);
if (intensity > 1) intensity = 1;
if (intensity < 0) intensity = 0;
//SW
ind = x + image_width * (image_height - y -1) ;
r = qRed (data[ind]) + (int)(intensity *
(r_bgnd - qRed (data[ind])));
g = qGreen(data[ind]) + (int)(intensity *
(g_bgnd - qGreen(data[ind])));
b = qBlue (data[ind]) + (int)(intensity *
(b_bgnd - qBlue (data[ind])));
if (r > 255) r = 255; if (r < 0 ) r = 0;
if (g > 255) g = 255; if (g < 0 ) g = 0;
if (b > 255) b = 255; if (b < 0 ) b = 0;
a = qAlpha(data[ind]);
data[ind] = qRgba(r, g, b, a);
//SE
ind = image_width-x-1 + image_width * (image_height - y - 1) ;
r = qRed (data[ind]) + (int)(intensity *
(r_bgnd - qRed (data[ind])));
g = qGreen(data[ind]) + (int)(intensity *
(g_bgnd - qGreen(data[ind])));
b = qBlue (data[ind]) + (int)(intensity *
(b_bgnd - qBlue (data[ind])));
if (r > 255) r = 255; if (r < 0 ) r = 0;
if (g > 255) g = 255; if (g < 0 ) g = 0;
if (b > 255) b = 255; if (b < 0 ) b = 0;
a = qAlpha(data[ind]);
data[ind] = qRgba(r, g, b, a);
}
}
}
-#ifndef NDEBUG
- else cerr << "OImageEffect::blend effect not implemented" << endl;
-#endif
+ else qDebug( "OImageEffect::blend effect not implemented" );
return image;
}
// Not very efficient as we create a third big image...
//
QImage& OImageEffect::blend(QImage &image1, QImage &image2,
GradientType gt, int xf, int yf)
{
if (image1.width() == 0 || image1.height() == 0 ||
image2.width() == 0 || image2.height() == 0)
return image1;
QImage image3;
image3 = OImageEffect::unbalancedGradient(image1.size(),
QColor(0,0,0), QColor(255,255,255),
gt, xf, yf, 0);
return blend(image1,image2,image3, Red); // Channel to use is arbitrary
}
// Blend image2 into image1, using an RBG channel of blendImage
//
QImage& OImageEffect::blend(QImage &image1, QImage &image2,
QImage &blendImage, RGBComponent channel)
{
if (image1.width() == 0 || image1.height() == 0 ||
image2.width() == 0 || image2.height() == 0 ||
blendImage.width() == 0 || blendImage.height() == 0) {
-#ifndef NDEBUG
- cerr << "OImageEffect::blend effect invalid image" << endl;
-#endif
+ qDebug( "OImageEffect::blend effect invalid image" );
return image1;
}
int r, g, b;
int ind1, ind2, ind3;
unsigned int x1, x2, x3, y1, y2, y3;
unsigned int a;
register int x, y;
// for image1 and image2, we only handle depth 32
if (image1.depth()<32) image1 = image1.convertDepth(32);
if (image2.depth()<32) image2 = image2.convertDepth(32);
// for blendImage, we handle depth 8 and 32
if (blendImage.depth()<8) blendImage = blendImage.convertDepth(8);
unsigned int *colorTable3 = (blendImage.depth()==8) ?
blendImage.colorTable():0;
unsigned int *data1 = (unsigned int *)image1.bits();
unsigned int *data2 = (unsigned int *)image2.bits();
unsigned int *data3 = (unsigned int *)blendImage.bits();
unsigned char *data3b = (unsigned char *)blendImage.bits();
unsigned int color3;
x1 = image1.width(); y1 = image1.height();
x2 = image2.width(); y2 = image2.height();
x3 = blendImage.width(); y3 = blendImage.height();
for (y = 0; y < (int)y1; y++) {
ind1 = x1*y;
ind2 = x2*(y%y2);
ind3 = x3*(y%y3);
x=0;
while(x < (int)x1) {
color3 = (colorTable3) ? colorTable3[data3b[ind3]] : data3[ind3];
a = (channel == Red) ? qRed(color3) :
(channel == Green) ? qGreen(color3) :
(channel == Blue) ? qBlue(color3) : qGray(color3);
r = (a*qRed(data1[ind1]) + (256-a)*qRed(data2[ind2]))/256;
g = (a*qGreen(data1[ind1]) + (256-a)*qGreen(data2[ind2]))/256;
b = (a*qBlue(data1[ind1]) + (256-a)*qBlue(data2[ind2]))/256;
a = qAlpha(data1[ind1]);
data1[ind1] = qRgba(r, g, b, a);
ind1++; ind2++; ind3++; x++;
if ( (x%x2) ==0) ind2 -= x2;
if ( (x%x3) ==0) ind3 -= x3;
}
}
return image1;
}
//======================================================================
//
// Hash effects
//
//======================================================================
unsigned int OImageEffect::lHash(unsigned int c)
{
unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
unsigned char nr, ng, nb;
nr =(r >> 1) + (r >> 2); nr = nr > r ? 0 : nr;
ng =(g >> 1) + (g >> 2); ng = ng > g ? 0 : ng;
nb =(b >> 1) + (b >> 2); nb = nb > b ? 0 : nb;
return qRgba(nr, ng, nb, a);
}
// -----------------------------------------------------------------------------
unsigned int OImageEffect::uHash(unsigned int c)
{
unsigned char r = qRed(c), g = qGreen(c), b = qBlue(c), a = qAlpha(c);
unsigned char nr, ng, nb;
nr = r + (r >> 3); nr = nr < r ? ~0 : nr;
ng = g + (g >> 3); ng = ng < g ? ~0 : ng;
nb = b + (b >> 3); nb = nb < b ? ~0 : nb;
return qRgba(nr, ng, nb, a);
}
// -----------------------------------------------------------------------------
QImage& OImageEffect::hash(QImage &image, Lighting lite, unsigned int spacing)
{
if (image.width() == 0 || image.height() == 0) {
-#ifndef NDEBUG
- cerr << "OImageEffect::hash effect invalid image" << endl;
-#endif
+ qDebug( "OImageEffect::hash effect invalid image" );
return image;
}
register int x, y;
unsigned int *data = (unsigned int *)image.bits();
unsigned int ind;
//CT no need to do it if not enough space
if ((lite == NorthLite ||
lite == SouthLite)&&
(unsigned)image.height() < 2+spacing) return image;
if ((lite == EastLite ||
lite == WestLite)&&
(unsigned)image.height() < 2+spacing) return image;
if (lite == NorthLite || lite == SouthLite) {
for (y = 0 ; y < image.height(); y = y + 2 + spacing) {
for (x = 0; x < image.width(); x++) {
ind = x + image.width() * y;
data[ind] = lite==NorthLite?uHash(data[ind]):lHash(data[ind]);
ind = ind + image.width();
data[ind] = lite==NorthLite?lHash(data[ind]):uHash(data[ind]);
}
}
}
else if (lite == EastLite || lite == WestLite) {
for (y = 0 ; y < image.height(); y++) {
for (x = 0; x < image.width(); x = x + 2 + spacing) {
ind = x + image.width() * y;
data[ind] = lite==EastLite?uHash(data[ind]):lHash(data[ind]);
ind++;
data[ind] = lite==EastLite?lHash(data[ind]):uHash(data[ind]);
}
}
}
else if (lite == NWLite || lite == SELite) {
for (y = 0 ; y < image.height(); y++) {
for (x = 0;
x < (int)(image.width() - ((y & 1)? 1 : 0) * spacing);
x = x + 2 + spacing) {
ind = x + image.width() * y + ((y & 1)? 1 : 0);
data[ind] = lite==NWLite?uHash(data[ind]):lHash(data[ind]);
ind++;
data[ind] = lite==NWLite?lHash(data[ind]):uHash(data[ind]);
}
}
}
else if (lite == SWLite || lite == NELite) {
for (y = 0 ; y < image.height(); y++) {
for (x = 0 + ((y & 1)? 1 : 0); x < image.width(); x = x + 2 + spacing) {
ind = x + image.width() * y - ((y & 1)? 1 : 0);
data[ind] = lite==SWLite?uHash(data[ind]):lHash(data[ind]);
ind++;
data[ind] = lite==SWLite?lHash(data[ind]):uHash(data[ind]);
}
}
}
return image;
}
//======================================================================
//
// Flatten effects
//
//======================================================================
QImage& OImageEffect::flatten(QImage &img, const QColor &ca,
const QColor &cb, int ncols)
{
if (img.width() == 0 || img.height() == 0)
return img;
// a bitmap is easy...
if (img.depth() == 1) {
img.setColor(0, ca.rgb());
img.setColor(1, cb.rgb());
return img;
}
int r1 = ca.red(); int r2 = cb.red();
int g1 = ca.green(); int g2 = cb.green();
int b1 = ca.blue(); int b2 = cb.blue();
int min = 0, max = 255;
QRgb col;
// Get minimum and maximum greylevel.
if (img.numColors()) {
// pseudocolor
for (int i = 0; i < img.numColors(); i++) {
col = img.color(i);
int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
min = QMIN(min, mean);
max = QMAX(max, mean);
}
} else {
// truecolor
for (int y=0; y < img.height(); y++)
for (int x=0; x < img.width(); x++) {
col = img.pixel(x, y);
int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
min = QMIN(min, mean);
max = QMAX(max, mean);
}
}
// Conversion factors
float sr = ((float) r2 - r1) / (max - min);
float sg = ((float) g2 - g1) / (max - min);
float sb = ((float) b2 - b1) / (max - min);
// Repaint the image
if (img.numColors()) {
for (int i=0; i < img.numColors(); i++) {
col = img.color(i);
int mean = (qRed(col) + qGreen(col) + qBlue(col)) / 3;
int r = (int) (sr * (mean - min) + r1 + 0.5);
int g = (int) (sg * (mean - min) + g1 + 0.5);
@@ -1669,259 +1645,257 @@ QImage& OImageEffect::dither(QImage &img, const QColor *palette, int size)
if (img.width() == 0 || img.height() == 0 ||
palette == 0 || img.depth() <= 8)
return img;
QImage dImage( img.width(), img.height(), 8, size );
int i;
dImage.setNumColors( size );
for ( i = 0; i < size; i++ )
dImage.setColor( i, palette[ i ].rgb() );
int *rerr1 = new int [ img.width() * 2 ];
int *gerr1 = new int [ img.width() * 2 ];
int *berr1 = new int [ img.width() * 2 ];
memset( rerr1, 0, sizeof( int ) * img.width() * 2 );
memset( gerr1, 0, sizeof( int ) * img.width() * 2 );
memset( berr1, 0, sizeof( int ) * img.width() * 2 );
int *rerr2 = rerr1 + img.width();
int *gerr2 = gerr1 + img.width();
int *berr2 = berr1 + img.width();
for ( int j = 0; j < img.height(); j++ )
{
uint *ip = (uint * )img.scanLine( j );
uchar *dp = dImage.scanLine( j );
for ( i = 0; i < img.width(); i++ )
{
rerr1[i] = rerr2[i] + qRed( *ip );
rerr2[i] = 0;
gerr1[i] = gerr2[i] + qGreen( *ip );
gerr2[i] = 0;
berr1[i] = berr2[i] + qBlue( *ip );
berr2[i] = 0;
ip++;
}
*dp++ = nearestColor( rerr1[0], gerr1[0], berr1[0], palette, size );
for ( i = 1; i < img.width()-1; i++ )
{
int indx = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
*dp = indx;
int rerr = rerr1[i];
rerr -= palette[indx].red();
int gerr = gerr1[i];
gerr -= palette[indx].green();
int berr = berr1[i];
berr -= palette[indx].blue();
// diffuse red error
rerr1[ i+1 ] += ( rerr * 7 ) >> 4;
rerr2[ i-1 ] += ( rerr * 3 ) >> 4;
rerr2[ i ] += ( rerr * 5 ) >> 4;
rerr2[ i+1 ] += ( rerr ) >> 4;
// diffuse green error
gerr1[ i+1 ] += ( gerr * 7 ) >> 4;
gerr2[ i-1 ] += ( gerr * 3 ) >> 4;
gerr2[ i ] += ( gerr * 5 ) >> 4;
gerr2[ i+1 ] += ( gerr ) >> 4;
// diffuse red error
berr1[ i+1 ] += ( berr * 7 ) >> 4;
berr2[ i-1 ] += ( berr * 3 ) >> 4;
berr2[ i ] += ( berr * 5 ) >> 4;
berr2[ i+1 ] += ( berr ) >> 4;
dp++;
}
*dp = nearestColor( rerr1[i], gerr1[i], berr1[i], palette, size );
}
delete [] rerr1;
delete [] gerr1;
delete [] berr1;
img = dImage;
return img;
}
int OImageEffect::nearestColor( int r, int g, int b, const QColor *palette, int size )
{
if (palette == 0)
return 0;
int dr = palette[0].red() - r;
int dg = palette[0].green() - g;
int db = palette[0].blue() - b;
int minDist = dr*dr + dg*dg + db*db;
int nearest = 0;
for (int i = 1; i < size; i++ )
{
dr = palette[i].red() - r;
dg = palette[i].green() - g;
db = palette[i].blue() - b;
int dist = dr*dr + dg*dg + db*db;
if ( dist < minDist )
{
minDist = dist;
nearest = i;
}
}
return nearest;
}
bool OImageEffect::blend(
const QImage & upper,
const QImage & lower,
QImage & output
)
{
if (
upper.width() > lower.width() ||
upper.height() > lower.height() ||
upper.depth() != 32 ||
lower.depth() != 32
)
{
-#ifndef NDEBUG
- cerr << "OImageEffect::blend : Sizes not correct\n" ;
-#endif
+ qDebug( "OImageEffect::blend : Sizes not correct" );
return false;
}
output = lower.copy();
register uchar *i, *o;
register int a;
register int col;
register int w = upper.width();
int row(upper.height() - 1);
do {
i = upper.scanLine(row);
o = output.scanLine(row);
col = w << 2;
--col;
do {
while (!(a = i[col]) && (col != 3)) {
--col; --col; --col; --col;
}
--col;
o[col] += ((i[col] - o[col]) * a) >> 8;
--col;
o[col] += ((i[col] - o[col]) * a) >> 8;
--col;
o[col] += ((i[col] - o[col]) * a) >> 8;
} while (col--);
} while (row--);
return true;
}
#if 0
// Not yet...
bool OImageEffect::blend(
const QImage & upper,
const QImage & lower,
QImage & output,
const QRect & destRect
)
{
output = lower.copy();
return output;
}
#endif
bool OImageEffect::blend(
int &x, int &y,
const QImage & upper,
const QImage & lower,
QImage & output
)
{
int cx=0, cy=0, cw=upper.width(), ch=upper.height();
if ( upper.width() + x > lower.width() ||
upper.height() + y > lower.height() ||
x < 0 || y < 0 ||
upper.depth() != 32 || lower.depth() != 32 )
{
if ( x > lower.width() || y > lower.height() ) return false;
if ( upper.width()<=0 || upper.height() <= 0 ) return false;
if ( lower.width()<=0 || lower.height() <= 0 ) return false;
if (x<0) {cx=-x; cw+=x; x=0; };
if (cw + x > lower.width()) { cw=lower.width()-x; };
if (y<0) {cy=-y; ch+=y; y=0; };
if (ch + y > lower.height()) { ch=lower.height()-y; };
if ( cx >= upper.width() || cy >= upper.height() ) return true;
if ( cw <= 0 || ch <= 0 ) return true;
}
output.create(cw,ch,32);
// output.setAlphaBuffer(true); // I should do some benchmarks to see if
// this is worth the effort
register QRgb *i, *o, *b;
register int a;
register int j,k;
for (j=0; j<ch; j++)
{
b=reinterpret_cast<QRgb *>(&lower.scanLine(y+j) [ (x+cw) << 2 ]);
i=reinterpret_cast<QRgb *>(&upper.scanLine(cy+j)[ (cx+cw) << 2 ]);
o=reinterpret_cast<QRgb *>(&output.scanLine(j) [ cw << 2 ]);
k=cw-1;
--b; --i; --o;
do
{
while ( !(a=qAlpha(*i)) && k>0 )
{
i--;
// *o=0;
*o=*b;
--o; --b;
k--;
};
// *o=0xFF;
*o = qRgb(qRed(*b) + (((qRed(*i) - qRed(*b)) * a) >> 8),
qGreen(*b) + (((qGreen(*i) - qGreen(*b)) * a) >> 8),
qBlue(*b) + (((qBlue(*i) - qBlue(*b)) * a) >> 8));
--i; --o; --b;
} while (k--);
}
return true;
}
bool OImageEffect::blendOnLower(
int x, int y,
const QImage & upper,
const QImage & lower
)
{
int cx=0, cy=0, cw=upper.width(), ch=upper.height();