summaryrefslogtreecommitdiffstats
path: root/digikam/libs/curves/imagecurves.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'digikam/libs/curves/imagecurves.cpp')
-rw-r--r--digikam/libs/curves/imagecurves.cpp768
1 files changed, 768 insertions, 0 deletions
diff --git a/digikam/libs/curves/imagecurves.cpp b/digikam/libs/curves/imagecurves.cpp
new file mode 100644
index 0000000..a41f55c
--- /dev/null
+++ b/digikam/libs/curves/imagecurves.cpp
@@ -0,0 +1,768 @@
+/* ============================================================
+ *
+ * This file is a part of digiKam project
+ * http://www.digikam.org
+ *
+ * Date : 2004-12-01
+ * Description : image curves manipulation methods.
+ *
+ * Copyright (C) 2004-2008 by Gilles Caulier <caulier dot gilles at gmail dot com>
+ *
+ * Some code parts are inspired from gimp 2.0
+ * app/base/curves.c, gimplut.c, and app/base/gimpcurvetool.c
+ * source files.
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * 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 CLAMP(x,l,u) ((x)<(l)?(l):((x)>(u)?(u):(x)))
+
+// C++ includes.
+
+#include <cstdio>
+#include <cmath>
+#include <cstring>
+#include <cstdlib>
+#include <cerrno>
+
+// Qt includes.
+
+#include <qfile.h>
+
+// Local includes.
+
+#include "ddebug.h"
+#include "imagecurves.h"
+
+namespace Digikam
+{
+
+class ImageCurvesPriv
+{
+
+public:
+
+ struct _Curves
+ {
+ ImageCurves::CurveType curve_type[5]; // Curve types by channels (Smooth or Free).
+ int points[5][17][2]; // Curve main points in Smooth mode ([channel][point id][x,y]).
+ unsigned short curve[5][65536]; // Curve values by channels.
+ };
+
+ struct _Lut
+ {
+ unsigned short **luts;
+ int nchannels;
+ };
+
+public:
+
+ ImageCurvesPriv()
+ {
+ curves = 0;
+ lut = 0;
+ dirty = false;
+ }
+
+ // Curves data.
+ struct _Curves *curves;
+
+ // Lut data.
+ struct _Lut *lut;
+
+ int segmentMax;
+
+ bool dirty;
+};
+
+ImageCurves::CRMatrix CR_basis =
+{
+ { -0.5, 1.5, -1.5, 0.5 },
+ { 1.0, -2.5, 2.0, -0.5 },
+ { -0.5, 0.0, 0.5, 0.0 },
+ { 0.0, 1.0, 0.0, 0.0 },
+};
+
+ImageCurves::ImageCurves(bool sixteenBit)
+{
+ d = new ImageCurvesPriv;
+ d->lut = new ImageCurvesPriv::_Lut;
+ d->curves = new ImageCurvesPriv::_Curves;
+ d->segmentMax = sixteenBit ? 65535 : 255;
+
+ curvesReset();
+}
+
+ImageCurves::~ImageCurves()
+{
+ if (d->lut)
+ {
+ if (d->lut->luts)
+ {
+ for (int i = 0 ; i < d->lut->nchannels ; i++)
+ delete [] d->lut->luts[i];
+
+ delete [] d->lut->luts;
+ }
+
+ delete d->lut;
+ }
+
+ if (d->curves)
+ delete d->curves;
+
+ delete d;
+}
+
+bool ImageCurves::isDirty()
+{
+ return d->dirty;
+}
+
+bool ImageCurves::isSixteenBits()
+{
+ return (d->segmentMax == 65535);
+}
+
+void ImageCurves::curvesReset()
+{
+ memset(d->curves, 0, sizeof(struct ImageCurvesPriv::_Curves));
+ d->lut->luts = NULL;
+ d->lut->nchannels = 0;
+ d->dirty = false;
+
+ for (int channel = 0 ; channel < 5 ; channel++)
+ {
+ setCurveType(channel, CURVE_SMOOTH);
+ curvesChannelReset(channel);
+ }
+}
+
+void ImageCurves::curvesChannelReset(int channel)
+{
+ int j;
+
+ if (!d->curves) return;
+
+ // Contruct a linear curve.
+
+ for (j = 0 ; j <= d->segmentMax ; j++)
+ d->curves->curve[channel][j] = j;
+
+ // Init coordinates points to null.
+
+ for (j = 0 ; j < 17 ; j++)
+ {
+ d->curves->points[channel][j][0] = -1;
+ d->curves->points[channel][j][1] = -1;
+ }
+
+ // First and last points init.
+
+ d->curves->points[channel][0][0] = 0;
+ d->curves->points[channel][0][1] = 0;
+ d->curves->points[channel][16][0] = d->segmentMax;
+ d->curves->points[channel][16][1] = d->segmentMax;
+}
+
+void ImageCurves::curvesCalculateCurve(int channel)
+{
+ int i;
+ int points[17];
+ int num_pts;
+ int p1, p2, p3, p4;
+
+ if (!d->curves) return;
+
+ switch (d->curves->curve_type[channel])
+ {
+ case CURVE_FREE:
+ break;
+
+ case CURVE_SMOOTH:
+ {
+ // Cycle through the curves
+
+ num_pts = 0;
+
+ for (i = 0 ; i < 17 ; i++)
+ if (d->curves->points[channel][i][0] != -1)
+ points[num_pts++] = i;
+
+ // Initialize boundary curve points
+
+ if (num_pts != 0)
+ {
+ for (i = 0 ; i < d->curves->points[channel][points[0]][0] ; i++)
+ {
+ d->curves->curve[channel][i] = d->curves->points[channel][points[0]][1];
+ }
+
+ for (i = d->curves->points[channel][points[num_pts - 1]][0] ; i <= d->segmentMax ; i++)
+ {
+ d->curves->curve[channel][i] = d->curves->points[channel][points[num_pts - 1]][1];
+ }
+ }
+
+ for (i = 0 ; i < num_pts - 1 ; i++)
+ {
+ p1 = (i == 0) ? points[i] : points[(i - 1)];
+ p2 = points[i];
+ p3 = points[(i + 1)];
+ p4 = (i == (num_pts - 2)) ? points[(num_pts - 1)] : points[(i + 2)];
+
+ curvesPlotCurve(channel, p1, p2, p3, p4);
+ }
+
+ // Ensure that the control points are used exactly
+
+ for (i = 0 ; i < num_pts ; i++)
+ {
+ int x, y;
+
+ x = d->curves->points[channel][points[i]][0];
+ y = d->curves->points[channel][points[i]][1];
+ d->curves->curve[channel][x] = y;
+ }
+
+ break;
+ }
+ }
+}
+
+float ImageCurves::curvesLutFunc(int n_channels, int channel, float value)
+{
+ float f;
+ int index;
+ double inten;
+ int j;
+
+ if (!d->curves) return 0.0;
+
+ if (n_channels == 1)
+ j = 0;
+ else
+ j = channel + 1;
+
+ inten = value;
+
+ // For color images this runs through the loop with j = channel +1
+ // the first time and j = 0 the second time.
+
+ // For bw images this runs through the loop with j = 0 the first and
+ // only time.
+
+ for ( ; j >= 0 ; j -= (channel + 1))
+ {
+ // Don't apply the overall curve to the alpha channel.
+
+ if (j == 0 && (n_channels == 2 || n_channels == 4) && channel == n_channels -1)
+ return inten;
+
+ if (inten < 0.0)
+ inten = d->curves->curve[j][0]/(float)d->segmentMax;
+ else if (inten >= 1.0)
+ inten = d->curves->curve[j][d->segmentMax]/(float)(d->segmentMax);
+ else // interpolate the curve.
+ {
+ index = (int)floor(inten * (float)(d->segmentMax));
+ f = inten * (float)(d->segmentMax) - index;
+ inten = ((1.0 - f) * d->curves->curve[j][index ] +
+ ( f) * d->curves->curve[j][index + 1] ) / (float)(d->segmentMax);
+ }
+ }
+
+ return inten;
+}
+
+void ImageCurves::curvesPlotCurve(int channel, int p1, int p2, int p3, int p4)
+{
+ CRMatrix geometry;
+ CRMatrix tmp1, tmp2;
+ CRMatrix deltas;
+ double x, dx, dx2, dx3;
+ double y, dy, dy2, dy3;
+ double d1, d2, d3;
+ int lastx, lasty;
+ int newx, newy;
+ int i;
+ int loopdiv = d->segmentMax * 3;
+
+ if (!d->curves) return;
+
+ // Construct the geometry matrix from the segment.
+
+ for (i = 0 ; i < 4 ; i++)
+ {
+ geometry[i][2] = 0;
+ geometry[i][3] = 0;
+ }
+
+ for (i = 0 ; i < 2 ; i++)
+ {
+ geometry[0][i] = d->curves->points[channel][p1][i];
+ geometry[1][i] = d->curves->points[channel][p2][i];
+ geometry[2][i] = d->curves->points[channel][p3][i];
+ geometry[3][i] = d->curves->points[channel][p4][i];
+ }
+
+ // Subdivide the curve 1000 times.
+ // n can be adjusted to give a finer or coarser curve.
+
+ d1 = 1.0 / loopdiv;
+ d2 = d1 * d1;
+ d3 = d1 * d1 * d1;
+
+ // Construct a temporary matrix for determining the forward differencing deltas.
+
+ tmp2[0][0] = 0; tmp2[0][1] = 0; tmp2[0][2] = 0; tmp2[0][3] = 1;
+ tmp2[1][0] = d3; tmp2[1][1] = d2; tmp2[1][2] = d1; tmp2[1][3] = 0;
+ tmp2[2][0] = 6*d3; tmp2[2][1] = 2*d2; tmp2[2][2] = 0; tmp2[2][3] = 0;
+ tmp2[3][0] = 6*d3; tmp2[3][1] = 0; tmp2[3][2] = 0; tmp2[3][3] = 0;
+
+ // Compose the basis and geometry matrices.
+
+ curvesCRCompose(CR_basis, geometry, tmp1);
+
+ // Compose the above results to get the deltas matrix.
+
+ curvesCRCompose(tmp2, tmp1, deltas);
+
+ // Extract the x deltas.
+
+ x = deltas[0][0];
+ dx = deltas[1][0];
+ dx2 = deltas[2][0];
+ dx3 = deltas[3][0];
+
+ // Extract the y deltas.
+
+ y = deltas[0][1];
+ dy = deltas[1][1];
+ dy2 = deltas[2][1];
+ dy3 = deltas[3][1];
+
+ lastx = (int)CLAMP (x, 0, d->segmentMax);
+ lasty = (int)CLAMP (y, 0, d->segmentMax);
+
+ d->curves->curve[channel][lastx] = lasty;
+
+ // Loop over the curve.
+
+ for (i = 0 ; i < loopdiv ; i++)
+ {
+ // Increment the x values.
+
+ x += dx;
+ dx += dx2;
+ dx2 += dx3;
+
+ // Increment the y values.
+
+ y += dy;
+ dy += dy2;
+ dy2 += dy3;
+
+ newx = CLAMP(ROUND (x), 0, d->segmentMax);
+ newy = CLAMP(ROUND (y), 0, d->segmentMax);
+
+ // If this point is different than the last one...then draw it.
+
+ if ((lastx != newx) || (lasty != newy))
+ d->curves->curve[channel][newx] = newy;
+
+ lastx = newx;
+ lasty = newy;
+ }
+}
+
+void ImageCurves::curvesCRCompose(CRMatrix a, CRMatrix b, CRMatrix ab)
+{
+ int i, j;
+
+ for (i = 0 ; i < 4 ; i++)
+ {
+ for (j = 0 ; j < 4 ; j++)
+ {
+ ab[i][j] = (a[i][0] * b[0][j] +
+ a[i][1] * b[1][j] +
+ a[i][2] * b[2][j] +
+ a[i][3] * b[3][j]);
+ }
+ }
+}
+
+void ImageCurves::curvesLutSetup(int nchannels)
+{
+ int i;
+ uint v;
+ double val;
+
+ if (d->lut->luts)
+ {
+ for (i = 0 ; i < d->lut->nchannels ; i++)
+ delete [] d->lut->luts[i];
+
+ delete [] d->lut->luts;
+ }
+
+ d->lut->nchannels = nchannels;
+ d->lut->luts = new unsigned short*[d->lut->nchannels];
+
+ for (i = 0 ; i < d->lut->nchannels ; i++)
+ {
+ d->lut->luts[i] = new unsigned short[d->segmentMax+1];
+
+ for (v = 0 ; v <= (uint)d->segmentMax ; v++)
+ {
+ // To add gamma correction use func(v ^ g) ^ 1/g instead.
+
+ val = (float)(d->segmentMax) * curvesLutFunc( d->lut->nchannels, i, v / (float)(d->segmentMax)) + 0.5;
+
+ d->lut->luts[i][v] = (unsigned short)CLAMP (val, 0, d->segmentMax);
+ }
+ }
+}
+
+void ImageCurves::curvesLutProcess(uchar *srcPR, uchar *destPR, int w, int h)
+{
+ unsigned short *lut0 = NULL, *lut1 = NULL, *lut2 = NULL, *lut3 = NULL;
+
+ int i;
+
+ if (d->lut->nchannels > 0)
+ lut0 = d->lut->luts[0];
+ if (d->lut->nchannels > 1)
+ lut1 = d->lut->luts[1];
+ if (d->lut->nchannels > 2)
+ lut2 = d->lut->luts[2];
+ if (d->lut->nchannels > 3)
+ lut3 = d->lut->luts[3];
+
+ if (d->segmentMax == 255) // 8 bits image.
+ {
+ uchar red, green, blue, alpha;
+ uchar *ptr = srcPR;
+ uchar *dst = destPR;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if ( d->lut->nchannels > 0 )
+ red = lut0[red];
+
+ if ( d->lut->nchannels > 1 )
+ green = lut1[green];
+
+ if ( d->lut->nchannels > 2 )
+ blue = lut2[blue];
+
+ if ( d->lut->nchannels > 3 )
+ alpha = lut3[alpha];
+
+ dst[0] = blue;
+ dst[1] = green;
+ dst[2] = red;
+ dst[3] = alpha;
+
+ ptr += 4;
+ dst += 4;
+ }
+ }
+ else // 16 bits image.
+ {
+ unsigned short red, green, blue, alpha;
+ unsigned short *ptr = (unsigned short *)srcPR;
+ unsigned short *dst = (unsigned short *)destPR;
+
+ for (i = 0 ; i < w*h ; i++)
+ {
+ blue = ptr[0];
+ green = ptr[1];
+ red = ptr[2];
+ alpha = ptr[3];
+
+ if ( d->lut->nchannels > 0 )
+ red = lut0[red];
+
+ if ( d->lut->nchannels > 1 )
+ green = lut1[green];
+
+ if ( d->lut->nchannels > 2 )
+ blue = lut2[blue];
+
+ if ( d->lut->nchannels > 3 )
+ alpha = lut3[alpha];
+
+ dst[0] = blue;
+ dst[1] = green;
+ dst[2] = red;
+ dst[3] = alpha;
+
+ ptr += 4;
+ dst += 4;
+ }
+ }
+}
+
+int ImageCurves::getCurveValue(int channel, int bin)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ bin>=0 && bin<=d->segmentMax )
+ return(d->curves->curve[channel][bin]);
+
+ return 0;
+}
+
+QPoint ImageCurves::getCurvePoint(int channel, int point)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ point>=0 && point<=17 )
+ return(QPoint(d->curves->points[channel][point][0],
+ d->curves->points[channel][point][1]) );
+
+ return QPoint(-1, -1);
+}
+
+QPointArray ImageCurves::getCurvePoints(int channel)
+{
+ QPointArray array(18);
+
+ if ( d->curves &&
+ channel>=0 && channel<5)
+ {
+ for (int j = 0 ; j <= 17 ; j++)
+ array.setPoint(j, getCurvePoint(channel, j));
+ }
+
+ return array;
+}
+
+int ImageCurves::getCurvePointX(int channel, int point)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ point>=0 && point<=17 )
+ return(d->curves->points[channel][point][0]);
+
+ return(-1);
+}
+
+int ImageCurves::getCurvePointY(int channel, int point)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ point>=0 && point<=17 )
+ return(d->curves->points[channel][point][1]);
+
+ return (-1);
+}
+
+int ImageCurves::getCurveType(int channel)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 )
+ return ( d->curves->curve_type[channel] );
+
+ return (-1);
+}
+
+void ImageCurves::setCurveValue(int channel, int bin, int val)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ bin>=0 && bin<=d->segmentMax )
+ {
+ d->dirty = true;
+ d->curves->curve[channel][bin] = val;
+ }
+}
+
+void ImageCurves::setCurvePoint(int channel, int point, const QPoint& val)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ point>=0 && point<=17 &&
+ val.x()>=-1 && val.x()<=d->segmentMax && // x can be egal to -1
+ val.y()>=0 && val.y()<=d->segmentMax) // if the current point is disable !!!
+ {
+ d->dirty = true;
+ d->curves->points[channel][point][0] = val.x();
+ d->curves->points[channel][point][1] = val.y();
+ }
+}
+
+void ImageCurves::setCurvePoints(int channel, const QPointArray& vals)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ vals.size() == 18 )
+ {
+ d->dirty = true;
+ for (int j = 0 ; j <= 17 ; j++)
+ {
+ setCurvePoint(channel, j, vals.point(j));
+ }
+ }
+}
+
+void ImageCurves::setCurvePointX(int channel, int point, int x)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ point>=0 && point<=17 &&
+ x>=-1 && x<=d->segmentMax) // x can be egal to -1 if the current point is disable !!!
+ {
+ d->dirty = true;
+ d->curves->points[channel][point][0] = x;
+ }
+}
+
+void ImageCurves::setCurvePointY(int channel, int point, int y)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ point>=0 && point<=17 &&
+ y>=0 && y<=d->segmentMax)
+ {
+ d->dirty = true;
+ d->curves->points[channel][point][1] = y;
+ }
+}
+
+void ImageCurves::setCurveType(int channel, CurveType type)
+{
+ if ( d->curves &&
+ channel>=0 && channel<5 &&
+ type>=CURVE_SMOOTH && type<=CURVE_FREE )
+ d->curves->curve_type[channel] = type;
+}
+
+bool ImageCurves::loadCurvesFromGimpCurvesFile(const KURL& fileUrl)
+{
+ // TODO : support KURL !
+
+ FILE *file;
+ int i, j;
+ int fields;
+ char buf[50];
+ int index[5][17];
+ int value[5][17];
+
+ file = fopen(QFile::encodeName(fileUrl.path()), "r");
+ if (!file)
+ return false;
+
+ if (! fgets (buf, sizeof (buf), file))
+ {
+ fclose(file);
+ return false;
+ }
+
+ if (strcmp (buf, "# GIMP Curves File\n") != 0)
+ return false;
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ for (j = 0 ; j < 17 ; j++)
+ {
+ fields = fscanf (file, "%d %d ", &index[i][j], &value[i][j]);
+ if (fields != 2)
+ {
+ DWarning() << "Invalid Gimp curves file!" << endl;
+ fclose(file);
+ return false;
+ }
+ }
+ }
+
+ curvesReset();
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ d->curves->curve_type[i] = CURVE_SMOOTH;
+
+ for (j = 0 ; j < 17 ; j++)
+ {
+ d->curves->points[i][j][0] = ((d->segmentMax == 65535) && (index[i][j] !=-1) ?
+ index[i][j]*255 : index[i][j]);
+ d->curves->points[i][j][1] = ((d->segmentMax == 65535) && (value[i][j] !=-1) ?
+ value[i][j]*255 : value[i][j]);
+ }
+ }
+
+ for (i = 0 ; i < 5 ; i++)
+ curvesCalculateCurve(i);
+
+ fclose(file);
+ return true;
+}
+
+bool ImageCurves::saveCurvesToGimpCurvesFile(const KURL& fileUrl)
+{
+ // TODO : support KURL !
+
+ FILE *file;
+ int i, j;
+ int index;
+
+ file = fopen(QFile::encodeName(fileUrl.path()), "w");
+
+ if (!file)
+ return false;
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ if (d->curves->curve_type[i] == CURVE_FREE)
+ {
+ // Pick representative points from the curve and make them control points.
+
+ for (j = 0 ; j <= 8 ; j++)
+ {
+ index = CLAMP(j * 32, 0, d->segmentMax);
+ d->curves->points[i][j * 2][0] = index;
+ d->curves->points[i][j * 2][1] = d->curves->curve[i][index];
+ }
+ }
+ }
+
+ fprintf (file, "# GIMP Curves File\n");
+
+ for (i = 0 ; i < 5 ; i++)
+ {
+ for (j = 0 ; j < 17 ; j++)
+ {
+ fprintf (file, "%d %d ",
+ ((d->segmentMax == 65535) && (d->curves->points[i][j][0]!=-1) ?
+ d->curves->points[i][j][0]/255 : d->curves->points[i][j][0]),
+ ((d->segmentMax == 65535) && (d->curves->points[i][j][1]!=-1) ?
+ d->curves->points[i][j][1]/255 : d->curves->points[i][j][1]));
+
+ fprintf (file, "\n");
+ }
+ }
+
+ fflush(file);
+ fclose(file);
+
+ return true;
+}
+
+} // NameSpace Digikam