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.c,v 1.49 2004/08/03 17:57:48 rleigh 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 }

Generated on Wed Aug 25 07:56:13 2004 for libgimpprint API Reference by doxygen 1.3.6