Main Page   Modules   Alphabetical List   Data Structures   File List   Data Fields   Globals  

src/main/curve.c

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

Generated on Sat Jun 26 10:11:52 2004 for libgimpprint API Reference by doxygen1.2.17