diff options
Diffstat (limited to 'src/imageplugins/raindrop/raindrop.cpp')
-rw-r--r-- | src/imageplugins/raindrop/raindrop.cpp | 457 |
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 |