summaryrefslogtreecommitdiffstats
path: root/src/imageplugins/raindrop/raindrop.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imageplugins/raindrop/raindrop.cpp')
-rw-r--r--src/imageplugins/raindrop/raindrop.cpp457
1 files changed, 457 insertions, 0 deletions
diff --git a/src/imageplugins/raindrop/raindrop.cpp b/src/imageplugins/raindrop/raindrop.cpp
new file mode 100644
index 00000000..7be977fc
--- /dev/null
+++ b/src/imageplugins/raindrop/raindrop.cpp
@@ -0,0 +1,457 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-05-25
+ * Description : Raindrop threaded image filter.
+ *
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ * Copyright (C) 2006-2007 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
+ *
+ * Original RainDrop algorithm copyrighted 2004-2005 by
+ * Pieter Z. Voloshyn <pieter dot voloshyn at gmail dot com>.
+ *
+ * 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, 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.
+ *
+ * ============================================================ */
+
+// C++ includes.
+
+#include <cmath>
+#include <cstdlib>
+
+// TQt includes.
+
+#include <tqdeepcopy.h>
+#include <tqdatetime.h>
+#include <tqrect.h>
+
+// Local includes.
+
+#include "dimg.h"
+#include "dimgimagefilters.h"
+#include "raindrop.h"
+
+namespace DigikamRainDropImagesPlugin
+{
+
+RainDrop::RainDrop(Digikam::DImg *orgImage, TQObject *parent, int drop,
+ int amount, int coeff, TQRect *selection)
+ : Digikam::DImgThreadedFilter(orgImage, parent, "RainDrop")
+{
+ m_drop = drop;
+ m_amount = amount;
+ m_coeff = coeff;
+
+ m_selectedX = m_selectedY = m_selectedW = m_selectedH = 0;
+
+ if ( selection )
+ {
+ m_selectedX = selection->left();
+ m_selectedY = selection->top();
+ m_selectedW = selection->width();
+ m_selectedH = selection->height();
+ }
+
+ initFilter();
+}
+
+void RainDrop::filterImage(void)
+{
+ int w = m_orgImage.width();
+ int h = m_orgImage.height();
+
+ // If we have a region selection in image, use it to apply the filter modification around,
+ // else, applied the filter on the full image.
+
+ if (m_selectedW && m_selectedH)
+ {
+ Digikam::DImg zone1, zone2, zone3, zone4,
+ zone1Dest, zone2Dest, zone3Dest, zone4Dest,
+ selectedImg;
+ selectedImg = m_orgImage.copy(m_selectedX, m_selectedY, m_selectedW, m_selectedH);
+
+ // Cut the original image in 4 areas without clipping region.
+
+ zone1 = m_orgImage.copy(0, 0, m_selectedX, w);
+ zone2 = m_orgImage.copy(m_selectedX, 0, m_selectedX + m_selectedW, m_selectedY);
+ zone3 = m_orgImage.copy(m_selectedX, m_selectedY + m_selectedH, m_selectedX + m_selectedW, h);
+ zone4 = m_orgImage.copy(m_selectedX + m_selectedW, 0, w, h);
+
+ zone1Dest = Digikam::DImg(zone1.width(), zone1.height(), zone1.sixteenBit(), zone1.hasAlpha());
+ zone2Dest = Digikam::DImg(zone2.width(), zone2.height(), zone2.sixteenBit(), zone2.hasAlpha());
+ zone3Dest = Digikam::DImg(zone3.width(), zone3.height(), zone3.sixteenBit(), zone3.hasAlpha());
+ zone4Dest = Digikam::DImg(zone4.width(), zone4.height(), zone4.sixteenBit(), zone4.hasAlpha());
+
+ // Apply effect on each area.
+
+ rainDropsImage(&zone1, &zone1Dest, 0, m_drop, m_amount, m_coeff, true, 0, 25);
+ rainDropsImage(&zone2, &zone2Dest, 0, m_drop, m_amount, m_coeff, true, 25, 50);
+ rainDropsImage(&zone3, &zone3Dest, 0, m_drop, m_amount, m_coeff, true, 50, 75);
+ rainDropsImage(&zone4, &zone4Dest, 0, m_drop, m_amount, m_coeff, true, 75, 100);
+
+ // Build the target image.
+
+ m_destImage.bitBltImage(&zone1Dest, 0, 0);
+ m_destImage.bitBltImage(&zone2Dest, m_selectedX, 0);
+ m_destImage.bitBltImage(&zone3Dest, m_selectedX, m_selectedY + m_selectedH);
+ m_destImage.bitBltImage(&zone4Dest, m_selectedX + m_selectedW, 0);
+ m_destImage.bitBltImage(&selectedImg, m_selectedX, m_selectedY);
+ }
+ else
+ {
+ rainDropsImage(&m_orgImage, &m_destImage, 0, m_drop, m_amount, m_coeff, true, 0, 100);
+ }
+}
+
+/* Function to apply the RainDrops effect backported from ImageProcessing version 2
+ *
+ * orgImage => The image
+ * MinDropSize => It's the minimum random size for rain drop.
+ * MaxDropSize => It's the minimum random size for rain drop.
+ * Amount => It's the maximum number for rain drops inside the image.
+ * Coeff => It's the fisheye's coefficient.
+ * bLimitRange => If true, the drop will not be cut.
+ * progressMin => Min. value for progress bar (can be different if using clipping area).
+ * progressMax => Max. value for progress bar (can be different if using clipping area).
+ *
+ * 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=MaxDropSize)
+ * will be applied, after this, a shadow will be applied too.
+ * and after this, a blur function will finish the effect.
+ */
+void RainDrop::rainDropsImage(Digikam::DImg *orgImage, Digikam::DImg *destImage, int MinDropSize, int MaxDropSize,
+ int Amount, int Coeff, bool bLimitRange, int progressMin, int progressMax)
+{
+ bool bResp;
+ int nRandSize, i;
+ int nRandX, nRandY;
+ int nCounter = 0;
+ int nWidth = orgImage->width();
+ int nHeight = orgImage->height();
+ bool sixteenBit = orgImage->sixteenBit();
+ int bytesDepth = orgImage->bytesDepth();
+ uchar *data = orgImage->bits();
+ uchar *pResBits = destImage->bits();
+
+ if (Amount <= 0)
+ return;
+
+ if (MinDropSize >= MaxDropSize)
+ MaxDropSize = MinDropSize + 1;
+
+ if (MaxDropSize <= 0)
+ return;
+
+ uchar *pStatusBits = new uchar[nHeight * nWidth];
+ memset(pStatusBits, 0, sizeof(nHeight * nWidth));
+
+ // Initially, copy all pixels to destination
+
+ destImage->bitBltImage(orgImage, 0, 0);
+
+ // Randomize.
+
+ TQDateTime dt = TQDateTime::currentDateTime();
+ TQDateTime Y2000( TQDate(2000, 1, 1), TQTime(0, 0, 0) );
+ uint seed = dt.secsTo(Y2000);
+
+ for (i = 0; !m_cancel && (i < Amount); i++)
+ {
+ nCounter = 0;
+
+ do
+ {
+ nRandX = (int)(rand_r(&seed) * ((double)( nWidth - 1) / RAND_MAX));
+ nRandY = (int)(rand_r(&seed) * ((double)(nHeight - 1) / RAND_MAX));
+
+ nRandSize = (rand() % (MaxDropSize - MinDropSize)) + MinDropSize;
+
+ bResp = CreateRainDrop (data, nWidth, nHeight, sixteenBit, bytesDepth,
+ pResBits, pStatusBits,
+ nRandX, nRandY, nRandSize, Coeff, bLimitRange);
+
+ nCounter++;
+ }
+ while ((bResp == false) && (nCounter < 10000) && !m_cancel);
+
+ // Update the progress bar in dialog.
+ if (nCounter >= 10000)
+ {
+ i = Amount;
+
+ postProgress(progressMax);
+ break;
+ }
+
+ postProgress( (int)(progressMin + ((double)(i) *
+ (double)(progressMax-progressMin)) / (double)Amount) );
+ }
+
+ delete [] pStatusBits;
+}
+
+bool RainDrop::CreateRainDrop(uchar *pBits, int Width, int Height, bool sixteenBit, int bytesDepth,
+ uchar *pResBits, uchar* pStatusBits,
+ int X, int Y, int DropSize, double Coeff, bool bLimitRange)
+{
+ int w, h, nw1, nh1, nw2, nh2;
+ int nHalfSize = DropSize / 2;
+ int nBright;
+ double lfRadius, lfOldRadius, lfAngle, lfDiv;
+
+ Digikam::DColor imageData;
+
+ uint nTotalR, nTotalG, nTotalB, offset;
+ int nBlurPixels, nBlurRadius;
+
+ if (CanBeDropped(Width, Height, pStatusBits, X, Y, DropSize, bLimitRange))
+ {
+ Coeff *= 0.01;
+ lfDiv = (double)nHalfSize / log (Coeff * (double)nHalfSize + 1.0);
+
+ for (h = -nHalfSize; !m_cancel && (h <= nHalfSize); h++)
+ {
+ for (w = -nHalfSize; !m_cancel && (w <= nHalfSize); w++)
+ {
+ lfRadius = sqrt (h * h + w * w);
+ lfAngle = atan2 ((double)h, (double)w);
+
+ if (lfRadius <= (double)nHalfSize)
+ {
+ lfOldRadius = lfRadius;
+ lfRadius = (exp (lfRadius / lfDiv) - 1.0) / Coeff;
+
+ nw1 = (int)((double)X + lfRadius * cos (lfAngle));
+ nh1 = (int)((double)Y + lfRadius * sin (lfAngle));
+
+ nw2 = X + w;
+ nh2 = Y + h;
+
+ if (IsInside(Width, Height, nw1, nh1))
+ {
+ if (IsInside(Width, Height, nw2, nh2))
+ {
+ nBright = 0;
+
+ if (lfOldRadius >= 0.9 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.0) && (lfAngle < 2.25))
+ nBright = -80;
+ else if ((lfAngle >= 2.25) && (lfAngle < 2.5))
+ nBright = -40;
+ else if ((lfAngle >= -0.25) && (lfAngle < 0.0))
+ nBright = -40;
+ }
+
+ else if (lfOldRadius >= 0.8 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.75) && (lfAngle < 1.50))
+ nBright = -40;
+ else if ((lfAngle >= -0.10) && (lfAngle < 0.75))
+ nBright = -30;
+ else if ((lfAngle >= 1.50) && (lfAngle < 2.35))
+ nBright = -30;
+ }
+
+ else if (lfOldRadius >= 0.7 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.10) && (lfAngle < 2.0))
+ nBright = -20;
+ else if ((lfAngle >= -2.50) && (lfAngle < -1.90))
+ nBright = 60;
+ }
+
+ else if (lfOldRadius >= 0.6 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.50) && (lfAngle < 1.75))
+ nBright = -20;
+ else if ((lfAngle >= 0.0) && (lfAngle < 0.25))
+ nBright = 20;
+ else if ((lfAngle >= 2.0) && (lfAngle < 2.25))
+ nBright = 20;
+ }
+
+ else if (lfOldRadius >= 0.5 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.25) && (lfAngle < 0.50))
+ nBright = 30;
+ else if ((lfAngle >= 1.75 ) && (lfAngle < 2.0))
+ nBright = 30;
+ }
+
+ else if (lfOldRadius >= 0.4 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.5) && (lfAngle < 1.75))
+ nBright = 40;
+ }
+
+ else if (lfOldRadius >= 0.3 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.0) && (lfAngle < 2.25))
+ nBright = 30;
+ }
+
+ else if (lfOldRadius >= 0.2 * (double)nHalfSize)
+ {
+ if ((lfAngle >= 0.5) && (lfAngle < 1.75))
+ nBright = 20;
+ }
+
+ imageData.setColor(pBits + Offset(Width, nw1, nh1, bytesDepth), sixteenBit);
+
+ if (sixteenBit)
+ {
+ // convert difference to 16-bit range
+ if (nBright > 0)
+ nBright = (nBright + 1) * 256 - 1;
+ else
+ nBright = (nBright - 1) * 256 + 1;
+
+ imageData.setRed (LimitValues16(imageData.red() + nBright));
+ imageData.setGreen(LimitValues16(imageData.green() + nBright));
+ imageData.setBlue (LimitValues16(imageData.blue() + nBright));
+ }
+ else
+ {
+ imageData.setRed (LimitValues8(imageData.red() + nBright));
+ imageData.setGreen(LimitValues8(imageData.green() + nBright));
+ imageData.setBlue (LimitValues8(imageData.blue() + nBright));
+ }
+
+ imageData.setPixel(pResBits + Offset(Width, nw2, nh2, bytesDepth));
+
+ }
+ }
+ }
+ }
+ }
+
+ nBlurRadius = DropSize / 25 + 1;
+
+ for (h = -nHalfSize - nBlurRadius; !m_cancel && (h <= nHalfSize + nBlurRadius); h++)
+ {
+ for (w = -nHalfSize - nBlurRadius; !m_cancel && (w <= nHalfSize + nBlurRadius); w++)
+ {
+ lfRadius = sqrt (h * h + w * w);
+
+ if (lfRadius <= (double)nHalfSize * 1.1)
+ {
+ nTotalR = nTotalG = nTotalB = 0;
+ nBlurPixels = 0;
+
+ for (nh1 = -nBlurRadius; !m_cancel && (nh1 <= nBlurRadius); nh1++)
+ {
+ for (nw1 = -nBlurRadius; !m_cancel && (nw1 <= nBlurRadius); nw1++)
+ {
+ nw2 = X + w + nw1;
+ nh2 = Y + h + nh1;
+
+ if (IsInside (Width, Height, nw2, nh2))
+ {
+ imageData.setColor(pResBits + Offset(Width, nw2, nh2, bytesDepth), sixteenBit);
+
+ nTotalR += imageData.red();
+ nTotalG += imageData.green();
+ nTotalB += imageData.blue();
+ nBlurPixels++;
+ }
+ }
+ }
+
+ nw1 = X + w;
+ nh1 = Y + h;
+
+ if (IsInside (Width, Height, nw1, nh1))
+ {
+ offset = Offset(Width, nw1, nh1, bytesDepth);
+
+ // to preserve alpha channel
+ imageData.setColor(pResBits + offset, sixteenBit);
+
+ imageData.setRed (nTotalR / nBlurPixels);
+ imageData.setGreen(nTotalG / nBlurPixels);
+ imageData.setBlue (nTotalB / nBlurPixels);
+
+ imageData.setPixel(pResBits + offset);
+ }
+ }
+ }
+ }
+
+ SetDropStatusBits (Width, Height, pStatusBits, X, Y, DropSize);
+ }
+ else
+ return (false);
+
+ return (true);
+}
+
+
+bool RainDrop::CanBeDropped(int Width, int Height, uchar *pStatusBits, int X, int Y,
+ int DropSize, bool bLimitRange)
+{
+ int w, h, i = 0;
+ int nHalfSize = DropSize / 2;
+
+ if (pStatusBits == NULL)
+ return (true);
+
+ for (h = Y - nHalfSize; h <= Y + nHalfSize; h++)
+ {
+ for (w = X - nHalfSize; w <= X + nHalfSize; w++)
+ {
+ if (IsInside (Width, Height, w, h))
+ {
+ i = h * Width + w;
+ if (pStatusBits[i])
+ return (false);
+ }
+ else
+ {
+ if (bLimitRange)
+ return (false);
+ }
+ }
+ }
+
+ return (true);
+}
+
+bool RainDrop::SetDropStatusBits (int Width, int Height, uchar *pStatusBits,
+ int X, int Y, int DropSize)
+{
+ int w, h, i = 0;
+ int nHalfSize = DropSize / 2;
+
+ if (pStatusBits == NULL)
+ return (false);
+
+ for (h = Y - nHalfSize; h <= Y + nHalfSize; h++)
+ {
+ for (w = X - nHalfSize; w <= X + nHalfSize; w++)
+ {
+ if (IsInside (Width, Height, w, h))
+ {
+ i = h * Width + w;
+ pStatusBits[i] = 255;
+ }
+ }
+ }
+
+ return (true);
+}
+
+} // NameSpace DigikamRainDropImagesPlugin