iPXE
pnm.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2013 Michael Brown <mbrown@fensystems.co.uk>.
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License as
00006  * published by the Free Software Foundation; either version 2 of the
00007  * License, or any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful, but
00010  * WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  *
00019  * You can also choose to distribute this program under the terms of
00020  * the Unmodified Binary Distribution Licence (as given in the file
00021  * COPYING.UBDL), provided that you have satisfied its requirements.
00022  */
00023 
00024 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
00025 
00026 /** @file
00027  *
00028  * Portable anymap format (PNM)
00029  *
00030  */
00031 
00032 #include <stdlib.h>
00033 #include <errno.h>
00034 #include <ctype.h>
00035 #include <ipxe/image.h>
00036 #include <ipxe/pixbuf.h>
00037 #include <ipxe/pnm.h>
00038 
00039 /**
00040  * Extract PNM ASCII value
00041  *
00042  * @v image             PNM image
00043  * @v pnm               PNM context
00044  * @ret value           Value, or negative error
00045  */
00046 static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
00047         char buf[ pnm->ascii_len + 1 /* NUL */ ];
00048         char *endp;
00049         size_t len;
00050         int value;
00051         int in_comment = 0;
00052 
00053         /* Skip any leading whitespace and comments */
00054         for ( ; pnm->offset < image->len ; pnm->offset++ ) {
00055                 copy_from_user ( &buf[0], image->data, pnm->offset,
00056                                  sizeof ( buf[0] ) );
00057                 if ( in_comment ) {
00058                         if ( buf[0] == '\n' )
00059                                 in_comment = 0;
00060                 } else {
00061                         if ( buf[0] == '#' ) {
00062                                 in_comment = 1;
00063                         } else if ( ! isspace ( buf[0] ) ) {
00064                                 break;
00065                         }
00066                 }
00067         }
00068 
00069         /* Fail if no value is present */
00070         len = ( image->len - pnm->offset );
00071         if ( len == 0 ) {
00072                 DBGC ( image, "PNM %s ran out of ASCII data\n", image->name );
00073                 return -EINVAL;
00074         }
00075 
00076         /* Copy ASCII value to buffer and ensure string is NUL-terminated */
00077         if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) )
00078                 len = ( sizeof ( buf ) - 1 /* NUL */ );
00079         copy_from_user ( buf, image->data, pnm->offset, len );
00080         buf[len] = '\0';
00081 
00082         /* Parse value and update offset */
00083         value = strtoul ( buf, &endp, 0 );
00084         pnm->offset += ( endp - buf );
00085 
00086         /* Check and skip terminating whitespace character, if present */
00087         if ( ( pnm->offset != image->len ) && ( *endp != '\0' ) ) {
00088                 if ( ! isspace ( *endp ) ) {
00089                         DBGC ( image, "PNM %s invalid ASCII integer\n",
00090                                image->name );
00091                         return -EINVAL;
00092                 }
00093                 pnm->offset++;
00094         }
00095 
00096         return value;
00097 }
00098 
00099 /**
00100  * Extract PNM binary value
00101  *
00102  * @v image             PNM image
00103  * @v pnm               PNM context
00104  * @ret value           Value, or negative error
00105  */
00106 static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
00107         uint8_t value;
00108 
00109         /* Sanity check */
00110         if ( pnm->offset == image->len ) {
00111                 DBGC ( image, "PNM %s ran out of binary data\n",
00112                        image->name );
00113                 return -EINVAL;
00114         }
00115 
00116         /* Extract value */
00117         copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
00118         pnm->offset++;
00119 
00120         return value;
00121 }
00122 
00123 /**
00124  * Scale PNM scalar value
00125  *
00126  * @v image             PNM image
00127  * @v pnm               PNM context
00128  * @v value             Raw value
00129  * @ret value           Scaled value (in range 0-255)
00130  */
00131 static int pnm_scale ( struct image *image, struct pnm_context *pnm,
00132                        unsigned int value ) {
00133 
00134         if ( value > pnm->max ) {
00135                 DBGC ( image, "PNM %s has out-of-range value %d (max %d)\n",
00136                        image->name, value, pnm->max );
00137                 return -EINVAL;
00138         }
00139         return ( ( 255 * value ) / pnm->max );
00140 }
00141 
00142 /**
00143  * Convert PNM bitmap composite value to RGB
00144  *
00145  * @v composite         Composite value
00146  * @v index             Pixel index within this composite value
00147  * @ret rgb             24-bit RGB value
00148  */
00149 static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) {
00150 
00151         /* Composite value is an 8-bit bitmask */
00152         return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff );
00153 }
00154 
00155 /**
00156  * Convert PNM greymap composite value to RGB
00157  *
00158  * @v composite         Composite value
00159  * @v index             Pixel index within this composite value
00160  * @ret rgb             24-bit RGB value
00161  */
00162 static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){
00163 
00164         /* Composite value is an 8-bit greyscale value */
00165         return ( ( composite << 16 ) | ( composite << 8 ) | composite );
00166 }
00167 
00168 /**
00169  * Convert PNM pixmap composite value to RGB
00170  *
00171  * @v composite         Composite value
00172  * @v index             Pixel index within this composite value
00173  * @ret rgb             24-bit RGB value
00174  */
00175 static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
00176 
00177         /* Composite value is already an RGB value */
00178         return composite;
00179 }
00180 
00181 /**
00182  * Extract PNM pixel data
00183  *
00184  * @v image             PNM image
00185  * @v pnm               PNM context
00186  * @v pixbuf            Pixel buffer
00187  * @ret rc              Return status code
00188  */
00189 static int pnm_data ( struct image *image, struct pnm_context *pnm,
00190                       struct pixel_buffer *pixbuf ) {
00191         struct pnm_type *type = pnm->type;
00192         size_t offset = 0;
00193         unsigned int xpos = 0;
00194         int scalar;
00195         uint32_t composite;
00196         uint32_t rgb;
00197         unsigned int i;
00198 
00199         /* Fill pixel buffer */
00200         while ( offset < pixbuf->len ) {
00201 
00202                 /* Extract a scaled composite scalar value from the file */
00203                 composite = 0;
00204                 for ( i = 0 ; i < type->depth ; i++ ) {
00205                         scalar = type->scalar ( image, pnm );
00206                         if ( scalar < 0 )
00207                                 return scalar;
00208                         scalar = pnm_scale ( image, pnm, scalar );
00209                         if ( scalar < 0 )
00210                                 return scalar;
00211                         composite = ( ( composite << 8 ) | scalar );
00212                 }
00213 
00214                 /* Extract 24-bit RGB values from composite value */
00215                 for ( i = 0 ; i < type->packing ; i++ ) {
00216                         if ( offset >= pixbuf->len ) {
00217                                 DBGC ( image, "PNM %s has too many pixels\n",
00218                                        image->name );
00219                                 return -EINVAL;
00220                         }
00221                         rgb = type->rgb ( composite, i );
00222                         copy_to_user ( pixbuf->data, offset, &rgb,
00223                                        sizeof ( rgb ) );
00224                         offset += sizeof ( rgb );
00225                         if ( ++xpos == pixbuf->width ) {
00226                                 xpos = 0;
00227                                 break;
00228                         }
00229                 }
00230         }
00231 
00232         return 0;
00233 }
00234 
00235 /** PNM image types */
00236 static struct pnm_type pnm_types[] = {
00237         {
00238                 .type = '1',
00239                 .depth = 1,
00240                 .packing = 1,
00241                 .flags = PNM_BITMAP,
00242                 .scalar = pnm_ascii,
00243                 .rgb = pnm_bitmap,
00244         },
00245         {
00246                 .type = '2',
00247                 .depth = 1,
00248                 .packing = 1,
00249                 .scalar = pnm_ascii,
00250                 .rgb = pnm_greymap,
00251         },
00252         {
00253                 .type = '3',
00254                 .depth = 3,
00255                 .packing = 1,
00256                 .scalar = pnm_ascii,
00257                 .rgb = pnm_pixmap,
00258         },
00259         {
00260                 .type = '4',
00261                 .depth = 1,
00262                 .packing = 8,
00263                 .flags = PNM_BITMAP,
00264                 .scalar = pnm_binary,
00265                 .rgb = pnm_bitmap,
00266         },
00267         {
00268                 .type = '5',
00269                 .depth = 1,
00270                 .packing = 1,
00271                 .scalar = pnm_binary,
00272                 .rgb = pnm_greymap,
00273         },
00274         {
00275                 .type = '6',
00276                 .depth = 3,
00277                 .packing = 1,
00278                 .scalar = pnm_binary,
00279                 .rgb = pnm_pixmap,
00280         },
00281 };
00282 
00283 /**
00284  * Determine PNM image type
00285  *
00286  * @v image             PNM image
00287  * @ret type            PNM image type, or NULL if not found
00288  */
00289 static struct pnm_type * pnm_type ( struct image *image ) {
00290         struct pnm_signature signature;
00291         struct pnm_type *type;
00292         unsigned int i;
00293 
00294         /* Extract signature */
00295         assert ( image->len >= sizeof ( signature ) );
00296         copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
00297 
00298         /* Check for supported types */
00299         for ( i = 0 ; i < ( sizeof ( pnm_types ) /
00300                             sizeof ( pnm_types[0] ) ) ; i++ ) {
00301                 type = &pnm_types[i];
00302                 if ( type->type == signature.type )
00303                         return type;
00304         }
00305         return NULL;
00306 }
00307 
00308 /**
00309  * Convert PNM image to pixel buffer
00310  *
00311  * @v image             PNM image
00312  * @v pixbuf            Pixel buffer to fill in
00313  * @ret rc              Return status code
00314  */
00315 static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
00316         struct pnm_context pnm;
00317         int width;
00318         int height;
00319         int max;
00320         int rc;
00321 
00322         /* Initialise PNM context */
00323         pnm.type = pnm_type ( image );
00324         if ( ! pnm.type ) {
00325                 rc = -ENOTSUP;
00326                 goto err_type;
00327         }
00328         pnm.offset = sizeof ( struct pnm_signature );
00329         pnm.ascii_len = PNM_ASCII_LEN;
00330 
00331         /* Extract width */
00332         if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) {
00333                 rc = width;
00334                 goto err_width;
00335         }
00336 
00337         /* Extract height */
00338         if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) {
00339                 rc = height;
00340                 goto err_height;
00341         }
00342 
00343         /* Extract maximum scalar value, if not predefined */
00344         if ( pnm.type->flags & PNM_BITMAP ) {
00345                 pnm.max = ( ( 1 << pnm.type->packing ) - 1 );
00346                 pnm.ascii_len = 1;
00347         } else {
00348                 if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) {
00349                         rc = max;
00350                         goto err_max;
00351                 }
00352                 pnm.max = max;
00353         }
00354         if ( pnm.max == 0 ) {
00355                 DBGC ( image, "PNM %s has invalid maximum value 0\n",
00356                        image->name );
00357                 rc = -EINVAL;
00358                 goto err_max;
00359         }
00360         DBGC ( image, "PNM %s is type %c width %d height %d max %d\n",
00361                image->name, pnm.type->type, width, height, pnm.max );
00362 
00363         /* Allocate pixel buffer */
00364         *pixbuf = alloc_pixbuf ( width, height );
00365         if ( ! *pixbuf ) {
00366                 rc = -ENOMEM;
00367                 goto err_alloc_pixbuf;
00368         }
00369 
00370         /* Extract pixel data */
00371         if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 )
00372                 goto err_data;
00373 
00374         return 0;
00375 
00376  err_data:
00377         pixbuf_put ( *pixbuf );
00378  err_alloc_pixbuf:
00379  err_max:
00380  err_height:
00381  err_width:
00382  err_type:
00383         return rc;
00384 }
00385 
00386 /**
00387  * Probe PNM image
00388  *
00389  * @v image             PNM image
00390  * @ret rc              Return status code
00391  */
00392 static int pnm_probe ( struct image *image ) {
00393         struct pnm_signature signature;
00394 
00395         /* Sanity check */
00396         if ( image->len < sizeof ( signature ) ) {
00397                 DBGC ( image, "PNM %s is too short\n", image->name );
00398                 return -ENOEXEC;
00399         }
00400 
00401         /* Check signature */
00402         copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
00403         if ( ! ( ( signature.magic == PNM_MAGIC ) &&
00404                  ( isdigit ( signature.type ) ) &&
00405                  ( isspace ( signature.space ) ) ) ) {
00406                 DBGC ( image, "PNM %s has invalid signature\n", image->name );
00407                 return -ENOEXEC;
00408         }
00409         DBGC ( image, "PNM %s is type %c\n", image->name, signature.type );
00410 
00411         return 0;
00412 }
00413 
00414 /** PNM image type */
00415 struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = {
00416         .name = "PNM",
00417         .probe = pnm_probe,
00418         .pixbuf = pnm_pixbuf,
00419 };