iPXE
pnm.c
Go to the documentation of this file.
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 };
#define EINVAL
Invalid argument.
Definition: errno.h:428
static int pnm_probe(struct image *image)
Probe PNM image.
Definition: pnm.c:392
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
struct pixel_buffer * alloc_pixbuf(unsigned int width, unsigned int height)
Allocate pixel buffer.
Definition: pixbuf.c:58
int(* scalar)(struct image *image, struct pnm_context *pnm)
Extract scalar value.
Definition: pnm.h:60
userptr_t data
Raw file image.
Definition: image.h:41
#define max(x, y)
Definition: ath.h:39
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned long strtoul(const char *string, char **endp, int base)
Convert string to numeric value.
Definition: string.c:456
uint8_t flags
Flags.
Definition: pnm.h:53
Error codes.
static int pnm_binary(struct image *image, struct pnm_context *pnm)
Extract PNM binary value.
Definition: pnm.c:106
#define ENOEXEC
Exec format error.
Definition: errno.h:519
#define PNM_ASCII_LEN
Default maximum length of ASCII values.
Definition: pnm.h:42
uint8_t type
Type.
Definition: ena.h:16
static __always_inline void copy_from_user(void *dest, userptr_t src, off_t src_off, size_t len)
Copy data from user buffer.
Definition: uaccess.h:337
#define DBGC(...)
Definition: compiler.h:505
struct image_type pnm_image_type __image_type(PROBE_NORMAL)
PNM image type.
An executable image type.
Definition: image.h:76
static struct pnm_type * pnm_type(struct image *image)
Determine PNM image type.
Definition: pnm.c:289
#define PROBE_NORMAL
Normal image probe priority.
Definition: image.h:129
An executable image.
Definition: image.h:24
Pixel buffer.
Character types.
static struct pnm_type pnm_types[]
PNM image types.
Definition: pnm.c:236
unsigned int max
Maximum pixel value.
Definition: pnm.h:38
char * name
Name of this image type.
Definition: image.h:78
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
u8 signature
Definition: CIB_PRM.h:35
static int isdigit(int character)
Check if character is a decimal digit.
Definition: ctype.h:18
#define ENOMEM
Not enough space.
Definition: errno.h:534
size_t len
Total length.
Definition: pixbuf.h:27
static int pnm_pixbuf(struct image *image, struct pixel_buffer **pixbuf)
Convert PNM image to pixel buffer.
Definition: pnm.c:315
static uint32_t pnm_pixmap(uint32_t composite, unsigned int index __unused)
Convert PNM pixmap composite value to RGB.
Definition: pnm.c:175
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
Executable images.
static userptr_t size_t offset
Offset of the first segment within the content.
Definition: deflate.h:259
pseudo_bit_t value[0x00020]
Definition: arbel.h:13
static int pnm_ascii(struct image *image, struct pnm_context *pnm)
Extract PNM ASCII value.
Definition: pnm.c:46
size_t ascii_len
Maximum length of ASCII values.
Definition: pnm.h:36
static uint32_t pnm_bitmap(uint32_t composite, unsigned int index)
Convert PNM bitmap composite value to RGB.
Definition: pnm.c:149
size_t len
Length of raw file image.
Definition: image.h:43
uint8_t packing
Number of pixels per composite value.
Definition: pnm.h:51
A pixel buffer.
Definition: pixbuf.h:17
static __always_inline void copy_to_user(userptr_t dest, off_t dest_off, const void *src, size_t len)
Copy data to user buffer.
Definition: uaccess.h:324
int isspace(int character)
Check to see if character is a space.
Definition: ctype.c:41
unsigned char uint8_t
Definition: stdint.h:10
PNM type.
Definition: pnm.h:45
unsigned int uint32_t
Definition: stdint.h:12
struct pnm_type * type
PNM type.
Definition: pnm.h:32
PNM signature.
Definition: pnm.h:17
static int pnm_scale(struct image *image, struct pnm_context *pnm, unsigned int value)
Scale PNM scalar value.
Definition: pnm.c:131
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
uint32_t len
Length.
Definition: ena.h:14
Portable anymap format (PNM)
char type
PNM type.
Definition: pnm.h:47
int(* pixbuf)(struct image *image, struct pixel_buffer **pixbuf)
Create pixel buffer from image.
Definition: image.h:102
Bitmap format.
Definition: pnm.h:81
#define PNM_MAGIC
PNM magic byte.
Definition: pnm.h:27
static uint32_t pnm_greymap(uint32_t composite, unsigned int index __unused)
Convert PNM greymap composite value to RGB.
Definition: pnm.c:162
userptr_t data
32-bit (8:8:8:8) xRGB pixel data, in host-endian order
Definition: pixbuf.h:25
uint64_t index
Index of the first segment within the content.
Definition: pccrc.h:21
char * name
Name.
Definition: image.h:34
#define NULL
NULL pointer (VOID *)
Definition: Base.h:362
size_t offset
Current byte offset.
Definition: pnm.h:34
uint32_t(* rgb)(uint32_t composite, unsigned int index)
Convert composite value to 24-bit RGB.
Definition: pnm.h:67
PNM context.
Definition: pnm.h:30
unsigned int width
Width.
Definition: pixbuf.h:21
static int pnm_data(struct image *image, struct pnm_context *pnm, struct pixel_buffer *pixbuf)
Extract PNM pixel data.
Definition: pnm.c:189