Home | History | Annotate | Download | only in tools
      1 /*-
      2  * convert.c
      3  *
      4  * Last changed in libpng 1.6.0 [February 14, 2013]
      5  *
      6  * COPYRIGHT: Written by John Cunningham Bowler, 2013.
      7  * To the extent possible under law, the author has waived all copyright and
      8  * related or neighboring rights to this work.  This work is published from:
      9  * United States.
     10  *
     11  * Convert 8-bit sRGB or 16-bit linear values to another format.
     12  */
     13 #define _ISOC99_SOURCE 1
     14 
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <math.h>
     18 #include <stdio.h>
     19 
     20 #include <fenv.h>
     21 
     22 #include "sRGB.h"
     23 
     24 static void
     25 usage(const char *prog)
     26 {
     27    fprintf(stderr,
     28       "%s: usage: %s [-linear|-sRGB] [-gray|-color] component{1,4}\n",
     29       prog, prog);
     30    exit(1);
     31 }
     32 
     33 unsigned long
     34 component(const char *prog, const char *arg, int issRGB)
     35 {
     36    char *ep;
     37    unsigned long c = strtoul(arg, &ep, 0);
     38 
     39    if (ep <= arg || *ep || c > 65535 || (issRGB && c > 255))
     40    {
     41       fprintf(stderr, "%s: %s: invalid component value (%lu)\n", prog, arg, c);
     42       usage(prog);
     43    }
     44 
     45    return c;
     46 }
     47 
     48 int
     49 main(int argc, const char **argv)
     50 {
     51    const char *prog = *argv++;
     52    int to_linear = 0, to_gray = 0, to_color = 0;
     53    int channels = 0;
     54    double c[4];
     55 
     56    /* FE_TONEAREST is the IEEE754 round to nearest, preferring even, mode; i.e.
     57     * everything rounds to the nearest value except that '.5' rounds to the
     58     * nearest even value.
     59     */
     60    fesetround(FE_TONEAREST);
     61 
     62    c[3] = c[2] = c[1] = c[0] = 0;
     63 
     64    while (--argc > 0 && **argv == '-')
     65    {
     66       const char *arg = 1+*argv++;
     67 
     68       if (strcmp(arg, "sRGB") == 0)
     69          to_linear = 0;
     70 
     71       else if (strcmp(arg, "linear") == 0)
     72          to_linear = 1;
     73 
     74       else if (strcmp(arg, "gray") == 0)
     75          to_gray = 1, to_color = 0;
     76 
     77       else if (strcmp(arg, "color") == 0)
     78          to_gray = 0, to_color = 1;
     79 
     80       else
     81          usage(prog);
     82    }
     83 
     84    switch (argc)
     85    {
     86       default:
     87          usage(prog);
     88          break;
     89 
     90       case 4:
     91          c[3] = component(prog, argv[3], to_linear);
     92          ++channels;
     93       case 3:
     94          c[2] = component(prog, argv[2], to_linear);
     95          ++channels;
     96       case 2:
     97          c[1] = component(prog, argv[1], to_linear);
     98          ++channels;
     99       case 1:
    100          c[0] = component(prog, argv[0], to_linear);
    101          ++channels;
    102          break;
    103       }
    104 
    105    if (to_linear)
    106    {
    107       int i;
    108       int components = channels;
    109 
    110       if ((components & 1) == 0)
    111          --components;
    112 
    113       for (i=0; i<components; ++i) c[i] = linear_from_sRGB(c[i] / 255);
    114       if (components < channels)
    115          c[components] = c[components] / 255;
    116    }
    117 
    118    else
    119    {
    120       int i;
    121       for (i=0; i<4; ++i) c[i] /= 65535;
    122 
    123       if ((channels & 1) == 0)
    124       {
    125          double alpha = c[channels-1];
    126 
    127          if (alpha > 0)
    128             for (i=0; i<channels-1; ++i) c[i] /= alpha;
    129          else
    130             for (i=0; i<channels-1; ++i) c[i] = 1;
    131       }
    132    }
    133 
    134    if (to_gray)
    135    {
    136       if (channels < 3)
    137       {
    138          fprintf(stderr, "%s: too few channels (%d) for -gray\n",
    139             prog, channels);
    140          usage(prog);
    141       }
    142 
    143       c[0] = YfromRGB(c[0], c[1], c[2]);
    144       channels -= 2;
    145    }
    146 
    147    if (to_color)
    148    {
    149       if (channels > 2)
    150       {
    151          fprintf(stderr, "%s: too many channels (%d) for -color\n",
    152             prog, channels);
    153          usage(prog);
    154       }
    155 
    156       c[3] = c[1]; /* alpha, if present */
    157       c[2] = c[1] = c[0];
    158    }
    159 
    160    if (to_linear)
    161    {
    162       int i;
    163       if ((channels & 1) == 0)
    164       {
    165          double alpha = c[channels-1];
    166          for (i=0; i<channels-1; ++i) c[i] *= alpha;
    167       }
    168 
    169       for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 65535);
    170    }
    171 
    172    else /* to sRGB */
    173    {
    174       int i = (channels+1)&~1;
    175       while (--i >= 0)
    176          c[i] = sRGB_from_linear(c[i]);
    177 
    178       for (i=0; i<channels; ++i) c[i] = nearbyint(c[i] * 255);
    179    }
    180 
    181    {
    182       int i;
    183       for (i=0; i<channels; ++i) printf(" %g", c[i]);
    184    }
    185    printf("\n");
    186 
    187    return 0;
    188 }
    189