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.

pnm.c 9.5KB

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