These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / ipxe / src / image / pnm.c
1 /*
2  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26 /** @file
27  *
28  * Portable anymap format (PNM)
29  *
30  */
31
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <ctype.h>
35 #include <ipxe/image.h>
36 #include <ipxe/pixbuf.h>
37 #include <ipxe/pnm.h>
38
39 /**
40  * Extract PNM ASCII value
41  *
42  * @v image             PNM image
43  * @v pnm               PNM context
44  * @ret value           Value, or negative error
45  */
46 static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
47         char buf[ pnm->ascii_len + 1 /* NUL */ ];
48         char *endp;
49         size_t len;
50         int value;
51         int in_comment = 0;
52
53         /* Skip any leading whitespace and comments */
54         for ( ; pnm->offset < image->len ; pnm->offset++ ) {
55                 copy_from_user ( &buf[0], image->data, pnm->offset,
56                                  sizeof ( buf[0] ) );
57                 if ( in_comment ) {
58                         if ( buf[0] == '\n' )
59                                 in_comment = 0;
60                 } else {
61                         if ( buf[0] == '#' ) {
62                                 in_comment = 1;
63                         } else if ( ! isspace ( buf[0] ) ) {
64                                 break;
65                         }
66                 }
67         }
68
69         /* Fail if no value is present */
70         len = ( image->len - pnm->offset );
71         if ( len == 0 ) {
72                 DBGC ( image, "PNM %s ran out of ASCII data\n", image->name );
73                 return -EINVAL;
74         }
75
76         /* Copy ASCII value to buffer and ensure string is NUL-terminated */
77         if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) )
78                 len = ( sizeof ( buf ) - 1 /* NUL */ );
79         copy_from_user ( buf, image->data, pnm->offset, len );
80         buf[len] = '\0';
81
82         /* Parse value and update offset */
83         value = strtoul ( buf, &endp, 0 );
84         pnm->offset += ( endp - buf );
85
86         /* Check and skip terminating whitespace character, if present */
87         if ( ( pnm->offset != image->len ) && ( *endp != '\0' ) ) {
88                 if ( ! isspace ( *endp ) ) {
89                         DBGC ( image, "PNM %s invalid ASCII integer\n",
90                                image->name );
91                         return -EINVAL;
92                 }
93                 pnm->offset++;
94         }
95
96         return value;
97 }
98
99 /**
100  * Extract PNM binary value
101  *
102  * @v image             PNM image
103  * @v pnm               PNM context
104  * @ret value           Value, or negative error
105  */
106 static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
107         uint8_t value;
108
109         /* Sanity check */
110         if ( pnm->offset == image->len ) {
111                 DBGC ( image, "PNM %s ran out of binary data\n",
112                        image->name );
113                 return -EINVAL;
114         }
115
116         /* Extract value */
117         copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
118         pnm->offset++;
119
120         return value;
121 }
122
123 /**
124  * Scale PNM scalar value
125  *
126  * @v image             PNM image
127  * @v pnm               PNM context
128  * @v value             Raw value
129  * @ret value           Scaled value (in range 0-255)
130  */
131 static int pnm_scale ( struct image *image, struct pnm_context *pnm,
132                        unsigned int value ) {
133
134         if ( value > pnm->max ) {
135                 DBGC ( image, "PNM %s has out-of-range value %d (max %d)\n",
136                        image->name, value, pnm->max );
137                 return -EINVAL;
138         }
139         return ( ( 255 * value ) / pnm->max );
140 }
141
142 /**
143  * Convert PNM bitmap composite value to RGB
144  *
145  * @v composite         Composite value
146  * @v index             Pixel index within this composite value
147  * @ret rgb             24-bit RGB value
148  */
149 static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) {
150
151         /* Composite value is an 8-bit bitmask */
152         return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff );
153 }
154
155 /**
156  * Convert PNM greymap composite value to RGB
157  *
158  * @v composite         Composite value
159  * @v index             Pixel index within this composite value
160  * @ret rgb             24-bit RGB value
161  */
162 static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){
163
164         /* Composite value is an 8-bit greyscale value */
165         return ( ( composite << 16 ) | ( composite << 8 ) | composite );
166 }
167
168 /**
169  * Convert PNM pixmap composite value to RGB
170  *
171  * @v composite         Composite value
172  * @v index             Pixel index within this composite value
173  * @ret rgb             24-bit RGB value
174  */
175 static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
176
177         /* Composite value is already an RGB value */
178         return composite;
179 }
180
181 /**
182  * Extract PNM pixel data
183  *
184  * @v image             PNM image
185  * @v pnm               PNM context
186  * @v pixbuf            Pixel buffer
187  * @ret rc              Return status code
188  */
189 static int pnm_data ( struct image *image, struct pnm_context *pnm,
190                       struct pixel_buffer *pixbuf ) {
191         struct pnm_type *type = pnm->type;
192         size_t offset = 0;
193         unsigned int xpos = 0;
194         int scalar;
195         uint32_t composite;
196         uint32_t rgb;
197         unsigned int i;
198
199         /* Fill pixel buffer */
200         while ( offset < pixbuf->len ) {
201
202                 /* Extract a scaled composite scalar value from the file */
203                 composite = 0;
204                 for ( i = 0 ; i < type->depth ; i++ ) {
205                         scalar = type->scalar ( image, pnm );
206                         if ( scalar < 0 )
207                                 return scalar;
208                         scalar = pnm_scale ( image, pnm, scalar );
209                         if ( scalar < 0 )
210                                 return scalar;
211                         composite = ( ( composite << 8 ) | scalar );
212                 }
213
214                 /* Extract 24-bit RGB values from composite value */
215                 for ( i = 0 ; i < type->packing ; i++ ) {
216                         if ( offset >= pixbuf->len ) {
217                                 DBGC ( image, "PNM %s has too many pixels\n",
218                                        image->name );
219                                 return -EINVAL;
220                         }
221                         rgb = type->rgb ( composite, i );
222                         copy_to_user ( pixbuf->data, offset, &rgb,
223                                        sizeof ( rgb ) );
224                         offset += sizeof ( rgb );
225                         if ( ++xpos == pixbuf->width ) {
226                                 xpos = 0;
227                                 break;
228                         }
229                 }
230         }
231
232         return 0;
233 }
234
235 /** PNM image types */
236 static struct pnm_type pnm_types[] = {
237         {
238                 .type = '1',
239                 .depth = 1,
240                 .packing = 1,
241                 .flags = PNM_BITMAP,
242                 .scalar = pnm_ascii,
243                 .rgb = pnm_bitmap,
244         },
245         {
246                 .type = '2',
247                 .depth = 1,
248                 .packing = 1,
249                 .scalar = pnm_ascii,
250                 .rgb = pnm_greymap,
251         },
252         {
253                 .type = '3',
254                 .depth = 3,
255                 .packing = 1,
256                 .scalar = pnm_ascii,
257                 .rgb = pnm_pixmap,
258         },
259         {
260                 .type = '4',
261                 .depth = 1,
262                 .packing = 8,
263                 .flags = PNM_BITMAP,
264                 .scalar = pnm_binary,
265                 .rgb = pnm_bitmap,
266         },
267         {
268                 .type = '5',
269                 .depth = 1,
270                 .packing = 1,
271                 .scalar = pnm_binary,
272                 .rgb = pnm_greymap,
273         },
274         {
275                 .type = '6',
276                 .depth = 3,
277                 .packing = 1,
278                 .scalar = pnm_binary,
279                 .rgb = pnm_pixmap,
280         },
281 };
282
283 /**
284  * Determine PNM image type
285  *
286  * @v image             PNM image
287  * @ret type            PNM image type, or NULL if not found
288  */
289 static struct pnm_type * pnm_type ( struct image *image ) {
290         struct pnm_signature signature;
291         struct pnm_type *type;
292         unsigned int i;
293
294         /* Extract signature */
295         assert ( image->len >= sizeof ( signature ) );
296         copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
297
298         /* Check for supported types */
299         for ( i = 0 ; i < ( sizeof ( pnm_types ) /
300                             sizeof ( pnm_types[0] ) ) ; i++ ) {
301                 type = &pnm_types[i];
302                 if ( type->type == signature.type )
303                         return type;
304         }
305         return NULL;
306 }
307
308 /**
309  * Convert PNM image to pixel buffer
310  *
311  * @v image             PNM image
312  * @v pixbuf            Pixel buffer to fill in
313  * @ret rc              Return status code
314  */
315 static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
316         struct pnm_context pnm;
317         int width;
318         int height;
319         int max;
320         int rc;
321
322         /* Initialise PNM context */
323         pnm.type = pnm_type ( image );
324         if ( ! pnm.type ) {
325                 rc = -ENOTSUP;
326                 goto err_type;
327         }
328         pnm.offset = sizeof ( struct pnm_signature );
329         pnm.ascii_len = PNM_ASCII_LEN;
330
331         /* Extract width */
332         if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) {
333                 rc = width;
334                 goto err_width;
335         }
336
337         /* Extract height */
338         if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) {
339                 rc = height;
340                 goto err_height;
341         }
342
343         /* Extract maximum scalar value, if not predefined */
344         if ( pnm.type->flags & PNM_BITMAP ) {
345                 pnm.max = ( ( 1 << pnm.type->packing ) - 1 );
346                 pnm.ascii_len = 1;
347         } else {
348                 if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) {
349                         rc = max;
350                         goto err_max;
351                 }
352                 pnm.max = max;
353         }
354         if ( pnm.max == 0 ) {
355                 DBGC ( image, "PNM %s has invalid maximum value 0\n",
356                        image->name );
357                 rc = -EINVAL;
358                 goto err_max;
359         }
360         DBGC ( image, "PNM %s is type %c width %d height %d max %d\n",
361                image->name, pnm.type->type, width, height, pnm.max );
362
363         /* Allocate pixel buffer */
364         *pixbuf = alloc_pixbuf ( width, height );
365         if ( ! *pixbuf ) {
366                 rc = -ENOMEM;
367                 goto err_alloc_pixbuf;
368         }
369
370         /* Extract pixel data */
371         if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 )
372                 goto err_data;
373
374         return 0;
375
376  err_data:
377         pixbuf_put ( *pixbuf );
378  err_alloc_pixbuf:
379  err_max:
380  err_height:
381  err_width:
382  err_type:
383         return rc;
384 }
385
386 /**
387  * Probe PNM image
388  *
389  * @v image             PNM image
390  * @ret rc              Return status code
391  */
392 static int pnm_probe ( struct image *image ) {
393         struct pnm_signature signature;
394
395         /* Sanity check */
396         if ( image->len < sizeof ( signature ) ) {
397                 DBGC ( image, "PNM %s is too short\n", image->name );
398                 return -ENOEXEC;
399         }
400
401         /* Check signature */
402         copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
403         if ( ! ( ( signature.magic == PNM_MAGIC ) &&
404                  ( isdigit ( signature.type ) ) &&
405                  ( isspace ( signature.space ) ) ) ) {
406                 DBGC ( image, "PNM %s has invalid signature\n", image->name );
407                 return -ENOEXEC;
408         }
409         DBGC ( image, "PNM %s is type %c\n", image->name, signature.type );
410
411         return 0;
412 }
413
414 /** PNM image type */
415 struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = {
416         .name = "PNM",
417         .probe = pnm_probe,
418         .pixbuf = pnm_pixbuf,
419 };