summaryrefslogtreecommitdiffstats
path: root/src/imageplugins/charcoal/charcoal.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/imageplugins/charcoal/charcoal.cpp')
-rw-r--r--src/imageplugins/charcoal/charcoal.cpp249
1 files changed, 249 insertions, 0 deletions
diff --git a/src/imageplugins/charcoal/charcoal.cpp b/src/imageplugins/charcoal/charcoal.cpp
new file mode 100644
index 00000000..a4c54a3c
--- /dev/null
+++ b/src/imageplugins/charcoal/charcoal.cpp
@@ -0,0 +1,249 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2005-05-25
+ * Description : Charcoal threaded image filter.
+ *
+ * Copyright (C) 2005-2007 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Original Charcoal algorithm copyright 2002
+ * by Daniel M. Duley <mosfet@kde.org> from KImageEffect API.
+ *
+ * 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.
+ *
+ * ============================================================ */
+
+#define SQ2PI 2.50662827463100024161235523934010416269302368164062
+#define Epsilon 1.0e-12
+
+// C++ includes.
+
+#include <cmath>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "dimg.h"
+#include "dimggaussianblur.h"
+#include "dimgimagefilters.h"
+#include "charcoal.h"
+
+namespace DigikamCharcoalImagesPlugin
+{
+
+Charcoal::Charcoal(Digikam::DImg *orgImage, TQObject *parent, double pencil, double smooth)
+ : Digikam::DImgThreadedFilter(orgImage, parent, "Charcoal")
+{
+ m_pencil = pencil;
+ m_smooth = smooth;
+
+ initFilter();
+}
+
+void Charcoal::filterImage(void)
+{
+ if (m_orgImage.isNull())
+ {
+ DWarning() << k_funcinfo << "No image data available!"
+ << endl;
+ return;
+ }
+
+ if (m_pencil <= 0.0)
+ {
+ m_destImage = m_orgImage;
+ return;
+ }
+
+ // -- Applying Edge effect -----------------------------------------------
+
+ long i=0;
+ int kernelWidth = getOptimalKernelWidth(m_pencil, m_smooth);
+
+ if((int)m_orgImage.width() < kernelWidth)
+ {
+ DWarning() << k_funcinfo << "Image is smaller than radius!"
+ << endl;
+ return;
+ }
+
+ double *kernel = new double[kernelWidth*kernelWidth];
+
+ if(!kernel)
+ {
+ DWarning() << k_funcinfo << "Unable to allocate memory!"
+ << endl;
+ return;
+ }
+
+ for(i = 0 ; i < (kernelWidth*kernelWidth) ; i++)
+ kernel[i]=(-1.0);
+
+ kernel[i/2]=kernelWidth*kernelWidth-1.0;
+ convolveImage(kernelWidth, kernel);
+ delete [] kernel;
+
+ // -- Applying Gaussian blur effect ---------------------------------------
+
+ Digikam::DImgGaussianBlur(this, m_destImage, m_destImage, 50, 60, (int)(m_smooth/10.0));
+
+ if (m_cancel)
+ return;
+
+ // -- Applying strech contrast color effect -------------------------------
+
+ Digikam::DImgImageFilters().stretchContrastImage(m_destImage.bits(), m_destImage.width(),
+ m_destImage.height(), m_destImage.sixteenBit());
+ postProgress( 70 );
+ if (m_cancel)
+ return;
+
+ // -- Inverting image color -----------------------------------------------
+
+ Digikam::DImgImageFilters().invertImage(m_destImage.bits(), m_destImage.width(),
+ m_destImage.height(), m_destImage.sixteenBit());
+ postProgress( 80 );
+ if (m_cancel)
+ return;
+
+ // -- Convert to neutral black & white ------------------------------------
+
+ Digikam::DImgImageFilters().channelMixerImage(
+ m_destImage.bits(), m_destImage.width(),
+ m_destImage.height(), m_destImage.sixteenBit(), // Image data.
+ true, // Preserve luminosity.
+ true, // Monochrome.
+ 0.3, 0.59 , 0.11, // Red channel gains.
+ 0.0, 1.0, 0.0, // Green channel gains (not used).
+ 0.0, 0.0, 1.0); // Blue channel gains (not used).
+ postProgress( 90 );
+ if (m_cancel)
+ return;
+}
+
+bool Charcoal::convolveImage(const unsigned int order, const double *kernel)
+{
+ uint x, y;
+ int mx, my, sx, sy, mcx, mcy, progress;
+ long kernelWidth, i;
+ double red, green, blue, alpha, normalize=0.0;
+ double *k=0;
+ Digikam::DColor color;
+
+ kernelWidth = order;
+
+ if((kernelWidth % 2) == 0)
+ {
+ DWarning() << k_funcinfo << "Kernel width must be an odd number!"
+ << endl;
+ return(false);
+ }
+
+ double *normal_kernel = new double[kernelWidth*kernelWidth];
+
+ if(!normal_kernel)
+ {
+ DWarning() << k_funcinfo << "Unable to allocate memory!"
+ << endl;
+ return(false);
+ }
+
+ for(i=0 ; i < (kernelWidth*kernelWidth) ; i++)
+ normalize += kernel[i];
+
+ if(fabs(normalize) <= Epsilon)
+ normalize=1.0;
+
+ normalize = 1.0/normalize;
+
+ for(i=0 ; i < (kernelWidth*kernelWidth) ; i++)
+ normal_kernel[i] = normalize*kernel[i];
+
+ double maxClamp = m_destImage.sixteenBit() ? 16777215.0 : 65535.0;
+
+ for(y=0 ; !m_cancel && (y < m_destImage.height()) ; y++)
+ {
+ sy = y-(kernelWidth/2);
+
+ for(x=0 ; !m_cancel && (x < m_destImage.width()) ; x++)
+ {
+ k = normal_kernel;
+ red = green = blue = alpha = 0;
+ sy = y-(kernelWidth/2);
+
+ for(mcy=0 ; !m_cancel && (mcy < kernelWidth) ; mcy++, sy++)
+ {
+ my = sy < 0 ? 0 : sy > (int)m_destImage.height()-1 ? m_destImage.height()-1 : sy;
+ sx = x+(-kernelWidth/2);
+
+ for(mcx=0 ; !m_cancel && (mcx < kernelWidth) ; mcx++, sx++)
+ {
+ mx = sx < 0 ? 0 : sx > (int)m_destImage.width()-1 ? m_destImage.width()-1 : sx;
+ color = m_orgImage.getPixelColor(mx, my);
+ red += (*k)*(color.red() * 257.0);
+ green += (*k)*(color.green() * 257.0);
+ blue += (*k)*(color.blue() * 257.0);
+ alpha += (*k)*(color.alpha() * 257.0);
+ k++;
+ }
+ }
+
+ red = red < 0.0 ? 0.0 : red > maxClamp ? maxClamp : red+0.5;
+ green = green < 0.0 ? 0.0 : green > maxClamp ? maxClamp : green+0.5;
+ blue = blue < 0.0 ? 0.0 : blue > maxClamp ? maxClamp : blue+0.5;
+ alpha = alpha < 0.0 ? 0.0 : alpha > maxClamp ? maxClamp : alpha+0.5;
+
+ m_destImage.setPixelColor(x, y, Digikam::DColor((int)(red / 257UL), (int)(green / 257UL),
+ (int)(blue / 257UL), (int)(alpha / 257UL),
+ m_destImage.sixteenBit()));
+ }
+
+ progress = (int)(((double)y * 50.0) / m_destImage.height());
+ if ( progress%5 == 0 )
+ postProgress( progress );
+ }
+
+ delete [] normal_kernel;
+ return(true);
+}
+
+int Charcoal::getOptimalKernelWidth(double radius, double sigma)
+{
+ double normalize, value;
+ long kernelWidth;
+ long u;
+
+ if(radius > 0.0)
+ return((int)(2.0*ceil(radius)+1.0));
+
+ for(kernelWidth=5; ;)
+ {
+ normalize=0.0;
+
+ for(u=(-kernelWidth/2) ; u <= (kernelWidth/2) ; u++)
+ normalize += exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma);
+
+ u = kernelWidth/2;
+ value = exp(-((double) u*u)/(2.0*sigma*sigma))/(SQ2PI*sigma)/normalize;
+
+ if((long)(65535*value) <= 0)
+ break;
+
+ kernelWidth+=2;
+ }
+
+ return((int)kernelWidth-2);
+}
+
+} // NameSpace DigikamCharcoalImagesPlugin