int stp_curve_set_subrange(stp_curve_t *curve, const stp_curve_t *range, size_t start) { double blo, bhi; double rlo, rhi; const double *data; size_t count; CHECK_CURVE(curve); if (start + stp_curve_count_points(range) > stp_curve_count_points(curve)) return 0; if (curve->piecewise) return 0; stp_sequence_get_bounds(curve->seq, &blo, &bhi); stp_sequence_get_range(curve->seq, &rlo, &rhi); if (rlo < blo || rhi > bhi) return 0; stp_sequence_get_data(range->seq, &count, &data); curve->recompute_interval = 1; curve->gamma = 0.0; invalidate_auxiliary_data(curve); stp_sequence_set_subrange(curve->seq, start, stp_curve_count_points(range), data); return 1; }
int stp_dither_matrix_validate_array(const stp_array_t *array) { double low, high; const stp_sequence_t *seq = stp_array_get_sequence(array); stp_sequence_get_bounds(seq, &low, &high); if (low < 0 || high > 65535) return 0; return 1; }
static inline double interpolate_point_internal(const stp_curve_t *curve, double where) { int integer = where; double frac = where - (double) integer; double bhi, blo; if (frac == 0.0) { double val; if ((stp_sequence_get_point(curve->seq, integer, &val)) == 0) return HUGE_VAL; /* Infinity */ return val; } if (curve->recompute_interval) compute_intervals((stpi_cast_safe(curve))); if (curve->curve_type == STP_CURVE_TYPE_LINEAR) { double val; if ((stp_sequence_get_point(curve->seq, integer, &val)) == 0) return HUGE_VAL; /* Infinity */ return val + frac * curve->interval[integer]; } else { size_t point_count; double ival, ip1val; double retval; int i = integer; int ip1 = integer + 1; point_count = get_point_count(curve); if (ip1 >= point_count) ip1 -= point_count; if ((stp_sequence_get_point(curve->seq, i, &ival)) == 0 || (stp_sequence_get_point(curve->seq, ip1, &ip1val)) == 0) return HUGE_VAL; /* Infinity */ retval = do_interpolate_spline(ival, ip1val, frac, curve->interval[i], curve->interval[ip1], 1.0); stp_sequence_get_bounds(curve->seq, &blo, &bhi); if (retval > bhi) retval = bhi; if (retval < blo) retval = blo; return retval; } }
static int stpi_curve_check_parameters(stp_curve_t *curve, size_t points) { double blo, bhi; if (curve->gamma && curve->wrap_mode) { stp_deprintf(STP_DBG_CURVE_ERRORS, "curve sets both gamma and wrap_mode\n"); return 0; } stp_sequence_get_bounds(curve->seq, &blo, &bhi); if (blo > bhi) { stp_deprintf(STP_DBG_CURVE_ERRORS, "curve low bound is greater than high bound\n"); return 0; } return 1; }
static inline double interpolate_gamma_internal(const stp_curve_t *curve, double where) { double fgamma = curve->gamma; double blo, bhi; size_t real_point_count; real_point_count = get_real_point_count(curve);; if (real_point_count) where /= (real_point_count - 1); if (fgamma < 0) { where = 1.0 - where; fgamma = -fgamma; } stp_sequence_get_bounds(curve->seq, &blo, &bhi); stp_deprintf(STP_DBG_CURVE, "interpolate_gamma %f %f %f %f %f\n", where, fgamma, blo, bhi, pow(where, fgamma)); return blo + (bhi - blo) * pow(where, fgamma); }
int stp_curve_set_data(stp_curve_t *curve, size_t count, const double *data) { size_t i; size_t real_count = count; double low, high; CHECK_CURVE(curve); if (count < 2) return 0; if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) real_count++; if (real_count > curve_point_limit) return 0; /* Validate the data before we commit to it. */ stp_sequence_get_bounds(curve->seq, &low, &high); for (i = 0; i < count; i++) if (! isfinite(data[i]) || data[i] < low || data[i] > high) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data: datum out of bounds: " "%g (require %g <= x <= %g), n = %ld\n", data[i], low, high, (long)i); return 0; } /* Allocate sequence; also accounts for WRAP_MODE */ stpi_curve_set_points(curve, count); curve->gamma = 0.0; stp_sequence_set_subrange(curve->seq, 0, count, data); if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) stp_sequence_set_point(curve->seq, count, data[0]); curve->recompute_interval = 1; curve->piecewise = 0; return 1; }
int stp_curve_rescale(stp_curve_t *curve, double scale, stp_curve_compose_t mode, stp_curve_bounds_t bounds_mode) { int i; double nblo; double nbhi; size_t count; CHECK_CURVE(curve); stp_sequence_get_bounds(curve->seq, &nblo, &nbhi); if (bounds_mode == STP_CURVE_BOUNDS_RESCALE) { switch (mode) { case STP_CURVE_COMPOSE_ADD: nblo += scale; nbhi += scale; break; case STP_CURVE_COMPOSE_MULTIPLY: if (scale < 0) { double tmp = nblo * scale; nblo = nbhi * scale; nbhi = tmp; } else { nblo *= scale; nbhi *= scale; } break; case STP_CURVE_COMPOSE_EXPONENTIATE: if (scale == 0.0) return 0; if (nblo < 0) return 0; nblo = pow(nblo, scale); nbhi = pow(nbhi, scale); break; default: return 0; } } if (! isfinite(nbhi) || ! isfinite(nblo)) return 0; count = get_point_count(curve); if (count) { double *tmp; size_t scount; int stride = 1; int offset = 0; const double *data; if (curve->piecewise) { stride = 2; offset = 1; } stp_sequence_get_data(curve->seq, &scount, &data); tmp = stp_malloc(sizeof(double) * scount); memcpy(tmp, data, scount * sizeof(double)); for (i = offset; i < scount; i += stride) { switch (mode) { case STP_CURVE_COMPOSE_ADD: tmp[i] = tmp[i] + scale; break; case STP_CURVE_COMPOSE_MULTIPLY: tmp[i] = tmp[i] * scale; break; case STP_CURVE_COMPOSE_EXPONENTIATE: tmp[i] = pow(tmp[i], scale); break; } if (tmp[i] > nbhi || tmp[i] < nblo) { if (bounds_mode == STP_CURVE_BOUNDS_ERROR) { stp_free(tmp); return(0); } else if (tmp[i] > nbhi) tmp[i] = nbhi; else tmp[i] = nblo; } } stp_sequence_set_bounds(curve->seq, nblo, nbhi); curve->gamma = 0.0; stpi_curve_set_points(curve, count); stp_sequence_set_subrange(curve->seq, 0, scount, tmp); stp_free(tmp); curve->recompute_interval = 1; invalidate_auxiliary_data(curve); } return 1; }
int stp_curve_set_data_points(stp_curve_t *curve, size_t count, const stp_curve_point_t *data) { size_t i; size_t real_count = count; double low, high; double last_x = -1; CHECK_CURVE(curve); if (count < 2) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: too few points %ld\n", (long)count); return 0; } if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) real_count++; if (real_count > curve_point_limit) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: too many points %ld\n", (long)real_count); return 0; } /* Validate the data before we commit to it. */ stp_sequence_get_bounds(curve->seq, &low, &high); for (i = 0; i < count; i++) { if (! isfinite(data[i].y) || data[i].y < low || data[i].y > high) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: datum out of bounds: " "%g (require %g <= x <= %g), n = %ld\n", data[i].y, low, high, (long)i); return 0; } if (i == 0 && data[i].x != 0.0) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: first point must have x=0\n"); return 0; } if (curve->wrap_mode == STP_CURVE_WRAP_NONE && i == count - 1 && data[i].x != 1.0) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: last point must have x=1\n"); return 0; } if (curve->wrap_mode == STP_CURVE_WRAP_AROUND && data[i].x >= 1.0 - .000001) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: horizontal value must " "not exceed .99999\n"); return 0; } if (data[i].x < 0 || data[i].x > 1) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: horizontal position out of bounds: " "%g, n = %ld\n", data[i].x, (long)i); return 0; } if (data[i].x - .000001 < last_x) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_set_data_points: horizontal position must " "exceed previous position by .000001: %g, %g, n = %ld\n", data[i].x, last_x, (long)i); return 0; } last_x = data[i].x; } /* Allocate sequence; also accounts for WRAP_MODE */ curve->piecewise = 1; stpi_curve_set_points(curve, count); curve->gamma = 0.0; stp_sequence_set_subrange(curve->seq, 0, count * 2, (const double *) data); if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) { stp_sequence_set_point(curve->seq, count * 2, data[0].x); stp_sequence_set_point(curve->seq, count * 2 + 1, data[0].y); } curve->recompute_interval = 1; return 1; }
void stp_curve_get_bounds(const stp_curve_t *curve, double *low, double *high) { CHECK_CURVE(curve); stp_sequence_get_bounds(curve->seq, low, high); }
stp_curve_t * stp_curve_create_from_xmltree(stp_mxml_node_t *curve) /* The curve node */ { const char *stmp; /* Temporary string */ stp_mxml_node_t *child; /* Child sequence node */ stp_curve_t *ret = NULL; /* Curve to return */ stp_curve_type_t curve_type; /* Type of curve */ stp_curve_wrap_mode_t wrap_mode; /* Curve wrap mode */ double fgamma; /* Gamma value */ stp_sequence_t *seq = NULL; /* Sequence data */ double low, high; /* Sequence bounds */ int piecewise = 0; stp_xml_init(); /* Get curve type */ stmp = stp_mxmlElementGetAttr(curve, "type"); if (stmp) { if (!strcmp(stmp, "linear")) curve_type = STP_CURVE_TYPE_LINEAR; else if (!strcmp(stmp, "spline")) curve_type = STP_CURVE_TYPE_SPLINE; else { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: %s: \"type\" invalid\n", stmp); goto error; } } else { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: \"type\" missing\n"); goto error; } /* Get curve wrap mode */ stmp = stp_mxmlElementGetAttr(curve, "wrap"); if (stmp) { if (!strcmp(stmp, "nowrap")) wrap_mode = STP_CURVE_WRAP_NONE; else if (!strcmp(stmp, "wrap")) { wrap_mode = STP_CURVE_WRAP_AROUND; } else { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: %s: \"wrap\" invalid\n", stmp); goto error; } } else { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: \"wrap\" missing\n"); goto error; } /* Get curve gamma */ stmp = stp_mxmlElementGetAttr(curve, "gamma"); if (stmp) { fgamma = stp_xmlstrtod(stmp); } else { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: \"gamma\" missing\n"); goto error; } /* If gamma is set, wrap_mode must be STP_CURVE_WRAP_NONE */ if (fgamma && wrap_mode != STP_CURVE_WRAP_NONE) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: " "gamma set and \"wrap\" is not STP_CURVE_WRAP_NONE\n"); goto error; } stmp = stp_mxmlElementGetAttr(curve, "piecewise"); if (stmp && strcmp(stmp, "true") == 0) piecewise = 1; /* Set up the curve */ ret = stp_curve_create(wrap_mode); stp_curve_set_interpolation_type(ret, curve_type); child = stp_mxmlFindElement(curve, curve, "sequence", NULL, NULL, STP_MXML_DESCEND); if (child) seq = stp_sequence_create_from_xmltree(child); if (seq == NULL) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: sequence read failed\n"); goto error; } /* Set curve bounds */ stp_sequence_get_bounds(seq, &low, &high); stp_curve_set_bounds(ret, low, high); if (fgamma) stp_curve_set_gamma(ret, fgamma); else /* Not a gamma curve, so set points */ { size_t seq_count; const double* data; stp_sequence_get_data(seq, &seq_count, &data); if (piecewise) { if ((seq_count % 2) != 0) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: invalid data count %ld\n", (long)seq_count); goto error; } if (stp_curve_set_data_points(ret, seq_count / 2, (const stp_curve_point_t *) data) == 0) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: failed to set curve data points\n"); goto error; } } else { if (stp_curve_set_data(ret, seq_count, data) == 0) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: failed to set curve data\n"); goto error; } } } if (seq) { stp_sequence_destroy(seq); seq = NULL; } /* Validate curve */ if (stpi_curve_check_parameters(ret, stp_curve_count_points(ret)) == 0) { stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: parameter check failed\n"); goto error; } stp_xml_exit(); return ret; error: stp_deprintf(STP_DBG_CURVE_ERRORS, "stp_curve_create_from_xmltree: error during curve read\n"); if (ret) stp_curve_destroy(ret); stp_xml_exit(); return NULL; }
int stp_curve_resample(stp_curve_t *curve, size_t points) { size_t limit = points; size_t old; size_t i; double *new_vec; CHECK_CURVE(curve); if (points == get_point_count(curve) && curve->seq && !(curve->piecewise)) return 1; if (points < 2) return 1; if (curve->wrap_mode == STP_CURVE_WRAP_AROUND) limit++; if (limit > curve_point_limit) return 0; old = get_real_point_count(curve); if (old) old--; if (!old) old = 1; new_vec = stp_malloc(sizeof(double) * limit); /* * Be very careful how we calculate the location along the scale! * If we're not careful how we do it, we might get a small roundoff * error */ if (curve->piecewise) { double blo, bhi; int curpos = 0; stp_sequence_get_bounds(curve->seq, &blo, &bhi); if (curve->recompute_interval) compute_intervals(curve); for (i = 0; i < old; i++) { double low; double high; double low_y; double high_y; double x_delta; if (!stp_sequence_get_point(curve->seq, i * 2, &low)) { stp_free(new_vec); return 0; } if (i == old - 1) high = 1.0; else if (!stp_sequence_get_point(curve->seq, ((i + 1) * 2), &high)) { stp_free(new_vec); return 0; } if (!stp_sequence_get_point(curve->seq, (i * 2) + 1, &low_y)) { stp_free(new_vec); return 0; } if (!stp_sequence_get_point(curve->seq, ((i + 1) * 2) + 1, &high_y)) { stp_free(new_vec); return 0; } stp_deprintf(STP_DBG_CURVE, "Filling slots at %ld %d: %f %f %f %f %ld\n", (long)i,curpos, high, low, high_y, low_y, (long)limit); x_delta = high - low; high *= (limit - 1); low *= (limit - 1); while (curpos <= high) { double frac = (curpos - low) / (high - low); if (curve->curve_type == STP_CURVE_TYPE_LINEAR) new_vec[curpos] = low_y + frac * (high_y - low_y); else new_vec[curpos] = do_interpolate_spline(low_y, high_y, frac, curve->interval[i], curve->interval[i + 1], x_delta); if (new_vec[curpos] < blo) new_vec[curpos] = blo; if (new_vec[curpos] > bhi) new_vec[curpos] = bhi; stp_deprintf(STP_DBG_CURVE, " Filling slot %d %f %f\n", curpos, frac, new_vec[curpos]); curpos++; } } curve->piecewise = 0; } else { for (i = 0; i < limit; i++) if (curve->gamma) new_vec[i] = interpolate_gamma_internal(curve, ((double) i * (double) old / (double) (limit - 1))); else new_vec[i] = interpolate_point_internal(curve, ((double) i * (double) old / (double) (limit - 1))); } stpi_curve_set_points(curve, points); stp_sequence_set_subrange(curve->seq, 0, limit, new_vec); curve->recompute_interval = 1; stp_free(new_vec); return 1; }