mirror of
				https://git.code.sf.net/p/libpng/code.git
				synced 2025-07-10 18:04:09 +02:00 
			
		
		
		
	Most of these are back-portable to earlier versions (contrib/libtests should just work with earlier versions), however the 1.7 specific changes in pngvalid mean that it probably won't work against 1.7 without the commits following this one. Signed-off-by: John Bowler <jbowler@acm.org>
		
			
				
	
	
		
			209 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			209 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* dynamic-range.c
 | 
						|
 *
 | 
						|
 * Last changed in libpng 1.7.0
 | 
						|
 *
 | 
						|
 * COPYRIGHT: Written by John Cunningham Bowler, 2015
 | 
						|
 * To the extent possible under law, the author has waived all copyright and
 | 
						|
 * related or neighboring rights to this work.  This work is published from:
 | 
						|
 * United States.
 | 
						|
 *
 | 
						|
 * Find the dynamic range of a given gamma encoding given a (linear) precision
 | 
						|
 * and a maximum number of encoded values.
 | 
						|
 */
 | 
						|
#include <stdlib.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <float.h>
 | 
						|
#include <math.h>
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
double range(unsigned int steps, double factor, double gamma)
 | 
						|
{
 | 
						|
   return pow((steps * (pow(factor, 1/gamma) - 1)), gamma);
 | 
						|
}
 | 
						|
 | 
						|
double max_range_gamma(unsigned int steps, double factor, double *max_range,
 | 
						|
      double glo, double rlo, double gmid, double rmid, double ghi, double rhi)
 | 
						|
{
 | 
						|
   /* Given three values which contain a peak value (so rmid > rlo and rmid >
 | 
						|
    * rhi) find the peak by repeated division of the range.  The algorithm is to
 | 
						|
    * find the range for two gamma values mid-way between the two pairs
 | 
						|
    * (glo,gmid), (ghi,gmid) then find the max; this gives us a new glo/ghi
 | 
						|
    * which must be half the distance apart of the previous pair.
 | 
						|
    */
 | 
						|
   double gammas[5];
 | 
						|
   double ranges[5];
 | 
						|
 | 
						|
   gammas[0] = glo;  ranges[0] = rlo;
 | 
						|
   gammas[2] = gmid; ranges[2] = rmid;
 | 
						|
   gammas[4] = ghi;  ranges[4] = rhi;
 | 
						|
 | 
						|
   for (;;)
 | 
						|
   {
 | 
						|
      int i, m;
 | 
						|
 | 
						|
      ranges[1] = range(steps, factor, gammas[1] = (gammas[0]+gammas[2])/2);
 | 
						|
      ranges[3] = range(steps, factor, gammas[3] = (gammas[2]+gammas[4])/2);
 | 
						|
 | 
						|
      for (m=1, i=2; i<4; ++i)
 | 
						|
         if (ranges[i] >= ranges[m])
 | 
						|
            m = i;
 | 
						|
 | 
						|
      assert(gammas[0] < gammas[m] && gammas[m] < gammas[4]);
 | 
						|
      assert(ranges[0] < ranges[m] && ranges[m] > ranges[4]);
 | 
						|
 | 
						|
      gammas[0] = gammas[m-1]; ranges[0] = ranges[m-1];
 | 
						|
      gammas[4] = gammas[m+1]; ranges[4] = ranges[m+1];
 | 
						|
      gammas[2] = gammas[m];   ranges[2] = ranges[m];
 | 
						|
 | 
						|
      if (((gammas[4] - gammas[0])/gammas[2]-1) < 3*DBL_EPSILON ||
 | 
						|
          ((ranges[2] - ranges[0])/ranges[2]-1) < 6*DBL_EPSILON)
 | 
						|
      {
 | 
						|
         *max_range = ranges[2];
 | 
						|
         return gammas[2];
 | 
						|
      }
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
double best_gamma(unsigned int values, double precision, double *best_range)
 | 
						|
{
 | 
						|
   /* The 'guess' gamma value is determined by the following formula, which is
 | 
						|
    * itself derived from linear regression using values returned by this
 | 
						|
    * program:
 | 
						|
    */
 | 
						|
   double gtry = values * precision / 2.736;
 | 
						|
   double rtry;
 | 
						|
 | 
						|
   /* 'values' needs to be the number of steps after the first, we have to
 | 
						|
    * reserve the first value, 0, for 0, so subtract 2 from values.  precision
 | 
						|
    * must be adjusted to the step factor.
 | 
						|
    */
 | 
						|
   values -= 2U;
 | 
						|
   precision += 1;
 | 
						|
   rtry = range(values, precision, gtry);
 | 
						|
 | 
						|
   /* Now find two values either side of gtry with a lower range. */
 | 
						|
   {
 | 
						|
      double glo, ghi, rlo, rhi, gbest, rbest;
 | 
						|
 | 
						|
      glo = gtry;
 | 
						|
      do
 | 
						|
      {
 | 
						|
         glo *= 0.9;
 | 
						|
         rlo = range(values, precision, glo);
 | 
						|
      }
 | 
						|
      while (rlo >= rtry);
 | 
						|
 | 
						|
      ghi = gtry;
 | 
						|
      do
 | 
						|
      {
 | 
						|
         ghi *= 1.1;
 | 
						|
         rhi = range(values, precision, ghi);
 | 
						|
      }
 | 
						|
      while (rhi >= rtry);
 | 
						|
 | 
						|
      gbest = max_range_gamma(values, precision, &rbest,
 | 
						|
            glo, rlo, gtry, rtry, ghi, rhi);
 | 
						|
 | 
						|
      *best_range = rbest / precision;
 | 
						|
      return gbest;
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
double linear_regression(double precision, double *bp)
 | 
						|
{
 | 
						|
   unsigned int values, count = 0;
 | 
						|
   double g_sum = 0, g2_sum = 0, v_sum = 0, v2_sum = 0, gv_sum = 0;
 | 
						|
 | 
						|
   /* Perform simple linear regression to get:
 | 
						|
    *
 | 
						|
    *    gamma = a + b.values
 | 
						|
    */
 | 
						|
   for (values = 128; values < 65536; ++values, ++count)
 | 
						|
   {
 | 
						|
      double range;
 | 
						|
      double gamma = best_gamma(values, precision, &range);
 | 
						|
 | 
						|
      g_sum += gamma;
 | 
						|
      g2_sum += gamma * gamma;
 | 
						|
      v_sum += values;
 | 
						|
      v2_sum += values * (double)values;
 | 
						|
      gv_sum += gamma * values;
 | 
						|
      /* printf("%u %g %g\n", values, gamma, range); */
 | 
						|
   }
 | 
						|
 | 
						|
   g_sum /= count;
 | 
						|
   g2_sum /= count;
 | 
						|
   v_sum /= count;
 | 
						|
   v2_sum /= count;
 | 
						|
   gv_sum /= count;
 | 
						|
 | 
						|
   {
 | 
						|
      double b = (gv_sum - g_sum * v_sum) / (v2_sum - v_sum * v_sum);
 | 
						|
      *bp = b;
 | 
						|
      return g_sum - b * v_sum;
 | 
						|
   }
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
main(int argc, const char **argv)
 | 
						|
{
 | 
						|
   double precision = argc == 2 ? atof(argv[1]) : 0;
 | 
						|
 | 
						|
   /* Perform a second linear regression here on b:
 | 
						|
    *
 | 
						|
    *    b = bA + bB * precision
 | 
						|
    */
 | 
						|
   if (precision == 0)
 | 
						|
   {
 | 
						|
      double b_sum = 0, b2_sum = 0, p_sum = 0, p2_sum = 0, bp_sum = 0,
 | 
						|
             a_sum = 0, count = 0;
 | 
						|
 | 
						|
      for (precision = .001; precision <= 0.01; precision += .001, count += 1)
 | 
						|
      {
 | 
						|
         double b;
 | 
						|
         double a = linear_regression(precision, &b);
 | 
						|
 | 
						|
         b_sum += b;
 | 
						|
         b2_sum += b * b;
 | 
						|
         p_sum += precision;
 | 
						|
         p2_sum += precision * precision;
 | 
						|
         bp_sum += b * precision;
 | 
						|
         a_sum += a;
 | 
						|
      }
 | 
						|
 | 
						|
      b_sum /= count;
 | 
						|
      b2_sum /= count;
 | 
						|
      p_sum /= count;
 | 
						|
      p2_sum /= count;
 | 
						|
      bp_sum /= count;
 | 
						|
      a_sum /= count;
 | 
						|
 | 
						|
      {
 | 
						|
         double bB = (bp_sum - b_sum * p_sum) / (p2_sum - p_sum * p_sum);
 | 
						|
         double bA = b_sum - bB * p_sum;
 | 
						|
 | 
						|
         printf("a = %g, b = %g + precision/%g\n", a_sum, bA, 1/bB);
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   else
 | 
						|
   {
 | 
						|
      unsigned int bits;
 | 
						|
      double b;
 | 
						|
      double a = linear_regression(precision, &b);
 | 
						|
      printf("precision %g: gamma = %g + values*%g\n", precision, a, b);
 | 
						|
 | 
						|
      /* For information, given a precision: */
 | 
						|
      for (bits=7U; bits <= 16U; ++bits)
 | 
						|
      {
 | 
						|
         unsigned int values = 1U<<bits;
 | 
						|
         double gamma = values*precision/2.736;
 | 
						|
         double r = range(values-2U, 1+precision, gamma);
 | 
						|
 | 
						|
         printf("bits: %u, gamma: %g, range: 1:%g\n", bits, gamma, r);
 | 
						|
      }
 | 
						|
   }
 | 
						|
 | 
						|
   return 0;
 | 
						|
}
 |