summaryrefslogtreecommitdiffstats
path: root/chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp')
-rw-r--r--chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp439
1 files changed, 439 insertions, 0 deletions
diff --git a/chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp b/chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp
new file mode 100644
index 000000000..acb8c2e17
--- /dev/null
+++ b/chalk/plugins/filters/raindropsfilter/kis_raindrops_filter.cpp
@@ -0,0 +1,439 @@
+/*
+ * 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 ) );
+ }
+}