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.
koffice/chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cc

440 lines
16 KiB

/*
* This file is part of the KDE project
*
* Copyright (c) 2004 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
*
* ported from digikam, Copyright 2004 by Gilles Caulier,
* Original RainDrops algorithm copyrighted 2004 by
* Pieter Z. Voloshyn <pieter_voloshyn at ame.com.br>.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <vector>
#include <tqpoint.h>
#include <tqspinbox.h>
#include <tdelocale.h>
#include <kiconloader.h>
#include <kinstance.h>
#include <tdemessagebox.h>
#include <kstandarddirs.h>
#include <tdetempfile.h>
#include <kdebug.h>
#include <kgenericfactory.h>
#include <knuminput.h>
#include <kis_doc.h>
#include <kis_image.h>
#include <kis_iterators_pixel.h>
#include <kis_random_accessor.h>
#include <kis_layer.h>
#include <kis_filter_registry.h>
#include <kis_filter.h>
#include <kis_global.h>
#include <kis_types.h>
#include <kis_view.h>
#include <kis_progress_display_interface.h>
#include "kis_multi_integer_filter_widget.h"
#include "kis_raindrops_filter.h"
KisRainDropsFilter::KisRainDropsFilter() : KisFilter(id(), "artistic", i18n("&Raindrops..."))
{
}
void KisRainDropsFilter::process(KisPaintDeviceSP src, KisPaintDeviceSP dst, KisFilterConfiguration* configuration, const TQRect& rect)
{
Q_UNUSED(dst);
//read the filter configuration values from the KisFilterConfiguration object
TQ_UINT32 dropSize = ((KisRainDropsFilterConfiguration*)configuration)->dropSize();
TQ_UINT32 number = ((KisRainDropsFilterConfiguration*)configuration)->number();
TQ_UINT32 fishEyes = ((KisRainDropsFilterConfiguration*)configuration)->fishEyes();
rainDrops(src, dst, rect, dropSize, number, fishEyes);
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* Function to apply the RainDrops effect (inspired from Jason Waltman code)
*
* data => The image data in RGBA mode.
* Width => Width of image.
* Height => Height of image.
* DropSize => Raindrop size
* Amount => Maximum number of raindrops
* Coeff => FishEye coefficient
*
* Theory => This functions does several math's functions and the engine
* is simple to undestand, but a little hard to implement. A
* control will indicate if there is or not a raindrop in that
* area, if not, a fisheye effect with a random size (max=DropSize)
* will be applied, after this, a shadow will be applied too.
* and after this, a blur function will finish the effect.
*/
void KisRainDropsFilter::rainDrops(KisPaintDeviceSP src, KisPaintDeviceSP dst, const TQRect& rect, int DropSize, int Amount, int Coeff)
{
setProgressTotalSteps(Amount);
setProgressStage(i18n("Applying oilpaint filter..."),0);
if (Coeff <= 0) Coeff = 1;
if (Coeff > 100) Coeff = 100;
int Width = rect.width();
int Height = rect.height();
bool** BoolMatrix = CreateBoolArray (Width, Height);
int i, j, k, l, m, n; // loop variables
int Bright; // Bright value for shadows and highlights
int x, y; // center coordinates
int Counter = 0; // Counter (duh !)
int NewSize; // Size of current raindrop
int halfSize; // Half of the current raindrop
int Radius; // Maximum radius for raindrop
int BlurRadius; // Blur Radius
int BlurPixels;
double r, a; // polar coordinates
double OldRadius; // Radius before processing
double NewCoeff = (double)Coeff * 0.01; // FishEye Coefficients
double s;
double R, G, B;
bool FindAnother = false; // To search for good coordinates
KisColorSpace * cs = src->colorSpace();
TQDateTime dt = TQDateTime::currentDateTime();
TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) );
srand ((uint) dt.secsTo(Y2000));
// Init booleen Matrix.
for (i = 0 ; !cancelRequested() && (i < Width) ; ++i)
{
for (j = 0 ; !cancelRequested() && (j < Height) ; ++j)
{
BoolMatrix[i][j] = false;
}
}
KisRandomAccessorPixel oldIt = src->createRandomAccessor(0,0,false);
KisRandomAccessorPixel dstIt = dst->createRandomAccessor(0,0,true);
for (int NumBlurs = 0 ; !cancelRequested() && (NumBlurs <= Amount) ; ++NumBlurs)
{
NewSize = (int)(rand() * ((double)(DropSize - 5) / RAND_MAX) + 5);
halfSize = NewSize / 2;
Radius = halfSize;
s = Radius / log (NewCoeff * Radius + 1);
Counter = 0;
do
{
FindAnother = false;
y = (int)(rand() * ((double)( Width - 1) / RAND_MAX));
x = (int)(rand() * ((double)(Height - 1) / RAND_MAX));
if (BoolMatrix[y][x])
FindAnother = true;
else
for (i = x - halfSize ; !cancelRequested() && (i <= x + halfSize) ; i++)
for (j = y - halfSize ; !cancelRequested() && (j <= y + halfSize) ; j++)
if ((i >= 0) && (i < Height) && (j >= 0) && (j < Width))
if (BoolMatrix[j][i])
FindAnother = true;
Counter++;
}
while (!cancelRequested() && (FindAnother && (Counter < 10000)) );
if (Counter >= 10000)
{
NumBlurs = Amount;
break;
}
for (i = -1 * halfSize ; !cancelRequested() && (i < NewSize - halfSize) ; i++)
{
for (j = -1 * halfSize ; !cancelRequested() && (j < NewSize - halfSize) ; j++)
{
r = sqrt (i * i + j * j);
a = atan2 (i, j);
if (r <= Radius)
{
OldRadius = r;
r = (exp (r / s) - 1) / NewCoeff;
k = x + (int)(r * sin (a));
l = y + (int)(r * cos (a));
m = x + i;
n = y + j;
if ((k >= 0) && (k < Height) && (l >= 0) && (l < Width))
{
if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width))
{
Bright = 0;
if (OldRadius >= 0.9 * Radius)
{
if ((a <= 0) && (a > -2.25))
Bright = -80;
else if ((a <= -2.25) && (a > -2.5))
Bright = -40;
else if ((a <= 0.25) && (a > 0))
Bright = -40;
}
else if (OldRadius >= 0.8 * Radius)
{
if ((a <= -0.75) && (a > -1.50))
Bright = -40;
else if ((a <= 0.10) && (a > -0.75))
Bright = -30;
else if ((a <= -1.50) && (a > -2.35))
Bright = -30;
}
else if (OldRadius >= 0.7 * Radius)
{
if ((a <= -0.10) && (a > -2.0))
Bright = -20;
else if ((a <= 2.50) && (a > 1.90))
Bright = 60;
}
else if (OldRadius >= 0.6 * Radius)
{
if ((a <= -0.50) && (a > -1.75))
Bright = -20;
else if ((a <= 0) && (a > -0.25))
Bright = 20;
else if ((a <= -2.0) && (a > -2.25))
Bright = 20;
}
else if (OldRadius >= 0.5 * Radius)
{
if ((a <= -0.25) && (a > -0.50))
Bright = 30;
else if ((a <= -1.75 ) && (a > -2.0))
Bright = 30;
}
else if (OldRadius >= 0.4 * Radius)
{
if ((a <= -0.5) && (a > -1.75))
Bright = 40;
}
else if (OldRadius >= 0.3 * Radius)
{
if ((a <= 0) && (a > -2.25))
Bright = 30;
}
else if (OldRadius >= 0.2 * Radius)
{
if ((a <= -0.5) && (a > -1.75))
Bright = 20;
}
BoolMatrix[n][m] = true;
TQColor originalColor;
oldIt.moveTo(rect.x() + l, rect.y() + k);
cs->toTQColor(oldIt.oldRawData(), &originalColor);
int newRed = CLAMP(originalColor.red() + Bright, 0, TQ_UINT8_MAX);
int newGreen = CLAMP(originalColor.green() + Bright, 0, TQ_UINT8_MAX);
int newBlue = CLAMP(originalColor.blue() + Bright, 0, TQ_UINT8_MAX);
TQColor newColor;
newColor.setRgb(newRed, newGreen, newBlue);
dstIt.moveTo(rect.x() + n, rect.y() + m);
cs->fromTQColor(newColor, dstIt.rawData());
}
}
}
}
}
BlurRadius = NewSize / 25 + 1;
for (i = -1 * halfSize - BlurRadius ; !cancelRequested() && (i < NewSize - halfSize + BlurRadius) ; i++)
{
for (j = -1 * halfSize - BlurRadius ; !cancelRequested() && (j < NewSize - halfSize + BlurRadius) ; j++)
{
r = sqrt (i * i + j * j);
if (r <= Radius * 1.1)
{
R = G = B = 0;
BlurPixels = 0;
for (k = -1 * BlurRadius; k < BlurRadius + 1; k++)
for (l = -1 * BlurRadius; l < BlurRadius + 1; l++)
{
m = x + i + k;
n = y + j + l;
if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width))
{
TQColor color;
dstIt.moveTo(rect.x() + n, rect.y() + m);
cs->toTQColor(dstIt.rawData(), &color);
R += color.red();
G += color.green();
B += color.blue();
BlurPixels++;
}
}
m = x + i;
n = y + j;
if ((m >= 0) && (m < Height) && (n >= 0) && (n < Width))
{
TQColor color;
color.setRgb((int)(R / BlurPixels), (int)(G / BlurPixels), (int)(B / BlurPixels));
dstIt.moveTo(rect.x() + n, rect.y() + m);
cs->fromTQColor(color, dstIt.rawData());
}
}
}
}
setProgress(NumBlurs);
}
/* KisRectIteratorPixel srcIt2 = src->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), false);
KisRectIteratorPixel dstIt2 = src->createRectIterator(rect.x(), rect.y(), rect.width(), rect.height(), true);
while (!srcIt2.isDone()) {
if (!srcIt2.isSelected()) {
memcpy(dstIt2.rawData(), srcIt2.oldRawData(), src->pixelSize());
}
++srcIt2;
}
*/
FreeBoolArray (BoolMatrix, Width);
setProgressDone();
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* Function to free a dinamic boolean array
*
* lpbArray => Dynamic boolean array
* Columns => The array bidimension value
*
* Theory => An easy to undestand 'for' statement
*/
void KisRainDropsFilter::FreeBoolArray (bool** lpbArray, uint Columns)
{
for (uint i = 0; i < Columns; ++i)
free (lpbArray[i]);
free (lpbArray);
}
/* Function to create a bidimentional dinamic boolean array
*
* Columns => Number of columns
* Rows => Number of rows
*
* Theory => Using 'for' statement, we can alloc multiple dinamic arrays
* To create more dimentions, just add some 'for's, ok?
*/
bool** KisRainDropsFilter::CreateBoolArray (uint Columns, uint Rows)
{
bool** lpbArray = NULL;
lpbArray = (bool**) malloc (Columns * sizeof (bool*));
if (lpbArray == NULL)
return (NULL);
for (uint i = 0; i < Columns; ++i)
{
lpbArray[i] = (bool*) malloc (Rows * sizeof (bool));
if (lpbArray[i] == NULL)
{
FreeBoolArray (lpbArray, Columns);
return (NULL);
}
}
return (lpbArray);
}
// This method have been ported from Pieter Z. Voloshyn algorithm code.
/* This function limits the RGB values
*
* ColorValue => Here, is an RGB value to be analized
*
* Theory => A color is represented in RGB value (e.g. 0xFFFFFF is
* white color). But R, G and B values has 256 values to be used
* so, this function analize the value and limits to this range
*/
uchar KisRainDropsFilter::LimitValues (int ColorValue)
{
if (ColorValue > 255) // MAX = 255
ColorValue = 255;
if (ColorValue < 0) // MIN = 0
ColorValue = 0;
return ((uchar) ColorValue);
}
KisFilterConfigWidget * KisRainDropsFilter::createConfigurationWidget(TQWidget* parent, KisPaintDeviceSP)
{
vKisIntegerWidgetParam param;
param.push_back( KisIntegerWidgetParam( 1, 200, 80, i18n("Drop size"), "dropsize" ) );
param.push_back( KisIntegerWidgetParam( 1, 500, 80, i18n("Number"), "number" ) );
param.push_back( KisIntegerWidgetParam( 1, 100, 30, i18n("Fish eyes"), "fishEyes" ) );
return new KisMultiIntegerFilterWidget(parent, id().id().ascii(), id().id().ascii(), param );
}
KisFilterConfiguration* KisRainDropsFilter::configuration(TQWidget* nwidget)
{
KisMultiIntegerFilterWidget* widget = (KisMultiIntegerFilterWidget*) nwidget;
if( widget == 0 )
{
return new KisRainDropsFilterConfiguration( 30, 80, 20);
} else {
return new KisRainDropsFilterConfiguration( widget->valueAt( 0 ), widget->valueAt( 1 ), widget->valueAt( 2 ) );
}
}