1 /* Convert an YUY2 image to a PAM image
2  *
3  * See
4  * http://msdn.microsoft.com/en-us/library/aa904813%28VS.80%29.aspx#yuvformats_2
5  * and http://www.digitalpreservation.gov/formats/fdd/fdd000364.shtml for
6  * details.
7  *
8  * By Michael Haardt 2014.
9  *
10  * Contributed to the public domain by its author.
11  *
12  * Recoded in Netpbm style by Bryan Henderson
13  */
14 
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include "pm_c_util.h"
19 #include "mallocvar.h"
20 #include "pm.h"
21 #include "pam.h"
22 #include "shhopt.h"
23 
24 
25 
26 struct CmdlineInfo {
27     const char * inputFileName;
28     unsigned int width;
29     unsigned int height;
30 };
31 
32 
33 
34 static void
parseCommandLine(int argc,const char ** argv,struct CmdlineInfo * const cmdlineP)35 parseCommandLine(int argc, const char ** argv,
36                  struct CmdlineInfo * const cmdlineP) {
37 /* --------------------------------------------------------------------------
38    Parse program command line described in Unix standard form by argc
39    and argv.  Return the information in the options as *cmdlineP.
40 
41    If command line is internally inconsistent (invalid options, etc.),
42    issue error message to stderr and abort program.
43 
44    Note that the strings we return are stored in the storage that
45    was passed to us as the argv array.  We also trash *argv.
46 --------------------------------------------------------------------------*/
47     optEntry * option_def;
48         /* Instructions to pm_optParseOptions3 on how to parse our options. */
49     optStruct3 opt;
50 
51     unsigned int widthSpec, heightSpec;
52     unsigned int option_def_index;
53 
54     MALLOCARRAY_NOFAIL(option_def, 100);
55 
56     option_def_index = 0;   /* incremented by OPTENT3 */
57     OPTENT3(0, "width",    OPT_UINT,
58             &cmdlineP->width,   &widthSpec,                             0);
59     OPTENT3(0, "height",   OPT_UINT,
60             &cmdlineP->height,  &heightSpec,                            0);
61 
62     opt.opt_table = option_def;
63     opt.short_allowed = false;  /* We have no short (old-fashioned) options */
64     opt.allowNegNum = false;   /* We have no parms that are negative numbers */
65 
66     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
67         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
68 
69     if (!widthSpec)
70         pm_error("You must specify the image width with -width");
71     if (cmdlineP->width == 0)
72         pm_error("-width cannot be zero");
73 
74     if (cmdlineP->width % 2 != 0)
75         pm_error("-width %u is odd, but YUY2 images must have an even width.",
76                  cmdlineP->width);
77 
78     if (!heightSpec)
79         pm_error("You must specify the image height with -height");
80     if (cmdlineP->height == 0)
81         pm_error("-height cannot be zero");
82 
83     if (argc-1 < 1)
84         cmdlineP->inputFileName = "-";
85     else {
86         cmdlineP->inputFileName = argv[1];
87 
88         if (argc-1 > 1)
89             pm_error("Too many arguments (%u).  The only non-option argument "
90                      "is the input file name.", argc-1);
91     }
92 }
93 
94 
95 
96 typedef struct {
97     int y0;
98     int y1;
99     int u;
100     int v;
101 } Yuy2Pixel;
102 
103 
104 
105 static Yuy2Pixel
readPixel(FILE * const ifP)106 readPixel(FILE * const ifP) {
107 /*----------------------------------------------------------------------------
108    Read one pixel from the YUY2 input.  YUY2 represents a pixel in 4 bytes.
109 -----------------------------------------------------------------------------*/
110     Yuy2Pixel retval;
111     unsigned char c;
112 
113     pm_readcharu(ifP, &c); retval.y0 = c -  16;
114     pm_readcharu(ifP, &c); retval.u  = c - 128;
115     pm_readcharu(ifP, &c); retval.y1 = c -  16;
116     pm_readcharu(ifP, &c); retval.v  = c - 128;
117 
118     return retval;
119 }
120 
121 
122 
123 typedef struct {
124     int a1;
125     int a2;
126     int a3;
127     int a4;
128 } UvCoeff;
129 
130 typedef struct {
131     int a0a;
132     int a0b;
133     UvCoeff uv;
134 } Coeff;
135 
136 
137 
138 static Coeff
coeffFromYuy2(Yuy2Pixel const yuy2)139 coeffFromYuy2(Yuy2Pixel const yuy2) {
140 
141     Coeff retval;
142 
143     retval.a0a   = 298 * yuy2.y0;
144     retval.a0b   = 298 * yuy2.y1;
145     retval.uv.a1 = 409 * yuy2.v;
146     retval.uv.a2 = 100 * yuy2.u;
147     retval.uv.a3 = 208 * yuy2.v;
148     retval.uv.a4 = 516 * yuy2.u;
149 
150     return retval;
151 }
152 
153 
154 
155 typedef struct {
156     int r;
157     int g;
158     int b;
159 } Rgb;
160 
161 
162 
163 static Rgb
rgbFromCoeff(int const a0,UvCoeff const uv)164 rgbFromCoeff(int     const a0,
165              UvCoeff const uv) {
166 
167     Rgb retval;
168 
169     retval.r = (a0 + uv.a1 + 128) >> 8;
170     retval.g = (a0 - uv.a2 - uv.a3 + 128) >> 8;
171     retval.b = (a0 + uv.a4 + 128) >> 8;
172 
173     return retval;
174 }
175 
176 
177 
178 static Rgb
rgbFromCoeff0(Coeff const coeff)179 rgbFromCoeff0(Coeff const coeff) {
180 
181     return rgbFromCoeff(coeff.a0a, coeff.uv);
182 }
183 
184 
185 
186 static Rgb
rgbFromCoeff1(Coeff const coeff)187 rgbFromCoeff1(Coeff const coeff) {
188 
189     return rgbFromCoeff(coeff.a0b, coeff.uv);
190 }
191 
192 
193 
194 static void
rgbToTuple(Rgb const rgb,tuple const out)195 rgbToTuple(Rgb   const rgb,
196            tuple const out) {
197 
198     out[PAM_RED_PLANE] = MIN(255, MAX(0, rgb.r));
199     out[PAM_GRN_PLANE] = MIN(255, MAX(0, rgb.g));
200     out[PAM_BLU_PLANE] = MIN(255, MAX(0, rgb.b));
201 }
202 
203 
204 
205 static void
yuy2topam(const char * const fileName,unsigned int const width,unsigned int const height)206 yuy2topam(const char * const fileName,
207           unsigned int const width,
208           unsigned int const height) {
209 
210     FILE * ifP;
211     struct pam outpam;
212     tuple * tuplerow;
213     unsigned int row;
214 
215     outpam.size             = sizeof(struct pam);
216     outpam.len              = PAM_STRUCT_SIZE(allocation_depth);
217     outpam.file             = stdout;
218     outpam.format           = PAM_FORMAT;
219     outpam.plainformat      = 0;
220     outpam.width            = width;
221     outpam.height           = height;
222     outpam.depth            = 3;
223     outpam.maxval           = 255;
224     outpam.bytes_per_sample = 1;
225     strcpy(outpam.tuple_type, PAM_PPM_TUPLETYPE);
226     outpam.allocation_depth = 3;
227 
228     ifP = pm_openr(fileName);
229 
230     pnm_writepaminit(&outpam);
231 
232     tuplerow = pnm_allocpamrow(&outpam);
233 
234     for (row = 0; row < outpam.height; ++row) {
235         unsigned int col;
236 
237         for (col = 0; col < outpam.width; col += 2) {
238             Yuy2Pixel const yuy2 = readPixel(ifP);
239 
240             Coeff const coeff = coeffFromYuy2(yuy2);
241 
242             rgbToTuple(rgbFromCoeff0(coeff), tuplerow[col]);
243             rgbToTuple(rgbFromCoeff1(coeff), tuplerow[col+1]);
244         }
245         pnm_writepamrow(&outpam, tuplerow);
246     }
247     pnm_freepamrow(tuplerow);
248 
249     pm_closer(ifP);
250 }
251 
252 
253 
254 int
main(int argc,const char * argv[])255 main(int argc, const char *argv[]) {
256 
257     struct CmdlineInfo cmdline;
258 
259     pm_proginit(&argc, argv);
260 
261     parseCommandLine(argc, argv, &cmdline);
262 
263     yuy2topam(cmdline.inputFileName, cmdline.width, cmdline.height);
264 
265     return 0;
266 }
267