You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
859 lines
28 KiB
859 lines
28 KiB
// -*- c++ -*-
|
|
|
|
/*
|
|
* Copyright (C) 2003, Ian Reinhart Geiser <geiseri@kde.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <kdebug.h>
|
|
#include <kglobal.h>
|
|
#include <kjsembed/jsopaqueproxy.h>
|
|
#include <kjsembed/jsbinding.h>
|
|
#include <kjsembed/jsfactory.h>
|
|
#include <kjsembed/jsfactory_imp.h>
|
|
#include <kjsembed/kjsembedpart.h>
|
|
#include <kjsembed/customobject_imp.h>
|
|
#include <qvariant.h>
|
|
#include <qbrush.h>
|
|
|
|
#include "imagefx_plugin.h"
|
|
|
|
namespace KJSEmbed {
|
|
namespace Bindings {
|
|
|
|
|
|
ImageFXLoader::ImageFXLoader( QObject *parent, const char *name, const QStringList &args ) :
|
|
JSBindingPlugin(parent, name, args)
|
|
{
|
|
}
|
|
|
|
KJS::Object ImageFXLoader::createBinding(KJSEmbedPart */*jspart*/, KJS::ExecState *exec, const KJS::List &/*args*/) const
|
|
{
|
|
kdDebug() << "Loading a ImageFX object" << endl;
|
|
JSOpaqueProxy *prx = new JSOpaqueProxy( (int*)0, "ImageFX" );
|
|
|
|
KJS::Object proxyObj(prx);
|
|
ImageFX::addBindings( exec, proxyObj );
|
|
return proxyObj;
|
|
}
|
|
|
|
ImageFX::ImageFX( KJS::ExecState *exec, int id )
|
|
: JSProxyImp(exec), mid(id)
|
|
{
|
|
}
|
|
|
|
ImageFX::~ImageFX()
|
|
{
|
|
}
|
|
|
|
void ImageFX::addBindings( KJS::ExecState *exec, KJS::Object &object ) {
|
|
|
|
kdDebug() << "ImageFX::addBindings()" << endl;
|
|
JSOpaqueProxy *op = JSProxy::toOpaqueProxy( object.imp() );
|
|
if ( !op ) {
|
|
kdWarning() << "ImageFX::addBindings() failed, not a JSOpaqueProxy" << endl;
|
|
return;
|
|
}
|
|
|
|
if ( op->typeName() != "ImageFX" ) {
|
|
kdWarning() << "ImageFX::addBindings() failed, type is " << op->typeName() << endl;
|
|
return;
|
|
}
|
|
|
|
JSProxy::MethodTable methods[] = {
|
|
{ Methodgradient, "gradient" },
|
|
{ MethodunbalancedGradient, "unbalancedGradient" },
|
|
{ MethodblendColor, "blendColor" },
|
|
{ MethodblendImage, "blendImage" },
|
|
{ MethodcomputeDestinationRect, "computeDestinationRect" },
|
|
{ MethodchannelIntensity, "channelIntensity" },
|
|
{ Methodfade, "fade" },
|
|
{ Methodflatten, "flatten" },
|
|
{ Methodhash, "hash" },
|
|
{ Methodintensity, "intensity" },
|
|
{ Methodmodulate, "modulate" },
|
|
{ MethodtoGray, "toGray" },
|
|
{ Methoddesaturate, "desaturate" },
|
|
{ Methoddither, "dither" },
|
|
{ MethodselectedImage, "selectedImage" },
|
|
{ MethodcontrastHSV, "contrastHSV" },
|
|
{ Methodnormalize, "normalize" },
|
|
{ Methodequalize, "equalize" },
|
|
{ Methodthreshold, "threshold" },
|
|
{ Methodsolarize, "solarize" },
|
|
{ Methodemboss, "emboss" },
|
|
{ Methoddespeckle, "despeckle" },
|
|
{ Methodcharcoal, "charcoal" },
|
|
{ Methodcharcoal2, "charcoal2" },
|
|
{ Methodrotate, "rotate" },
|
|
{ Methodsample, "sample" },
|
|
{ MethodaddNoise, "addNoise" },
|
|
{ Methodblur, "blur" },
|
|
{ Methodedge, "edge" },
|
|
{ Methodimplode, "implode" },
|
|
{ MethodoilPaintConvolve, "oilPaintConvolve" },
|
|
{ MethodoilPaint, "MethodoilPaint" },
|
|
{ Methodsharpen, "sharpen" },
|
|
{ Methodsharpen2, "sharpen2" },
|
|
{ Methodspread, "spread" },
|
|
{ Methodshade, "shade" },
|
|
{ Methodswirl, "swirl" },
|
|
{ Methodwave, "wave" },
|
|
{ Methodcontrast, "contrast" },
|
|
{ MethodbumpMap, "bumpmap" },
|
|
{ 0, 0 }
|
|
};
|
|
|
|
int idx = 0;
|
|
do {
|
|
ImageFX *meth = new ImageFX( exec, methods[idx].id );
|
|
object.put( exec , methods[idx].name, KJS::Object(meth) );
|
|
++idx;
|
|
} while( methods[idx].id );
|
|
|
|
//
|
|
// Define the enum constants
|
|
//
|
|
struct EnumValue {
|
|
const char *id;
|
|
int val;
|
|
};
|
|
|
|
EnumValue enums[] = {
|
|
// GradiantType
|
|
{ "VerticalGradient", 0 },
|
|
{ "HorizontalGradient", 1 },
|
|
{ "DiagonalGradient", 2 },
|
|
{ "CrossDiagonalGradient", 3 },
|
|
{ "PyramidGradient", 4 },
|
|
{ "RectangleGradient", 5 },
|
|
{ "PipeCrossGradient", 6 },
|
|
{ "EllipticGradient", 7 },
|
|
// RGBComponent
|
|
{ "Red", 0 },
|
|
{ "Green", 1 },
|
|
{ "Blue", 2 },
|
|
{ "Gray", 3 },
|
|
{ "All", 4 },
|
|
// Lighting
|
|
{ "NorthLite", 0 },
|
|
{ "NWLite", 1 },
|
|
{ "WestLite", 2 },
|
|
{ "SWLite", 3 },
|
|
{ "SouthLite", 4 },
|
|
{ "SELite", 5 },
|
|
{ "EastLite", 6 },
|
|
{ "NELite", 7 },
|
|
// ModulationType
|
|
{ "Intensity", 0 },
|
|
{ "Saturation", 1 },
|
|
{ "HueShift", 2 },
|
|
{ "Contrast", 3 },
|
|
// NoiseType
|
|
{ "UniformNoise", 0 },
|
|
{ "GaussianNoise", 1 },
|
|
{ "MultiplicativeGaussianNoise", 2 },
|
|
{ "ImpulseNoise", 3 },
|
|
{ "LaplacianNoise", 4 },
|
|
{ "PoissonNoise", 5 },
|
|
// RotateDirection
|
|
{ "Rotate90", 0 },
|
|
{ "Rotate180", 1 },
|
|
{ "Rotate270", 2 },
|
|
// BumpmapType
|
|
{ "Linear", 0},
|
|
{ "Spherical", 1},
|
|
{ "Sinuosidal", 2},
|
|
{ 0, 0 }
|
|
};
|
|
|
|
int enumidx = 0;
|
|
do {
|
|
object.put( exec, enums[enumidx].id, KJS::Number(enums[enumidx].val), KJS::ReadOnly );
|
|
++enumidx;
|
|
} while( enums[enumidx].id );
|
|
}
|
|
|
|
KJS::Value ImageFX::call( KJS::ExecState *exec, KJS::Object &self, const KJS::List &args ) {
|
|
|
|
kdDebug() << "ImageFX::call() " << mid << endl;
|
|
JSOpaqueProxy *op = JSProxy::toOpaqueProxy( self.imp() );
|
|
if ( !op ) {
|
|
kdWarning() << "ImageFX::call() failed, not a JSOpaqueProxy" << endl;
|
|
return KJS::Value();
|
|
}
|
|
|
|
if ( op->typeName() != "ImageFX" ) {
|
|
kdWarning() << "ImageFX::call() failed, type is " << op->typeName() << endl;
|
|
return KJS::Value();
|
|
}
|
|
|
|
|
|
KJS::Value retValue = KJS::Value();
|
|
switch ( mid ) {
|
|
case Methodgradient: {
|
|
QSize size = extractQSize(exec, args, 0);
|
|
QColor ca = extractQColor(exec, args, 1);
|
|
QColor cb = extractQColor(exec, args, 2);
|
|
int type = extractInt( exec, args, 3);
|
|
int ncols = extractInt( exec, args, 4);
|
|
QImage img = KImageEffect::gradient(size, ca, cb, (KImageEffect::GradientType)type, ncols);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodunbalancedGradient: {
|
|
QSize size = extractQSize(exec, args, 0);
|
|
QColor ca = extractQColor(exec, args, 1);
|
|
QColor cb = extractQColor(exec, args, 2);
|
|
int type = extractInt( exec, args, 3);
|
|
int xfactor = extractInt( exec, args, 4);
|
|
int yfactor = extractInt( exec, args, 5);
|
|
int ncols = extractInt( exec, args, 6);
|
|
QImage img = KImageEffect::unbalancedGradient(size, ca, cb, (KImageEffect::GradientType)type, xfactor, yfactor, ncols);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodblendColor: {
|
|
QColor clr = extractQColor(exec, args, 0);
|
|
QImage dst = extractQImage(exec, args, 1);
|
|
float opacity = (float)extractDouble(exec, args, 2);
|
|
QImage img = KImageEffect::blend(clr, dst, opacity);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodblendImage: {
|
|
QImage src = extractQImage(exec, args, 0);
|
|
QImage dst = extractQImage(exec, args, 1);
|
|
float opacity = (float)extractDouble(exec, args, 2);
|
|
QImage img = KImageEffect::blend(src, dst, opacity);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodcomputeDestinationRect: {
|
|
QSize lowerSize = extractQSize(exec, args, 0);
|
|
int disposition = extractInt(exec, args, 1);
|
|
QImage upper = extractQImage(exec, args, 2);
|
|
QRect rect = KImageEffect::computeDestinationRect(lowerSize, (KImageEffect::Disposition) disposition, upper);
|
|
retValue = convertToValue(exec, rect);
|
|
break;
|
|
}
|
|
case MethodchannelIntensity: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
float percent = (float)extractDouble(exec, args, 1);
|
|
int channel = extractInt(exec, args, 2);
|
|
QImage img = KImageEffect::channelIntensity(image, percent, (KImageEffect::RGBComponent)channel);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodfade: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
float val = (float)extractDouble(exec, args, 1);
|
|
QColor color = extractQColor(exec, args, 2);
|
|
QImage img = KImageEffect::fade(image, val, color);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodflatten: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
QColor ca = extractQColor(exec, args, 1);
|
|
QColor cb = extractQColor(exec, args, 2);
|
|
int ncols = extractInt(exec, args, 3);
|
|
QImage img = KImageEffect::flatten(image, ca, cb, ncols);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodhash: {
|
|
|
|
QImage image = extractQImage(exec, args, 0);
|
|
int lite = extractInt(exec, args, 1);
|
|
int spacing = extractInt(exec, args, 2);
|
|
QImage img = KImageEffect::hash(image, (KImageEffect::Lighting)lite, spacing);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodintensity: {
|
|
|
|
QImage image = extractQImage(exec, args, 0);
|
|
float percent = (float)extractDouble(exec, args, 1);
|
|
QImage img = KImageEffect::intensity(image, percent);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodmodulate: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
QImage modImage = extractQImage(exec, args, 0);
|
|
bool reverse = extractBool(exec, args, 1);
|
|
int type = extractInt(exec, args, 2);
|
|
int factor = extractInt(exec, args, 3);
|
|
int channel = extractInt(exec, args, 4);
|
|
QImage img = KImageEffect::modulate(image, modImage, reverse, (KImageEffect::ModulationType)type, factor, (KImageEffect::RGBComponent)channel);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
|
|
case MethodtoGray: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
bool fast = extractBool(exec, args, 1);
|
|
QImage img = KImageEffect::toGray(image, fast);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methoddesaturate: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
float desat = (float)extractDouble(exec, args, 1);
|
|
QImage img = KImageEffect::desaturate(image, desat);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methoddither: {
|
|
|
|
//dither( palette, size)
|
|
break;
|
|
}
|
|
case MethodselectedImage: {
|
|
|
|
QImage image = extractQImage(exec, args, 0);
|
|
QColor col = extractQColor(exec, args, 1);
|
|
QImage img = KImageEffect::selectedImage(image, col);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodcontrastHSV: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
bool sharpen = extractBool(exec, args, 1);
|
|
KImageEffect::contrastHSV(image, sharpen);
|
|
retValue = convertToValue(exec, image);
|
|
break;
|
|
}
|
|
case Methodnormalize: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
KImageEffect::normalize(image);
|
|
retValue = convertToValue(exec, image);
|
|
break;
|
|
}
|
|
case Methodequalize: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
KImageEffect::equalize(image);
|
|
retValue = convertToValue(exec, image);
|
|
break;
|
|
}
|
|
case Methodthreshold: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
uint value = extractUInt(exec, args, 1);
|
|
KImageEffect::threshold(image, value);
|
|
retValue = convertToValue(exec, image);
|
|
break;
|
|
}
|
|
case Methodsolarize: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double factor = extractDouble(exec, args, 1);
|
|
KImageEffect::solarize(image, factor);
|
|
retValue = convertToValue(exec, image);
|
|
break;
|
|
}
|
|
case Methodemboss: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double radius = extractDouble(exec, args, 1);
|
|
double sigma = extractDouble(exec, args, 2);
|
|
QImage img = KImageEffect::emboss(image, radius, sigma);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methoddespeckle: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
QImage img = KImageEffect::despeckle(image);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodcharcoal: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double factor = extractDouble(exec, args, 1);
|
|
QImage img = KImageEffect::charcoal( image, factor);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodcharcoal2: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double radius = extractDouble(exec, args, 1);
|
|
double sigma = extractDouble(exec, args, 2);
|
|
QImage img = KImageEffect::charcoal(image, radius, sigma);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodrotate: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
int r = extractInt(exec, args, 1);
|
|
QImage img = KImageEffect::rotate(image, (KImageEffect::RotateDirection) r);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodsample: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
int width = extractInt(exec, args, 1);
|
|
int height = extractInt(exec, args, 2);
|
|
QImage img = KImageEffect::sample(image, width, height);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodaddNoise: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
int type = extractInt(exec, args, 1);
|
|
QImage img = KImageEffect::addNoise(image, (KImageEffect::NoiseType) type);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodblur: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double radius = extractDouble(exec, args, 1);
|
|
double sigma = extractDouble(exec, args, 2);
|
|
QImage img = KImageEffect::blur(image, radius, sigma);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodedge: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double radius = extractDouble(exec, args, 1);
|
|
QImage img = KImageEffect::edge(image, radius);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodimplode: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double factor = extractDouble(exec, args, 1);
|
|
uint background = extractUInt(exec, args, 2);
|
|
QImage img = KImageEffect::implode(image, factor, background);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodoilPaintConvolve: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double radius = extractDouble(exec, args, 1);
|
|
QImage img = KImageEffect::oilPaintConvolve(image, radius);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodoilPaint: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
int radius = extractInt(exec, args, 1);
|
|
QImage img = KImageEffect::oilPaint(image, radius);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
|
|
|
|
case Methodsharpen: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double factor = extractDouble(exec, args, 1);
|
|
QImage img = KImageEffect::sharpen(image, factor);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodsharpen2: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double radius = extractDouble(exec, args, 1);
|
|
double sigma = extractDouble(exec, args, 2);
|
|
QImage img = KImageEffect::sharpen(image, radius, sigma);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodspread: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
uint amount = extractUInt(exec, args, 1);
|
|
QImage img = KImageEffect::spread(image, amount);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodshade: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
bool color_shading = extractBool(exec, args, 1);
|
|
double azimuth = extractDouble(exec, args, 2);
|
|
double elevation = extractDouble(exec, args, 3);
|
|
QImage img = KImageEffect::shade(image, color_shading, azimuth, elevation);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodswirl: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double degrees = extractDouble(exec, args, 1);
|
|
uint background = extractUInt(exec, args, 2);
|
|
QImage img = KImageEffect::swirl(image, degrees, background);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodwave: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
double amplitude = extractDouble(exec, args, 1);
|
|
double frequency = extractDouble(exec, args, 2);
|
|
uint background = extractUInt(exec, args, 3);
|
|
QImage img = KImageEffect::wave(image, amplitude, frequency, background);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case Methodcontrast: {
|
|
QImage image = extractQImage(exec, args, 0);
|
|
int c = extractInt(exec, args, 1);
|
|
QImage img = KImageEffect::contrast(image, c);
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
case MethodbumpMap: {
|
|
QImage mask = extractQImage(exec, args, 0);
|
|
QImage img = bumpmap(img,
|
|
mask,
|
|
extractDouble(exec, args, 1),
|
|
extractDouble(exec, args, 2),
|
|
extractInt(exec, args, 3),
|
|
extractInt(exec, args, 4),
|
|
extractInt(exec, args, 5),
|
|
extractInt(exec, args, 6),
|
|
extractInt(exec, args, 7),
|
|
extractBool(exec, args, 8),
|
|
extractBool(exec, args, 9),
|
|
(BumpmapType) extractInt(exec, args, 10),
|
|
extractBool(exec, args, 11));
|
|
|
|
retValue = convertToValue(exec, img);
|
|
break;
|
|
}
|
|
default:
|
|
kdWarning() << "ImageFX has no method " << mid << endl;
|
|
break;
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Here's a pretty fast bumpmap implementation.
|
|
* NOTE: remind me to move it to KImageEffects after 3.2.
|
|
*/
|
|
#define DegreesToRadians(x) ((x)*M_PI/180.0)
|
|
#define MOD(x, y) ((x) < 0 ? ((y) - 1 - ((y) - 1 - (x)) % (y)) : (x) % (y))
|
|
|
|
/**
|
|
* NOTE: kclamp needs to be moved to kglobals.h along kmin and kmax
|
|
*/
|
|
#define KCLAMP(x,low,high) kClamp(x,low,high)
|
|
template<class T>
|
|
inline const T& kClamp( const T& x, const T& low, const T& high )
|
|
{
|
|
if ( x < low )
|
|
return low;
|
|
else if ( x > high )
|
|
return high;
|
|
else
|
|
return x;
|
|
}
|
|
|
|
static inline unsigned int intensityValue( unsigned int color ) {
|
|
return (unsigned int)( (0.299*qRed( color ) +
|
|
0.587*qGreen( color ) +
|
|
0.1140000000000001*qBlue( color ) ) );
|
|
}
|
|
|
|
struct BumpmapParams {
|
|
BumpmapParams( double bm_azimuth, double bm_elevation,
|
|
int bm_depth, BumpmapType bm_type,
|
|
bool invert ) {
|
|
/* Convert to radians */
|
|
double azimuth = DegreesToRadians( bm_azimuth );
|
|
double elevation = DegreesToRadians( bm_elevation );
|
|
|
|
/* Calculate the light vector */
|
|
lx = (int)( cos(azimuth) * cos(elevation) * 255.0 );
|
|
ly = (int)( sin(azimuth) * cos(elevation) * 255.0 );
|
|
int lz = (int)( sin(elevation) * 255.0 );
|
|
|
|
/* Calculate constant Z component of surface normal */
|
|
int nz = (6 * 255) / bm_depth;
|
|
nz2 = nz * nz;
|
|
nzlz = nz * lz;
|
|
|
|
/* Optimize for vertical normals */
|
|
background = lz;
|
|
|
|
/* Calculate darkness compensation factor */
|
|
compensation = sin(elevation);
|
|
|
|
/* Create look-up table for map type */
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
double n = 0;
|
|
switch (bm_type)
|
|
{
|
|
case Spherical:
|
|
n = i / 255.0 - 1.0;
|
|
lut[i] = (int) (255.0 * sqrt(1.0 - n * n) + 0.5);
|
|
break;
|
|
|
|
case Sinuosidal:
|
|
n = i / 255.0;
|
|
lut[i] = (int) (255.0 * (sin((-M_PI / 2.0) + M_PI * n) + 1.0) /
|
|
2.0 + 0.5);
|
|
break;
|
|
|
|
case Linear:
|
|
default:
|
|
lut[i] = i;
|
|
}
|
|
|
|
if (invert)
|
|
lut[i] = 255 - lut[i];
|
|
}
|
|
}
|
|
int lx, ly;
|
|
int nz2, nzlz;
|
|
int background;
|
|
double compensation;
|
|
uchar lut[256];
|
|
};
|
|
|
|
|
|
static void bumpmap_convert_row( uint *row,
|
|
int width,
|
|
int bpp,
|
|
int has_alpha,
|
|
uchar *lut,
|
|
int waterlevel )
|
|
{
|
|
uint *p;
|
|
|
|
p = row;
|
|
|
|
has_alpha = has_alpha ? 1 : 0;
|
|
|
|
if (bpp >= 3)
|
|
for (; width; width--)
|
|
{
|
|
if (has_alpha) {
|
|
unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
|
|
*p++ = lut[(unsigned int) ( waterlevel +
|
|
( ( idx -
|
|
waterlevel) * qBlue( *row )) / 255.0 )];
|
|
} else {
|
|
unsigned int idx = (unsigned int)(intensityValue( *row ) + 0.5);
|
|
*p++ = lut[idx];
|
|
}
|
|
|
|
++row;
|
|
}
|
|
}
|
|
|
|
static void bumpmap_row( uint *src,
|
|
uint *dest,
|
|
int width,
|
|
int bpp,
|
|
int has_alpha,
|
|
uint *bm_row1,
|
|
uint *bm_row2,
|
|
uint *bm_row3,
|
|
int bm_width,
|
|
int bm_xofs,
|
|
bool tiled,
|
|
bool row_in_bumpmap,
|
|
int ambient,
|
|
bool compensate,
|
|
BumpmapParams *params )
|
|
{
|
|
int xofs1, xofs2, xofs3;
|
|
int shade;
|
|
int ndotl;
|
|
int nx, ny;
|
|
int x;
|
|
int pbpp;
|
|
int tmp;
|
|
|
|
if (has_alpha)
|
|
pbpp = bpp - 1;
|
|
else
|
|
pbpp = bpp;
|
|
|
|
tmp = bm_xofs;
|
|
xofs2 = MOD(tmp, bm_width);
|
|
|
|
for (x = 0; x < width; x++)
|
|
{
|
|
/* Calculate surface normal from bump map */
|
|
|
|
if (tiled || (row_in_bumpmap &&
|
|
x >= - tmp && x < - tmp + bm_width)) {
|
|
if (tiled) {
|
|
xofs1 = MOD(xofs2 - 1, bm_width);
|
|
xofs3 = MOD(xofs2 + 1, bm_width);
|
|
} else {
|
|
xofs1 = KCLAMP(xofs2 - 1, 0, bm_width - 1);
|
|
xofs3 = KCLAMP(xofs2 + 1, 0, bm_width - 1);
|
|
}
|
|
nx = (bm_row1[xofs1] + bm_row2[xofs1] + bm_row3[xofs1] -
|
|
bm_row1[xofs3] - bm_row2[xofs3] - bm_row3[xofs3]);
|
|
ny = (bm_row3[xofs1] + bm_row3[xofs2] + bm_row3[xofs3] -
|
|
bm_row1[xofs1] - bm_row1[xofs2] - bm_row1[xofs3]);
|
|
} else {
|
|
nx = ny = 0;
|
|
}
|
|
|
|
/* Shade */
|
|
|
|
if ((nx == 0) && (ny == 0))
|
|
shade = params->background;
|
|
else {
|
|
ndotl = nx * params->lx + ny * params->ly + params->nzlz;
|
|
|
|
if (ndotl < 0)
|
|
shade = (int)( params->compensation * ambient );
|
|
else {
|
|
shade = (int)( ndotl / sqrt(nx * nx + ny * ny + params->nz2) );
|
|
|
|
shade = (int)( shade + KMAX(0.0, (255 * params->compensation - shade)) *
|
|
ambient / 255 );
|
|
}
|
|
}
|
|
|
|
/* Paint */
|
|
|
|
/**
|
|
* NOTE: if we want to work with non-32bit images the alpha handling would
|
|
* also change
|
|
*/
|
|
if (compensate) {
|
|
int red = (int)((qRed( *src ) * shade) / (params->compensation * 255));
|
|
int green = (int)((qGreen( *src ) * shade) / (params->compensation * 255));
|
|
int blue = (int)((qBlue( *src ) * shade) / (params->compensation * 255));
|
|
int alpha = (int)((qAlpha( *src ) * shade) / (params->compensation * 255));
|
|
++src;
|
|
*dest++ = qRgba( red, green, blue, alpha );
|
|
} else {
|
|
int red = qRed( *src ) * shade / 255;
|
|
int green = qGreen( *src ) * shade / 255;
|
|
int blue = qBlue( *src ) * shade / 255;
|
|
int alpha = qAlpha( *src ) * shade / 255;
|
|
++src;
|
|
*dest++ = qRgba( red, green, blue, alpha );
|
|
}
|
|
|
|
/* Next pixel */
|
|
|
|
if (++xofs2 == bm_width)
|
|
xofs2 = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A bumpmapping algorithm.
|
|
*
|
|
* @param img the image you want bumpmap
|
|
* @param map the map used
|
|
* @param azimuth azimuth
|
|
* @param elevation elevation
|
|
* @param depth depth (not the depth of the image, but of the map)
|
|
* @param xofs X offset
|
|
* @param yofs Y offset
|
|
* @param waterlevel level that full transparency should represent
|
|
* @param ambient ambient lighting factor
|
|
* @param compensate compensate for darkening
|
|
* @param invert invert bumpmap
|
|
* @param type type of the bumpmap
|
|
*
|
|
* @return The destination image (dst) containing the result.
|
|
* @author Zack Rusin <zack@kde.org>
|
|
*/
|
|
QImage ImageFX::bumpmap(QImage &img, QImage &map, double azimuth, double elevation,
|
|
int depth, int xofs, int yofs, int waterlevel,
|
|
int ambient, bool compensate, bool invert,
|
|
BumpmapType type, bool tiled)
|
|
{
|
|
QImage dst;
|
|
|
|
if ( img.depth() != 32 || img.depth() != 32 ) {
|
|
qWarning( "Bump-mapping effect works only with 32 bit images");
|
|
return dst;
|
|
}
|
|
|
|
dst.create( img.width(), img.height(), img.depth() );
|
|
int bm_width = map.width();
|
|
int bm_height = map.height();
|
|
int bm_bpp = map.depth();
|
|
int bm_has_alpha = map.hasAlphaBuffer();
|
|
|
|
int yofs1, yofs2, yofs3;
|
|
|
|
if ( tiled ) {
|
|
yofs2 = MOD( yofs, bm_height );
|
|
yofs1 = MOD( yofs2 - 1, bm_height);
|
|
yofs3 = MOD( yofs2 + 1, bm_height);
|
|
} else {
|
|
yofs1 = 0;
|
|
yofs2 = 0;
|
|
yofs3 = KCLAMP( yofs2+1, 0, bm_height - 1 );
|
|
}
|
|
|
|
BumpmapParams params( azimuth, elevation, depth, type, invert );
|
|
|
|
uint* bm_row1 = (unsigned int*)map.scanLine( yofs1 );
|
|
uint* bm_row2 = (unsigned int*)map.scanLine( yofs2 );
|
|
uint* bm_row3 = (unsigned int*)map.scanLine( yofs3 );
|
|
|
|
bumpmap_convert_row( bm_row1, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
|
|
bumpmap_convert_row( bm_row2, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
|
|
bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha, params.lut, waterlevel );
|
|
|
|
for (int y = 0; y < img.height(); ++y)
|
|
{
|
|
int row_in_bumpmap = (y >= - yofs && y < - yofs + bm_height);
|
|
|
|
uint* src_row = (unsigned int*)img.scanLine( y );
|
|
uint* dest_row = (unsigned int*)dst.scanLine( y );
|
|
|
|
bumpmap_row( src_row, dest_row, img.width(), img.depth(), img.hasAlphaBuffer(),
|
|
bm_row1, bm_row2, bm_row3, bm_width, xofs,
|
|
tiled,
|
|
row_in_bumpmap, ambient, compensate,
|
|
¶ms );
|
|
|
|
/* Next line */
|
|
|
|
if (tiled || row_in_bumpmap)
|
|
{
|
|
uint* bm_tmprow = bm_row1;
|
|
bm_row1 = bm_row2;
|
|
bm_row2 = bm_row3;
|
|
bm_row3 = bm_tmprow;
|
|
|
|
if (++yofs2 == bm_height)
|
|
yofs2 = 0;
|
|
|
|
if (tiled)
|
|
yofs3 = MOD(yofs2 + 1, bm_height);
|
|
else
|
|
yofs3 = KCLAMP(yofs2 + 1, 0, bm_height - 1);
|
|
|
|
bm_row3 = (unsigned int*)map.scanLine( yofs3 );
|
|
bumpmap_convert_row( bm_row3, bm_width, bm_bpp, bm_has_alpha,
|
|
params.lut, waterlevel );
|
|
}
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
|
|
} // namespace KJSEmbed::Bindings
|
|
} // namespace KJSEmbed
|
|
|
|
#include <kgenericfactory.h>
|
|
typedef KGenericFactory<KJSEmbed::Bindings::ImageFXLoader> ImageFXLoaderFactory;
|
|
K_EXPORT_COMPONENT_FACTORY( libimagefxplugin, ImageFXLoaderFactory( "ImageFXLoader" ) )
|