2 * GRUB -- GRand Unified Bootloader
3 * Copyright (C) 2008 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>
26 #include <grub/bufio.h>
28 /* Uncomment following define to enable PNG debug. */
31 #define PNG_COLOR_MASK_PALETTE 1
32 #define PNG_COLOR_MASK_COLOR 2
33 #define PNG_COLOR_MASK_ALPHA 4
35 #define PNG_COLOR_TYPE_GRAY 0
36 #define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
37 #define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
38 #define PNG_COLOR_TYPE_RGBA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
39 #define PNG_COLOR_TYPE_GRAYA (PNG_COLOR_MASK_ALPHA)
41 #define PNG_COMPRESSION_BASE 0
43 #define PNG_INTERLACE_NONE 0
44 #define PNG_INTERLACE_ADAM7 1
46 #define PNG_FILTER_TYPE_BASE 0
48 #define PNG_FILTER_VALUE_NONE 0
49 #define PNG_FILTER_VALUE_SUB 1
50 #define PNG_FILTER_VALUE_UP 2
51 #define PNG_FILTER_VALUE_AVG 3
52 #define PNG_FILTER_VALUE_PAETH 4
53 #define PNG_FILTER_VALUE_LAST 5
55 #define PNG_CHUNK_IHDR 0x49484452
56 #define PNG_CHUNK_IDAT 0x49444154
57 #define PNG_CHUNK_IEND 0x49454e44
60 #define Z_FLAG_DICT 32
62 #define INFLATE_STORED 0
63 #define INFLATE_FIXED 1
64 #define INFLATE_DYNAMIC 2
68 #define DEFLATE_HCLEN_BASE 4
69 #define DEFLATE_HCLEN_MAX 19
70 #define DEFLATE_HLIT_BASE 257
71 #define DEFLATE_HLIT_MAX 288
72 #define DEFLATE_HDIST_BASE 1
73 #define DEFLATE_HDIST_MAX 30
75 #define DEFLATE_HUFF_LEN 16
79 int *values
, *maxval
, *offset
;
80 int num_values
, max_length
;
86 struct grub_video_bitmap
**bitmap
;
88 int bit_count
, bit_save
;
90 grub_uint32_t next_offset
;
92 int image_width
, image_height
, bpp
, is_16bit
, raw_bytes
;
93 grub_uint8_t
*image_data
;
95 int inside_idat
, idat_remain
;
97 int code_values
[DEFLATE_HLIT_MAX
];
98 int code_maxval
[DEFLATE_HUFF_LEN
];
99 int code_offset
[DEFLATE_HUFF_LEN
];
101 int dist_values
[DEFLATE_HDIST_MAX
];
102 int dist_maxval
[DEFLATE_HUFF_LEN
];
103 int dist_offset
[DEFLATE_HUFF_LEN
];
105 struct huff_table code_table
;
106 struct huff_table dist_table
;
108 grub_uint8_t slide
[WSIZE
];
111 grub_uint8_t
*cur_rgb
;
113 int cur_colume
, cur_filter
, first_line
;
117 grub_png_get_dword (struct grub_png_data
*data
)
122 grub_file_read (data
->file
, (char *) &r
, sizeof (grub_uint32_t
));
124 return grub_be_to_cpu32 (r
);
128 grub_png_get_byte (struct grub_png_data
*data
)
132 if ((data
->inside_idat
) && (data
->idat_remain
== 0))
134 grub_uint32_t len
, type
;
138 /* Skip crc checksum. */
139 grub_png_get_dword (data
);
141 if (data
->file
->offset
!= data
->next_offset
)
143 grub_error (GRUB_ERR_BAD_FILE_TYPE
,
144 "png: chunk size error");
148 len
= grub_png_get_dword (data
);
149 type
= grub_png_get_dword (data
);
150 if (type
!= PNG_CHUNK_IDAT
)
152 grub_error (GRUB_ERR_BAD_FILE_TYPE
,
153 "png: unexpected end of data");
157 data
->next_offset
= data
->file
->offset
+ len
+ 4;
160 data
->idat_remain
= len
;
164 grub_file_read (data
->file
, (char *) &r
, 1);
166 if (data
->inside_idat
)
173 grub_png_get_bits (struct grub_png_data
*data
, int num
)
177 if (data
->bit_count
== 0)
179 data
->bit_save
= grub_png_get_byte (data
);
185 while (grub_errno
== 0)
193 code
+= (int) (data
->bit_save
& ((1 << n
) - 1)) << shift
;
197 data
->bit_count
-= n
;
198 data
->bit_save
>>= n
;
204 data
->bit_save
= grub_png_get_byte (data
);
212 grub_png_decode_image_header (struct grub_png_data
*data
)
217 data
->image_width
= grub_png_get_dword (data
);
218 data
->image_height
= grub_png_get_dword (data
);
220 if ((!data
->image_height
) || (!data
->image_width
))
221 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: invalid image size");
223 color_bits
= grub_png_get_byte (data
);
224 if ((color_bits
!= 8) && (color_bits
!= 16))
225 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
226 "png: bit depth must be 8 or 16");
227 data
->is_16bit
= (color_bits
== 16);
229 color_type
= grub_png_get_byte (data
);
230 if (color_type
== PNG_COLOR_TYPE_RGB
)
232 if (grub_video_bitmap_create (data
->bitmap
, data
->image_width
,
234 GRUB_VIDEO_BLIT_FORMAT_RGB_888
))
238 else if (color_type
== PNG_COLOR_TYPE_RGBA
)
240 if (grub_video_bitmap_create (data
->bitmap
, data
->image_width
,
242 GRUB_VIDEO_BLIT_FORMAT_RGBA_8888
))
247 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
248 "png: color type not supported");
254 data
->image_data
= grub_malloc (data
->image_height
*
255 data
->image_width
* data
->bpp
);
259 data
->cur_rgb
= data
->image_data
;
263 data
->image_data
= 0;
264 data
->cur_rgb
= (*data
->bitmap
)->data
;
267 data
->raw_bytes
= data
->image_height
* (data
->image_width
+ 1) * data
->bpp
;
269 data
->cur_colume
= 0;
270 data
->first_line
= 1;
272 if (grub_png_get_byte (data
) != PNG_COMPRESSION_BASE
)
273 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
274 "png: compression method not supported");
276 if (grub_png_get_byte (data
) != PNG_FILTER_TYPE_BASE
)
277 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
278 "png: filter method not supported");
280 if (grub_png_get_byte (data
) != PNG_INTERLACE_NONE
)
281 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
282 "png: interlace method not supported");
284 /* Skip crc checksum. */
285 grub_png_get_dword (data
);
290 /* Order of the bit length code lengths. */
291 static const grub_uint8_t bitorder
[] = {
292 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
295 /* Copy lengths for literal codes 257..285. */
296 static const int cplens
[] = {
297 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
298 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
301 /* Extra bits for literal codes 257..285. */
302 static const grub_uint8_t cplext
[] = {
303 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
304 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
307 /* Copy offsets for distance codes 0..29. */
308 static const int cpdist
[] = {
309 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
310 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
311 8193, 12289, 16385, 24577
314 /* Extra bits for distance codes. */
315 static const grub_uint8_t cpdext
[] = {
316 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
317 7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
322 grub_png_init_huff_table (struct huff_table
*ht
, int cur_maxlen
,
323 int *cur_values
, int *cur_maxval
, int *cur_offset
)
325 ht
->values
= cur_values
;
326 ht
->maxval
= cur_maxval
;
327 ht
->offset
= cur_offset
;
329 ht
->max_length
= cur_maxlen
;
330 grub_memset (cur_maxval
, 0, sizeof (int) * cur_maxlen
);
334 grub_png_insert_huff_item (struct huff_table
*ht
, int code
, int len
)
341 if (len
> ht
->max_length
)
343 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: invalid code length");
348 for (i
= len
; i
< ht
->max_length
; i
++)
351 for (i
= 0; i
< n
; i
++)
352 ht
->values
[ht
->num_values
- i
] = ht
->values
[ht
->num_values
- i
- 1];
354 ht
->values
[ht
->num_values
- n
] = code
;
356 ht
->maxval
[len
- 1]++;
360 grub_png_build_huff_table (struct huff_table
*ht
)
366 for (i
= 0; i
< ht
->max_length
; i
++)
368 base
+= ht
->maxval
[i
];
369 ofs
+= ht
->maxval
[i
];
371 ht
->maxval
[i
] = base
;
372 ht
->offset
[i
] = ofs
- base
;
379 grub_png_get_huff_code (struct grub_png_data
*data
, struct huff_table
*ht
)
384 for (i
= 0; i
< ht
->max_length
; i
++)
386 code
= (code
<< 1) + grub_png_get_bits (data
, 1);
387 if (code
< ht
->maxval
[i
])
388 return ht
->values
[code
+ ht
->offset
[i
]];
394 grub_png_init_fixed_block (struct grub_png_data
*data
)
398 grub_png_init_huff_table (&data
->code_table
, DEFLATE_HUFF_LEN
,
399 data
->code_values
, data
->code_maxval
,
402 for (i
= 0; i
< 144; i
++)
403 grub_png_insert_huff_item (&data
->code_table
, i
, 8);
406 grub_png_insert_huff_item (&data
->code_table
, i
, 9);
409 grub_png_insert_huff_item (&data
->code_table
, i
, 7);
411 for (; i
< DEFLATE_HLIT_MAX
; i
++)
412 grub_png_insert_huff_item (&data
->code_table
, i
, 8);
414 grub_png_build_huff_table (&data
->code_table
);
416 grub_png_init_huff_table (&data
->dist_table
, DEFLATE_HUFF_LEN
,
417 data
->dist_values
, data
->dist_maxval
,
420 for (i
= 0; i
< DEFLATE_HDIST_MAX
; i
++)
421 grub_png_insert_huff_item (&data
->dist_table
, i
, 5);
423 grub_png_build_huff_table (&data
->dist_table
);
429 grub_png_init_dynamic_block (struct grub_png_data
*data
)
431 int nl
, nd
, nb
, i
, prev
;
432 struct huff_table cl
;
433 int cl_values
[sizeof (bitorder
)];
436 grub_uint8_t lens
[DEFLATE_HCLEN_MAX
];
438 nl
= DEFLATE_HLIT_BASE
+ grub_png_get_bits (data
, 5);
439 nd
= DEFLATE_HDIST_BASE
+ grub_png_get_bits (data
, 5);
440 nb
= DEFLATE_HCLEN_BASE
+ grub_png_get_bits (data
, 4);
442 if ((nl
> DEFLATE_HLIT_MAX
) || (nd
> DEFLATE_HDIST_MAX
) ||
443 (nb
> DEFLATE_HCLEN_MAX
))
444 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: too much data");
446 grub_png_init_huff_table (&cl
, 8, cl_values
, cl_maxval
, cl_offset
);
448 for (i
= 0; i
< nb
; i
++)
449 lens
[bitorder
[i
]] = grub_png_get_bits (data
, 3);
451 for (; i
< DEFLATE_HCLEN_MAX
; i
++)
452 lens
[bitorder
[i
]] = 0;
454 for (i
= 0; i
< DEFLATE_HCLEN_MAX
; i
++)
455 grub_png_insert_huff_item (&cl
, i
, lens
[i
]);
457 grub_png_build_huff_table (&cl
);
459 grub_png_init_huff_table (&data
->code_table
, DEFLATE_HUFF_LEN
,
460 data
->code_values
, data
->code_maxval
,
463 grub_png_init_huff_table (&data
->dist_table
, DEFLATE_HUFF_LEN
,
464 data
->dist_values
, data
->dist_maxval
,
468 for (i
= 0; i
< nl
+ nd
; i
++)
471 struct huff_table
*ht
;
478 ht
= &data
->code_table
;
483 ht
= &data
->dist_table
;
487 n
= grub_png_get_huff_code (data
, &cl
);
490 grub_png_insert_huff_item (ht
, code
, n
);
497 c
= 3 + grub_png_get_bits (data
, 2);
500 grub_png_insert_huff_item (ht
, code
++, prev
);
507 i
+= 3 + grub_png_get_bits (data
, 3) - 1;
509 i
+= 11 + grub_png_get_bits (data
, 7) - 1;
512 grub_png_build_huff_table (&data
->code_table
);
513 grub_png_build_huff_table (&data
->dist_table
);
519 grub_png_output_byte (struct grub_png_data
*data
, grub_uint8_t n
)
523 if (--data
->raw_bytes
< 0)
524 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "image size overflown");
526 if (data
->cur_colume
== 0)
528 if (n
>= PNG_FILTER_VALUE_LAST
)
529 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid filter value");
531 data
->cur_filter
= n
;
534 *(data
->cur_rgb
++) = n
;
537 row_bytes
= data
->image_width
* data
->bpp
;
538 if (data
->cur_colume
== row_bytes
+ 1)
540 grub_uint8_t
*blank_line
= NULL
;
541 grub_uint8_t
*cur
= data
->cur_rgb
- row_bytes
;
542 grub_uint8_t
*left
= cur
;
545 if (data
->first_line
)
547 blank_line
= grub_malloc (row_bytes
);
548 if (blank_line
== NULL
)
551 grub_memset (blank_line
, 0, row_bytes
);
555 up
= cur
- row_bytes
;
557 switch (data
->cur_filter
)
559 case PNG_FILTER_VALUE_SUB
:
564 for (i
= data
->bpp
; i
< row_bytes
; i
++, cur
++, left
++)
569 case PNG_FILTER_VALUE_UP
:
573 for (i
= 0; i
< row_bytes
; i
++, cur
++, up
++)
578 case PNG_FILTER_VALUE_AVG
:
582 for (i
= 0; i
< data
->bpp
; i
++, cur
++, up
++)
585 for (; i
< row_bytes
; i
++, cur
++, up
++, left
++)
586 *cur
+= ((int) *up
+ (int) *left
) >> 1;
590 case PNG_FILTER_VALUE_PAETH
:
593 grub_uint8_t
*upper_left
= up
;
595 for (i
= 0; i
< data
->bpp
; i
++, cur
++, up
++)
598 for (; i
< row_bytes
; i
++, cur
++, up
++, left
++, upper_left
++)
600 int a
, b
, c
, pa
, pb
, pc
;
619 *cur
+= ((pa
<= pb
) && (pa
<= pc
)) ? a
: (pb
<= pc
) ? b
: c
;
625 grub_free (blank_line
);
627 data
->cur_colume
= 0;
628 data
->first_line
= 0;
635 grub_png_read_dynamic_block (struct grub_png_data
*data
)
637 while (grub_errno
== 0)
641 n
= grub_png_get_huff_code (data
, &data
->code_table
);
644 data
->slide
[data
->wp
] = n
;
645 grub_png_output_byte (data
, n
);
648 if (data
->wp
>= WSIZE
)
660 len
+= grub_png_get_bits (data
, cplext
[n
]);
662 n
= grub_png_get_huff_code (data
, &data
->dist_table
);
665 dist
+= grub_png_get_bits (data
, cpdext
[n
]);
667 pos
= data
->wp
- dist
;
673 data
->slide
[data
->wp
] = data
->slide
[pos
];
674 grub_png_output_byte (data
, data
->slide
[data
->wp
]);
677 if (data
->wp
>= WSIZE
)
693 grub_png_decode_image_data (struct grub_png_data
*data
)
695 grub_uint8_t cmf
, flg
;
698 cmf
= grub_png_get_byte (data
);
699 flg
= grub_png_get_byte (data
);
701 if ((cmf
& 0xF) != Z_DEFLATED
)
702 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
703 "png: only support deflate compression method");
705 if (flg
& Z_FLAG_DICT
)
706 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
707 "png: dictionary not supported");
713 final
= grub_png_get_bits (data
, 1);
714 block_type
= grub_png_get_bits (data
, 2);
720 grub_uint16_t i
, len
;
723 len
= grub_png_get_byte (data
);
724 len
+= ((grub_uint16_t
) grub_png_get_byte (data
)) << 8;
726 /* Skip NLEN field. */
727 grub_png_get_byte (data
);
728 grub_png_get_byte (data
);
730 for (i
= 0; i
< len
; i
++)
731 grub_png_output_byte (data
, grub_png_get_byte (data
));
737 grub_png_init_fixed_block (data
);
738 grub_png_read_dynamic_block (data
);
741 case INFLATE_DYNAMIC
:
742 grub_png_init_dynamic_block (data
);
743 grub_png_read_dynamic_block (data
);
747 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
748 "png: unknown block type");
751 while ((!final
) && (grub_errno
== 0));
753 /* Skip adler checksum. */
754 grub_png_get_dword (data
);
756 /* Skip crc checksum. */
757 grub_png_get_dword (data
);
762 static const grub_uint8_t png_magic
[8] =
763 { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a };
766 grub_png_convert_image (struct grub_png_data
*data
)
769 grub_uint8_t
*d1
, *d2
;
771 d1
= (*data
->bitmap
)->data
;
772 d2
= data
->image_data
+ 1;
774 /* Only copy the upper 8 bit. */
775 for (i
= 0; i
< (data
->image_width
* data
->image_height
* data
->bpp
>> 1);
781 grub_png_decode_png (struct grub_png_data
*data
)
783 grub_uint8_t magic
[8];
785 if (grub_file_read (data
->file
, (char *) &magic
[0], 8) != 8)
788 if (grub_memcmp (magic
, png_magic
, sizeof (png_magic
)))
789 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: not a png file");
793 grub_uint32_t len
, type
;
795 len
= grub_png_get_dword (data
);
796 type
= grub_png_get_dword (data
);
797 data
->next_offset
= data
->file
->offset
+ len
+ 4;
802 grub_png_decode_image_header (data
);
806 data
->inside_idat
= 1;
807 data
->idat_remain
= len
;
810 grub_png_decode_image_data (data
);
812 data
->inside_idat
= 0;
817 grub_png_convert_image (data
);
822 grub_file_seek (data
->file
, data
->file
->offset
+ len
+ 4);
828 if (data
->file
->offset
!= data
->next_offset
)
829 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
830 "png: chunk size error");
837 grub_video_reader_png (struct grub_video_bitmap
**bitmap
,
838 const char *filename
)
841 struct grub_png_data
*data
;
843 file
= grub_buffile_open (filename
, 0);
847 data
= grub_malloc (sizeof (*data
));
850 grub_memset (data
, 0, sizeof (*data
));
852 data
->bitmap
= bitmap
;
854 grub_png_decode_png (data
);
856 grub_free (data
->image_data
);
860 if (grub_errno
!= GRUB_ERR_NONE
)
862 grub_video_bitmap_destroy (*bitmap
);
866 grub_file_close (file
);
870 #if defined(PNG_DEBUG)
872 grub_cmd_pngtest (struct grub_arg_list
*state
__attribute__ ((unused
)),
873 int argc
, char **args
)
875 struct grub_video_bitmap
*bitmap
= 0;
878 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
880 grub_video_reader_png (&bitmap
, args
[0]);
881 if (grub_errno
!= GRUB_ERR_NONE
)
884 grub_video_bitmap_destroy (bitmap
);
886 return GRUB_ERR_NONE
;
890 static struct grub_video_bitmap_reader png_reader
= {
892 .reader
= grub_video_reader_png
,
896 GRUB_MOD_INIT (video_reader_png
)
898 grub_video_bitmap_reader_register (&png_reader
);
899 #if defined(PNG_DEBUG)
900 grub_register_command ("pngtest", grub_cmd_pngtest
,
901 GRUB_COMMAND_FLAG_BOTH
, "pngtest FILE",
902 "Tests loading of PNG bitmap.", 0);
906 GRUB_MOD_FINI (video_reader_png
)
908 #if defined(PNG_DEBUG)
909 grub_unregister_command ("pngtest");
911 grub_video_bitmap_reader_unregister (&png_reader
);