diff options
Diffstat (limited to 'src/main/curve.c')
-rw-r--r-- | src/main/curve.c | 1901 |
1 files changed, 1901 insertions, 0 deletions
diff --git a/src/main/curve.c b/src/main/curve.c new file mode 100644 index 0000000..03c2ede --- /dev/null +++ b/src/main/curve.c @@ -0,0 +1,1901 @@ +/* + * "$Id: curve.c,v 1.51 2005/04/10 22:57:40 rlk Exp $" + * + * Print plug-in driver utility functions for the GIMP. + * + * Copyright 1997-2000 Michael Sweet (mike@easysw.com) and + * Robert Krawitz (rlk@alum.mit.edu) + * + * 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 of the License, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <gutenprint/gutenprint.h> +#include "gutenprint-internal.h" +#include <gutenprint/gutenprint-intl-internal.h> +#include <math.h> +#ifdef sun +#include <ieeefp.h> +#endif +#include <string.h> +#include <stdlib.h> +#include <limits.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef __GNUC__ +#define inline __inline__ +#endif + +#undef inline +#define inline + +static const int curve_point_limit = 1048576; + +struct stp_curve +{ + stp_curve_type_t curve_type; + stp_curve_wrap_mode_t wrap_mode; + int piecewise; + int recompute_interval; /* Do we need to recompute the deltas? */ + double gamma; /* 0.0 means that the curve is not a gamma */ + stp_sequence_t *seq; /* Sequence (contains the curve data) */ + double *interval; /* We allocate an extra slot for the + wrap-around value. */ + +}; + +static const char *const stpi_curve_type_names[] = + { + "linear", + "spline", + }; + +static const int stpi_curve_type_count = +(sizeof(stpi_curve_type_names) / sizeof(const char *)); + +static const char *const stpi_wrap_mode_names[] = + { + "nowrap", + "wrap" + }; + +static const int stpi_wrap_mode_count = +(sizeof(stpi_wrap_mode_names) / sizeof(const char *)); + +/* + * We could do more sanity checks here if we want. + */ +static void +check_curve(const stp_curve_t *curve) +{ + if (curve == NULL) + { + stp_erprintf("Null curve! Please report this bug.\n"); + stp_abort(); + } + if (curve->seq == NULL) + { + stp_erprintf("Bad curve (seq == NULL)! Please report this bug.\n"); + stp_abort(); + } +} + +/* + * Get the total number of points in the base sequence class + */ +static size_t +get_real_point_count(const stp_curve_t *curve) +{ + if (curve->piecewise) + return stp_sequence_get_size(curve->seq) / 2; + else + return stp_sequence_get_size(curve->seq); +} + +/* + * Get the number of points used by the curve (that are visible to the + * user). This is the real point count, but is decreased by 1 if the + * curve wraps around. + */ +static size_t +get_point_count(const stp_curve_t *curve) +{ + size_t count = get_real_point_count(curve); + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + count -= 1; + + return count; +} + +static void +invalidate_auxiliary_data(stp_curve_t *curve) +{ + STP_SAFE_FREE(curve->interval); +} + +static void +clear_curve_data(stp_curve_t *curve) +{ + if (curve->seq) + stp_sequence_set_size(curve->seq, 0); + curve->recompute_interval = 0; + invalidate_auxiliary_data(curve); +} + +static void +compute_linear_deltas(stp_curve_t *curve) +{ + int i; + size_t delta_count; + size_t seq_point_count; + const double *data; + + stp_sequence_get_data(curve->seq, &seq_point_count, &data); + if (data == NULL) + return; + + delta_count = get_real_point_count(curve); + + if (delta_count <= 1) /* No intervals can be computed */ + return; + delta_count--; /* One less than the real point count. Note size_t + is unsigned. */ + + curve->interval = stp_malloc(sizeof(double) * delta_count); + for (i = 0; i < delta_count; i++) + { + if (curve->piecewise) + curve->interval[i] = data[(2 * (i + 1)) + 1] - data[(2 * i) + 1]; + else + curve->interval[i] = data[i + 1] - data[i]; + } +} + +static void +compute_spline_deltas_piecewise(stp_curve_t *curve) +{ + int i; + int k; + double *u; + double *y2; + const double *data = NULL; + const stp_curve_point_t *dp; + size_t point_count; + size_t real_point_count; + double sig; + double p; + + point_count = get_point_count(curve); + + stp_sequence_get_data(curve->seq, &real_point_count, &data); + dp = (const stp_curve_point_t *)data; + real_point_count = real_point_count / 2; + + u = stp_malloc(sizeof(double) * real_point_count); + y2 = stp_malloc(sizeof(double) * real_point_count); + + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + { + int reps = 3; + int count = reps * real_point_count; + double *y2a = stp_malloc(sizeof(double) * count); + double *ua = stp_malloc(sizeof(double) * count); + y2a[0] = 0.0; + ua[0] = 0.0; + for (i = 1; i < count - 1; i++) + { + int im1 = (i - 1) % point_count; + int ia = i % point_count; + int ip1 = (i + 1) % point_count; + + sig = (dp[ia].x - dp[im1].x) / (dp[ip1].x - dp[im1].x); + p = sig * y2a[im1] + 2.0; + y2a[i] = (sig - 1.0) / p; + + ua[i] = ((dp[ip1].y - dp[ia].y) / (dp[ip1].x - dp[ia].x)) - + ((dp[ia].y - dp[im1].y) / (dp[ia].x - dp[im1].x)); + ua[i] = + (((6.0 * ua[ia]) / (dp[ip1].x - dp[im1].x)) - (sig * ua[im1])) / p; + } + y2a[count - 1] = 0.0; + for (k = count - 2 ; k >= 0; k--) + y2a[k] = y2a[k] * y2a[k + 1] + ua[k]; + memcpy(u, ua + ((reps / 2) * point_count), + sizeof(double) * real_point_count); + memcpy(y2, y2a + ((reps / 2) * point_count), + sizeof(double) * real_point_count); + stp_free(y2a); + stp_free(ua); + } + else + { + int count = real_point_count - 1; + + y2[0] = 0; + u[0] = 2 * (dp[1].y - dp[0].y); + for (i = 1; i < count; i++) + { + int im1 = (i - 1); + int ip1 = (i + 1); + + sig = (dp[i].x - dp[im1].x) / (dp[ip1].x - dp[im1].x); + p = sig * y2[im1] + 2.0; + y2[i] = (sig - 1.0) / p; + + u[i] = ((dp[ip1].y - dp[i].y) / (dp[ip1].x - dp[i].x)) - + ((dp[i].y - dp[im1].y) / (dp[i].x - dp[im1].x)); + u[i] = + (((6.0 * u[i]) / (dp[ip1].x - dp[im1].x)) - (sig * u[im1])) / p; + stp_deprintf(STP_DBG_CURVE, + "%d sig %f p %f y2 %f u %f x %f %f %f y %f %f %f\n", + i, sig, p, y2[i], u[i], + dp[im1].x, dp[i].x, dp[ip1].x, + dp[im1].y, dp[i].y, dp[ip1].y); + } + y2[count] = 0.0; + u[count] = 0.0; + for (k = real_point_count - 2; k >= 0; k--) + y2[k] = y2[k] * y2[k + 1] + u[k]; + } + + curve->interval = y2; + stp_free(u); +} + +static void +compute_spline_deltas_dense(stp_curve_t *curve) +{ + int i; + int k; + double *u; + double *y2; + const double *y; + size_t point_count; + size_t real_point_count; + double sig; + double p; + + point_count = get_point_count(curve); + + stp_sequence_get_data(curve->seq, &real_point_count, &y); + u = stp_malloc(sizeof(double) * real_point_count); + y2 = stp_malloc(sizeof(double) * real_point_count); + + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + { + int reps = 3; + int count = reps * real_point_count; + double *y2a = stp_malloc(sizeof(double) * count); + double *ua = stp_malloc(sizeof(double) * count); + y2a[0] = 0.0; + ua[0] = 0.0; + for (i = 1; i < count - 1; i++) + { + int im1 = (i - 1); + int ip1 = (i + 1); + int im1a = im1 % point_count; + int ia = i % point_count; + int ip1a = ip1 % point_count; + + sig = (i - im1) / (ip1 - im1); + p = sig * y2a[im1] + 2.0; + y2a[i] = (sig - 1.0) / p; + + ua[i] = y[ip1a] - 2 * y[ia] + y[im1a]; + ua[i] = 3.0 * ua[i] - sig * ua[im1] / p; + } + y2a[count - 1] = 0.0; + for (k = count - 2 ; k >= 0; k--) + y2a[k] = y2a[k] * y2a[k + 1] + ua[k]; + memcpy(u, ua + ((reps / 2) * point_count), + sizeof(double) * real_point_count); + memcpy(y2, y2a + ((reps / 2) * point_count), + sizeof(double) * real_point_count); + stp_free(y2a); + stp_free(ua); + } + else + { + int count = real_point_count - 1; + + y2[0] = 0; + u[0] = 2 * (y[1] - y[0]); + for (i = 1; i < count; i++) + { + int im1 = (i - 1); + int ip1 = (i + 1); + + sig = (i - im1) / (ip1 - im1); + p = sig * y2[im1] + 2.0; + y2[i] = (sig - 1.0) / p; + + u[i] = y[ip1] - 2 * y[i] + y[im1]; + u[i] = 3.0 * u[i] - sig * u[im1] / p; + } + + u[count] = 2 * (y[count] - y[count - 1]); + y2[count] = 0.0; + + u[count] = 0.0; + for (k = real_point_count - 2; k >= 0; k--) + y2[k] = y2[k] * y2[k + 1] + u[k]; + } + + curve->interval = y2; + stp_free(u); +} + +static void +compute_spline_deltas(stp_curve_t *curve) +{ + if (curve->piecewise) + compute_spline_deltas_piecewise(curve); + else + compute_spline_deltas_dense(curve); +} + +/* + * Recompute the delta values for interpolation. + * When we actually do support spline curves, this routine will + * compute the second derivatives for that purpose, too. + */ +static void +compute_intervals(stp_curve_t *curve) +{ + if (curve->interval) + { + stp_free(curve->interval); + curve->interval = NULL; + } + if (stp_sequence_get_size(curve->seq) > 0) + { + switch (curve->curve_type) + { + case STP_CURVE_TYPE_SPLINE: + compute_spline_deltas(curve); + break; + case STP_CURVE_TYPE_LINEAR: + compute_linear_deltas(curve); + break; + } + } + curve->recompute_interval = 0; +} + +static int +stpi_curve_set_points(stp_curve_t *curve, size_t points) +{ + if (points < 2) + return 0; + if (points > curve_point_limit || + (curve->wrap_mode == STP_CURVE_WRAP_AROUND && + points > curve_point_limit - 1)) + return 0; + clear_curve_data(curve); + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + points++; + if (curve->piecewise) + points *= 2; + if ((stp_sequence_set_size(curve->seq, points)) == 0) + return 0; + return 1; +} + +/* + * Create a default curve + */ +static void +stpi_curve_ctor(stp_curve_t *curve, stp_curve_wrap_mode_t wrap_mode) +{ + curve->seq = stp_sequence_create(); + stp_sequence_set_bounds(curve->seq, 0.0, 1.0); + curve->curve_type = STP_CURVE_TYPE_LINEAR; + curve->wrap_mode = wrap_mode; + curve->piecewise = 0; + stpi_curve_set_points(curve, 2); + curve->recompute_interval = 1; + if (wrap_mode == STP_CURVE_WRAP_NONE) + curve->gamma = 1.0; + stp_sequence_set_point(curve->seq, 0, 0); + stp_sequence_set_point(curve->seq, 1, 1); +} + +stp_curve_t * +stp_curve_create(stp_curve_wrap_mode_t wrap_mode) +{ + stp_curve_t *ret; + if (wrap_mode != STP_CURVE_WRAP_NONE && wrap_mode != STP_CURVE_WRAP_AROUND) + return NULL; + ret = stp_zalloc(sizeof(stp_curve_t)); + stpi_curve_ctor(ret, wrap_mode); + return ret; +} + +static void +curve_dtor(stp_curve_t *curve) +{ + check_curve(curve); + clear_curve_data(curve); + if (curve->seq) + stp_sequence_destroy(curve->seq); + memset(curve, 0, sizeof(stp_curve_t)); + curve->curve_type = -1; +} + +void +stp_curve_destroy(stp_curve_t *curve) +{ + curve_dtor(curve); + stp_free(curve); +} + +void +stp_curve_copy(stp_curve_t *dest, const stp_curve_t *source) +{ + check_curve(dest); + check_curve(source); + curve_dtor(dest); + dest->curve_type = source->curve_type; + dest->wrap_mode = source->wrap_mode; + dest->gamma = source->gamma; + dest->seq = stp_sequence_create_copy(source->seq); + dest->piecewise = source->piecewise; + dest->recompute_interval = 1; +} + +stp_curve_t * +stp_curve_create_copy(const stp_curve_t *curve) +{ + stp_curve_t *ret; + check_curve(curve); + ret = stp_curve_create(curve->wrap_mode); + stp_curve_copy(ret, curve); + return ret; +} + +int +stp_curve_set_bounds(stp_curve_t *curve, double low, double high) +{ + check_curve(curve); + return stp_sequence_set_bounds(curve->seq, low, high); +} + +void +stp_curve_get_bounds(const stp_curve_t *curve, double *low, double *high) +{ + check_curve(curve); + stp_sequence_get_bounds(curve->seq, low, high); +} + +/* + * Find the minimum and maximum points on the curve. This does not + * attempt to find the minimum and maximum interpolations; with cubic + * splines these could exceed the boundaries. That's OK; the interpolation + * code will clip them to the bounds. + */ +void +stp_curve_get_range(const stp_curve_t *curve, double *low, double *high) +{ + check_curve(curve); + stp_sequence_get_range(curve->seq, low, high); +} + +size_t +stp_curve_count_points(const stp_curve_t *curve) +{ + check_curve(curve); + return get_point_count(curve); +} + +stp_curve_wrap_mode_t +stp_curve_get_wrap(const stp_curve_t *curve) +{ + check_curve(curve); + return curve->wrap_mode; +} + +int +stp_curve_is_piecewise(const stp_curve_t *curve) +{ + check_curve(curve); + return curve->piecewise; +} + +int +stp_curve_set_interpolation_type(stp_curve_t *curve, stp_curve_type_t itype) +{ + check_curve(curve); + if (itype < 0 || itype >= stpi_curve_type_count) + return 0; + curve->curve_type = itype; + return 1; +} + +stp_curve_type_t +stp_curve_get_interpolation_type(const stp_curve_t *curve) +{ + check_curve(curve); + return curve->curve_type; +} + +int +stp_curve_set_gamma(stp_curve_t *curve, double fgamma) +{ + check_curve(curve); + if (curve->wrap_mode || ! finite(fgamma) || fgamma == 0.0) + return 0; + clear_curve_data(curve); + curve->gamma = fgamma; + stp_curve_resample(curve, 2); + return 1; +} + +double +stp_curve_get_gamma(const stp_curve_t *curve) +{ + check_curve(curve); + return curve->gamma; +} + +int +stp_curve_set_data(stp_curve_t *curve, size_t count, const double *data) +{ + size_t i; + size_t real_count = count; + double low, high; + check_curve(curve); + if (count < 2) + return 0; + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + real_count++; + if (real_count > curve_point_limit) + return 0; + + /* Validate the data before we commit to it. */ + stp_sequence_get_bounds(curve->seq, &low, &high); + for (i = 0; i < count; i++) + if (! finite(data[i]) || data[i] < low || data[i] > high) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data: datum out of bounds: " + "%g (require %g <= x <= %g), n = %d\n", + data[i], low, high, i); + return 0; + } + /* Allocate sequence; also accounts for WRAP_MODE */ + stpi_curve_set_points(curve, count); + curve->gamma = 0.0; + stp_sequence_set_subrange(curve->seq, 0, count, data); + + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + stp_sequence_set_point(curve->seq, count, data[0]); + curve->recompute_interval = 1; + curve->piecewise = 0; + + return 1; +} + +int +stp_curve_set_data_points(stp_curve_t *curve, size_t count, + const stp_curve_point_t *data) +{ + size_t i; + size_t real_count = count; + double low, high; + double last_x = -1; + check_curve(curve); + if (count < 2) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: too few points %d\n", count); + return 0; + } + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + real_count++; + if (real_count > curve_point_limit) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: too many points %d\n", + real_count); + return 0; + } + + /* Validate the data before we commit to it. */ + stp_sequence_get_bounds(curve->seq, &low, &high); + for (i = 0; i < count; i++) + { + if (! finite(data[i].y) || data[i].y < low || data[i].y > high) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: datum out of bounds: " + "%g (require %g <= x <= %g), n = %d\n", + data[i].y, low, high, i); + return 0; + } + if (i == 0 && data[i].x != 0.0) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: first point must have x=0\n"); + return 0; + } + if (curve->wrap_mode == STP_CURVE_WRAP_NONE && i == count - 1 && + data[i].x != 1.0) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: last point must have x=1\n"); + return 0; + } + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND && + data[i].x >= 1.0 - .000001) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: horizontal value must " + "not exceed .99999\n"); + return 0; + } + if (data[i].x < 0 || data[i].x > 1) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: horizontal position out of bounds: " + "%g, n = %d\n", + data[i].x, i); + return 0; + } + if (data[i].x - .000001 < last_x) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_set_data_points: horizontal position must " + "exceed previous position by .000001: %g, %g, n = %d\n", + data[i].x, last_x, i); + return 0; + } + last_x = data[i].x; + } + /* Allocate sequence; also accounts for WRAP_MODE */ + curve->piecewise = 1; + stpi_curve_set_points(curve, count); + curve->gamma = 0.0; + stp_sequence_set_subrange(curve->seq, 0, count * 2, (const double *) data); + + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + { + stp_sequence_set_point(curve->seq, count * 2, data[0].x); + stp_sequence_set_point(curve->seq, count * 2 + 1, data[0].y); + } + curve->recompute_interval = 1; + + return 1; +} + + +/* + * Note that we return a pointer to the raw data here. + * A lot of operations change the data vector, that's why we don't + * guarantee it across non-const calls. + */ +const double * +stp_curve_get_data(const stp_curve_t *curve, size_t *count) +{ + const double *ret; + check_curve(curve); + if (curve->piecewise) + return NULL; + stp_sequence_get_data(curve->seq, count, &ret); + *count = get_point_count(curve); + return ret; +} + +const stp_curve_point_t * +stp_curve_get_data_points(const stp_curve_t *curve, size_t *count) +{ + const double *ret; + check_curve(curve); + if (!curve->piecewise) + return NULL; + stp_sequence_get_data(curve->seq, count, &ret); + *count = get_point_count(curve); + return (const stp_curve_point_t *) ret; +} + +static const double * +stpi_curve_get_data_internal(const stp_curve_t *curve, size_t *count) +{ + const double *ret; + check_curve(curve); + stp_sequence_get_data(curve->seq, count, &ret); + *count = get_point_count(curve); + if (curve->piecewise) + *count *= 2; + return ret; +} + + +/* "Overloaded" functions */ + +#define DEFINE_DATA_SETTER(t, name) \ +int \ +stp_curve_set_##name##_data(stp_curve_t *curve, size_t count, const t *data) \ +{ \ + double *tmp_data; \ + size_t i; \ + int status; \ + size_t real_count = count; \ + \ + check_curve(curve); \ + if (count < 2) \ + return 0; \ + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) \ + real_count++; \ + if (real_count > curve_point_limit) \ + return 0; \ + tmp_data = stp_malloc(count * sizeof(double)); \ + for (i = 0; i < count; i++) \ + tmp_data[i] = (double) data[i]; \ + status = stp_curve_set_data(curve, count, tmp_data); \ + stp_free(tmp_data); \ + return status; \ + } + +DEFINE_DATA_SETTER(float, float) +DEFINE_DATA_SETTER(long, long) +DEFINE_DATA_SETTER(unsigned long, ulong) +DEFINE_DATA_SETTER(int, int) +DEFINE_DATA_SETTER(unsigned int, uint) +DEFINE_DATA_SETTER(short, short) +DEFINE_DATA_SETTER(unsigned short, ushort) + + +#define DEFINE_DATA_ACCESSOR(t, name) \ +const t * \ +stp_curve_get_##name##_data(const stp_curve_t *curve, size_t *count) \ +{ \ + if (curve->piecewise) \ + return 0; \ + return stp_sequence_get_##name##_data(curve->seq, count); \ +} + +DEFINE_DATA_ACCESSOR(float, float) +DEFINE_DATA_ACCESSOR(long, long) +DEFINE_DATA_ACCESSOR(unsigned long, ulong) +DEFINE_DATA_ACCESSOR(int, int) +DEFINE_DATA_ACCESSOR(unsigned int, uint) +DEFINE_DATA_ACCESSOR(short, short) +DEFINE_DATA_ACCESSOR(unsigned short, ushort) + + +stp_curve_t * +stp_curve_get_subrange(const stp_curve_t *curve, size_t start, size_t count) +{ + stp_curve_t *retval; + size_t ncount; + double blo, bhi; + const double *data; + if (start + count > stp_curve_count_points(curve) || count < 2) + return NULL; + if (curve->piecewise) + return NULL; + retval = stp_curve_create(STP_CURVE_WRAP_NONE); + stp_curve_get_bounds(curve, &blo, &bhi); + stp_curve_set_bounds(retval, blo, bhi); + data = stp_curve_get_data(curve, &ncount); + if (! stp_curve_set_data(retval, count, data + start)) + { + stp_curve_destroy(retval); + return NULL; + } + return retval; +} + +int +stp_curve_set_subrange(stp_curve_t *curve, const stp_curve_t *range, + size_t start) +{ + double blo, bhi; + double rlo, rhi; + const double *data; + size_t count; + check_curve(curve); + if (start + stp_curve_count_points(range) > stp_curve_count_points(curve)) + return 0; + if (curve->piecewise) + return 0; + stp_sequence_get_bounds(curve->seq, &blo, &bhi); + stp_sequence_get_range(curve->seq, &rlo, &rhi); + if (rlo < blo || rhi > bhi) + return 0; + stp_sequence_get_data(range->seq, &count, &data); + curve->recompute_interval = 1; + curve->gamma = 0.0; + invalidate_auxiliary_data(curve); + stp_sequence_set_subrange(curve->seq, start, stp_curve_count_points(range), + data); + return 1; +} + + +int +stp_curve_set_point(stp_curve_t *curve, size_t where, double data) +{ + check_curve(curve); + if (where >= get_point_count(curve)) + return 0; + curve->gamma = 0.0; + + if (curve->piecewise) + return 0; + if ((stp_sequence_set_point(curve->seq, where, data)) == 0) + return 0; + if (where == 0 && curve->wrap_mode == STP_CURVE_WRAP_AROUND) + if ((stp_sequence_set_point(curve->seq, + get_point_count(curve), data)) == 0) + return 0; + invalidate_auxiliary_data(curve); + return 1; +} + +int +stp_curve_get_point(const stp_curve_t *curve, size_t where, double *data) +{ + check_curve(curve); + if (where >= get_point_count(curve)) + return 0; + if (curve->piecewise) + return 0; + return stp_sequence_get_point(curve->seq, where, data); +} + +const stp_sequence_t * +stp_curve_get_sequence(const stp_curve_t *curve) +{ + check_curve(curve); + if (curve->piecewise) + return NULL; + return curve->seq; +} + +int +stp_curve_rescale(stp_curve_t *curve, double scale, + stp_curve_compose_t mode, stp_curve_bounds_t bounds_mode) +{ + size_t real_point_count; + int i; + double nblo; + double nbhi; + size_t count; + + check_curve(curve); + + real_point_count = get_real_point_count(curve); + + stp_sequence_get_bounds(curve->seq, &nblo, &nbhi); + if (bounds_mode == STP_CURVE_BOUNDS_RESCALE) + { + switch (mode) + { + case STP_CURVE_COMPOSE_ADD: + nblo += scale; + nbhi += scale; + break; + case STP_CURVE_COMPOSE_MULTIPLY: + if (scale < 0) + { + double tmp = nblo * scale; + nblo = nbhi * scale; + nbhi = tmp; + } + else + { + nblo *= scale; + nbhi *= scale; + } + break; + case STP_CURVE_COMPOSE_EXPONENTIATE: + if (scale == 0.0) + return 0; + if (nblo < 0) + return 0; + nblo = pow(nblo, scale); + nbhi = pow(nbhi, scale); + break; + default: + return 0; + } + } + + if (! finite(nbhi) || ! finite(nblo)) + return 0; + + count = get_point_count(curve); + if (count) + { + double *tmp; + size_t scount; + int stride = 1; + int offset = 0; + const double *data; + if (curve->piecewise) + { + stride = 2; + offset = 1; + } + stp_sequence_get_data(curve->seq, &scount, &data); + tmp = stp_malloc(sizeof(double) * scount); + memcpy(tmp, data, scount * sizeof(double)); + for (i = offset; i < scount; i += stride) + { + switch (mode) + { + case STP_CURVE_COMPOSE_ADD: + tmp[i] = tmp[i] + scale; + break; + case STP_CURVE_COMPOSE_MULTIPLY: + tmp[i] = tmp[i] * scale; + break; + case STP_CURVE_COMPOSE_EXPONENTIATE: + tmp[i] = pow(tmp[i], scale); + break; + } + if (tmp[i] > nbhi || tmp[i] < nblo) + { + if (bounds_mode == STP_CURVE_BOUNDS_ERROR) + { + stp_free(tmp); + return(0); + } + else if (tmp[i] > nbhi) + tmp[i] = nbhi; + else + tmp[i] = nblo; + } + } + stp_sequence_set_bounds(curve->seq, nblo, nbhi); + curve->gamma = 0.0; + stpi_curve_set_points(curve, count); + stp_sequence_set_subrange(curve->seq, 0, scount, tmp); + stp_free(tmp); + curve->recompute_interval = 1; + invalidate_auxiliary_data(curve); + } + return 1; +} + +static int +stpi_curve_check_parameters(stp_curve_t *curve, size_t points) +{ + double blo, bhi; + if (curve->gamma && curve->wrap_mode) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "curve sets both gamma and wrap_mode\n"); + return 0; + } + stp_sequence_get_bounds(curve->seq, &blo, &bhi); + if (blo > bhi) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "curve low bound is greater than high bound\n"); + return 0; + } + return 1; +} + +static inline double +interpolate_gamma_internal(const stp_curve_t *curve, double where) +{ + double fgamma = curve->gamma; + double blo, bhi; + size_t real_point_count; + + real_point_count = get_real_point_count(curve);; + + if (real_point_count) + where /= (real_point_count - 1); + if (fgamma < 0) + { + where = 1.0 - where; + fgamma = -fgamma; + } + stp_sequence_get_bounds(curve->seq, &blo, &bhi); + stp_deprintf(STP_DBG_CURVE, + "interpolate_gamma %f %f %f %f %f\n", where, fgamma, + blo, bhi, pow(where, fgamma)); + return blo + (bhi - blo) * pow(where, fgamma); +} + +static inline double +do_interpolate_spline(double low, double high, double frac, + double interval_low, double interval_high, + double x_interval) +{ + double a = 1.0 - frac; + double b = frac; + double retval = + ((a * a * a - a) * interval_low) + ((b * b * b - b) * interval_high); + retval = retval * x_interval * x_interval / 6; + retval += (a * low) + (b * high); + return retval; +} + +static inline double +interpolate_point_internal(stp_curve_t *curve, double where) +{ + int integer = where; + double frac = where - (double) integer; + double bhi, blo; + + if (frac == 0.0) + { + double val; + if ((stp_sequence_get_point(curve->seq, integer, &val)) == 0) + return HUGE_VAL; /* Infinity */ + return val; + } + if (curve->recompute_interval) + compute_intervals(curve); + if (curve->curve_type == STP_CURVE_TYPE_LINEAR) + { + double val; + if ((stp_sequence_get_point(curve->seq, integer, &val)) == 0) + return HUGE_VAL; /* Infinity */ + return val + frac * curve->interval[integer]; + } + else + { + size_t point_count; + double ival, ip1val; + double retval; + int i = integer; + int ip1 = integer + 1; + + point_count = get_point_count(curve); + + if (ip1 >= point_count) + ip1 -= point_count; + + if ((stp_sequence_get_point(curve->seq, i, &ival)) == 0 || + (stp_sequence_get_point(curve->seq, ip1, &ip1val)) == 0) + return HUGE_VAL; /* Infinity */ + + retval = do_interpolate_spline(ival, ip1val, frac, curve->interval[i], + curve->interval[ip1], 1.0); + + stp_sequence_get_bounds(curve->seq, &blo, &bhi); + if (retval > bhi) + retval = bhi; + if (retval < blo) + retval = blo; + return retval; + } +} + +int +stp_curve_interpolate_value(const stp_curve_t *curve, double where, + double *result) +{ + size_t limit; + + check_curve(curve); + if (curve->piecewise) + return 0; + + limit = get_real_point_count(curve); + + if (where < 0 || where > limit) + return 0; + if (curve->gamma) /* this means a pure gamma curve */ + *result = interpolate_gamma_internal(curve, where); + else + *result = interpolate_point_internal((stp_curve_t *) curve, where); + return 1; +} + +int +stp_curve_resample(stp_curve_t *curve, size_t points) +{ + size_t limit = points; + size_t old; + size_t i; + double *new_vec; + + check_curve(curve); + + if (points == get_point_count(curve) && curve->seq && !(curve->piecewise)) + return 1; + + if (points < 2) + return 1; + + if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) + limit++; + if (limit > curve_point_limit) + return 0; + old = get_real_point_count(curve); + if (old) + old--; + if (!old) + old = 1; + + new_vec = stp_malloc(sizeof(double) * limit); + + /* + * Be very careful how we calculate the location along the scale! + * If we're not careful how we do it, we might get a small roundoff + * error + */ + if (curve->piecewise) + { + double blo, bhi; + int curpos = 0; + stp_sequence_get_bounds(curve->seq, &blo, &bhi); + if (curve->recompute_interval) + compute_intervals(curve); + for (i = 0; i < old; i++) + { + double low; + double high; + double low_y; + double high_y; + double x_delta; + if (!stp_sequence_get_point(curve->seq, i * 2, &low)) + { + stp_free(new_vec); + return 0; + } + if (i == old - 1) + high = 1.0; + else if (!stp_sequence_get_point(curve->seq, ((i + 1) * 2), &high)) + { + stp_free(new_vec); + return 0; + } + if (!stp_sequence_get_point(curve->seq, (i * 2) + 1, &low_y)) + { + stp_free(new_vec); + return 0; + } + if (!stp_sequence_get_point(curve->seq, ((i + 1) * 2) + 1, &high_y)) + { + stp_free(new_vec); + return 0; + } + stp_deprintf(STP_DBG_CURVE, + "Filling slots at %d %d: %f %f %f %f %d\n", + i,curpos, high, low, high_y, low_y, limit); + x_delta = high - low; + high *= (limit - 1); + low *= (limit - 1); + while (curpos <= high) + { + double frac = (curpos - low) / (high - low); + if (curve->curve_type == STP_CURVE_TYPE_LINEAR) + new_vec[curpos] = low_y + frac * (high_y - low_y); + else + new_vec[curpos] = + do_interpolate_spline(low_y, high_y, frac, + curve->interval[i], + curve->interval[i + 1], + x_delta); + if (new_vec[curpos] < blo) + new_vec[curpos] = blo; + if (new_vec[curpos] > bhi) + new_vec[curpos] = bhi; + stp_deprintf(STP_DBG_CURVE, + " Filling slot %d %f %f\n", + curpos, frac, new_vec[curpos]); + curpos++; + } + } + curve->piecewise = 0; + } + else + { + for (i = 0; i < limit; i++) + if (curve->gamma) + new_vec[i] = + interpolate_gamma_internal(curve, ((double) i * (double) old / + (double) (limit - 1))); + else + new_vec[i] = + interpolate_point_internal(curve, ((double) i * (double) old / + (double) (limit - 1))); + } + stpi_curve_set_points(curve, points); + stp_sequence_set_subrange(curve->seq, 0, limit, new_vec); + curve->recompute_interval = 1; + stp_free(new_vec); + return 1; +} + +static unsigned +gcd(unsigned a, unsigned b) +{ + unsigned tmp; + if (b > a) + { + tmp = a; + a = b; + b = tmp; + } + while (1) + { + tmp = a % b; + if (tmp == 0) + return b; + a = b; + b = tmp; + } +} + +static unsigned +lcm(unsigned a, unsigned b) +{ + if (a == b) + return a; + else if (a * b == 0) + return a > b ? a : b; + else + { + double rval = (double) a / gcd(a, b) * b; + if (rval > curve_point_limit) + return curve_point_limit; + else + return rval; + } +} + +static int +create_gamma_curve(stp_curve_t **retval, double lo, double hi, double fgamma, + int points) +{ + *retval = stp_curve_create(STP_CURVE_WRAP_NONE); + if (stp_curve_set_bounds(*retval, lo, hi) && + stp_curve_set_gamma(*retval, fgamma) && + stp_curve_resample(*retval, points)) + return 1; + stp_curve_destroy(*retval); + *retval = 0; + return 0; +} + +static int +interpolate_points(stp_curve_t *a, stp_curve_t *b, + stp_curve_compose_t mode, + int points, double *tmp_data) +{ + double pa, pb; + int i; + size_t points_a = stp_curve_count_points(a); + size_t points_b = stp_curve_count_points(b); + for (i = 0; i < points; i++) + { + if (!stp_curve_interpolate_value + (a, (double) i * (points_a - 1) / (points - 1), &pa)) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "interpolate_points: interpolate curve a value failed\n"); + return 0; + } + if (!stp_curve_interpolate_value + (b, (double) i * (points_b - 1) / (points - 1), &pb)) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "interpolate_points: interpolate curve b value failed\n"); + return 0; + } + if (mode == STP_CURVE_COMPOSE_ADD) + pa += pb; + else + pa *= pb; + if (! finite(pa)) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "interpolate_points: interpolated point %lu is invalid\n", + (unsigned long) i); + return 0; + } + tmp_data[i] = pa; + } + return 1; +} + +int +stp_curve_compose(stp_curve_t **retval, + stp_curve_t *a, stp_curve_t *b, + stp_curve_compose_t mode, int points) +{ + stp_curve_t *ret; + double *tmp_data; + double gamma_a = stp_curve_get_gamma(a); + double gamma_b = stp_curve_get_gamma(b); + unsigned points_a = stp_curve_count_points(a); + unsigned points_b = stp_curve_count_points(b); + double alo, ahi, blo, bhi; + + if (a->piecewise && b->piecewise) + return 0; + if (a->piecewise) + { + stp_curve_t *a_save = a; + a = stp_curve_create_copy(a_save); + stp_curve_resample(a, stp_curve_count_points(b)); + } + if (b->piecewise) + { + stp_curve_t *b_save = b; + b = stp_curve_create_copy(b_save); + stp_curve_resample(b, stp_curve_count_points(a)); + } + + if (mode != STP_CURVE_COMPOSE_ADD && mode != STP_CURVE_COMPOSE_MULTIPLY) + return 0; + if (stp_curve_get_wrap(a) != stp_curve_get_wrap(b)) + return 0; + stp_curve_get_bounds(a, &alo, &ahi); + stp_curve_get_bounds(b, &blo, &bhi); + if (mode == STP_CURVE_COMPOSE_MULTIPLY && (alo < 0 || blo < 0)) + return 0; + + if (stp_curve_get_wrap(a) == STP_CURVE_WRAP_AROUND) + { + points_a++; + points_b++; + } + if (points == -1) + { + points = lcm(points_a, points_b); + if (stp_curve_get_wrap(a) == STP_CURVE_WRAP_AROUND) + points--; + } + if (points < 2 || points > curve_point_limit || + ((stp_curve_get_wrap(a) == STP_CURVE_WRAP_AROUND) && + points > curve_point_limit - 1)) + return 0; + + if (gamma_a && gamma_b && gamma_a * gamma_b > 0 && + mode == STP_CURVE_COMPOSE_MULTIPLY) + return create_gamma_curve(retval, alo * blo, ahi * bhi, gamma_a + gamma_b, + points); + tmp_data = stp_malloc(sizeof(double) * points); + if (!interpolate_points(a, b, mode, points, tmp_data)) + { + stp_free(tmp_data); + return 0; + } + ret = stp_curve_create(stp_curve_get_wrap(a)); + if (mode == STP_CURVE_COMPOSE_ADD) + { + stp_curve_rescale(ret, (ahi - alo) + (bhi - blo), + STP_CURVE_COMPOSE_MULTIPLY, STP_CURVE_BOUNDS_RESCALE); + stp_curve_rescale(ret, alo + blo, + STP_CURVE_COMPOSE_ADD, STP_CURVE_BOUNDS_RESCALE); + } + else + { + stp_curve_rescale(ret, (ahi - alo) * (bhi - blo), + STP_CURVE_COMPOSE_MULTIPLY, STP_CURVE_BOUNDS_RESCALE); + stp_curve_rescale(ret, alo * blo, + STP_CURVE_COMPOSE_ADD, STP_CURVE_BOUNDS_RESCALE); + } + if (! stp_curve_set_data(ret, points, tmp_data)) + goto bad1; + *retval = ret; + stp_free(tmp_data); + return 1; + bad1: + stp_curve_destroy(ret); + stp_free(tmp_data); + return 0; +} + + +stp_curve_t * +stp_curve_create_from_xmltree(stp_mxml_node_t *curve) /* The curve node */ +{ + const char *stmp; /* Temporary string */ + stp_mxml_node_t *child; /* Child sequence node */ + stp_curve_t *ret = NULL; /* Curve to return */ + stp_curve_type_t curve_type; /* Type of curve */ + stp_curve_wrap_mode_t wrap_mode; /* Curve wrap mode */ + double fgamma; /* Gamma value */ + stp_sequence_t *seq = NULL; /* Sequence data */ + double low, high; /* Sequence bounds */ + int piecewise = 0; + + stp_xml_init(); + /* Get curve type */ + stmp = stp_mxmlElementGetAttr(curve, "type"); + if (stmp) + { + if (!strcmp(stmp, "linear")) + curve_type = STP_CURVE_TYPE_LINEAR; + else if (!strcmp(stmp, "spline")) + curve_type = STP_CURVE_TYPE_SPLINE; + else + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: %s: \"type\" invalid\n", stmp); + goto error; + } + } + else + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: \"type\" missing\n"); + goto error; + } + /* Get curve wrap mode */ + stmp = stp_mxmlElementGetAttr(curve, "wrap"); + if (stmp) + { + if (!strcmp(stmp, "nowrap")) + wrap_mode = STP_CURVE_WRAP_NONE; + else if (!strcmp(stmp, "wrap")) + { + wrap_mode = STP_CURVE_WRAP_AROUND; + } + else + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: %s: \"wrap\" invalid\n", stmp); + goto error; + } + } + else + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: \"wrap\" missing\n"); + goto error; + } + /* Get curve gamma */ + stmp = stp_mxmlElementGetAttr(curve, "gamma"); + if (stmp) + { + fgamma = stp_xmlstrtod(stmp); + } + else + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: \"gamma\" missing\n"); + goto error; + } + /* If gamma is set, wrap_mode must be STP_CURVE_WRAP_NONE */ + if (fgamma && wrap_mode != STP_CURVE_WRAP_NONE) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: " + "gamma set and \"wrap\" is not STP_CURVE_WRAP_NONE\n"); + goto error; + } + stmp = stp_mxmlElementGetAttr(curve, "piecewise"); + if (stmp && strcmp(stmp, "true") == 0) + piecewise = 1; + + /* Set up the curve */ + ret = stp_curve_create(wrap_mode); + stp_curve_set_interpolation_type(ret, curve_type); + + child = stp_mxmlFindElement(curve, curve, "sequence", NULL, NULL, STP_MXML_DESCEND); + if (child) + seq = stp_sequence_create_from_xmltree(child); + + if (seq == NULL) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: sequence read failed\n"); + goto error; + } + + /* Set curve bounds */ + stp_sequence_get_bounds(seq, &low, &high); + stp_curve_set_bounds(ret, low, high); + + if (fgamma) + stp_curve_set_gamma(ret, fgamma); + else /* Not a gamma curve, so set points */ + { + size_t seq_count; + const double* data; + + stp_sequence_get_data(seq, &seq_count, &data); + if (piecewise) + { + if ((seq_count % 2) != 0) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: invalid data count %d\n", + seq_count); + goto error; + } + if (stp_curve_set_data_points(ret, seq_count / 2, + (const stp_curve_point_t *) data) == 0) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: failed to set curve data points\n"); + goto error; + } + } + else + { + if (stp_curve_set_data(ret, seq_count, data) == 0) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: failed to set curve data\n"); + goto error; + } + } + } + + if (seq) + { + stp_sequence_destroy(seq); + seq = NULL; + } + + /* Validate curve */ + if (stpi_curve_check_parameters(ret, stp_curve_count_points(ret)) == 0) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: parameter check failed\n"); + goto error; + } + + stp_xml_exit(); + + return ret; + + error: + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_xmltree: error during curve read\n"); + if (ret) + stp_curve_destroy(ret); + stp_xml_exit(); + return NULL; +} + + +stp_mxml_node_t * +stp_xmltree_create_from_curve(const stp_curve_t *curve) /* The curve */ +{ + stp_curve_wrap_mode_t wrapmode; + stp_curve_type_t interptype; + double gammaval, low, high; + stp_sequence_t *seq; + + char *cgamma; + + stp_mxml_node_t *curvenode = NULL; + stp_mxml_node_t *child = NULL; + + stp_xml_init(); + + /* Get curve details */ + wrapmode = stp_curve_get_wrap(curve); + interptype = stp_curve_get_interpolation_type(curve); + gammaval = stp_curve_get_gamma(curve); + + if (gammaval && wrapmode != STP_CURVE_WRAP_NONE) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_xmltree_create_from_curve: " + "curve sets gamma and wrap_mode is not STP_CURVE_WRAP_NONE\n"); + goto error; + } + + /* Construct the allocated strings required */ + stp_asprintf(&cgamma, "%g", gammaval); + + curvenode = stp_mxmlNewElement(NULL, "curve"); + stp_mxmlElementSetAttr(curvenode, "wrap", stpi_wrap_mode_names[wrapmode]); + stp_mxmlElementSetAttr(curvenode, "type", stpi_curve_type_names[interptype]); + stp_mxmlElementSetAttr(curvenode, "gamma", cgamma); + if (curve->piecewise) + stp_mxmlElementSetAttr(curvenode, "piecewise", "true"); + else + stp_mxmlElementSetAttr(curvenode, "piecewise", "false"); + + stp_free(cgamma); + + seq = stp_sequence_create(); + stp_curve_get_bounds(curve, &low, &high); + stp_sequence_set_bounds(seq, low, high); + if (gammaval != 0) /* A gamma curve does not require sequence data */ + { + stp_sequence_set_size(seq, 0); + } + else + { + const double *data; + size_t count; + data = stpi_curve_get_data_internal(curve, &count); + stp_sequence_set_data(seq, count, data); + } + + child = stp_xmltree_create_from_sequence(seq); + + if (seq) + { + stp_sequence_destroy(seq); + seq = NULL; + } + + if (child == NULL) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_xmltree_create_from_curve: sequence node is NULL\n"); + goto error; + } + stp_mxmlAdd(curvenode, STP_MXML_ADD_AFTER, NULL, child); + + stp_xml_exit(); + + return curvenode; + + error: + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_xmltree_create_from_curve: error during xmltree creation\n"); + if (curvenode) + stp_mxmlDelete(curvenode); + if (child) + stp_mxmlDelete(child); + stp_xml_exit(); + + return NULL; +} + +static stp_mxml_node_t * +xmldoc_create_from_curve(const stp_curve_t *curve) +{ + stp_mxml_node_t *xmldoc; + stp_mxml_node_t *rootnode; + stp_mxml_node_t *curvenode; + + /* Get curve details */ + curvenode = stp_xmltree_create_from_curve(curve); + if (curvenode == NULL) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "xmldoc_create_from_curve: error creating curve node\n"); + return NULL; + } + /* Create the XML tree */ + xmldoc = stp_xmldoc_create_generic(); + if (xmldoc == NULL) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "xmldoc_create_from_curve: error creating XML document\n"); + return NULL; + } + rootnode = xmldoc->child; + if (rootnode == NULL) + { + stp_mxmlDelete(xmldoc); + stp_deprintf(STP_DBG_CURVE_ERRORS, + "xmldoc_create_from_curve: error getting XML document root node\n"); + return NULL; + } + + stp_mxmlAdd(rootnode, STP_MXML_ADD_AFTER, NULL, curvenode); + + return xmldoc; +} + +static int +curve_whitespace_callback(stp_mxml_node_t *node, int where) +{ + if (node->type != STP_MXML_ELEMENT) + return 0; + if (strcasecmp(node->value.element.name, "gutenprint") == 0) + { + switch (where) + { + case STP_MXML_WS_AFTER_OPEN: + case STP_MXML_WS_BEFORE_CLOSE: + case STP_MXML_WS_AFTER_CLOSE: + return '\n'; + case STP_MXML_WS_BEFORE_OPEN: + default: + return 0; + } + } + else if (strcasecmp(node->value.element.name, "curve") == 0) + { + switch (where) + { + case STP_MXML_WS_AFTER_OPEN: + return '\n'; + case STP_MXML_WS_BEFORE_CLOSE: + case STP_MXML_WS_AFTER_CLOSE: + case STP_MXML_WS_BEFORE_OPEN: + default: + return 0; + } + } + else if (strcasecmp(node->value.element.name, "sequence") == 0) + { + const char *count; + switch (where) + { + case STP_MXML_WS_BEFORE_CLOSE: + count = stp_mxmlElementGetAttr(node, "count"); + if (strcmp(count, "0") == 0) + return 0; + else + return '\n'; + case STP_MXML_WS_AFTER_OPEN: + case STP_MXML_WS_AFTER_CLOSE: + return '\n'; + case STP_MXML_WS_BEFORE_OPEN: + default: + return 0; + } + } + else + return 0; +} + + +int +stp_curve_write(FILE *file, const stp_curve_t *curve) /* The curve */ +{ + stp_mxml_node_t *xmldoc = NULL; + + stp_xml_init(); + + xmldoc = xmldoc_create_from_curve(curve); + if (xmldoc == NULL) + { + stp_xml_exit(); + return 1; + } + + stp_mxmlSaveFile(xmldoc, file, curve_whitespace_callback); + + if (xmldoc) + stp_mxmlDelete(xmldoc); + + stp_xml_exit(); + + return 0; +} + +char * +stp_curve_write_string(const stp_curve_t *curve) /* The curve */ +{ + stp_mxml_node_t *xmldoc = NULL; + char *retval; + + stp_xml_init(); + + xmldoc = xmldoc_create_from_curve(curve); + if (xmldoc == NULL) + { + stp_xml_exit(); + return NULL; + } + + retval = stp_mxmlSaveAllocString(xmldoc, curve_whitespace_callback); + + if (xmldoc) + stp_mxmlDelete(xmldoc); + + stp_xml_exit(); + + return retval; +} + +static stp_curve_t * +xml_doc_get_curve(stp_mxml_node_t *doc) +{ + stp_mxml_node_t *cur; + stp_mxml_node_t *xmlcurve; + stp_curve_t *curve = NULL; + + if (doc == NULL ) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "xml_doc_get_curve: XML file not parsed successfully.\n"); + return NULL; + } + + cur = doc->child; + + if (cur == NULL) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "xml_doc_get_curve: empty document\n"); + return NULL; + } + + xmlcurve = stp_xml_get_node(cur, "gutenprint", "curve", NULL); + + if (xmlcurve) + curve = stp_curve_create_from_xmltree(xmlcurve); + + return curve; +} + +stp_curve_t * +stp_curve_create_from_file(const char* file) +{ + stp_curve_t *curve = NULL; + stp_mxml_node_t *doc; + FILE *fp = fopen(file, "r"); + if (!fp) + { + stp_deprintf(STP_DBG_CURVE_ERRORS, + "stp_curve_create_from_file: unable to open %s: %s\n", + file, strerror(errno)); + return NULL; + } + stp_deprintf(STP_DBG_XML, "stp_curve_create_from_file: reading `%s'...\n", + file); + + stp_xml_init(); + + doc = stp_mxmlLoadFile(NULL, fp, STP_MXML_NO_CALLBACK); + + curve = xml_doc_get_curve(doc); + + if (doc) + stp_mxmlDelete(doc); + + stp_xml_exit(); + (void) fclose(fp); + return curve; + +} + +stp_curve_t * +stp_curve_create_from_stream(FILE* fp) +{ + stp_curve_t *curve = NULL; + stp_mxml_node_t *doc; + stp_deprintf(STP_DBG_XML, "stp_curve_create_from_fp: reading...\n"); + + stp_xml_init(); + + doc = stp_mxmlLoadFile(NULL, fp, STP_MXML_NO_CALLBACK); + + curve = xml_doc_get_curve(doc); + + if (doc) + stp_mxmlDelete(doc); + + stp_xml_exit(); + return curve; + +} + +stp_curve_t * +stp_curve_create_from_string(const char* string) +{ + stp_curve_t *curve = NULL; + stp_mxml_node_t *doc; + stp_deprintf(STP_DBG_XML, + "stp_curve_create_from_string: reading '%s'...\n", string); + stp_xml_init(); + + doc = stp_mxmlLoadString(NULL, string, STP_MXML_NO_CALLBACK); + + curve = xml_doc_get_curve(doc); + + if (doc) + stp_mxmlDelete(doc); + + stp_xml_exit(); + return curve; +} |