2 * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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
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.
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
28 * Portable anymap format (PNM)
35 #include <ipxe/image.h>
36 #include <ipxe/pixbuf.h>
40 * Extract PNM ASCII value
44 * @ret value Value, or negative error
46 static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
47 char buf[ pnm->ascii_len + 1 /* NUL */ ];
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,
61 if ( buf[0] == '#' ) {
63 } else if ( ! isspace ( buf[0] ) ) {
69 /* Fail if no value is present */
70 len = ( image->len - pnm->offset );
72 DBGC ( image, "PNM %s ran out of ASCII data\n", image->name );
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 );
82 /* Parse value and update offset */
83 value = strtoul ( buf, &endp, 0 );
84 pnm->offset += ( endp - buf );
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",
100 * Extract PNM binary value
104 * @ret value Value, or negative error
106 static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
110 if ( pnm->offset == image->len ) {
111 DBGC ( image, "PNM %s ran out of binary data\n",
117 copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
124 * Scale PNM scalar value
129 * @ret value Scaled value (in range 0-255)
131 static int pnm_scale ( struct image *image, struct pnm_context *pnm,
132 unsigned int value ) {
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 );
139 return ( ( 255 * value ) / pnm->max );
143 * Convert PNM bitmap composite value to RGB
145 * @v composite Composite value
146 * @v index Pixel index within this composite value
147 * @ret rgb 24-bit RGB value
149 static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) {
151 /* Composite value is an 8-bit bitmask */
152 return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff );
156 * Convert PNM greymap composite value to RGB
158 * @v composite Composite value
159 * @v index Pixel index within this composite value
160 * @ret rgb 24-bit RGB value
162 static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){
164 /* Composite value is an 8-bit greyscale value */
165 return ( ( composite << 16 ) | ( composite << 8 ) | composite );
169 * Convert PNM pixmap composite value to RGB
171 * @v composite Composite value
172 * @v index Pixel index within this composite value
173 * @ret rgb 24-bit RGB value
175 static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
177 /* Composite value is already an RGB value */
182 * Extract PNM pixel data
186 * @v pixbuf Pixel buffer
187 * @ret rc Return status code
189 static int pnm_data ( struct image *image, struct pnm_context *pnm,
190 struct pixel_buffer *pixbuf ) {
191 struct pnm_type *type = pnm->type;
193 unsigned int xpos = 0;
199 /* Fill pixel buffer */
200 while ( offset < pixbuf->len ) {
202 /* Extract a scaled composite scalar value from the file */
204 for ( i = 0 ; i < type->depth ; i++ ) {
205 scalar = type->scalar ( image, pnm );
208 scalar = pnm_scale ( image, pnm, scalar );
211 composite = ( ( composite << 8 ) | scalar );
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",
221 rgb = type->rgb ( composite, i );
222 copy_to_user ( pixbuf->data, offset, &rgb,
224 offset += sizeof ( rgb );
225 if ( ++xpos == pixbuf->width ) {
235 /** PNM image types */
236 static struct pnm_type pnm_types[] = {
264 .scalar = pnm_binary,
271 .scalar = pnm_binary,
278 .scalar = pnm_binary,
284 * Determine PNM image type
287 * @ret type PNM image type, or NULL if not found
289 static struct pnm_type * pnm_type ( struct image *image ) {
290 struct pnm_signature signature;
291 struct pnm_type *type;
294 /* Extract signature */
295 assert ( image->len >= sizeof ( signature ) );
296 copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
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 )
309 * Convert PNM image to pixel buffer
312 * @v pixbuf Pixel buffer to fill in
313 * @ret rc Return status code
315 static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
316 struct pnm_context pnm;
322 /* Initialise PNM context */
323 pnm.type = pnm_type ( image );
328 pnm.offset = sizeof ( struct pnm_signature );
329 pnm.ascii_len = PNM_ASCII_LEN;
332 if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) {
338 if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) {
343 /* Extract maximum scalar value, if not predefined */
344 if ( pnm.type->flags & PNM_BITMAP ) {
345 pnm.max = ( ( 1 << pnm.type->packing ) - 1 );
348 if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) {
354 if ( pnm.max == 0 ) {
355 DBGC ( image, "PNM %s has invalid maximum value 0\n",
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 );
363 /* Allocate pixel buffer */
364 *pixbuf = alloc_pixbuf ( width, height );
367 goto err_alloc_pixbuf;
370 /* Extract pixel data */
371 if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 )
377 pixbuf_put ( *pixbuf );
390 * @ret rc Return status code
392 static int pnm_probe ( struct image *image ) {
393 struct pnm_signature signature;
396 if ( image->len < sizeof ( signature ) ) {
397 DBGC ( image, "PNM %s is too short\n", image->name );
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 );
409 DBGC ( image, "PNM %s is type %c\n", image->name, signature.type );
414 /** PNM image type */
415 struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = {
418 .pixbuf = pnm_pixbuf,