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/file.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 286
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_R8G8B8
))
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_R8G8B8A8
))
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_dynamic_block (struct grub_png_data
*data
)
396 int nl
, nd
, nb
, i
, prev
;
397 struct huff_table cl
;
398 int cl_values
[sizeof (bitorder
)];
401 grub_uint8_t lens
[DEFLATE_HCLEN_MAX
];
403 nl
= DEFLATE_HLIT_BASE
+ grub_png_get_bits (data
, 5);
404 nd
= DEFLATE_HDIST_BASE
+ grub_png_get_bits (data
, 5);
405 nb
= DEFLATE_HCLEN_BASE
+ grub_png_get_bits (data
, 4);
407 if ((nl
> DEFLATE_HLIT_MAX
) || (nd
> DEFLATE_HDIST_MAX
) ||
408 (nb
> DEFLATE_HCLEN_MAX
))
409 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: too much data");
411 grub_png_init_huff_table (&cl
, 8, cl_values
, cl_maxval
, cl_offset
);
413 for (i
= 0; i
< nb
; i
++)
414 lens
[bitorder
[i
]] = grub_png_get_bits (data
, 3);
416 for (; i
< DEFLATE_HCLEN_MAX
; i
++)
417 lens
[bitorder
[i
]] = 0;
419 for (i
= 0; i
< DEFLATE_HCLEN_MAX
; i
++)
420 grub_png_insert_huff_item (&cl
, i
, lens
[i
]);
422 grub_png_build_huff_table (&cl
);
424 grub_png_init_huff_table (&data
->code_table
, DEFLATE_HUFF_LEN
,
425 data
->code_values
, data
->code_maxval
,
428 grub_png_init_huff_table (&data
->dist_table
, DEFLATE_HUFF_LEN
,
429 data
->dist_values
, data
->dist_maxval
,
433 for (i
= 0; i
< nl
+ nd
; i
++)
436 struct huff_table
*ht
;
443 ht
= &data
->code_table
;
448 ht
= &data
->dist_table
;
452 n
= grub_png_get_huff_code (data
, &cl
);
455 grub_png_insert_huff_item (ht
, code
, n
);
462 c
= 3 + grub_png_get_bits (data
, 2);
465 grub_png_insert_huff_item (ht
, code
++, prev
);
472 i
+= 3 + grub_png_get_bits (data
, 3) - 1;
474 i
+= 11 + grub_png_get_bits (data
, 7) - 1;
477 grub_png_build_huff_table (&data
->code_table
);
478 grub_png_build_huff_table (&data
->dist_table
);
484 grub_png_output_byte (struct grub_png_data
*data
, grub_uint8_t n
)
488 if (--data
->raw_bytes
< 0)
489 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "image size overflown");
491 if (data
->cur_colume
== 0)
493 if (n
>= PNG_FILTER_VALUE_LAST
)
494 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "invalid filter value");
496 data
->cur_filter
= n
;
499 *(data
->cur_rgb
++) = n
;
502 row_bytes
= data
->image_width
* data
->bpp
;
503 if (data
->cur_colume
== row_bytes
+ 1)
505 grub_uint8_t
*blank_line
= NULL
;
506 grub_uint8_t
*cur
= data
->cur_rgb
- row_bytes
;
507 grub_uint8_t
*left
= cur
;
510 if (data
->first_line
)
512 blank_line
= grub_malloc (row_bytes
);
513 if (blank_line
== NULL
)
516 grub_memset (blank_line
, 0, row_bytes
);
520 up
= cur
- row_bytes
;
522 switch (data
->cur_filter
)
524 case PNG_FILTER_VALUE_SUB
:
529 for (i
= data
->bpp
; i
< row_bytes
; i
++, cur
++, left
++)
534 case PNG_FILTER_VALUE_UP
:
538 for (i
= 0; i
< row_bytes
; i
++, cur
++, up
++)
543 case PNG_FILTER_VALUE_AVG
:
547 for (i
= 0; i
< data
->bpp
; i
++, cur
++, up
++)
550 for (; i
< row_bytes
; i
++, cur
++, up
++, left
++)
551 *cur
+= ((int) *up
+ (int) *left
) >> 1;
555 case PNG_FILTER_VALUE_PAETH
:
558 grub_uint8_t
*upper_left
= up
;
560 for (i
= 0; i
< data
->bpp
; i
++, cur
++, up
++)
563 for (; i
< row_bytes
; i
++, cur
++, up
++, left
++, upper_left
++)
565 int a
, b
, c
, pa
, pb
, pc
;
584 *cur
+= ((pa
<= pb
) && (pa
<= pc
)) ? a
: (pb
<= pc
) ? b
: c
;
590 grub_free (blank_line
);
592 data
->cur_colume
= 0;
593 data
->first_line
= 0;
600 grub_png_read_dynamic_block (struct grub_png_data
*data
)
602 while (grub_errno
== 0)
606 n
= grub_png_get_huff_code (data
, &data
->code_table
);
609 data
->slide
[data
->wp
] = n
;
610 grub_png_output_byte (data
, n
);
613 if (data
->wp
>= WSIZE
)
625 len
+= grub_png_get_bits (data
, cplext
[n
]);
627 n
= grub_png_get_huff_code (data
, &data
->dist_table
);
630 dist
+= grub_png_get_bits (data
, cpdext
[n
]);
632 pos
= data
->wp
- dist
;
638 data
->slide
[data
->wp
] = data
->slide
[pos
];
639 grub_png_output_byte (data
, data
->slide
[data
->wp
]);
642 if (data
->wp
>= WSIZE
)
658 grub_png_decode_image_data (struct grub_png_data
*data
)
660 grub_uint8_t cmf
, flg
;
663 cmf
= grub_png_get_byte (data
);
664 flg
= grub_png_get_byte (data
);
666 if ((cmf
& 0xF) != Z_DEFLATED
)
667 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
668 "png: only support deflate compression method");
670 if (flg
& Z_FLAG_DICT
)
671 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
672 "png: dictionary not supported");
678 final
= grub_png_get_bits (data
, 1);
679 block_type
= grub_png_get_bits (data
, 2);
685 grub_uint16_t i
, len
;
688 len
= grub_png_get_byte (data
);
689 len
+= ((grub_uint16_t
) grub_png_get_byte (data
)) << 8;
691 /* Skip NLEN field. */
692 grub_png_get_byte (data
);
693 grub_png_get_byte (data
);
695 for (i
= 0; i
< len
; i
++)
696 grub_png_output_byte (data
, grub_png_get_byte (data
));
702 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
703 "png: block type fixed not supported");
705 case INFLATE_DYNAMIC
:
706 grub_png_init_dynamic_block (data
);
707 grub_png_read_dynamic_block (data
);
711 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
712 "png: unknown block type");
715 while ((!final
) && (grub_errno
== 0));
717 /* Skip adler checksum. */
718 grub_png_get_dword (data
);
720 /* Skip crc checksum. */
721 grub_png_get_dword (data
);
726 static const grub_uint8_t png_magic
[8] =
727 { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a };
730 grub_png_convert_image (struct grub_png_data
*data
)
733 grub_uint8_t
*d1
, *d2
;
735 d1
= (*data
->bitmap
)->data
;
736 d2
= data
->image_data
+ 1;
738 /* Only copy the upper 8 bit. */
739 for (i
= 0; i
< (data
->image_width
* data
->image_height
* data
->bpp
>> 1);
745 grub_png_decode_png (struct grub_png_data
*data
)
747 grub_uint8_t magic
[8];
749 if (grub_file_read (data
->file
, (char *) &magic
[0], 8) != 8)
752 if (grub_memcmp (magic
, png_magic
, sizeof (png_magic
)))
753 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "png: not a png file");
757 grub_uint32_t len
, type
;
759 len
= grub_png_get_dword (data
);
760 type
= grub_png_get_dword (data
);
761 data
->next_offset
= data
->file
->offset
+ len
+ 4;
766 grub_png_decode_image_header (data
);
770 data
->inside_idat
= 1;
771 data
->idat_remain
= len
;
774 grub_png_decode_image_data (data
);
776 data
->inside_idat
= 0;
781 grub_png_convert_image (data
);
786 grub_file_seek (data
->file
, data
->file
->offset
+ len
+ 4);
792 if (data
->file
->offset
!= data
->next_offset
)
793 return grub_error (GRUB_ERR_BAD_FILE_TYPE
,
794 "png: chunk size error");
801 grub_video_reader_png (struct grub_video_bitmap
**bitmap
,
802 const char *filename
)
805 struct grub_png_data
*data
;
807 file
= grub_file_open (filename
);
811 data
= grub_malloc (sizeof (*data
));
814 grub_memset (data
, 0, sizeof (*data
));
816 data
->bitmap
= bitmap
;
818 grub_png_decode_png (data
);
820 grub_free (data
->image_data
);
824 if (grub_errno
!= GRUB_ERR_NONE
)
826 grub_video_bitmap_destroy (*bitmap
);
830 grub_file_close (file
);
834 #if defined(PNG_DEBUG)
836 grub_cmd_pngtest (struct grub_arg_list
*state
__attribute__ ((unused
)),
837 int argc
, char **args
)
839 struct grub_video_bitmap
*bitmap
= 0;
842 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "file name required");
844 grub_video_reader_png (&bitmap
, args
[0]);
845 if (grub_errno
!= GRUB_ERR_NONE
)
848 grub_video_bitmap_destroy (bitmap
);
850 return GRUB_ERR_NONE
;
854 static struct grub_video_bitmap_reader png_reader
= {
856 .reader
= grub_video_reader_png
,
860 GRUB_MOD_INIT (video_reader_png
)
862 grub_video_bitmap_reader_register (&png_reader
);
863 #if defined(PNG_DEBUG)
864 grub_register_command ("pngtest", grub_cmd_pngtest
,
865 GRUB_COMMAND_FLAG_BOTH
, "pngtest FILE",
866 "Tests loading of PNG bitmap.", 0);
870 GRUB_MOD_FINI (video_reader_png
)
872 #if defined(PNG_DEBUG)
873 grub_unregister_command ("pngtest");
875 grub_video_bitmap_reader_unregister (&png_reader
);