1ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* makesRGB.c -- build sRGB-to-linear and linear-to-sRGB conversion tables
2ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
3ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Last changed in libpng 1.6.0 [February 14, 2013]
4ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
5ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * COPYRIGHT: Written by John Cunningham Bowler, 2013.
6ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * To the extent possible under law, the author has waived all copyright and
7ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * related or neighboring rights to this work.  This work is published from:
8ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * United States.
9ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
10ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Make a table to convert 8-bit sRGB encoding values into the closest 16-bit
11ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * linear value.
12ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik *
13ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * Make two tables to take a linear value scaled to 255*65535 and return an
14ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * approximation to the 8-bit sRGB encoded value.  Calculate the error in these
15ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * tables and display it.
16ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik */
17ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define _C99_SOURCE 1
18ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <stdio.h>
19ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <math.h>
20ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include <stdlib.h>
21ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
22ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* pngpriv.h includes the definition of 'PNG_sRGB_FROM_LINEAR' which is required
23ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * to verify the actual code.
24ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik */
25ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include "../../pngpriv.h"
26ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
27ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#include "sRGB.h"
28ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
29ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik/* The tables are declared 'const' in pngpriv.h, so this redefines the tables to
30ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik * be used.
31ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik */
32ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define png_sRGB_table sRGB_table
33ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define png_sRGB_base sRGB_base
34ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik#define png_sRGB_delta sRGB_delta
35ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
36ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikstatic png_uint_16 png_sRGB_table[256];
37ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikstatic png_uint_16 png_sRGB_base[512];
38ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikstatic png_byte png_sRGB_delta[512];
39ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
40ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikstatic const unsigned int max_input = 255*65535;
41ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
42ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikdouble
43ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris CraikfsRGB(double l)
44ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
45ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   return sRGB_from_linear(l/max_input);
46ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
47ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
48ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikdouble
49ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris CraiksRGB(unsigned int i)
50ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
51ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   return fsRGB(i);
52ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
53ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
54ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikdouble
55ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris CraikfinvsRGB(unsigned int i)
56ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
57ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   return 65535 * linear_from_sRGB(i/255.);
58ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
59ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
60ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikpng_uint_16
61ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris CraikinvsRGB(unsigned int i)
62ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
63ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int x = nearbyint(finvsRGB(i));
64ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
65ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (x > 65535)
66ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
67ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      fprintf(stderr, "invsRGB(%u) overflows to %u\n", i, x);
68ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      exit(1);
69ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
70ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
71ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   return (png_uint_16)x;
72ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
73ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
74ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikint
75ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craikmain(int argc, char **argv)
76ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik{
77ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int i, i16, ibase;
78ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   double min_error = 0;
79ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   double max_error = 0;
80ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   double min_error16 = 0;
81ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   double max_error16 = 0;
82ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   double adjust;
83ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   double adjust_lo = 0.4, adjust_hi = 0.6, adjust_mid = 0.5;
84ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int ec_lo = 0, ec_hi = 0, ec_mid = 0;
85ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int error_count = 0;
86ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   unsigned int error_count16 = 0;
87ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   int test_only = 0;
88ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
89ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (argc > 1)
90ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      test_only = strcmp("--test", argv[1]) == 0;
91ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
92ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Initialize the encoding table first. */
93ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (i=0; i<256; ++i)
94ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
95ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      png_sRGB_table[i] = invsRGB(i);
96ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
97ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
98ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Now work out the decoding tables (this is where the error comes in because
99ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik    * there are 512 set points and 512 straight lines between them.)
100ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik    */
101ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (;;)
102ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
103ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (ec_lo == 0)
104ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust = adjust_lo;
105ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
106ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (ec_hi == 0)
107ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust = adjust_hi;
108ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
109ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (ec_mid == 0)
110ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust = adjust_mid;
111ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
112ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (ec_mid < ec_hi)
113ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust = (adjust_mid + adjust_hi)/2;
114ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
115ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (ec_mid < ec_lo)
116ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust = (adjust_mid + adjust_lo)/2;
117ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
118ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else
119ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
120ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         fprintf(stderr, "not reached: %u .. %u .. %u\n", ec_lo, ec_mid, ec_hi);
121ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         exit(1);
122ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
123ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
124ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Calculate the table using the current 'adjust' */
125ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<=511; ++i)
126ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
127ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         double lo = 255 * sRGB(i << 15);
128ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         double hi = 255 * sRGB((i+1) << 15);
129ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         unsigned int calc;
130ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
131ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         calc = nearbyint((lo+adjust) * 256);
132ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (calc > 65535)
133ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
134ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            fprintf(stderr, "table[%d][0]: overflow %08x (%d)\n", i, calc,
135ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               calc);
136ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            exit(1);
137ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
138ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         png_sRGB_base[i] = calc;
139ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
140ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         calc = nearbyint((hi-lo) * 32);
141ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (calc > 255)
142ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
143ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            fprintf(stderr, "table[%d][1]: overflow %08x (%d)\n", i, calc,
144ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               calc);
145ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            exit(1);
146ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
147ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         png_sRGB_delta[i] = calc;
148ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
149ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
150ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Check the 16-bit linear values alone: */
151ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      error_count16 = 0;
152ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i16=0; i16 <= 65535; ++i16)
153ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
154ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         unsigned int i = 255*i16;
155ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         unsigned int iexact = nearbyint(255*sRGB(i));
156ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
157ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
158ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (icalc != iexact)
159ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ++error_count16;
160ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
161ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
162ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      /* Now try changing the adjustment. */
163ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (ec_lo == 0)
164ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ec_lo = error_count16;
165ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
166ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (ec_hi == 0)
167ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ec_hi = error_count16;
168ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
169ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (ec_mid == 0)
170ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
171ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ec_mid = error_count16;
172ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* initial error counts: %u .. %u .. %u */\n", ec_lo, ec_mid,
173ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ec_hi);
174ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
175ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
176ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (error_count16 < ec_mid)
177ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
178ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* adjust (mid ): %f: %u -> %u */\n", adjust, ec_mid,
179ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            error_count16);
180ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ec_mid = error_count16;
181ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust_mid = adjust;
182ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
183ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
184ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (adjust < adjust_mid && error_count16 < ec_lo)
185ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
186ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* adjust (low ): %f: %u -> %u */\n", adjust, ec_lo,
187ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            error_count16);
188ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ec_lo = error_count16;
189ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust_lo = adjust;
190ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
191ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
192ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (adjust > adjust_mid && error_count16 < ec_hi)
193ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
194ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* adjust (high): %f: %u -> %u */\n", adjust, ec_hi,
195ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            error_count16);
196ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ec_hi = error_count16;
197ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust_hi = adjust;
198ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
199ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
200ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else
201ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
202ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         adjust = adjust_mid;
203ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* adjust: %f: %u */\n", adjust, ec_mid);
204ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         break;
205ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
206ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
207ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
208ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* For each entry in the table try to adjust it to minimize the error count
209ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik    * in that entry.  Each entry corresponds to 128 input values.
210ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik    */
211ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (ibase=0; ibase<65536; ibase+=128)
212ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
213ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      png_uint_16 base = png_sRGB_base[ibase >> 7], trybase = base, ob=base;
214ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      png_byte delta = png_sRGB_delta[ibase >> 7], trydelta = delta, od=delta;
215ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int ecbase = 0, eco;
216ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
217ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (;;)
218ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
219ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         png_sRGB_base[ibase >> 7] = trybase;
220ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         png_sRGB_delta[ibase >> 7] = trydelta;
221ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
222ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         /* Check the 16-bit linear values alone: */
223ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         error_count16 = 0;
224ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         for (i16=ibase; i16 < ibase+128; ++i16)
225ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
226ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            unsigned int i = 255*i16;
227ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            unsigned int iexact = nearbyint(255*sRGB(i));
228ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
229ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
230ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            if (icalc != iexact)
231ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               ++error_count16;
232ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
233ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
234ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (error_count16 == 0)
235ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            break;
236ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
237ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (ecbase == 0)
238ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
239ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            eco = ecbase = error_count16;
240ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ++trybase; /* First test */
241ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
242ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
243ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         else if (error_count16 < ecbase)
244ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
245ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            if (trybase > base)
246ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            {
247ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               base = trybase;
248ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               ++trybase;
249ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            }
250ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else if (trybase < base)
251ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            {
252ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               base = trybase;
253ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               --trybase;
254ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            }
255ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else if (trydelta > delta)
256ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            {
257ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               delta = trydelta;
258ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               ++trydelta;
259ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            }
260ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else if (trydelta < delta)
261ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            {
262ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               delta = trydelta;
263ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               --trydelta;
264ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            }
265ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else
266ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            {
267ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               fprintf(stderr, "makesRGB: impossible\n");
268ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               exit(1);
269ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            }
270ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ecbase = error_count16;
271ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
272ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
273ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         else
274ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
275ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            if (trybase > base)
276ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               trybase = base-1;
277ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else if (trybase < base)
278ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            {
279ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               trybase = base;
280ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               ++trydelta;
281ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            }
282ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else if (trydelta > delta)
283ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               trydelta = delta-1;
284ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            else if (trydelta < delta)
285ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               break; /* end of tests */
286ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
287ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
288ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
289ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      png_sRGB_base[ibase >> 7] = base;
290ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      png_sRGB_delta[ibase >> 7] = delta;
291ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (base != ob || delta != od)
292ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
293ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* table[%u]={%u,%u} -> {%u,%u} %u -> %u errors */\n",
294ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ibase>>7, ob, od, base, delta, eco, ecbase);
295ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
296ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      else if (0)
297ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* table[%u]={%u,%u} %u errors */\n", ibase>>7, ob, od,
298ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            ecbase);
299ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
300ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
301ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Only do the full (slow) test at the end: */
302ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   min_error = -.4999;
303ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   max_error = .4999;
304ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   error_count = 0;
305ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
306ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (i=0; i <= max_input; ++i)
307ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
308ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int iexact = nearbyint(255*sRGB(i));
309ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
310ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
311ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (icalc != iexact)
312ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
313ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         double err = 255*sRGB(i) - icalc;
314ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
315ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (err > (max_error+.001) || err < (min_error-.001))
316ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
317ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            printf(
318ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               "/* 0x%08x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n",
319ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               i, iexact, icalc, png_sRGB_base[i>>15],
320ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               png_sRGB_delta[i>>15], err);
321ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
322ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
323ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ++error_count;
324ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (err > max_error)
325ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            max_error = err;
326ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         else if (err < min_error)
327ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            min_error = err;
328ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
329ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
330ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
331ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Re-check the 16-bit cases too, including the warning if there is an error
332ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik    * bigger than 1.
333ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik    */
334ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   error_count16 = 0;
335ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   max_error16 = 0;
336ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   min_error16 = 0;
337ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (i16=0; i16 <= 65535; ++i16)
338ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
339ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int i = 255*i16;
340ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int iexact = nearbyint(255*sRGB(i));
341ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
342ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
343ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (icalc != iexact)
344ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
345ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         double err = 255*sRGB(i) - icalc;
346ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
347ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         ++error_count16;
348ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (err > max_error16)
349ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            max_error16 = err;
350ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         else if (err < min_error16)
351ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            min_error16 = err;
352ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
353ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (abs(icalc - iexact) > 1)
354ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            printf(
355ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               "/* 0x%04x: exact: %3d, got: %3d [tables: %08x, %08x] (%f) */\n",
356ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               i16, iexact, icalc, png_sRGB_base[i>>15],
357ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik               png_sRGB_delta[i>>15], err);
358ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
359ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
360ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
361ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   /* Check the round trip for each 8-bit sRGB value. */
362ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   for (i16=0; i16 <= 255; ++i16)
363ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
364ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int i = 255 * png_sRGB_table[i16];
365ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int iexact = nearbyint(255*sRGB(i));
366ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      unsigned int icalc = PNG_sRGB_FROM_LINEAR(i);
367ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
368ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (i16 != iexact)
369ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
370ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         fprintf(stderr, "8-bit rounding error: %d -> %d\n", i16, iexact);
371ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         exit(1);
372ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
373ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
374ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      if (icalc != i16)
375ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
376ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         double finv = finvsRGB(i16);
377ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
378ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         printf("/* 8-bit roundtrip error: %d -> %f -> %d(%f) */\n",
379ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            i16, finv, icalc, fsRGB(255*finv));
380ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
381ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
382ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
383ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
384ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   printf("/* error: %g - %g, %u (%g%%) of readings inexact */\n",
385ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      min_error, max_error, error_count, (100.*error_count)/max_input);
386ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   printf("/* 16-bit error: %g - %g, %u (%g%%) of readings inexact */\n",
387ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      min_error16, max_error16, error_count16, (100.*error_count16)/65535);
388ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
389ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   if (!test_only)
390ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   {
391ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      printf("PNG_CONST png_uint_16 png_sRGB_table[256] =\n{\n   ");
392ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<255; )
393ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
394ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         do
395ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
396ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            printf("%d,", png_sRGB_table[i++]);
397ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
398ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         while ((i & 0x7) != 0 && i<255);
399ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (i<255) printf("\n   ");
400ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
401ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      printf("%d\n};\n\n", png_sRGB_table[i]);
402ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
403ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
404ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      printf("PNG_CONST png_uint_16 png_sRGB_base[512] =\n{\n   ");
405ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<511; )
406ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
407ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         do
408ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
409ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            printf("%d,", png_sRGB_base[i++]);
410ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
411ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         while ((i & 0x7) != 0 && i<511);
412ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (i<511) printf("\n   ");
413ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
414ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      printf("%d\n};\n\n", png_sRGB_base[i]);
415ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
416ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      printf("PNG_CONST png_byte png_sRGB_delta[512] =\n{\n   ");
417ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      for (i=0; i<511; )
418ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      {
419ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         do
420ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         {
421ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik            printf("%d,", png_sRGB_delta[i++]);
422ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         }
423ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         while ((i & 0xf) != 0 && i<511);
424ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik         if (i<511) printf("\n   ");
425ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      }
426ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik      printf("%d\n};\n\n", png_sRGB_delta[i]);
427ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   }
428ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik
429ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik   return 0;
430ca2bf81b02c99afa2e76b3b2c6eb232c239221e0Chris Craik}
431