2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2008,2009 Free Software Foundation, Inc.
5 * GRUB is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * GRUB is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
19 #include <grub/bitmap.h>
20 #include <grub/types.h>
21 #include <grub/normal.h>
24 #include <grub/misc.h>
25 #include <grub/bufio.h>
27 GRUB_MOD_LICENSE ("GPLv3+");
29 /* Uncomment following define to enable PNG debug. */
32 #define PNG_COLOR_MASK_PALETTE 1
33 #define PNG_COLOR_MASK_COLOR 2
34 #define PNG_COLOR_MASK_ALPHA 4
36 #define PNG_COLOR_TYPE_GRAY 0
37 #define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
38 #define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
39 #define PNG_COLOR_TYPE_RGBA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
40 #define PNG_COLOR_TYPE_GRAYA (PNG_COLOR_MASK_ALPHA)
42 #define PNG_COMPRESSION_BASE 0
44 #define PNG_INTERLACE_NONE 0
45 #define PNG_INTERLACE_ADAM7 1
47 #define PNG_FILTER_TYPE_BASE 0
49 #define PNG_FILTER_VALUE_NONE 0
50 #define PNG_FILTER_VALUE_SUB 1
51 #define PNG_FILTER_VALUE_UP 2
52 #define PNG_FILTER_VALUE_AVG 3
53 #define PNG_FILTER_VALUE_PAETH 4
54 #define PNG_FILTER_VALUE_LAST 5
56 #define PNG_CHUNK_IHDR 0x49484452
57 #define PNG_CHUNK_IDAT 0x49444154
58 #define PNG_CHUNK_IEND 0x49454e44
61 #define Z_FLAG_DICT 32
63 #define INFLATE_STORED 0
64 #define INFLATE_FIXED 1
65 #define INFLATE_DYNAMIC 2
69 #define DEFLATE_HCLEN_BASE 4
70 #define DEFLATE_HCLEN_MAX 19
71 #define DEFLATE_HLIT_BASE 257
72 #define DEFLATE_HLIT_MAX 288
73 #define DEFLATE_HDIST_BASE 1
74 #define DEFLATE_HDIST_MAX 30
76 #define DEFLATE_HUFF_LEN 16
79 static grub_command_t cmd
;
84 int *values
, *maxval
, *offset
;
85 int num_values
, max_length
;
91 struct grub_video_bitmap
**bitmap
;
93 int bit_count
, bit_save
;
95 grub_uint32_t next_offset
;
97 int image_width
, image_height
, bpp
, is_16bit
, raw_bytes
;
98 grub_uint8_t
*image_data
;
100 int inside_idat
, idat_remain
;
102 int code_values
[DEFLATE_HLIT_MAX
];
103 int code_maxval
[DEFLATE_HUFF_LEN
];
104 int code_offset
[DEFLATE_HUFF_LEN
];
106 int dist_values
[DEFLATE_HDIST_MAX
];
107 int dist_maxval
[DEFLATE_HUFF_LEN
];
108 int dist_offset
[DEFLATE_HUFF_LEN
];
110 struct huff_table code_table
;
111 struct huff_table dist_table
;
113 grub_uint8_t slide
[WSIZE
];
116 grub_uint8_t
*cur_rgb
;
118 int cur_column
, cur_filter
, first_line
;
122 grub_png_get_dword (struct grub_png_data
*data
)
127 grub_file_read (data
->file
, &r
, sizeof (grub_uint32_t
));
129 return grub_be_to_cpu32 (r
);
133 grub_png_get_byte (struct grub_png_data
*data
)
137 if ((data
->inside_idat
) && (data
->idat_remain
== 0))
139 grub_uint32_t len
, type
;
143 /* Skip crc checksum. */
144 grub_png_get_dword (data
);
146 if (data
->file
->offset
!= data
->next_offset
)
148 grub_error (GRUB_ERR_BAD_FILE_TYPE
,
149 "png: chunk size error");
153 len
= grub_png_get_dword (data
);
154 type
= grub_png_get_dword (data
);
155 if (type
!= PNG_CHUNK_IDAT
)
157 grub_error (GRUB_ERR_BAD_FILE_TYPE
,
158 "png: unexpected end of data");
162 data
->next_offset
= data
->file
->offset
+ len
+ 4;
165 data
->idat_remain
= len
;
169 grub_file_read (data
->file
, &r
, 1);
171 if (data
->inside_idat
)
178 grub_png_get_bits (struct grub_png_data
*data
, int num
)
182 if (data
->bit_count
== 0)
184 data
->bit_save
= grub_png_get_byte (data
);
190 while (grub_errno
== 0)
198 code
+= (int) (data
->bit_save
& ((1 << n
) - 1)) << shift
;
202 data
->bit_count
-= n
;
203 data
->bit_save
>>= n
;
209 data
->bit_save
= grub_png_get_byte (data
);
217 grub_png_decode_image_header (struct grub_png_data
*data
)
222 data
->image_width
= grub_png_get_dword (data
);
223 data
->image_height
= grub_png_get_dword (data
);
225 if ((!data
->image_height
) || (!data
->image_width
))
226 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: invalid image size");
228 color_bits
= grub_png_get_byte (data
);
229 if ((color_bits
!= 8) && (color_bits
!= 16))
230 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
231 "png: bit depth must be 8 or 16");
232 data
->is_16bit
= (color_bits
== 16);
234 color_type
= grub_png_get_byte (data
);
235 if (color_type
== PNG_COLOR_TYPE_RGB
)
237 if (grub_video_bitmap_create (data
->bitmap
, data
->image_width
,
239 GRUB_VIDEO_BLIT_FORMAT_RGB_888
))
243 else if (color_type
== PNG_COLOR_TYPE_RGBA
)
245 if (grub_video_bitmap_create (data
->bitmap
, data
->image_width
,
247 GRUB_VIDEO_BLIT_FORMAT_RGBA_8888
))
252 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
253 "png: color type not supported");
259 data
->image_data
= grub_malloc (data
->image_height
*
260 data
->image_width
* data
->bpp
);
264 data
->cur_rgb
= data
->image_data
;
268 data
->image_data
= 0;
269 data
->cur_rgb
= (*data
->bitmap
)->data
;
272 data
->raw_bytes
= data
->image_height
* (data
->image_width
+ 1) * data
->bpp
;
274 data
->cur_column
= 0;
275 data
->first_line
= 1;
277 if (grub_png_get_byte (data
) != PNG_COMPRESSION_BASE
)
278 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
279 "png: compression method not supported");
281 if (grub_png_get_byte (data
) != PNG_FILTER_TYPE_BASE
)
282 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
283 "png: filter method not supported");
285 if (grub_png_get_byte (data
) != PNG_INTERLACE_NONE
)
286 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
287 "png: interlace method not supported");
289 /* Skip crc checksum. */
290 grub_png_get_dword (data
);
295 /* Order of the bit length code lengths. */
296 static const grub_uint8_t bitorder
[] = {
297 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
300 /* Copy lengths for literal codes 257..285. */
301 static const int cplens
[] = {
302 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
303 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
306 /* Extra bits for literal codes 257..285. */
307 static const grub_uint8_t cplext
[] = {
308 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
309 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
312 /* Copy offsets for distance codes 0..29. */
313 static const int cpdist
[] = {
314 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
315 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
316 8193, 12289, 16385, 24577
319 /* Extra bits for distance codes. */
320 static const grub_uint8_t cpdext
[] = {
321 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
322 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
327 grub_png_init_huff_table (struct huff_table
*ht
, int cur_maxlen
,
328 int *cur_values
, int *cur_maxval
, int *cur_offset
)
330 ht
->values
= cur_values
;
331 ht
->maxval
= cur_maxval
;
332 ht
->offset
= cur_offset
;
334 ht
->max_length
= cur_maxlen
;
335 grub_memset (cur_maxval
, 0, sizeof (int) * cur_maxlen
);
339 grub_png_insert_huff_item (struct huff_table
*ht
, int code
, int len
)
346 if (len
> ht
->max_length
)
348 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: invalid code length");
353 for (i
= len
; i
< ht
->max_length
; i
++)
356 for (i
= 0; i
< n
; i
++)
357 ht
->values
[ht
->num_values
- i
] = ht
->values
[ht
->num_values
- i
- 1];
359 ht
->values
[ht
->num_values
- n
] = code
;
361 ht
->maxval
[len
- 1]++;
365 grub_png_build_huff_table (struct huff_table
*ht
)
371 for (i
= 0; i
< ht
->max_length
; i
++)
373 base
+= ht
->maxval
[i
];
374 ofs
+= ht
->maxval
[i
];
376 ht
->maxval
[i
] = base
;
377 ht
->offset
[i
] = ofs
- base
;
384 grub_png_get_huff_code (struct grub_png_data
*data
, struct huff_table
*ht
)
389 for (i
= 0; i
< ht
->max_length
; i
++)
391 code
= (code
<< 1) + grub_png_get_bits (data
, 1);
392 if (code
< ht
->maxval
[i
])
393 return ht
->values
[code
+ ht
->offset
[i
]];
399 grub_png_init_fixed_block (struct grub_png_data
*data
)
403 grub_png_init_huff_table (&data
->code_table
, DEFLATE_HUFF_LEN
,
404 data
->code_values
, data
->code_maxval
,
407 for (i
= 0; i
< 144; i
++)
408 grub_png_insert_huff_item (&data
->code_table
, i
, 8);
411 grub_png_insert_huff_item (&data
->code_table
, i
, 9);
414 grub_png_insert_huff_item (&data
->code_table
, i
, 7);
416 for (; i
< DEFLATE_HLIT_MAX
; i
++)
417 grub_png_insert_huff_item (&data
->code_table
, i
, 8);
419 grub_png_build_huff_table (&data
->code_table
);
421 grub_png_init_huff_table (&data
->dist_table
, DEFLATE_HUFF_LEN
,
422 data
->dist_values
, data
->dist_maxval
,
425 for (i
= 0; i
< DEFLATE_HDIST_MAX
; i
++)
426 grub_png_insert_huff_item (&data
->dist_table
, i
, 5);
428 grub_png_build_huff_table (&data
->dist_table
);
434 grub_png_init_dynamic_block (struct grub_png_data
*data
)
436 int nl
, nd
, nb
, i
, prev
;
437 struct huff_table cl
;
438 int cl_values
[sizeof (bitorder
)];
441 grub_uint8_t lens
[DEFLATE_HCLEN_MAX
];
443 nl
= DEFLATE_HLIT_BASE
+ grub_png_get_bits (data
, 5);
444 nd
= DEFLATE_HDIST_BASE
+ grub_png_get_bits (data
, 5);
445 nb
= DEFLATE_HCLEN_BASE
+ grub_png_get_bits (data
, 4);
447 if ((nl
> DEFLATE_HLIT_MAX
) || (nd
> DEFLATE_HDIST_MAX
) ||
448 (nb
> DEFLATE_HCLEN_MAX
))
449 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: too much data");
451 grub_png_init_huff_table (&cl
, 8, cl_values
, cl_maxval
, cl_offset
);
453 for (i
= 0; i
< nb
; i
++)
454 lens
[bitorder
[i
]] = grub_png_get_bits (data
, 3);
456 for (; i
< DEFLATE_HCLEN_MAX
; i
++)
457 lens
[bitorder
[i
]] = 0;
459 for (i
= 0; i
< DEFLATE_HCLEN_MAX
; i
++)
460 grub_png_insert_huff_item (&cl
, i
, lens
[i
]);
462 grub_png_build_huff_table (&cl
);
464 grub_png_init_huff_table (&data
->code_table
, DEFLATE_HUFF_LEN
,
465 data
->code_values
, data
->code_maxval
,
468 grub_png_init_huff_table (&data
->dist_table
, DEFLATE_HUFF_LEN
,
469 data
->dist_values
, data
->dist_maxval
,
473 for (i
= 0; i
< nl
+ nd
; i
++)
476 struct huff_table
*ht
;
483 ht
= &data
->code_table
;
488 ht
= &data
->dist_table
;
492 n
= grub_png_get_huff_code (data
, &cl
);
495 grub_png_insert_huff_item (ht
, code
, n
);
502 c
= 3 + grub_png_get_bits (data
, 2);
505 grub_png_insert_huff_item (ht
, code
++, prev
);
512 i
+= 3 + grub_png_get_bits (data
, 3) - 1;
514 i
+= 11 + grub_png_get_bits (data
, 7) - 1;
517 grub_png_build_huff_table (&data
->code_table
);
518 grub_png_build_huff_table (&data
->dist_table
);
524 grub_png_output_byte (struct grub_png_data
*data
, grub_uint8_t n
)
528 if (--data
->raw_bytes
< 0)
529 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "image size overflown");
531 if (data
->cur_column
== 0)
533 if (n
>= PNG_FILTER_VALUE_LAST
)
534 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid filter value");
536 data
->cur_filter
= n
;
539 *(data
->cur_rgb
++) = n
;
542 row_bytes
= data
->image_width
* data
->bpp
;
543 if (data
->cur_column
== row_bytes
+ 1)
545 grub_uint8_t
*blank_line
= NULL
;
546 grub_uint8_t
*cur
= data
->cur_rgb
- row_bytes
;
547 grub_uint8_t
*left
= cur
;
550 if (data
->first_line
)
552 blank_line
= grub_zalloc (row_bytes
);
553 if (blank_line
== NULL
)
559 up
= cur
- row_bytes
;
561 switch (data
->cur_filter
)
563 case PNG_FILTER_VALUE_SUB
:
568 for (i
= data
->bpp
; i
< row_bytes
; i
++, cur
++, left
++)
573 case PNG_FILTER_VALUE_UP
:
577 for (i
= 0; i
< row_bytes
; i
++, cur
++, up
++)
582 case PNG_FILTER_VALUE_AVG
:
586 for (i
= 0; i
< data
->bpp
; i
++, cur
++, up
++)
589 for (; i
< row_bytes
; i
++, cur
++, up
++, left
++)
590 *cur
+= ((int) *up
+ (int) *left
) >> 1;
594 case PNG_FILTER_VALUE_PAETH
:
597 grub_uint8_t
*upper_left
= up
;
599 for (i
= 0; i
< data
->bpp
; i
++, cur
++, up
++)
602 for (; i
< row_bytes
; i
++, cur
++, up
++, left
++, upper_left
++)
604 int a
, b
, c
, pa
, pb
, pc
;
623 *cur
+= ((pa
<= pb
) && (pa
<= pc
)) ? a
: (pb
<= pc
) ? b
: c
;
628 grub_free (blank_line
);
630 data
->cur_column
= 0;
631 data
->first_line
= 0;
638 grub_png_read_dynamic_block (struct grub_png_data
*data
)
640 while (grub_errno
== 0)
644 n
= grub_png_get_huff_code (data
, &data
->code_table
);
647 data
->slide
[data
->wp
] = n
;
648 grub_png_output_byte (data
, n
);
651 if (data
->wp
>= WSIZE
)
663 len
+= grub_png_get_bits (data
, cplext
[n
]);
665 n
= grub_png_get_huff_code (data
, &data
->dist_table
);
668 dist
+= grub_png_get_bits (data
, cpdext
[n
]);
670 pos
= data
->wp
- dist
;
676 data
->slide
[data
->wp
] = data
->slide
[pos
];
677 grub_png_output_byte (data
, data
->slide
[data
->wp
]);
680 if (data
->wp
>= WSIZE
)
696 grub_png_decode_image_data (struct grub_png_data
*data
)
698 grub_uint8_t cmf
, flg
;
701 cmf
= grub_png_get_byte (data
);
702 flg
= grub_png_get_byte (data
);
704 if ((cmf
& 0xF) != Z_DEFLATED
)
705 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
706 "png: only support deflate compression method");
708 if (flg
& Z_FLAG_DICT
)
709 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
710 "png: dictionary not supported");
716 final
= grub_png_get_bits (data
, 1);
717 block_type
= grub_png_get_bits (data
, 2);
723 grub_uint16_t i
, len
;
726 len
= grub_png_get_byte (data
);
727 len
+= ((grub_uint16_t
) grub_png_get_byte (data
)) << 8;
729 /* Skip NLEN field. */
730 grub_png_get_byte (data
);
731 grub_png_get_byte (data
);
733 for (i
= 0; i
< len
; i
++)
734 grub_png_output_byte (data
, grub_png_get_byte (data
));
740 grub_png_init_fixed_block (data
);
741 grub_png_read_dynamic_block (data
);
744 case INFLATE_DYNAMIC
:
745 grub_png_init_dynamic_block (data
);
746 grub_png_read_dynamic_block (data
);
750 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
751 "png: unknown block type");
754 while ((!final
) && (grub_errno
== 0));
756 /* Skip adler checksum. */
757 grub_png_get_dword (data
);
759 /* Skip crc checksum. */
760 grub_png_get_dword (data
);
765 static const grub_uint8_t png_magic
[8] =
766 { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a };
769 grub_png_convert_image (struct grub_png_data
*data
)
772 grub_uint8_t
*d1
, *d2
;
774 d1
= (*data
->bitmap
)->data
;
775 d2
= data
->image_data
+ 1;
777 /* Only copy the upper 8 bit. */
778 for (i
= 0; i
< (data
->image_width
* data
->image_height
* data
->bpp
>> 1);
784 grub_png_decode_png (struct grub_png_data
*data
)
786 grub_uint8_t magic
[8];
788 if (grub_file_read (data
->file
, &magic
[0], 8) != 8)
791 if (grub_memcmp (magic
, png_magic
, sizeof (png_magic
)))
792 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: not a png file");
796 grub_uint32_t len
, type
;
798 len
= grub_png_get_dword (data
);
799 type
= grub_png_get_dword (data
);
800 data
->next_offset
= data
->file
->offset
+ len
+ 4;
805 grub_png_decode_image_header (data
);
809 data
->inside_idat
= 1;
810 data
->idat_remain
= len
;
813 grub_png_decode_image_data (data
);
815 data
->inside_idat
= 0;
820 grub_png_convert_image (data
);
825 grub_file_seek (data
->file
, data
->file
->offset
+ len
+ 4);
831 if (data
->file
->offset
!= data
->next_offset
)
832 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
833 "png: chunk size error");
840 grub_video_reader_png (struct grub_video_bitmap
**bitmap
,
841 const char *filename
)
844 struct grub_png_data
*data
;
846 file
= grub_buffile_open (filename
, 0);
850 data
= grub_zalloc (sizeof (*data
));
854 data
->bitmap
= bitmap
;
856 grub_png_decode_png (data
);
858 grub_free (data
->image_data
);
862 if (grub_errno
!= GRUB_ERR_NONE
)
864 grub_video_bitmap_destroy (*bitmap
);
868 grub_file_close (file
);
872 #if defined(PNG_DEBUG)
874 grub_cmd_pngtest (grub_command_t cmd
__attribute__ ((unused
)),
875 int argc
, char **args
)
877 struct grub_video_bitmap
*bitmap
= 0;
880 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("filename expected"));
882 grub_video_reader_png (&bitmap
, args
[0]);
883 if (grub_errno
!= GRUB_ERR_NONE
)
886 grub_video_bitmap_destroy (bitmap
);
888 return GRUB_ERR_NONE
;
892 static struct grub_video_bitmap_reader png_reader
= {
894 .reader
= grub_video_reader_png
,
900 grub_video_bitmap_reader_register (&png_reader
);
901 #if defined(PNG_DEBUG)
902 cmd
= grub_register_command ("pngtest", grub_cmd_pngtest
,
904 "Tests loading of PNG bitmap.");
910 #if defined(PNG_DEBUG)
911 grub_unregister_command (cmd
);
913 grub_video_bitmap_reader_unregister (&png_reader
);