1 /*
2    I recently had a visit from my mom who owns a Sony Mavica camera.
3    This camera produces standard MPEG and JPEG files, but it also
4    creates 64x48 pixel thumbnails for preview/index on its own tiny
5    LCD screen.  These files are named with an extension that is
6    ".411".
7 
8    Sony appears not to want to document the ".411" file format, but it
9    is clear from various web pages that it is a variant of the
10    CCIR.601 standard YUV encoding used in MPEG.  The name indicates
11    that the file content consists of chunks of 6 bytes: 4 bytes of
12    image Y values, followed by 1 bytes of U and one byte of V values
13    that apply to the previous 4 Y pixel values.
14 
15    There appear to be some commercial 411 file readers on the net, and
16    there is the Java-based Javica program, but I prefer Open Source
17    command-line utilities.  So, I grabbed a copy of netpbm-9.11 from
18    SourceForge and hacked the eyuvtoppm.c file so that it looks like
19    this.  While this may not be exactly the right thing to do, it
20    produces results which are close enough for me.
21 
22    There are all sorts of user-interface gotchas possible in here that
23    I'm not going to bother changing -- especially not without actual
24    documentation from Sony about what they intend to do with ".411"
25    files in the future.  I place my modifications into the public
26    domain, but I ask that my name & e-mail be mentioned in the
27    commentary of any derived version.
28 
29    Steve Allen <sla@alumni.caltech.edu>, 2001-03-01
30 
31    Bryan Henderson reworked the program to use the Netpbm libraries to
32    create the PPM output and follow some other Netpbm conventions.
33    2001-03-03.  Bryan's contribution is public domain.
34 */
35 /*
36  * Copyright (c) 1995 The Regents of the University of California.
37  * All rights reserved.
38  *
39  * Permission to use, copy, modify, and distribute this software and its
40  * documentation for any purpose, without fee, and without written agreement is
41  * hereby granted, provided that the above copyright notice and the following
42  * two paragraphs appear in all copies of this software.
43  *
44  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
45  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
46  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
47  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  *
49  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
50  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
51  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
52  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
53  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.  */
54 
55 /*==============*
56  * HEADER FILES *
57  *==============*/
58 #include <stdio.h>
59 
60 #include "pm_c_util.h"
61 #include "mallocvar.h"
62 #include "shhopt.h"
63 #include "ppm.h"
64 
65 typedef unsigned char uint8;
66 
67 #define CHOP(x)     ((x < 0) ? 0 : ((x > 255) ? 255 : x))
68 
69 struct CmdlineInfo {
70     /* All the information the user supplied in the command line,
71        in a form easy for the program to use.
72     */
73     const char * inputFileName;
74     int width;
75     int height;
76 };
77 
78 
79 
80 static void
parseCommandLine(int argc,const char ** argv,struct CmdlineInfo * cmdlineP)81 parseCommandLine(int argc, const char ** argv,
82                  struct CmdlineInfo *cmdlineP) {
83 /*----------------------------------------------------------------------------
84    Note that the file spec array we return is stored in the storage that
85    was passed to us as the argv array.
86 -----------------------------------------------------------------------------*/
87 
88     optEntry * option_def;
89         /* Instructions to OptParseOptions2 on how to parse our options.
90          */
91     optStruct3 opt;
92 
93     unsigned int option_def_index;
94 
95     MALLOCARRAY_NOFAIL(option_def, 100);
96 
97     option_def_index = 0;   /* incremented by OPTENT3 */
98     OPTENT3(0,   "width",      OPT_INT,    &cmdlineP->width,  NULL,   0);
99     OPTENT3(0,   "height",     OPT_INT,    &cmdlineP->height, NULL,   0);
100 
101     /* Set the defaults */
102     cmdlineP->width = 64;
103     cmdlineP->height = 48;
104 
105     opt.opt_table = option_def;
106     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
107     opt.allowNegNum = FALSE;  /* We have no parms that are negative numbers */
108 
109     pm_optParseOptions3(&argc, (char **)argv, opt, sizeof(opt), 0);
110         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
111 
112     if (cmdlineP->width <= 0)
113         pm_error("-width must be positive.");
114     if (cmdlineP->width %4 != 0)
115         pm_error("-width must be a multiple of 4.");
116     if (cmdlineP->height <= 0)
117         pm_error("-height must be positive.");
118 
119     if (argc > 2)
120         pm_error("There is at most 1 argument: the input file spec.  "
121                  "You supplied %d", argc-1);
122     else {
123         if (argc > 1)
124             cmdlineP->inputFileName = argv[1];
125         else
126             cmdlineP->inputFileName = "-";
127     }
128     free(option_def);
129 }
130 
131 
132 
133 static void
ReadYUV(FILE * const ifP,uint8 * const inbuff)134 ReadYUV(FILE  * const ifP,
135         uint8 * const inbuff) {
136 
137     size_t bytesRead;
138 
139     bytesRead = fread(inbuff, 1, 6, ifP);
140 
141     if (bytesRead != 6 ) {
142         if (feof(ifP))
143             pm_error("Premature end of input.");
144         else
145             pm_error("Error reading input.");
146      }
147 }
148 
149 
150 
151 static void
YUVtoPPM(FILE * const ifP,int const width,int const height,pixel * const pixrow)152 YUVtoPPM(FILE  * const ifP,
153          int     const width,
154          int     const height,
155          pixel * const pixrow ) {
156 
157     unsigned int col;
158 
159     for (col = 0; col < width; ++col) {
160 
161         uint8 inbuff[6];
162 
163         uint8 * const origY  = &inbuff[0];
164         uint8 * const origCb = &inbuff[4];
165         uint8 * const origCr = &inbuff[5];
166         int   y, u, v;
167         int32_t tempR, tempG, tempB;
168         pixval r, g, b;
169 
170         if (col % 4 == 0) {
171             ReadYUV(ifP, inbuff);
172             u = origCb[0] - 128;
173             v = origCr[0] - 128;
174         }
175 
176         y = origY[col % 4] - 16;
177 
178         tempR = 104635 * v              + y * 76310;
179         tempG = -25690 * u + -53294 * v + y * 76310;
180         tempB = 132278 * u              + y * 76310;
181 
182         r = CHOP((int)(tempR >> 16));
183         g = CHOP((int)(tempG >> 16));
184         b = CHOP((int)(tempB >> 16));
185 
186         PPM_ASSIGN(pixrow[col], r, g, b);
187     }
188 }
189 
190 
191 
192 int
main(int argc,const char ** argv)193 main(int argc, const char **argv) {
194 
195     pixval const maxval = 255;
196     struct CmdlineInfo cmdline;
197     FILE  * ifP;
198     pixel * pixrow;
199     unsigned int row;
200 
201     pm_proginit(&argc, argv);
202 
203     parseCommandLine(argc, argv, &cmdline);
204 
205     pixrow = ppm_allocrow(cmdline.width);
206 
207     pm_message("Reading (%ux%u): '%s'", cmdline.width, cmdline.height,
208                cmdline.inputFileName);
209 
210     ifP = pm_openr(cmdline.inputFileName);
211 
212     ppm_writeppminit(stdout, cmdline.width, cmdline.height, maxval, 0);
213 
214     for (row = 0; row < cmdline.height; ++row) {
215         YUVtoPPM(ifP, cmdline.width, cmdline.height, pixrow);
216         ppm_writeppmrow(stdout, pixrow, cmdline.width, maxval, 0);
217     }
218 
219     if (fgetc(ifP) != EOF)
220         pm_message("Extraneous data at end of image.");
221 
222     pm_close(ifP);
223     ppm_freerow(pixrow);
224 
225     return 0;
226 }
227 
228 /*
229    By default a .411 file is width=64, height=48, 4608 bytes.
230    There is no header.
231 */
232