libgimpprint API Reference: src/main/curve.c Source File

Main Page | Modules | Alphabetical List | Data Structures | File List | Data Fields | Globals | Related Pages

# src/main/curve.c

Go to the documentation of this file.

    00001 /*
    00002  * "$Id: curve_8c-source.html,v 1.1.1.2 2004/12/22 23:48:57 jlovell Exp $"
    00003  *
    00004  *   Print plug-in driver utility functions for the GIMP.
    00005  *
    00006  *   Copyright 1997-2000 Michael Sweet (mike@easysw.com) and
    00007  *      Robert Krawitz (rlk@alum.mit.edu)
    00008  *
    00009  *   This program is free software; you can redistribute it and/or modify it
    00010  *   under the terms of the GNU General Public License as published by the Free
    00011  *   Software Foundation; either version 2 of the License, or (at your option)
    00012  *   any later version.
    00013  *
    00014  *   This program is distributed in the hope that it will be useful, but
    00015  *   WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    00016  *   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    00017  *   for more details.
    00018  *
    00019  *   You should have received a copy of the GNU General Public License
    00020  *   along with this program; if not, write to the Free Software
    00021  *   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    00022  */
    00023
    00024 #ifdef HAVE_CONFIG_H
    00025 #include <config.h>
    00026 #endif
    00027 #include <gimp-print/gimp-print.h>
    00028 #include "gimp-print-internal.h"
    00029 #include <gimp-print/gimp-print-intl-internal.h>
    00030 #include <math.h>
    00031 #ifdef sun
    00032 #include <ieeefp.h>
    00033 #endif
    00034 #include <string.h>
    00035 #include <stdlib.h>
    00036 #include <limits.h>
    00037 #include <unistd.h>
    00038 #include <sys/types.h>
    00039 #include <sys/stat.h>
    00040
    00041 #ifdef __GNUC__
    00042 #define inline __inline__
    00043 #endif
    00044
    00045 #undef inline
    00046 #define inline
    00047
    00048 static const int curve_point_limit = 1048576;
    00049
    00050 struct stp_curve
    00051 {
    00052   stp_curve_type_t curve_type;
    00053   stp_curve_wrap_mode_t wrap_mode;
    00054   int piecewise;
    00055   int recompute_interval;       /* Do we need to recompute the deltas? */
    00056   double gamma;                 /* 0.0 means that the curve is not a gamma */
    00057   stp_sequence_t *seq;          /* Sequence (contains the curve data) */
    00058   double *interval;             /* We allocate an extra slot for the
    00059                                    wrap-around value. */
    00060
    00061 };
    00062
    00063 static const char *const stpi_curve_type_names[] =
    00064   {
    00065     "linear",
    00066     "spline",
    00067   };
    00068
    00069 static const int stpi_curve_type_count =
    00070 (sizeof(stpi_curve_type_names) / sizeof(const char *));
    00071
    00072 static const char *const stpi_wrap_mode_names[] =
    00073   {
    00074     "nowrap",
    00075     "wrap"
    00076   };
    00077
    00078 static const int stpi_wrap_mode_count =
    00079 (sizeof(stpi_wrap_mode_names) / sizeof(const char *));
    00080
    00081 /*
    00082  * We could do more sanity checks here if we want.
    00083  */
    00084 static void
    00085 check_curve(const stp_curve_t *curve)
    00086 {
    00087   if (curve == NULL)
    00088     {
    00089       stp_erprintf("Null curve! Please report this bug.\n");
    00090       stp_abort();
    00091     }
    00092   if (curve->seq == NULL)
    00093     {
    00094       stp_erprintf("Bad curve (seq == NULL)! Please report this bug.\n");
    00095       stp_abort();
    00096     }
    00097 }
    00098
    00099 /*
    00100  * Get the total number of points in the base sequence class
    00101  */
    00102 static size_t
    00103 get_real_point_count(const stp_curve_t *curve)
    00104 {
    00105   if (curve->piecewise)
    00106     return stp_sequence_get_size(curve->seq) / 2;
    00107   else
    00108     return stp_sequence_get_size(curve->seq);
    00109 }
    00110
    00111 /*
    00112  * Get the number of points used by the curve (that are visible to the
    00113  * user).  This is the real point count, but is decreased by 1 if the
    00114  * curve wraps around.
    00115  */
    00116 static size_t
    00117 get_point_count(const stp_curve_t *curve)
    00118 {
    00119   size_t count = get_real_point_count(curve);
    00120   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00121     count -= 1;
    00122
    00123   return count;
    00124 }
    00125
    00126 static void
    00127 invalidate_auxiliary_data(stp_curve_t *curve)
    00128 {
    00129   STP_SAFE_FREE(curve->interval);
    00130 }
    00131
    00132 static void
    00133 clear_curve_data(stp_curve_t *curve)
    00134 {
    00135   if (curve->seq)
    00136     stp_sequence_set_size(curve->seq, 0);
    00137   curve->recompute_interval = 0;
    00138   invalidate_auxiliary_data(curve);
    00139 }
    00140
    00141 static void
    00142 compute_linear_deltas(stp_curve_t *curve)
    00143 {
    00144   int i;
    00145   size_t delta_count;
    00146   size_t seq_point_count;
    00147   const double *data;
    00148
    00149   stp_sequence_get_data(curve->seq, &seq_point_count, &data);
    00150   if (data == NULL)
    00151     return;
    00152
    00153   delta_count = get_real_point_count(curve);
    00154
    00155   if (delta_count <= 1) /* No intervals can be computed */
    00156     return;
    00157   delta_count--; /* One less than the real point count.  Note size_t
    00158                     is unsigned. */
    00159
    00160   curve->interval = stp_malloc(sizeof(double) * delta_count);
    00161   for (i = 0; i < delta_count; i++)
    00162     {
    00163       if (curve->piecewise)
    00164         curve->interval[i] = data[(2 * (i + 1)) + 1] - data[(2 * i) + 1];
    00165       else
    00166         curve->interval[i] = data[i + 1] - data[i];
    00167     }
    00168 }
    00169
    00170 static void
    00171 compute_spline_deltas_piecewise(stp_curve_t *curve)
    00172 {
    00173   int i;
    00174   int k;
    00175   double *u;
    00176   double *y2;
    00177   const double *data = NULL;
    00178   const stp_curve_point_t *dp;
    00179   size_t point_count;
    00180   size_t real_point_count;
    00181   double sig;
    00182   double p;
    00183
    00184   point_count = get_point_count(curve);
    00185
    00186   stp_sequence_get_data(curve->seq, &real_point_count, &data);
    00187   dp = (const stp_curve_point_t *)data;
    00188   real_point_count = real_point_count / 2;
    00189
    00190   u = stp_malloc(sizeof(double) * real_point_count);
    00191   y2 = stp_malloc(sizeof(double) * real_point_count);
    00192
    00193   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00194     {
    00195       int reps = 3;
    00196       int count = reps * real_point_count;
    00197       double *y2a = stp_malloc(sizeof(double) * count);
    00198       double *ua = stp_malloc(sizeof(double) * count);
    00199       y2a[0] = 0.0;
    00200       ua[0] = 0.0;
    00201       for (i = 1; i < count - 1; i++)
    00202         {
    00203           int im1 = (i - 1) % point_count;
    00204           int ia = i % point_count;
    00205           int ip1 = (i + 1) % point_count;
    00206
    00207           sig = (dp[ia].x - dp[im1].x) / (dp[ip1].x - dp[im1].x);
    00208           p = sig * y2a[im1] + 2.0;
    00209           y2a[i] = (sig - 1.0) / p;
    00210
    00211           ua[i] = ((dp[ip1].y - dp[ia].y) / (dp[ip1].x - dp[ia].x)) -
    00212             ((dp[ia].y - dp[im1].y) / (dp[ia].x - dp[im1].x));
    00213           ua[i] =
    00214             (((6.0 * ua[ia]) / (dp[ip1].x - dp[im1].x)) - (sig * ua[im1])) / p;
    00215         }
    00216       y2a[count - 1] = 0.0;
    00217       for (k = count - 2 ; k >= 0; k--)
    00218         y2a[k] = y2a[k] * y2a[k + 1] + ua[k];
    00219       memcpy(u, ua + ((reps / 2) * point_count),
    00220              sizeof(double) * real_point_count);
    00221       memcpy(y2, y2a + ((reps / 2) * point_count),
    00222              sizeof(double) * real_point_count);
    00223       stp_free(y2a);
    00224       stp_free(ua);
    00225     }
    00226   else
    00227     {
    00228       int count = real_point_count - 1;
    00229
    00230       y2[0] = 0;
    00231       u[0] = 2 * (dp[1].y - dp[0].y);
    00232       for (i = 1; i < count; i++)
    00233         {
    00234           int im1 = (i - 1);
    00235           int ip1 = (i + 1);
    00236
    00237           sig = (dp[i].x - dp[im1].x) / (dp[ip1].x - dp[im1].x);
    00238           p = sig * y2[im1] + 2.0;
    00239           y2[i] = (sig - 1.0) / p;
    00240
    00241           u[i] = ((dp[ip1].y - dp[i].y) / (dp[ip1].x - dp[i].x)) -
    00242             ((dp[i].y - dp[im1].y) / (dp[i].x - dp[im1].x));
    00243           u[i] =
    00244             (((6.0 * u[i]) / (dp[ip1].x - dp[im1].x)) - (sig * u[im1])) / p;
    00245           stp_deprintf(STP_DBG_CURVE,
    00246                        "%d sig %f p %f y2 %f u %f x %f %f %f y %f %f %f\n",
    00247                        i, sig, p, y2[i], u[i],
    00248                        dp[im1].x, dp[i].x, dp[ip1].x,
    00249                        dp[im1].y, dp[i].y, dp[ip1].y);
    00250         }
    00251       y2[count] = 0.0;
    00252       u[count] = 0.0;
    00253       for (k = real_point_count - 2; k >= 0; k--)
    00254         y2[k] = y2[k] * y2[k + 1] + u[k];
    00255     }
    00256
    00257   curve->interval = y2;
    00258   stp_free(u);
    00259 }
    00260
    00261 static void
    00262 compute_spline_deltas_dense(stp_curve_t *curve)
    00263 {
    00264   int i;
    00265   int k;
    00266   double *u;
    00267   double *y2;
    00268   const double *y;
    00269   size_t point_count;
    00270   size_t real_point_count;
    00271   double sig;
    00272   double p;
    00273
    00274   point_count = get_point_count(curve);
    00275
    00276   stp_sequence_get_data(curve->seq, &real_point_count, &y);
    00277   u = stp_malloc(sizeof(double) * real_point_count);
    00278   y2 = stp_malloc(sizeof(double) * real_point_count);
    00279
    00280   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00281     {
    00282       int reps = 3;
    00283       int count = reps * real_point_count;
    00284       double *y2a = stp_malloc(sizeof(double) * count);
    00285       double *ua = stp_malloc(sizeof(double) * count);
    00286       y2a[0] = 0.0;
    00287       ua[0] = 0.0;
    00288       for (i = 1; i < count - 1; i++)
    00289         {
    00290           int im1 = (i - 1);
    00291           int ip1 = (i + 1);
    00292           int im1a = im1 % point_count;
    00293           int ia = i % point_count;
    00294           int ip1a = ip1 % point_count;
    00295
    00296           sig = (i - im1) / (ip1 - im1);
    00297           p = sig * y2a[im1] + 2.0;
    00298           y2a[i] = (sig - 1.0) / p;
    00299
    00300           ua[i] = y[ip1a] - 2 * y[ia] + y[im1a];
    00301           ua[i] = 3.0 * ua[i] - sig * ua[im1] / p;
    00302         }
    00303       y2a[count - 1] = 0.0;
    00304       for (k = count - 2 ; k >= 0; k--)
    00305         y2a[k] = y2a[k] * y2a[k + 1] + ua[k];
    00306       memcpy(u, ua + ((reps / 2) * point_count),
    00307              sizeof(double) * real_point_count);
    00308       memcpy(y2, y2a + ((reps / 2) * point_count),
    00309              sizeof(double) * real_point_count);
    00310       stp_free(y2a);
    00311       stp_free(ua);
    00312     }
    00313   else
    00314     {
    00315       int count = real_point_count - 1;
    00316
    00317       y2[0] = 0;
    00318       u[0] = 2 * (y[1] - y[0]);
    00319       for (i = 1; i < count; i++)
    00320         {
    00321           int im1 = (i - 1);
    00322           int ip1 = (i + 1);
    00323
    00324           sig = (i - im1) / (ip1 - im1);
    00325           p = sig * y2[im1] + 2.0;
    00326           y2[i] = (sig - 1.0) / p;
    00327
    00328           u[i] = y[ip1] - 2 * y[i] + y[im1];
    00329           u[i] = 3.0 * u[i] - sig * u[im1] / p;
    00330         }
    00331
    00332       u[count] = 2 * (y[count] - y[count - 1]);
    00333       y2[count] = 0.0;
    00334
    00335       u[count] = 0.0;
    00336       for (k = real_point_count - 2; k >= 0; k--)
    00337         y2[k] = y2[k] * y2[k + 1] + u[k];
    00338     }
    00339
    00340   curve->interval = y2;
    00341   stp_free(u);
    00342 }
    00343
    00344 static void
    00345 compute_spline_deltas(stp_curve_t *curve)
    00346 {
    00347   if (curve->piecewise)
    00348     compute_spline_deltas_piecewise(curve);
    00349   else
    00350     compute_spline_deltas_dense(curve);
    00351 }
    00352
    00353 /*
    00354  * Recompute the delta values for interpolation.
    00355  * When we actually do support spline curves, this routine will
    00356  * compute the second derivatives for that purpose, too.
    00357  */
    00358 static void
    00359 compute_intervals(stp_curve_t *curve)
    00360 {
    00361   if (curve->interval)
    00362     {
    00363       stp_free(curve->interval);
    00364       curve->interval = NULL;
    00365     }
    00366   if (stp_sequence_get_size(curve->seq) > 0)
    00367     {
    00368       switch (curve->curve_type)
    00369         {
    00370         case STP_CURVE_TYPE_SPLINE:
    00371           compute_spline_deltas(curve);
    00372           break;
    00373         case STP_CURVE_TYPE_LINEAR:
    00374           compute_linear_deltas(curve);
    00375           break;
    00376         }
    00377     }
    00378   curve->recompute_interval = 0;
    00379 }
    00380
    00381 static int
    00382 stpi_curve_set_points(stp_curve_t *curve, size_t points)
    00383 {
    00384   if (points < 2)
    00385     return 0;
    00386   if (points > curve_point_limit ||
    00387       (curve->wrap_mode == STP_CURVE_WRAP_AROUND &&
    00388        points > curve_point_limit - 1))
    00389     return 0;
    00390   clear_curve_data(curve);
    00391   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00392     points++;
    00393   if (curve->piecewise)
    00394     points *= 2;
    00395   if ((stp_sequence_set_size(curve->seq, points)) == 0)
    00396     return 0;
    00397   return 1;
    00398 }
    00399
    00400 /*
    00401  * Create a default curve
    00402  */
    00403 static void
    00404 stpi_curve_ctor(stp_curve_t *curve, stp_curve_wrap_mode_t wrap_mode)
    00405 {
    00406   curve->seq = stp_sequence_create();
    00407   stp_sequence_set_bounds(curve->seq, 0.0, 1.0);
    00408   curve->curve_type = STP_CURVE_TYPE_LINEAR;
    00409   curve->wrap_mode = wrap_mode;
    00410   curve->piecewise = 0;
    00411   stpi_curve_set_points(curve, 2);
    00412   curve->recompute_interval = 1;
    00413   if (wrap_mode == STP_CURVE_WRAP_NONE)
    00414     curve->gamma = 1.0;
    00415   stp_sequence_set_point(curve->seq, 0, 0);
    00416   stp_sequence_set_point(curve->seq, 1, 1);
    00417 }
    00418
    00419 stp_curve_t *
    00420 stp_curve_create(stp_curve_wrap_mode_t wrap_mode)
    00421 {
    00422   stp_curve_t *ret;
    00423   if (wrap_mode != STP_CURVE_WRAP_NONE && wrap_mode != STP_CURVE_WRAP_AROUND)
    00424     return NULL;
    00425   ret = stp_zalloc(sizeof(stp_curve_t));
    00426   stpi_curve_ctor(ret, wrap_mode);
    00427   return ret;
    00428 }
    00429
    00430 static void
    00431 curve_dtor(stp_curve_t *curve)
    00432 {
    00433   check_curve(curve);
    00434   clear_curve_data(curve);
    00435   if (curve->seq)
    00436     stp_sequence_destroy(curve->seq);
    00437   memset(curve, 0, sizeof(stp_curve_t));
    00438   curve->curve_type = -1;
    00439 }
    00440
    00441 void
    00442 stp_curve_destroy(stp_curve_t *curve)
    00443 {
    00444   curve_dtor(curve);
    00445   stp_free(curve);
    00446 }
    00447
    00448 void
    00449 stp_curve_copy(stp_curve_t *dest, const stp_curve_t *source)
    00450 {
    00451   check_curve(dest);
    00452   check_curve(source);
    00453   curve_dtor(dest);
    00454   dest->curve_type = source->curve_type;
    00455   dest->wrap_mode = source->wrap_mode;
    00456   dest->gamma = source->gamma;
    00457   dest->seq = stp_sequence_create_copy(source->seq);
    00458   dest->piecewise = source->piecewise;
    00459   dest->recompute_interval = 1;
    00460 }
    00461
    00462 stp_curve_t *
    00463 stp_curve_create_copy(const stp_curve_t *curve)
    00464 {
    00465   stp_curve_t *ret;
    00466   check_curve(curve);
    00467   ret = stp_curve_create(curve->wrap_mode);
    00468   stp_curve_copy(ret, curve);
    00469   return ret;
    00470 }
    00471
    00472 int
    00473 stp_curve_set_bounds(stp_curve_t *curve, double low, double high)
    00474 {
    00475   check_curve(curve);
    00476   return stp_sequence_set_bounds(curve->seq, low, high);
    00477 }
    00478
    00479 void
    00480 stp_curve_get_bounds(const stp_curve_t *curve, double *low, double *high)
    00481 {
    00482   check_curve(curve);
    00483   stp_sequence_get_bounds(curve->seq, low, high);
    00484 }
    00485
    00486 /*
    00487  * Find the minimum and maximum points on the curve.  This does not
    00488  * attempt to find the minimum and maximum interpolations; with cubic
    00489  * splines these could exceed the boundaries.  That's OK; the interpolation
    00490  * code will clip them to the bounds.
    00491  */
    00492 void
    00493 stp_curve_get_range(const stp_curve_t *curve, double *low, double *high)
    00494 {
    00495   check_curve(curve);
    00496   stp_sequence_get_range(curve->seq, low, high);
    00497 }
    00498
    00499 size_t
    00500 stp_curve_count_points(const stp_curve_t *curve)
    00501 {
    00502   check_curve(curve);
    00503   return get_point_count(curve);
    00504 }
    00505
    00506 stp_curve_wrap_mode_t
    00507 stp_curve_get_wrap(const stp_curve_t *curve)
    00508 {
    00509   check_curve(curve);
    00510   return curve->wrap_mode;
    00511 }
    00512
    00513 int
    00514 stp_curve_is_piecewise(const stp_curve_t *curve)
    00515 {
    00516   check_curve(curve);
    00517   return curve->piecewise;
    00518 }
    00519
    00520 int
    00521 stp_curve_set_interpolation_type(stp_curve_t *curve, stp_curve_type_t itype)
    00522 {
    00523   check_curve(curve);
    00524   if (itype < 0 || itype >= stpi_curve_type_count)
    00525     return 0;
    00526   curve->curve_type = itype;
    00527   return 1;
    00528 }
    00529
    00530 stp_curve_type_t
    00531 stp_curve_get_interpolation_type(const stp_curve_t *curve)
    00532 {
    00533   check_curve(curve);
    00534   return curve->curve_type;
    00535 }
    00536
    00537 int
    00538 stp_curve_set_gamma(stp_curve_t *curve, double fgamma)
    00539 {
    00540   check_curve(curve);
    00541   if (curve->wrap_mode || ! finite(fgamma) || fgamma == 0.0)
    00542     return 0;
    00543   clear_curve_data(curve);
    00544   curve->gamma = fgamma;
    00545   stp_curve_resample(curve, 2);
    00546   return 1;
    00547 }
    00548
    00549 double
    00550 stp_curve_get_gamma(const stp_curve_t *curve)
    00551 {
    00552   check_curve(curve);
    00553   return curve->gamma;
    00554 }
    00555
    00556 int
    00557 stp_curve_set_data(stp_curve_t *curve, size_t count, const double *data)
    00558 {
    00559   size_t i;
    00560   size_t real_count = count;
    00561   double low, high;
    00562   check_curve(curve);
    00563   if (count < 2)
    00564     return 0;
    00565   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00566     real_count++;
    00567   if (real_count > curve_point_limit)
    00568     return 0;
    00569
    00570   /* Validate the data before we commit to it. */
    00571   stp_sequence_get_bounds(curve->seq, &low, &high);
    00572   for (i = 0; i < count; i++)
    00573     if (! finite(data[i]) || data[i] < low || data[i] > high)
    00574       {
    00575         stp_deprintf(STP_DBG_CURVE_ERRORS,
    00576                      "stp_curve_set_data: datum out of bounds: "
    00577                      "%g (require %g <= x <= %g), n = %d\n",
    00578                      data[i], low, high, i);
    00579         return 0;
    00580       }
    00581   /* Allocate sequence; also accounts for WRAP_MODE */
    00582   stpi_curve_set_points(curve, count);
    00583   curve->gamma = 0.0;
    00584   stp_sequence_set_subrange(curve->seq, 0, count, data);
    00585
    00586   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00587     stp_sequence_set_point(curve->seq, count, data[0]);
    00588   curve->recompute_interval = 1;
    00589   curve->piecewise = 0;
    00590
    00591   return 1;
    00592 }
    00593
    00594 int
    00595 stp_curve_set_data_points(stp_curve_t *curve, size_t count,
    00596                           const stp_curve_point_t *data)
    00597 {
    00598   size_t i;
    00599   size_t real_count = count;
    00600   double low, high;
    00601   double last_x = -1;
    00602   check_curve(curve);
    00603   if (count < 2)
    00604     {
    00605       stp_deprintf(STP_DBG_CURVE_ERRORS,
    00606                    "stp_curve_set_data_points: too few points %d\n", count);
    00607       return 0;
    00608     }
    00609   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00610     real_count++;
    00611   if (real_count > curve_point_limit)
    00612     {
    00613       stp_deprintf(STP_DBG_CURVE_ERRORS,
    00614                    "stp_curve_set_data_points: too many points %d\n",
    00615                    real_count);
    00616       return 0;
    00617     }
    00618
    00619   /* Validate the data before we commit to it. */
    00620   stp_sequence_get_bounds(curve->seq, &low, &high);
    00621   for (i = 0; i < count; i++)
    00622     {
    00623       if (! finite(data[i].y) || data[i].y < low || data[i].y > high)
    00624         {
    00625           stp_deprintf(STP_DBG_CURVE_ERRORS,
    00626                        "stp_curve_set_data_points: datum out of bounds: "
    00627                        "%g (require %g <= x <= %g), n = %d\n",
    00628                        data[i].y, low, high, i);
    00629           return 0;
    00630         }
    00631       if (i == 0 && data[i].x != 0.0)
    00632         {
    00633           stp_deprintf(STP_DBG_CURVE_ERRORS,
    00634                        "stp_curve_set_data_points: first point must have x=0\n");
    00635           return 0;
    00636         }
    00637       if (curve->wrap_mode == STP_CURVE_WRAP_NONE && i == count - 1 &&
    00638           data[i].x != 1.0)
    00639         {
    00640           stp_deprintf(STP_DBG_CURVE_ERRORS,
    00641                        "stp_curve_set_data_points: last point must have x=1\n");
    00642           return 0;
    00643         }
    00644       if (curve->wrap_mode == STP_CURVE_WRAP_AROUND &&
    00645           data[i].x >= 1.0 - .000001)
    00646         {
    00647           stp_deprintf(STP_DBG_CURVE_ERRORS,
    00648                        "stp_curve_set_data_points: horizontal value must "
    00649                        "not exceed .99999\n");
    00650           return 0;
    00651         }
    00652       if (data[i].x < 0 || data[i].x > 1)
    00653         {
    00654           stp_deprintf(STP_DBG_CURVE_ERRORS,
    00655                        "stp_curve_set_data_points: horizontal position out of bounds: "
    00656                        "%g, n = %d\n",
    00657                        data[i].x, i);
    00658           return 0;
    00659         }
    00660       if (data[i].x - .000001 < last_x)
    00661         {
    00662           stp_deprintf(STP_DBG_CURVE_ERRORS,
    00663                        "stp_curve_set_data_points: horizontal position must "
    00664                        "exceed previous position by .000001: %g, %g, n = %d\n",
    00665                        data[i].x, last_x, i);
    00666           return 0;
    00667         }
    00668       last_x = data[i].x;
    00669     }
    00670   /* Allocate sequence; also accounts for WRAP_MODE */
    00671   curve->piecewise = 1;
    00672   stpi_curve_set_points(curve, count);
    00673   curve->gamma = 0.0;
    00674   stp_sequence_set_subrange(curve->seq, 0, count * 2, (const double *) data);
    00675
    00676   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00677     {
    00678       stp_sequence_set_point(curve->seq, count * 2, data[0].x);
    00679       stp_sequence_set_point(curve->seq, count * 2 + 1, data[0].y);
    00680     }
    00681   curve->recompute_interval = 1;
    00682
    00683   return 1;
    00684 }
    00685
    00686
    00687 /*
    00688  * Note that we return a pointer to the raw data here.
    00689  * A lot of operations change the data vector, that's why we don't
    00690  * guarantee it across non-const calls.
    00691  */
    00692 const double *
    00693 stp_curve_get_data(const stp_curve_t *curve, size_t *count)
    00694 {
    00695   const double *ret;
    00696   check_curve(curve);
    00697   if (curve->piecewise)
    00698     return NULL;
    00699   stp_sequence_get_data(curve->seq, count, &ret);
    00700   *count = get_point_count(curve);
    00701   return ret;
    00702 }
    00703
    00704 const stp_curve_point_t *
    00705 stp_curve_get_data_points(const stp_curve_t *curve, size_t *count)
    00706 {
    00707   const stp_curve_point_t *ret;
    00708   check_curve(curve);
    00709   if (!curve->piecewise)
    00710     return NULL;
    00711   stp_sequence_get_data(curve->seq, count, (const double **) &ret);
    00712   *count = get_point_count(curve);
    00713   return ret;
    00714 }
    00715
    00716 static const double *
    00717 stpi_curve_get_data_internal(const stp_curve_t *curve, size_t *count)
    00718 {
    00719   const double *ret;
    00720   check_curve(curve);
    00721   stp_sequence_get_data(curve->seq, count, &ret);
    00722   *count = get_point_count(curve);
    00723   if (curve->piecewise)
    00724     *count *= 2;
    00725   return ret;
    00726 }
    00727
    00728
    00729 /* "Overloaded" functions */
    00730
    00731 #define DEFINE_DATA_SETTER(t, name)                                        \
    00732 int                                                                        \
    00733 stp_curve_set_##name##_data(stp_curve_t *curve, size_t count, const t *data) \
    00734 {                                                                          \
    00735   double *tmp_data;                                                        \
    00736   size_t i;                                                                \
    00737   int status;                                                              \
    00738   size_t real_count = count;                                               \
    00739                                                                            \
    00740   check_curve(curve);                                                      \
    00741   if (count < 2)                                                           \
    00742     return 0;                                                              \
    00743   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)                           \
    00744     real_count++;                                                          \
    00745   if (real_count > curve_point_limit)                                      \
    00746     return 0;                                                              \
    00747   tmp_data = stp_malloc(count * sizeof(double));                           \
    00748   for (i = 0; i < count; i++)                                              \
    00749     tmp_data[i] = (double) data[i];                                        \
    00750   status = stp_curve_set_data(curve, count, tmp_data);                     \
    00751   stp_free(tmp_data);                                                      \
    00752   return status;                                                           \
    00753  }
    00754
    00755 DEFINE_DATA_SETTER(float, float)
    00756 DEFINE_DATA_SETTER(long, long)
    00757 DEFINE_DATA_SETTER(unsigned long, ulong)
    00758 DEFINE_DATA_SETTER(int, int)
    00759 DEFINE_DATA_SETTER(unsigned int, uint)
    00760 DEFINE_DATA_SETTER(short, short)
    00761 DEFINE_DATA_SETTER(unsigned short, ushort)
    00762
    00763
    00764 #define DEFINE_DATA_ACCESSOR(t, name)                                   \
    00765 const t *                                                               \
    00766 stp_curve_get_##name##_data(const stp_curve_t *curve, size_t *count)    \
    00767 {                                                                       \
    00768   if (curve->piecewise)                                                 \
    00769     return 0;                                                           \
    00770   return stp_sequence_get_##name##_data(curve->seq, count);             \
    00771 }
    00772
    00773 DEFINE_DATA_ACCESSOR(float, float)
    00774 DEFINE_DATA_ACCESSOR(long, long)
    00775 DEFINE_DATA_ACCESSOR(unsigned long, ulong)
    00776 DEFINE_DATA_ACCESSOR(int, int)
    00777 DEFINE_DATA_ACCESSOR(unsigned int, uint)
    00778 DEFINE_DATA_ACCESSOR(short, short)
    00779 DEFINE_DATA_ACCESSOR(unsigned short, ushort)
    00780
    00781
    00782 stp_curve_t *
    00783 stp_curve_get_subrange(const stp_curve_t *curve, size_t start, size_t count)
    00784 {
    00785   stp_curve_t *retval;
    00786   size_t ncount;
    00787   double blo, bhi;
    00788   const double *data;
    00789   if (start + count > stp_curve_count_points(curve) || count < 2)
    00790     return NULL;
    00791   if (curve->piecewise)
    00792     return NULL;
    00793   retval = stp_curve_create(STP_CURVE_WRAP_NONE);
    00794   stp_curve_get_bounds(curve, &blo, &bhi);
    00795   stp_curve_set_bounds(retval, blo, bhi);
    00796   data = stp_curve_get_data(curve, &ncount);
    00797   if (! stp_curve_set_data(retval, count, data + start))
    00798     {
    00799       stp_curve_destroy(retval);
    00800       return NULL;
    00801     }
    00802   return retval;
    00803 }
    00804
    00805 int
    00806 stp_curve_set_subrange(stp_curve_t *curve, const stp_curve_t *range,
    00807                        size_t start)
    00808 {
    00809   double blo, bhi;
    00810   double rlo, rhi;
    00811   const double *data;
    00812   size_t count;
    00813   check_curve(curve);
    00814   if (start + stp_curve_count_points(range) > stp_curve_count_points(curve))
    00815     return 0;
    00816   if (curve->piecewise)
    00817     return 0;
    00818   stp_sequence_get_bounds(curve->seq, &blo, &bhi);
    00819   stp_sequence_get_range(curve->seq, &rlo, &rhi);
    00820   if (rlo < blo || rhi > bhi)
    00821     return 0;
    00822   stp_sequence_get_data(range->seq, &count, &data);
    00823   curve->recompute_interval = 1;
    00824   curve->gamma = 0.0;
    00825   invalidate_auxiliary_data(curve);
    00826   stp_sequence_set_subrange(curve->seq, start, stp_curve_count_points(range),
    00827                             data);
    00828   return 1;
    00829 }
    00830
    00831
    00832 int
    00833 stp_curve_set_point(stp_curve_t *curve, size_t where, double data)
    00834 {
    00835   check_curve(curve);
    00836   if (where >= get_point_count(curve))
    00837     return 0;
    00838   curve->gamma = 0.0;
    00839
    00840   if (curve->piecewise)
    00841     return 0;
    00842   if ((stp_sequence_set_point(curve->seq, where, data)) == 0)
    00843     return 0;
    00844   if (where == 0 && curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    00845     if ((stp_sequence_set_point(curve->seq,
    00846                                 get_point_count(curve), data)) == 0)
    00847       return 0;
    00848   invalidate_auxiliary_data(curve);
    00849   return 1;
    00850 }
    00851
    00852 int
    00853 stp_curve_get_point(const stp_curve_t *curve, size_t where, double *data)
    00854 {
    00855   check_curve(curve);
    00856   if (where >= get_point_count(curve))
    00857     return 0;
    00858   if (curve->piecewise)
    00859     return 0;
    00860   return stp_sequence_get_point(curve->seq, where, data);
    00861 }
    00862
    00863 const stp_sequence_t *
    00864 stp_curve_get_sequence(const stp_curve_t *curve)
    00865 {
    00866   check_curve(curve);
    00867   if (curve->piecewise)
    00868     return NULL;
    00869   return curve->seq;
    00870 }
    00871
    00872 int
    00873 stp_curve_rescale(stp_curve_t *curve, double scale,
    00874                   stp_curve_compose_t mode, stp_curve_bounds_t bounds_mode)
    00875 {
    00876   size_t real_point_count;
    00877   int i;
    00878   double nblo;
    00879   double nbhi;
    00880   size_t count;
    00881
    00882   check_curve(curve);
    00883
    00884   real_point_count = get_real_point_count(curve);
    00885
    00886   stp_sequence_get_bounds(curve->seq, &nblo, &nbhi);
    00887   if (bounds_mode == STP_CURVE_BOUNDS_RESCALE)
    00888     {
    00889       switch (mode)
    00890         {
    00891         case STP_CURVE_COMPOSE_ADD:
    00892           nblo += scale;
    00893           nbhi += scale;
    00894           break;
    00895         case STP_CURVE_COMPOSE_MULTIPLY:
    00896           if (scale < 0)
    00897             {
    00898               double tmp = nblo * scale;
    00899               nblo = nbhi * scale;
    00900               nbhi = tmp;
    00901             }
    00902           else
    00903             {
    00904               nblo *= scale;
    00905               nbhi *= scale;
    00906             }
    00907           break;
    00908         case STP_CURVE_COMPOSE_EXPONENTIATE:
    00909           if (scale == 0.0)
    00910             return 0;
    00911           if (nblo < 0)
    00912             return 0;
    00913           nblo = pow(nblo, scale);
    00914           nbhi = pow(nbhi, scale);
    00915           break;
    00916         default:
    00917           return 0;
    00918         }
    00919     }
    00920
    00921   if (! finite(nbhi) || ! finite(nblo))
    00922     return 0;
    00923
    00924   count = get_point_count(curve);
    00925   if (count)
    00926     {
    00927       double *tmp;
    00928       size_t scount;
    00929       int stride = 1;
    00930       int offset = 0;
    00931       const double *data;
    00932       if (curve->piecewise)
    00933         {
    00934           stride = 2;
    00935           offset = 1;
    00936         }
    00937       stp_sequence_get_data(curve->seq, &scount, &data);
    00938       tmp = stp_malloc(sizeof(double) * scount);
    00939       memcpy(tmp, data, scount * sizeof(double));
    00940       for (i = offset; i < scount; i += stride)
    00941         {
    00942           switch (mode)
    00943             {
    00944             case STP_CURVE_COMPOSE_ADD:
    00945               tmp[i] = tmp[i] + scale;
    00946               break;
    00947             case STP_CURVE_COMPOSE_MULTIPLY:
    00948               tmp[i] = tmp[i] * scale;
    00949               break;
    00950             case STP_CURVE_COMPOSE_EXPONENTIATE:
    00951               tmp[i] = pow(tmp[i], scale);
    00952               break;
    00953             }
    00954           if (tmp[i] > nbhi || tmp[i] < nblo)
    00955             {
    00956               if (bounds_mode == STP_CURVE_BOUNDS_ERROR)
    00957                 {
    00958                   stp_free(tmp);
    00959                   return(0);
    00960                 }
    00961               else if (tmp[i] > nbhi)
    00962                 tmp[i] = nbhi;
    00963               else
    00964                 tmp[i] = nblo;
    00965             }
    00966         }
    00967       stp_sequence_set_bounds(curve->seq, nblo, nbhi);
    00968       curve->gamma = 0.0;
    00969       stpi_curve_set_points(curve, count);
    00970       stp_sequence_set_subrange(curve->seq, 0, scount, tmp);
    00971       stp_free(tmp);
    00972       curve->recompute_interval = 1;
    00973       invalidate_auxiliary_data(curve);
    00974     }
    00975   return 1;
    00976 }
    00977
    00978 static int
    00979 stpi_curve_check_parameters(stp_curve_t *curve, size_t points)
    00980 {
    00981   double blo, bhi;
    00982   if (curve->gamma && curve->wrap_mode)
    00983     {
    00984       stp_deprintf(STP_DBG_CURVE_ERRORS,
    00985                    "curve sets both gamma and wrap_mode\n");
    00986       return 0;
    00987     }
    00988   stp_sequence_get_bounds(curve->seq, &blo, &bhi);
    00989   if (blo > bhi)
    00990     {
    00991       stp_deprintf(STP_DBG_CURVE_ERRORS,
    00992                    "curve low bound is greater than high bound\n");
    00993       return 0;
    00994     }
    00995   return 1;
    00996 }
    00997
    00998 static inline double
    00999 interpolate_gamma_internal(const stp_curve_t *curve, double where)
    01000 {
    01001   double fgamma = curve->gamma;
    01002   double blo, bhi;
    01003   size_t real_point_count;
    01004
    01005   real_point_count = get_real_point_count(curve);;
    01006
    01007   if (real_point_count)
    01008     where /= (real_point_count - 1);
    01009   if (fgamma < 0)
    01010     {
    01011       where = 1.0 - where;
    01012       fgamma = -fgamma;
    01013     }
    01014   stp_sequence_get_bounds(curve->seq, &blo, &bhi);
    01015   stp_deprintf(STP_DBG_CURVE,
    01016                "interpolate_gamma %f %f %f %f %f\n", where, fgamma,
    01017                blo, bhi, pow(where, fgamma));
    01018   return blo + (bhi - blo) * pow(where, fgamma);
    01019 }
    01020
    01021 static inline double
    01022 do_interpolate_spline(double low, double high, double frac,
    01023                       double interval_low, double interval_high,
    01024                       double x_interval)
    01025 {
    01026   double a = 1.0 - frac;
    01027   double b = frac;
    01028   double retval =
    01029     ((a * a * a - a) * interval_low) + ((b * b * b - b) * interval_high);
    01030   retval = retval * x_interval * x_interval / 6;
    01031   retval += (a * low) + (b * high);
    01032   return retval;
    01033 }
    01034
    01035 static inline double
    01036 interpolate_point_internal(stp_curve_t *curve, double where)
    01037 {
    01038   int integer = where;
    01039   double frac = where - (double) integer;
    01040   double bhi, blo;
    01041
    01042   if (frac == 0.0)
    01043     {
    01044       double val;
    01045       if ((stp_sequence_get_point(curve->seq, integer, &val)) == 0)
    01046         return HUGE_VAL; /* Infinity */
    01047       return val;
    01048     }
    01049   if (curve->recompute_interval)
    01050     compute_intervals(curve);
    01051   if (curve->curve_type == STP_CURVE_TYPE_LINEAR)
    01052     {
    01053       double val;
    01054       if ((stp_sequence_get_point(curve->seq, integer, &val)) == 0)
    01055         return HUGE_VAL; /* Infinity */
    01056       return val + frac * curve->interval[integer];
    01057     }
    01058   else
    01059     {
    01060       size_t point_count;
    01061       double ival, ip1val;
    01062       double retval;
    01063       int i = integer;
    01064       int ip1 = integer + 1;
    01065
    01066       point_count = get_point_count(curve);
    01067
    01068       if (ip1 >= point_count)
    01069         ip1 -= point_count;
    01070
    01071       if ((stp_sequence_get_point(curve->seq, i, &ival)) == 0 ||
    01072           (stp_sequence_get_point(curve->seq, ip1, &ip1val)) == 0)
    01073         return HUGE_VAL; /* Infinity */
    01074
    01075       retval = do_interpolate_spline(ival, ip1val, frac, curve->interval[i],
    01076                                      curve->interval[ip1], 1.0);
    01077
    01078       stp_sequence_get_bounds(curve->seq, &blo, &bhi);
    01079       if (retval > bhi)
    01080         retval = bhi;
    01081       if (retval < blo)
    01082         retval = blo;
    01083       return retval;
    01084     }
    01085 }
    01086
    01087 int
    01088 stp_curve_interpolate_value(const stp_curve_t *curve, double where,
    01089                             double *result)
    01090 {
    01091   size_t limit;
    01092
    01093   check_curve(curve);
    01094   if (curve->piecewise)
    01095     return 0;
    01096
    01097   limit = get_real_point_count(curve);
    01098
    01099   if (where < 0 || where > limit)
    01100     return 0;
    01101   if (curve->gamma)     /* this means a pure gamma curve */
    01102     *result = interpolate_gamma_internal(curve, where);
    01103   else
    01104     *result = interpolate_point_internal((stp_curve_t *) curve, where);
    01105   return 1;
    01106 }
    01107
    01108 int
    01109 stp_curve_resample(stp_curve_t *curve, size_t points)
    01110 {
    01111   size_t limit = points;
    01112   size_t old;
    01113   size_t i;
    01114   double *new_vec;
    01115
    01116   check_curve(curve);
    01117
    01118   if (points == get_point_count(curve) && curve->seq && !(curve->piecewise))
    01119     return 1;
    01120
    01121   if (points < 2)
    01122     return 1;
    01123
    01124   if (curve->wrap_mode == STP_CURVE_WRAP_AROUND)
    01125     limit++;
    01126   if (limit > curve_point_limit)
    01127     return 0;
    01128   old = get_real_point_count(curve);
    01129   if (old)
    01130     old--;
    01131   if (!old)
    01132     old = 1;
    01133
    01134   new_vec = stp_malloc(sizeof(double) * limit);
    01135
    01136   /*
    01137    * Be very careful how we calculate the location along the scale!
    01138    * If we're not careful how we do it, we might get a small roundoff
    01139    * error
    01140    */
    01141   if (curve->piecewise)
    01142     {
    01143       double blo, bhi;
    01144       int curpos = 0;
    01145       stp_sequence_get_bounds(curve->seq, &blo, &bhi);
    01146       if (curve->recompute_interval)
    01147         compute_intervals(curve);
    01148       for (i = 0; i < old; i++)
    01149         {
    01150           double low;
    01151           double high;
    01152           double low_y;
    01153           double high_y;
    01154           double x_delta;
    01155           if (!stp_sequence_get_point(curve->seq, i * 2, &low))
    01156             {
    01157               stp_free(new_vec);
    01158               return 0;
    01159             }
    01160           if (i == old - 1)
    01161             high = 1.0;
    01162           else if (!stp_sequence_get_point(curve->seq, ((i + 1) * 2), &high))
    01163             {
    01164               stp_free(new_vec);
    01165               return 0;
    01166             }
    01167           if (!stp_sequence_get_point(curve->seq, (i * 2) + 1, &low_y))
    01168             {
    01169               stp_free(new_vec);
    01170               return 0;
    01171             }
    01172           if (!stp_sequence_get_point(curve->seq, ((i + 1) * 2) + 1, &high_y))
    01173             {
    01174               stp_free(new_vec);
    01175               return 0;
    01176             }
    01177           stp_deprintf(STP_DBG_CURVE,
    01178                        "Filling slots at %d %d: %f %f  %f %f  %d\n",
    01179                        i,curpos, high, low, high_y, low_y, limit);
    01180           x_delta = high - low;
    01181           high *= (limit - 1);
    01182           low *= (limit - 1);
    01183           while (curpos <= high)
    01184             {
    01185               double frac = (curpos - low) / (high - low);
    01186               if (curve->curve_type == STP_CURVE_TYPE_LINEAR)
    01187                 new_vec[curpos] = low_y + frac * (high_y - low_y);
    01188               else
    01189                 new_vec[curpos] =
    01190                   do_interpolate_spline(low_y, high_y, frac,
    01191                                         curve->interval[i],
    01192                                         curve->interval[i + 1],
    01193                                         x_delta);
    01194               if (new_vec[curpos] < blo)
    01195                 new_vec[curpos] = blo;
    01196               if (new_vec[curpos] > bhi)
    01197                 new_vec[curpos] = bhi;
    01198               stp_deprintf(STP_DBG_CURVE,
    01199                            "  Filling slot %d %f %f\n",
    01200                            curpos, frac, new_vec[curpos]);
    01201               curpos++;
    01202             }
    01203         }
    01204       curve->piecewise = 0;
    01205     }
    01206   else
    01207     {
    01208       for (i = 0; i < limit; i++)
    01209         if (curve->gamma)
    01210           new_vec[i] =
    01211             interpolate_gamma_internal(curve, ((double) i * (double) old /
    01212                                                (double) (limit - 1)));
    01213         else
    01214           new_vec[i] =
    01215             interpolate_point_internal(curve, ((double) i * (double) old /
    01216                                                (double) (limit - 1)));
    01217     }
    01218   stpi_curve_set_points(curve, points);
    01219   stp_sequence_set_subrange(curve->seq, 0, limit, new_vec);
    01220   curve->recompute_interval = 1;
    01221   stp_free(new_vec);
    01222   return 1;
    01223 }
    01224
    01225 static unsigned
    01226 gcd(unsigned a, unsigned b)
    01227 {
    01228   unsigned tmp;
    01229   if (b > a)
    01230     {
    01231       tmp = a;
    01232       a = b;
    01233       b = tmp;
    01234     }
    01235   while (1)
    01236     {
    01237       tmp = a % b;
    01238       if (tmp == 0)
    01239         return b;
    01240       a = b;
    01241       b = tmp;
    01242     }
    01243 }
    01244
    01245 static unsigned
    01246 lcm(unsigned a, unsigned b)
    01247 {
    01248   if (a == b)
    01249     return a;
    01250   else if (a * b == 0)
    01251     return a > b ? a : b;
    01252   else
    01253     {
    01254       double rval = (double) a / gcd(a, b) * b;
    01255       if (rval > curve_point_limit)
    01256         return curve_point_limit;
    01257       else
    01258         return rval;
    01259     }
    01260 }
    01261
    01262 static int
    01263 create_gamma_curve(stp_curve_t **retval, double lo, double hi, double fgamma,
    01264                    int points)
    01265 {
    01266   *retval = stp_curve_create(STP_CURVE_WRAP_NONE);
    01267   if (stp_curve_set_bounds(*retval, lo, hi) &&
    01268       stp_curve_set_gamma(*retval, fgamma) &&
    01269       stp_curve_resample(*retval, points))
    01270     return 1;
    01271   stp_curve_destroy(*retval);
    01272   *retval = 0;
    01273   return 0;
    01274 }
    01275
    01276 static int
    01277 interpolate_points(stp_curve_t *a, stp_curve_t *b,
    01278                    stp_curve_compose_t mode,
    01279                    int points, double *tmp_data)
    01280 {
    01281   double pa, pb;
    01282   int i;
    01283   size_t points_a = stp_curve_count_points(a);
    01284   size_t points_b = stp_curve_count_points(b);
    01285   for (i = 0; i < points; i++)
    01286     {
    01287       if (!stp_curve_interpolate_value
    01288           (a, (double) i * (points_a - 1) / (points - 1), &pa))
    01289         {
    01290           stp_deprintf(STP_DBG_CURVE_ERRORS,
    01291                        "interpolate_points: interpolate curve a value failed\n");
    01292           return 0;
    01293         }
    01294       if (!stp_curve_interpolate_value
    01295           (b, (double) i * (points_b - 1) / (points - 1), &pb))
    01296         {
    01297           stp_deprintf(STP_DBG_CURVE_ERRORS,
    01298                        "interpolate_points: interpolate curve b value failed\n");
    01299           return 0;
    01300         }
    01301       if (mode == STP_CURVE_COMPOSE_ADD)
    01302         pa += pb;
    01303       else
    01304         pa *= pb;
    01305       if (! finite(pa))
    01306         {
    01307           stp_deprintf(STP_DBG_CURVE_ERRORS,
    01308                        "interpolate_points: interpolated point %lu is invalid\n",
    01309                        (unsigned long) i);
    01310           return 0;
    01311         }
    01312       tmp_data[i] = pa;
    01313     }
    01314   return 1;
    01315 }
    01316
    01317 int
    01318 stp_curve_compose(stp_curve_t **retval,
    01319                   stp_curve_t *a, stp_curve_t *b,
    01320                   stp_curve_compose_t mode, int points)
    01321 {
    01322   stp_curve_t *ret;
    01323   double *tmp_data;
    01324   double gamma_a = stp_curve_get_gamma(a);
    01325   double gamma_b = stp_curve_get_gamma(b);
    01326   unsigned points_a = stp_curve_count_points(a);
    01327   unsigned points_b = stp_curve_count_points(b);
    01328   double alo, ahi, blo, bhi;
    01329
    01330   if (a->piecewise && b->piecewise)
    01331     return 0;
    01332   if (a->piecewise)
    01333     {
    01334       stp_curve_t *a_save = a;
    01335       a = stp_curve_create_copy(a_save);
    01336       stp_curve_resample(a, stp_curve_count_points(b));
    01337     }
    01338   if (b->piecewise)
    01339     {
    01340       stp_curve_t *b_save = b;
    01341       b = stp_curve_create_copy(b_save);
    01342       stp_curve_resample(b, stp_curve_count_points(a));
    01343     }
    01344
    01345   if (mode != STP_CURVE_COMPOSE_ADD && mode != STP_CURVE_COMPOSE_MULTIPLY)
    01346     return 0;
    01347   if (stp_curve_get_wrap(a) != stp_curve_get_wrap(b))
    01348     return 0;
    01349   stp_curve_get_bounds(a, &alo, &ahi);
    01350   stp_curve_get_bounds(b, &blo, &bhi);
    01351   if (mode == STP_CURVE_COMPOSE_MULTIPLY && (alo < 0 || blo < 0))
    01352     return 0;
    01353
    01354   if (stp_curve_get_wrap(a) == STP_CURVE_WRAP_AROUND)
    01355     {
    01356       points_a++;
    01357       points_b++;
    01358     }
    01359   if (points == -1)
    01360     {
    01361       points = lcm(points_a, points_b);
    01362       if (stp_curve_get_wrap(a) == STP_CURVE_WRAP_AROUND)
    01363         points--;
    01364     }
    01365   if (points < 2 || points > curve_point_limit ||
    01366       ((stp_curve_get_wrap(a) == STP_CURVE_WRAP_AROUND) &&
    01367        points > curve_point_limit - 1))
    01368     return 0;
    01369
    01370   if (gamma_a && gamma_b && gamma_a * gamma_b > 0 &&
    01371       mode == STP_CURVE_COMPOSE_MULTIPLY)
    01372     return create_gamma_curve(retval, alo * blo, ahi * bhi, gamma_a + gamma_b,
    01373                               points);
    01374   tmp_data = stp_malloc(sizeof(double) * points);
    01375   if (!interpolate_points(a, b, mode, points, tmp_data))
    01376     {
    01377       stp_free(tmp_data);
    01378       return 0;
    01379     }
    01380   ret = stp_curve_create(stp_curve_get_wrap(a));
    01381   if (mode == STP_CURVE_COMPOSE_ADD)
    01382     {
    01383       stp_curve_rescale(ret, (ahi - alo) + (bhi - blo),
    01384                         STP_CURVE_COMPOSE_MULTIPLY, STP_CURVE_BOUNDS_RESCALE);
    01385       stp_curve_rescale(ret, alo + blo,
    01386                         STP_CURVE_COMPOSE_ADD, STP_CURVE_BOUNDS_RESCALE);
    01387     }
    01388   else
    01389     {
    01390       stp_curve_rescale(ret, (ahi - alo) * (bhi - blo),
    01391                         STP_CURVE_COMPOSE_MULTIPLY, STP_CURVE_BOUNDS_RESCALE);
    01392       stp_curve_rescale(ret, alo * blo,
    01393                         STP_CURVE_COMPOSE_ADD, STP_CURVE_BOUNDS_RESCALE);
    01394     }
    01395   if (! stp_curve_set_data(ret, points, tmp_data))
    01396     goto bad1;
    01397   *retval = ret;
    01398   stp_free(tmp_data);
    01399   return 1;
    01400  bad1:
    01401   stp_curve_destroy(ret);
    01402   stp_free(tmp_data);
    01403   return 0;
    01404 }
    01405
    01406
    01407 stp_curve_t *
    01408 stp_curve_create_from_xmltree(stp_mxml_node_t *curve)  /* The curve node */
    01409 {
    01410   const char *stmp;                       /* Temporary string */
    01411   stp_mxml_node_t *child;                 /* Child sequence node */
    01412   stp_curve_t *ret = NULL;                /* Curve to return */
    01413   stp_curve_type_t curve_type;            /* Type of curve */
    01414   stp_curve_wrap_mode_t wrap_mode;        /* Curve wrap mode */
    01415   double fgamma;                          /* Gamma value */
    01416   stp_sequence_t *seq = NULL;             /* Sequence data */
    01417   double low, high;                       /* Sequence bounds */
    01418   int piecewise = 0;
    01419
    01420   stp_xml_init();
    01421   /* Get curve type */
    01422   stmp = stp_mxmlElementGetAttr(curve, "type");
    01423   if (stmp)
    01424     {
    01425       if (!strcmp(stmp, "linear"))
    01426           curve_type = STP_CURVE_TYPE_LINEAR;
    01427       else if (!strcmp(stmp, "spline"))
    01428           curve_type = STP_CURVE_TYPE_SPLINE;
    01429       else
    01430         {
    01431           stp_deprintf(STP_DBG_CURVE_ERRORS,
    01432                        "stp_curve_create_from_xmltree: %s: \"type\" invalid\n", stmp);
    01433           goto error;
    01434         }
    01435     }
    01436   else
    01437     {
    01438       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01439                    "stp_curve_create_from_xmltree: \"type\" missing\n");
    01440       goto error;
    01441     }
    01442   /* Get curve wrap mode */
    01443   stmp = stp_mxmlElementGetAttr(curve, "wrap");
    01444   if (stmp)
    01445     {
    01446       if (!strcmp(stmp, "nowrap"))
    01447         wrap_mode = STP_CURVE_WRAP_NONE;
    01448       else if (!strcmp(stmp, "wrap"))
    01449         {
    01450           wrap_mode = STP_CURVE_WRAP_AROUND;
    01451         }
    01452       else
    01453         {
    01454           stp_deprintf(STP_DBG_CURVE_ERRORS,
    01455                        "stp_curve_create_from_xmltree: %s: \"wrap\" invalid\n", stmp);
    01456           goto error;
    01457         }
    01458     }
    01459   else
    01460     {
    01461       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01462                    "stp_curve_create_from_xmltree: \"wrap\" missing\n");
    01463       goto error;
    01464     }
    01465   /* Get curve gamma */
    01466   stmp = stp_mxmlElementGetAttr(curve, "gamma");
    01467   if (stmp)
    01468     {
    01469       fgamma = stp_xmlstrtod(stmp);
    01470     }
    01471   else
    01472     {
    01473       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01474                    "stp_curve_create_from_xmltree: \"gamma\" missing\n");
    01475       goto error;
    01476     }
    01477   /* If gamma is set, wrap_mode must be STP_CURVE_WRAP_NONE */
    01478   if (fgamma && wrap_mode != STP_CURVE_WRAP_NONE)
    01479     {
    01480       stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: "
    01481                    "gamma set and \"wrap\" is not STP_CURVE_WRAP_NONE\n");
    01482       goto error;
    01483     }
    01484   stmp = stp_mxmlElementGetAttr(curve, "piecewise");
    01485   if (stmp && strcmp(stmp, "true") == 0)
    01486     piecewise = 1;
    01487
    01488   /* Set up the curve */
    01489   ret = stp_curve_create(wrap_mode);
    01490   stp_curve_set_interpolation_type(ret, curve_type);
    01491
    01492   child = stp_mxmlFindElement(curve, curve, "sequence", NULL, NULL, STP_MXML_DESCEND);
    01493   if (child)
    01494     seq = stp_sequence_create_from_xmltree(child);
    01495
    01496   if (seq == NULL)
    01497     {
    01498       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01499                    "stp_curve_create_from_xmltree: sequence read failed\n");
    01500       goto error;
    01501     }
    01502
    01503   /* Set curve bounds */
    01504   stp_sequence_get_bounds(seq, &low, &high);
    01505   stp_curve_set_bounds(ret, low, high);
    01506
    01507   if (fgamma)
    01508     stp_curve_set_gamma(ret, fgamma);
    01509   else /* Not a gamma curve, so set points */
    01510     {
    01511       size_t seq_count;
    01512       const double* data;
    01513
    01514       stp_sequence_get_data(seq, &seq_count, &data);
    01515       if (piecewise)
    01516         {
    01517           if ((seq_count % 2) != 0)
    01518             {
    01519               stp_deprintf(STP_DBG_CURVE_ERRORS,
    01520                            "stp_curve_create_from_xmltree: invalid data count %d\n",
    01521                            seq_count);
    01522               goto error;
    01523             }
    01524           if (stp_curve_set_data_points(ret, seq_count / 2,
    01525                                         (const stp_curve_point_t *) data) == 0)
    01526             {
    01527               stp_deprintf(STP_DBG_CURVE_ERRORS,
    01528                            "stp_curve_create_from_xmltree: failed to set curve data points\n");
    01529               goto error;
    01530             }
    01531         }
    01532       else
    01533         {
    01534           if (stp_curve_set_data(ret, seq_count, data) == 0)
    01535             {
    01536               stp_deprintf(STP_DBG_CURVE_ERRORS,
    01537                            "stp_curve_create_from_xmltree: failed to set curve data\n");
    01538               goto error;
    01539             }
    01540         }
    01541     }
    01542
    01543   if (seq)
    01544     {
    01545       stp_sequence_destroy(seq);
    01546       seq = NULL;
    01547     }
    01548
    01549     /* Validate curve */
    01550   if (stpi_curve_check_parameters(ret, stp_curve_count_points(ret)) == 0)
    01551     {
    01552       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01553                    "stp_curve_create_from_xmltree: parameter check failed\n");
    01554       goto error;
    01555     }
    01556
    01557   stp_xml_exit();
    01558
    01559   return ret;
    01560
    01561  error:
    01562   stp_deprintf(STP_DBG_CURVE_ERRORS,
    01563                "stp_curve_create_from_xmltree: error during curve read\n");
    01564   if (ret)
    01565     stp_curve_destroy(ret);
    01566   stp_xml_exit();
    01567   return NULL;
    01568 }
    01569
    01570
    01571 stp_mxml_node_t *
    01572 stp_xmltree_create_from_curve(const stp_curve_t *curve)  /* The curve */
    01573 {
    01574   stp_curve_wrap_mode_t wrapmode;
    01575   stp_curve_type_t interptype;
    01576   double gammaval, low, high;
    01577   stp_sequence_t *seq;
    01578
    01579   char *cgamma;
    01580
    01581   stp_mxml_node_t *curvenode = NULL;
    01582   stp_mxml_node_t *child = NULL;
    01583
    01584   stp_xml_init();
    01585
    01586   /* Get curve details */
    01587   wrapmode = stp_curve_get_wrap(curve);
    01588   interptype = stp_curve_get_interpolation_type(curve);
    01589   gammaval = stp_curve_get_gamma(curve);
    01590
    01591   if (gammaval && wrapmode != STP_CURVE_WRAP_NONE)
    01592     {
    01593       stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_xmltree_create_from_curve: "
    01594                    "curve sets gamma and wrap_mode is not STP_CURVE_WRAP_NONE\n");
    01595       goto error;
    01596     }
    01597
    01598   /* Construct the allocated strings required */
    01599   stp_asprintf(&cgamma, "%g", gammaval);
    01600
    01601   curvenode = stp_mxmlNewElement(NULL, "curve");
    01602   stp_mxmlElementSetAttr(curvenode, "wrap", stpi_wrap_mode_names[wrapmode]);
    01603   stp_mxmlElementSetAttr(curvenode, "type", stpi_curve_type_names[interptype]);
    01604   stp_mxmlElementSetAttr(curvenode, "gamma", cgamma);
    01605   if (curve->piecewise)
    01606     stp_mxmlElementSetAttr(curvenode, "piecewise", "true");
    01607   else
    01608     stp_mxmlElementSetAttr(curvenode, "piecewise", "false");
    01609
    01610   stp_free(cgamma);
    01611
    01612   seq = stp_sequence_create();
    01613   stp_curve_get_bounds(curve, &low, &high);
    01614   stp_sequence_set_bounds(seq, low, high);
    01615   if (gammaval != 0) /* A gamma curve does not require sequence data */
    01616     {
    01617       stp_sequence_set_size(seq, 0);
    01618     }
    01619   else
    01620     {
    01621       const double *data;
    01622       size_t count;
    01623       data = stpi_curve_get_data_internal(curve, &count);
    01624       stp_sequence_set_data(seq, count, data);
    01625     }
    01626
    01627   child = stp_xmltree_create_from_sequence(seq);
    01628
    01629   if (seq)
    01630     {
    01631       stp_sequence_destroy(seq);
    01632       seq = NULL;
    01633     }
    01634
    01635   if (child == NULL)
    01636     {
    01637       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01638                    "stp_xmltree_create_from_curve: sequence node is NULL\n");
    01639       goto error;
    01640     }
    01641   stp_mxmlAdd(curvenode, STP_MXML_ADD_AFTER, NULL, child);
    01642
    01643   stp_xml_exit();
    01644
    01645   return curvenode;
    01646
    01647  error:
    01648   stp_deprintf(STP_DBG_CURVE_ERRORS,
    01649                "stp_xmltree_create_from_curve: error during xmltree creation\n");
    01650   if (curvenode)
    01651     stp_mxmlDelete(curvenode);
    01652   if (child)
    01653     stp_mxmlDelete(child);
    01654   stp_xml_exit();
    01655
    01656   return NULL;
    01657 }
    01658
    01659 static stp_mxml_node_t *
    01660 xmldoc_create_from_curve(const stp_curve_t *curve)
    01661 {
    01662   stp_mxml_node_t *xmldoc;
    01663   stp_mxml_node_t *rootnode;
    01664   stp_mxml_node_t *curvenode;
    01665
    01666   /* Get curve details */
    01667   curvenode = stp_xmltree_create_from_curve(curve);
    01668   if (curvenode == NULL)
    01669     {
    01670       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01671                    "xmldoc_create_from_curve: error creating curve node\n");
    01672       return NULL;
    01673     }
    01674   /* Create the XML tree */
    01675   xmldoc = stp_xmldoc_create_generic();
    01676   if (xmldoc == NULL)
    01677     {
    01678       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01679                    "xmldoc_create_from_curve: error creating XML document\n");
    01680       return NULL;
    01681     }
    01682   rootnode = xmldoc->child;
    01683   if (rootnode == NULL)
    01684     {
    01685       stp_mxmlDelete(xmldoc);
    01686       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01687                    "xmldoc_create_from_curve: error getting XML document root node\n");
    01688       return NULL;
    01689     }
    01690
    01691   stp_mxmlAdd(rootnode, STP_MXML_ADD_AFTER, NULL, curvenode);
    01692
    01693   return xmldoc;
    01694 }
    01695
    01696 static int
    01697 curve_whitespace_callback(stp_mxml_node_t *node, int where)
    01698 {
    01699   if (node->type != STP_MXML_ELEMENT)
    01700     return 0;
    01701   if (strcasecmp(node->value.element.name, "gimp-print") == 0)
    01702     {
    01703       switch (where)
    01704         {
    01705         case STP_MXML_WS_AFTER_OPEN:
    01706         case STP_MXML_WS_BEFORE_CLOSE:
    01707         case STP_MXML_WS_AFTER_CLOSE:
    01708           return '\n';
    01709         case STP_MXML_WS_BEFORE_OPEN:
    01710         default:
    01711           return 0;
    01712         }
    01713     }
    01714   else if (strcasecmp(node->value.element.name, "curve") == 0)
    01715     {
    01716       switch (where)
    01717         {
    01718         case STP_MXML_WS_AFTER_OPEN:
    01719           return '\n';
    01720         case STP_MXML_WS_BEFORE_CLOSE:
    01721         case STP_MXML_WS_AFTER_CLOSE:
    01722         case STP_MXML_WS_BEFORE_OPEN:
    01723         default:
    01724           return 0;
    01725         }
    01726     }
    01727   else if (strcasecmp(node->value.element.name, "sequence") == 0)
    01728     {
    01729       const char *count;
    01730       switch (where)
    01731         {
    01732         case STP_MXML_WS_BEFORE_CLOSE:
    01733           count = stp_mxmlElementGetAttr(node, "count");
    01734           if (strcmp(count, "0") == 0)
    01735             return 0;
    01736           else
    01737             return '\n';
    01738         case STP_MXML_WS_AFTER_OPEN:
    01739         case STP_MXML_WS_AFTER_CLOSE:
    01740           return '\n';
    01741         case STP_MXML_WS_BEFORE_OPEN:
    01742         default:
    01743           return 0;
    01744         }
    01745     }
    01746   else
    01747     return 0;
    01748 }
    01749
    01750
    01751 int
    01752 stp_curve_write(FILE *file, const stp_curve_t *curve)  /* The curve */
    01753 {
    01754   stp_mxml_node_t *xmldoc = NULL;
    01755
    01756   stp_xml_init();
    01757
    01758   xmldoc = xmldoc_create_from_curve(curve);
    01759   if (xmldoc == NULL)
    01760     {
    01761       stp_xml_exit();
    01762       return 1;
    01763     }
    01764
    01765   stp_mxmlSaveFile(xmldoc, file, curve_whitespace_callback);
    01766
    01767   if (xmldoc)
    01768     stp_mxmlDelete(xmldoc);
    01769
    01770   stp_xml_exit();
    01771
    01772   return 0;
    01773 }
    01774
    01775 char *
    01776 stp_curve_write_string(const stp_curve_t *curve)  /* The curve */
    01777 {
    01778   stp_mxml_node_t *xmldoc = NULL;
    01779   char *retval;
    01780
    01781   stp_xml_init();
    01782
    01783   xmldoc = xmldoc_create_from_curve(curve);
    01784   if (xmldoc == NULL)
    01785     {
    01786       stp_xml_exit();
    01787       return NULL;
    01788     }
    01789
    01790   retval = stp_mxmlSaveAllocString(xmldoc, curve_whitespace_callback);
    01791
    01792   if (xmldoc)
    01793     stp_mxmlDelete(xmldoc);
    01794
    01795   stp_xml_exit();
    01796
    01797   return retval;
    01798 }
    01799
    01800 static stp_curve_t *
    01801 xml_doc_get_curve(stp_mxml_node_t *doc)
    01802 {
    01803   stp_mxml_node_t *cur;
    01804   stp_mxml_node_t *xmlcurve;
    01805   stp_curve_t *curve = NULL;
    01806
    01807   if (doc == NULL )
    01808     {
    01809       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01810                    "xml_doc_get_curve: XML file not parsed successfully.\n");
    01811       return NULL;
    01812     }
    01813
    01814   cur = doc->child;
    01815
    01816   if (cur == NULL)
    01817     {
    01818       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01819                    "xml_doc_get_curve: empty document\n");
    01820       return NULL;
    01821     }
    01822
    01823   xmlcurve = stp_xml_get_node(cur, "gimp-print", "curve", NULL);
    01824
    01825   if (xmlcurve)
    01826     curve = stp_curve_create_from_xmltree(xmlcurve);
    01827
    01828   return curve;
    01829 }
    01830
    01831 stp_curve_t *
    01832 stp_curve_create_from_file(const char* file)
    01833 {
    01834   stp_curve_t *curve = NULL;
    01835   stp_mxml_node_t *doc;
    01836   FILE *fp = fopen(file, "r");
    01837   if (!fp)
    01838     {
    01839       stp_deprintf(STP_DBG_CURVE_ERRORS,
    01840                    "stp_curve_create_from_file: unable to open %s: %s\n",
    01841                     file, strerror(errno));
    01842       return NULL;
    01843     }
    01844   stp_deprintf(STP_DBG_XML, "stp_curve_create_from_file: reading `%s'...\n",
    01845                file);
    01846
    01847   stp_xml_init();
    01848
    01849   doc = stp_mxmlLoadFile(NULL, fp, STP_MXML_NO_CALLBACK);
    01850
    01851   curve = xml_doc_get_curve(doc);
    01852
    01853   if (doc)
    01854     stp_mxmlDelete(doc);
    01855
    01856   stp_xml_exit();
    01857   (void) fclose(fp);
    01858   return curve;
    01859
    01860 }
    01861
    01862 stp_curve_t *
    01863 stp_curve_create_from_stream(FILE* fp)
    01864 {
    01865   stp_curve_t *curve = NULL;
    01866   stp_mxml_node_t *doc;
    01867   stp_deprintf(STP_DBG_XML, "stp_curve_create_from_fp: reading...\n");
    01868
    01869   stp_xml_init();
    01870
    01871   doc = stp_mxmlLoadFile(NULL, fp, STP_MXML_NO_CALLBACK);
    01872
    01873   curve = xml_doc_get_curve(doc);
    01874
    01875   if (doc)
    01876     stp_mxmlDelete(doc);
    01877
    01878   stp_xml_exit();
    01879   return curve;
    01880
    01881 }
    01882
    01883 stp_curve_t *
    01884 stp_curve_create_from_string(const char* string)
    01885 {
    01886   stp_curve_t *curve = NULL;
    01887   stp_mxml_node_t *doc;
    01888   stp_deprintf(STP_DBG_XML,
    01889                "stp_curve_create_from_string: reading '%s'...\n", string);
    01890   stp_xml_init();
    01891
    01892   doc = stp_mxmlLoadString(NULL, string, STP_MXML_NO_CALLBACK);
    01893
    01894   curve = xml_doc_get_curve(doc);
    01895
    01896   if (doc)
    01897     stp_mxmlDelete(doc);
    01898
    01899   stp_xml_exit();
    01900   return curve;
    01901 }