all repos — mgba @ ef3cc7bd9f28d73fdff912587fc42260948d5b51

mGBA Game Boy Advance Emulator

src/third-party/libpng/contrib/tools/makesRGB.c (view raw)

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