You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. FILE_LICENCE ( GPL2_OR_LATER );
  20. /** @file
  21. *
  22. * Portable anymap format (PNM)
  23. *
  24. */
  25. #include <stdlib.h>
  26. #include <errno.h>
  27. #include <ctype.h>
  28. #include <ipxe/image.h>
  29. #include <ipxe/pixbuf.h>
  30. #include <ipxe/pnm.h>
  31. /**
  32. * Extract PNM ASCII value
  33. *
  34. * @v image PNM image
  35. * @v pnm PNM context
  36. * @ret value Value, or negative error
  37. */
  38. static int pnm_ascii ( struct image *image, struct pnm_context *pnm ) {
  39. char buf[ pnm->ascii_len + 1 /* NUL */ ];
  40. char *endp;
  41. size_t len;
  42. int value;
  43. int in_comment = 0;
  44. /* Skip any leading whitespace and comments */
  45. for ( ; pnm->offset < image->len ; pnm->offset++ ) {
  46. copy_from_user ( &buf[0], image->data, pnm->offset,
  47. sizeof ( buf[0] ) );
  48. if ( in_comment ) {
  49. if ( buf[0] == '\n' )
  50. in_comment = 0;
  51. } else {
  52. if ( buf[0] == '#' ) {
  53. in_comment = 1;
  54. } else if ( ! isspace ( buf[0] ) ) {
  55. break;
  56. }
  57. }
  58. }
  59. /* Fail if no value is present */
  60. len = ( image->len - pnm->offset );
  61. if ( len == 0 ) {
  62. DBGC ( image, "PNM %s ran out of ASCII data\n", image->name );
  63. return -EINVAL;
  64. }
  65. /* Copy ASCII value to buffer and ensure string is NUL-terminated */
  66. if ( len > ( sizeof ( buf ) - 1 /* NUL */ ) )
  67. len = ( sizeof ( buf ) - 1 /* NUL */ );
  68. copy_from_user ( buf, image->data, pnm->offset, len );
  69. buf[len] = '\0';
  70. /* Parse value and update offset */
  71. value = strtoul ( buf, &endp, 0 );
  72. pnm->offset += ( endp - buf );
  73. /* Check and skip terminating whitespace character, if present */
  74. if ( ( pnm->offset != image->len ) && ( *endp != '\0' ) ) {
  75. if ( ! isspace ( *endp ) ) {
  76. DBGC ( image, "PNM %s invalid ASCII integer\n",
  77. image->name );
  78. return -EINVAL;
  79. }
  80. pnm->offset++;
  81. }
  82. return value;
  83. }
  84. /**
  85. * Extract PNM binary value
  86. *
  87. * @v image PNM image
  88. * @v pnm PNM context
  89. * @ret value Value, or negative error
  90. */
  91. static int pnm_binary ( struct image *image, struct pnm_context *pnm ) {
  92. uint8_t value;
  93. /* Sanity check */
  94. if ( pnm->offset == image->len ) {
  95. DBGC ( image, "PNM %s ran out of binary data\n",
  96. image->name );
  97. return -EINVAL;
  98. }
  99. /* Extract value */
  100. copy_from_user ( &value, image->data, pnm->offset, sizeof ( value ) );
  101. pnm->offset++;
  102. return value;
  103. }
  104. /**
  105. * Scale PNM scalar value
  106. *
  107. * @v image PNM image
  108. * @v pnm PNM context
  109. * @v value Raw value
  110. * @ret value Scaled value (in range 0-255)
  111. */
  112. static int pnm_scale ( struct image *image, struct pnm_context *pnm,
  113. unsigned int value ) {
  114. if ( value > pnm->max ) {
  115. DBGC ( image, "PNM %s has out-of-range value %d (max %d)\n",
  116. image->name, value, pnm->max );
  117. return -EINVAL;
  118. }
  119. return ( ( 255 * value ) / pnm->max );
  120. }
  121. /**
  122. * Convert PNM bitmap composite value to RGB
  123. *
  124. * @v composite Composite value
  125. * @v index Pixel index within this composite value
  126. * @ret rgb 24-bit RGB value
  127. */
  128. static uint32_t pnm_bitmap ( uint32_t composite, unsigned int index ) {
  129. /* Composite value is an 8-bit bitmask */
  130. return ( ( ( composite << index ) & 0x80 ) ? 0x000000 : 0xffffff );
  131. }
  132. /**
  133. * Convert PNM greymap composite value to RGB
  134. *
  135. * @v composite Composite value
  136. * @v index Pixel index within this composite value
  137. * @ret rgb 24-bit RGB value
  138. */
  139. static uint32_t pnm_greymap ( uint32_t composite, unsigned int index __unused ){
  140. /* Composite value is an 8-bit greyscale value */
  141. return ( ( composite << 16 ) | ( composite << 8 ) | composite );
  142. }
  143. /**
  144. * Convert PNM pixmap composite value to RGB
  145. *
  146. * @v composite Composite value
  147. * @v index Pixel index within this composite value
  148. * @ret rgb 24-bit RGB value
  149. */
  150. static uint32_t pnm_pixmap ( uint32_t composite, unsigned int index __unused ) {
  151. /* Composite value is already an RGB value */
  152. return composite;
  153. }
  154. /**
  155. * Extract PNM pixel data
  156. *
  157. * @v image PNM image
  158. * @v pnm PNM context
  159. * @v pixbuf Pixel buffer
  160. * @ret rc Return status code
  161. */
  162. static int pnm_data ( struct image *image, struct pnm_context *pnm,
  163. struct pixel_buffer *pixbuf ) {
  164. struct pnm_type *type = pnm->type;
  165. size_t offset = 0;
  166. unsigned int xpos = 0;
  167. int scalar;
  168. uint32_t composite;
  169. uint32_t rgb;
  170. unsigned int i;
  171. /* Fill pixel buffer */
  172. while ( offset < pixbuf->len ) {
  173. /* Extract a scaled composite scalar value from the file */
  174. composite = 0;
  175. for ( i = 0 ; i < type->depth ; i++ ) {
  176. scalar = type->scalar ( image, pnm );
  177. if ( scalar < 0 )
  178. return scalar;
  179. scalar = pnm_scale ( image, pnm, scalar );
  180. if ( scalar < 0 )
  181. return scalar;
  182. composite = ( ( composite << 8 ) | scalar );
  183. }
  184. /* Extract 24-bit RGB values from composite value */
  185. for ( i = 0 ; i < type->packing ; i++ ) {
  186. if ( offset >= pixbuf->len ) {
  187. DBGC ( image, "PNM %s has too many pixels\n",
  188. image->name );
  189. return -EINVAL;
  190. }
  191. rgb = type->rgb ( composite, i );
  192. copy_to_user ( pixbuf->data, offset, &rgb,
  193. sizeof ( rgb ) );
  194. offset += sizeof ( rgb );
  195. if ( ++xpos == pixbuf->width ) {
  196. xpos = 0;
  197. break;
  198. }
  199. }
  200. }
  201. return 0;
  202. }
  203. /** PNM image types */
  204. static struct pnm_type pnm_types[] = {
  205. {
  206. .type = '1',
  207. .depth = 1,
  208. .packing = 1,
  209. .flags = PNM_BITMAP,
  210. .scalar = pnm_ascii,
  211. .rgb = pnm_bitmap,
  212. },
  213. {
  214. .type = '2',
  215. .depth = 1,
  216. .packing = 1,
  217. .scalar = pnm_ascii,
  218. .rgb = pnm_greymap,
  219. },
  220. {
  221. .type = '3',
  222. .depth = 3,
  223. .packing = 1,
  224. .scalar = pnm_ascii,
  225. .rgb = pnm_pixmap,
  226. },
  227. {
  228. .type = '4',
  229. .depth = 1,
  230. .packing = 8,
  231. .flags = PNM_BITMAP,
  232. .scalar = pnm_binary,
  233. .rgb = pnm_bitmap,
  234. },
  235. {
  236. .type = '5',
  237. .depth = 1,
  238. .packing = 1,
  239. .scalar = pnm_binary,
  240. .rgb = pnm_greymap,
  241. },
  242. {
  243. .type = '6',
  244. .depth = 3,
  245. .packing = 1,
  246. .scalar = pnm_binary,
  247. .rgb = pnm_pixmap,
  248. },
  249. };
  250. /**
  251. * Determine PNM image type
  252. *
  253. * @v image PNM image
  254. * @ret type PNM image type, or NULL if not found
  255. */
  256. static struct pnm_type * pnm_type ( struct image *image ) {
  257. struct pnm_signature signature;
  258. struct pnm_type *type;
  259. unsigned int i;
  260. /* Extract signature */
  261. assert ( image->len >= sizeof ( signature ) );
  262. copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
  263. /* Check for supported types */
  264. for ( i = 0 ; i < ( sizeof ( pnm_types ) /
  265. sizeof ( pnm_types[0] ) ) ; i++ ) {
  266. type = &pnm_types[i];
  267. if ( type->type == signature.type )
  268. return type;
  269. }
  270. return NULL;
  271. }
  272. /**
  273. * Convert PNM image to pixel buffer
  274. *
  275. * @v image PNM image
  276. * @v pixbuf Pixel buffer to fill in
  277. * @ret rc Return status code
  278. */
  279. static int pnm_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
  280. struct pnm_context pnm;
  281. int width;
  282. int height;
  283. int max;
  284. int rc;
  285. /* Initialise PNM context */
  286. pnm.type = pnm_type ( image );
  287. if ( ! pnm.type ) {
  288. rc = -ENOTSUP;
  289. goto err_type;
  290. }
  291. pnm.offset = sizeof ( struct pnm_signature );
  292. pnm.ascii_len = PNM_ASCII_LEN;
  293. /* Extract width */
  294. if ( ( width = pnm_ascii ( image, &pnm ) ) < 0 ) {
  295. rc = width;
  296. goto err_width;
  297. }
  298. /* Extract height */
  299. if ( ( height = pnm_ascii ( image, &pnm ) ) < 0 ) {
  300. rc = height;
  301. goto err_height;
  302. }
  303. /* Extract maximum scalar value, if not predefined */
  304. if ( pnm.type->flags & PNM_BITMAP ) {
  305. pnm.max = ( ( 1 << pnm.type->packing ) - 1 );
  306. pnm.ascii_len = 1;
  307. } else {
  308. if ( ( max = pnm_ascii ( image, &pnm ) ) < 0 ) {
  309. rc = max;
  310. goto err_max;
  311. }
  312. pnm.max = max;
  313. }
  314. if ( pnm.max == 0 ) {
  315. DBGC ( image, "PNM %s has invalid maximum value 0\n",
  316. image->name );
  317. rc = -EINVAL;
  318. goto err_max;
  319. }
  320. DBGC ( image, "PNM %s is type %c width %d height %d max %d\n",
  321. image->name, pnm.type->type, width, height, pnm.max );
  322. /* Allocate pixel buffer */
  323. *pixbuf = alloc_pixbuf ( width, height );
  324. if ( ! *pixbuf ) {
  325. rc = -ENOMEM;
  326. goto err_alloc_pixbuf;
  327. }
  328. /* Extract pixel data */
  329. if ( ( rc = pnm_data ( image, &pnm, *pixbuf ) ) != 0 )
  330. goto err_data;
  331. return 0;
  332. err_data:
  333. pixbuf_put ( *pixbuf );
  334. err_alloc_pixbuf:
  335. err_max:
  336. err_height:
  337. err_width:
  338. err_type:
  339. return rc;
  340. }
  341. /**
  342. * Probe PNM image
  343. *
  344. * @v image PNM image
  345. * @ret rc Return status code
  346. */
  347. static int pnm_probe ( struct image *image ) {
  348. struct pnm_signature signature;
  349. /* Sanity check */
  350. if ( image->len < sizeof ( signature ) ) {
  351. DBGC ( image, "PNM %s is too short\n", image->name );
  352. return -ENOEXEC;
  353. }
  354. /* Check signature */
  355. copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
  356. if ( ! ( ( signature.magic == PNM_MAGIC ) &&
  357. ( isdigit ( signature.type ) ) &&
  358. ( isspace ( signature.space ) ) ) ) {
  359. DBGC ( image, "PNM %s has invalid signature\n", image->name );
  360. return -ENOEXEC;
  361. }
  362. DBGC ( image, "PNM %s is type %c\n", image->name, signature.type );
  363. return 0;
  364. }
  365. /** PNM image type */
  366. struct image_type pnm_image_type __image_type ( PROBE_NORMAL ) = {
  367. .name = "PNM",
  368. .probe = pnm_probe,
  369. .pixbuf = pnm_pixbuf,
  370. };