You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
769 lines
18 KiB
769 lines
18 KiB
/* ============================================================
|
|
*
|
|
* 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>
|
|
|
|
// TQt includes.
|
|
|
|
#include <tqfile.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;
|
|
}
|
|
|
|
TQPoint ImageCurves::getCurvePoint(int channel, int point)
|
|
{
|
|
if ( d->curves &&
|
|
channel>=0 && channel<5 &&
|
|
point>=0 && point<=17 )
|
|
return(TQPoint(d->curves->points[channel][point][0],
|
|
d->curves->points[channel][point][1]) );
|
|
|
|
return TQPoint(-1, -1);
|
|
}
|
|
|
|
TQPointArray ImageCurves::getCurvePoints(int channel)
|
|
{
|
|
TQPointArray 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 TQPoint& 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 TQPointArray& 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(TQFile::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(TQFile::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
|