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
/* Uncomment following define to enable PNG debug. */
30
#define PNG_COLOR_MASK_PALETTE 1
31
#define PNG_COLOR_MASK_COLOR 2
32
#define PNG_COLOR_MASK_ALPHA 4
34
#define PNG_COLOR_TYPE_GRAY 0
35
#define PNG_COLOR_TYPE_PALETTE (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_PALETTE)
36
#define PNG_COLOR_TYPE_RGB (PNG_COLOR_MASK_COLOR)
37
#define PNG_COLOR_TYPE_RGBA (PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA)
38
#define PNG_COLOR_TYPE_GRAYA (PNG_COLOR_MASK_ALPHA)
40
#define PNG_COMPRESSION_BASE 0
42
#define PNG_INTERLACE_NONE 0
43
#define PNG_INTERLACE_ADAM7 1
45
#define PNG_FILTER_TYPE_BASE 0
47
#define PNG_FILTER_VALUE_NONE 0
48
#define PNG_FILTER_VALUE_SUB 1
49
#define PNG_FILTER_VALUE_UP 2
50
#define PNG_FILTER_VALUE_AVG 3
51
#define PNG_FILTER_VALUE_PAETH 4
52
#define PNG_FILTER_VALUE_LAST 5
54
#define PNG_CHUNK_IHDR 0x49484452
55
#define PNG_CHUNK_IDAT 0x49444154
56
#define PNG_CHUNK_IEND 0x49454e44
59
#define Z_FLAG_DICT 32
61
#define INFLATE_STORED 0
62
#define INFLATE_FIXED 1
63
#define INFLATE_DYNAMIC 2
67
#define DEFLATE_HCLEN_BASE 4
68
#define DEFLATE_HCLEN_MAX 19
69
#define DEFLATE_HLIT_BASE 257
70
#define DEFLATE_HLIT_MAX 288
71
#define DEFLATE_HDIST_BASE 1
72
#define DEFLATE_HDIST_MAX 30
74
#define DEFLATE_HUFF_LEN 16
77
static grub_command_t cmd;
82
int *values, *maxval, *offset;
83
int num_values, max_length;
89
struct grub_video_bitmap **bitmap;
91
int bit_count, bit_save;
93
grub_uint32_t next_offset;
95
int image_width, image_height, bpp, is_16bit, raw_bytes;
96
grub_uint8_t *image_data;
98
int inside_idat, idat_remain;
100
int code_values[DEFLATE_HLIT_MAX];
101
int code_maxval[DEFLATE_HUFF_LEN];
102
int code_offset[DEFLATE_HUFF_LEN];
104
int dist_values[DEFLATE_HDIST_MAX];
105
int dist_maxval[DEFLATE_HUFF_LEN];
106
int dist_offset[DEFLATE_HUFF_LEN];
108
struct huff_table code_table;
109
struct huff_table dist_table;
111
grub_uint8_t slide[WSIZE];
114
grub_uint8_t *cur_rgb;
116
int cur_column, cur_filter, first_line;
120
grub_png_get_dword (struct grub_png_data *data)
125
grub_file_read (data->file, &r, sizeof (grub_uint32_t));
127
return grub_be_to_cpu32 (r);
131
grub_png_get_byte (struct grub_png_data *data)
135
if ((data->inside_idat) && (data->idat_remain == 0))
137
grub_uint32_t len, type;
141
/* Skip crc checksum. */
142
grub_png_get_dword (data);
144
if (data->file->offset != data->next_offset)
146
grub_error (GRUB_ERR_BAD_FILE_TYPE,
147
"png: chunk size error");
151
len = grub_png_get_dword (data);
152
type = grub_png_get_dword (data);
153
if (type != PNG_CHUNK_IDAT)
155
grub_error (GRUB_ERR_BAD_FILE_TYPE,
156
"png: unexpected end of data");
160
data->next_offset = data->file->offset + len + 4;
163
data->idat_remain = len;
167
grub_file_read (data->file, &r, 1);
169
if (data->inside_idat)
176
grub_png_get_bits (struct grub_png_data *data, int num)
180
if (data->bit_count == 0)
182
data->bit_save = grub_png_get_byte (data);
188
while (grub_errno == 0)
196
code += (int) (data->bit_save & ((1 << n) - 1)) << shift;
200
data->bit_count -= n;
201
data->bit_save >>= n;
207
data->bit_save = grub_png_get_byte (data);
215
grub_png_decode_image_header (struct grub_png_data *data)
220
data->image_width = grub_png_get_dword (data);
221
data->image_height = grub_png_get_dword (data);
223
if ((!data->image_height) || (!data->image_width))
224
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid image size");
226
color_bits = grub_png_get_byte (data);
227
if ((color_bits != 8) && (color_bits != 16))
228
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
229
"png: bit depth must be 8 or 16");
230
data->is_16bit = (color_bits == 16);
232
color_type = grub_png_get_byte (data);
233
if (color_type == PNG_COLOR_TYPE_RGB)
235
if (grub_video_bitmap_create (data->bitmap, data->image_width,
237
GRUB_VIDEO_BLIT_FORMAT_RGB_888))
241
else if (color_type == PNG_COLOR_TYPE_RGBA)
243
if (grub_video_bitmap_create (data->bitmap, data->image_width,
245
GRUB_VIDEO_BLIT_FORMAT_RGBA_8888))
250
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
251
"png: color type not supported");
257
data->image_data = grub_malloc (data->image_height *
258
data->image_width * data->bpp);
262
data->cur_rgb = data->image_data;
266
data->image_data = 0;
267
data->cur_rgb = (*data->bitmap)->data;
270
data->raw_bytes = data->image_height * (data->image_width + 1) * data->bpp;
272
data->cur_column = 0;
273
data->first_line = 1;
275
if (grub_png_get_byte (data) != PNG_COMPRESSION_BASE)
276
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
277
"png: compression method not supported");
279
if (grub_png_get_byte (data) != PNG_FILTER_TYPE_BASE)
280
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
281
"png: filter method not supported");
283
if (grub_png_get_byte (data) != PNG_INTERLACE_NONE)
284
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
285
"png: interlace method not supported");
287
/* Skip crc checksum. */
288
grub_png_get_dword (data);
293
/* Order of the bit length code lengths. */
294
static const grub_uint8_t bitorder[] = {
295
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
298
/* Copy lengths for literal codes 257..285. */
299
static const int cplens[] = {
300
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
301
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
304
/* Extra bits for literal codes 257..285. */
305
static const grub_uint8_t cplext[] = {
306
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
307
3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 99, 99
310
/* Copy offsets for distance codes 0..29. */
311
static const int cpdist[] = {
312
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
313
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
314
8193, 12289, 16385, 24577
317
/* Extra bits for distance codes. */
318
static const grub_uint8_t cpdext[] = {
319
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
320
7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
325
grub_png_init_huff_table (struct huff_table *ht, int cur_maxlen,
326
int *cur_values, int *cur_maxval, int *cur_offset)
328
ht->values = cur_values;
329
ht->maxval = cur_maxval;
330
ht->offset = cur_offset;
332
ht->max_length = cur_maxlen;
333
grub_memset (cur_maxval, 0, sizeof (int) * cur_maxlen);
337
grub_png_insert_huff_item (struct huff_table *ht, int code, int len)
344
if (len > ht->max_length)
346
grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid code length");
351
for (i = len; i < ht->max_length; i++)
354
for (i = 0; i < n; i++)
355
ht->values[ht->num_values - i] = ht->values[ht->num_values - i - 1];
357
ht->values[ht->num_values - n] = code;
359
ht->maxval[len - 1]++;
363
grub_png_build_huff_table (struct huff_table *ht)
369
for (i = 0; i < ht->max_length; i++)
371
base += ht->maxval[i];
372
ofs += ht->maxval[i];
374
ht->maxval[i] = base;
375
ht->offset[i] = ofs - base;
382
grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht)
387
for (i = 0; i < ht->max_length; i++)
389
code = (code << 1) + grub_png_get_bits (data, 1);
390
if (code < ht->maxval[i])
391
return ht->values[code + ht->offset[i]];
397
grub_png_init_fixed_block (struct grub_png_data *data)
401
grub_png_init_huff_table (&data->code_table, DEFLATE_HUFF_LEN,
402
data->code_values, data->code_maxval,
405
for (i = 0; i < 144; i++)
406
grub_png_insert_huff_item (&data->code_table, i, 8);
409
grub_png_insert_huff_item (&data->code_table, i, 9);
412
grub_png_insert_huff_item (&data->code_table, i, 7);
414
for (; i < DEFLATE_HLIT_MAX; i++)
415
grub_png_insert_huff_item (&data->code_table, i, 8);
417
grub_png_build_huff_table (&data->code_table);
419
grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN,
420
data->dist_values, data->dist_maxval,
423
for (i = 0; i < DEFLATE_HDIST_MAX; i++)
424
grub_png_insert_huff_item (&data->dist_table, i, 5);
426
grub_png_build_huff_table (&data->dist_table);
432
grub_png_init_dynamic_block (struct grub_png_data *data)
434
int nl, nd, nb, i, prev;
435
struct huff_table cl;
436
int cl_values[sizeof (bitorder)];
439
grub_uint8_t lens[DEFLATE_HCLEN_MAX];
441
nl = DEFLATE_HLIT_BASE + grub_png_get_bits (data, 5);
442
nd = DEFLATE_HDIST_BASE + grub_png_get_bits (data, 5);
443
nb = DEFLATE_HCLEN_BASE + grub_png_get_bits (data, 4);
445
if ((nl > DEFLATE_HLIT_MAX) || (nd > DEFLATE_HDIST_MAX) ||
446
(nb > DEFLATE_HCLEN_MAX))
447
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: too much data");
449
grub_png_init_huff_table (&cl, 8, cl_values, cl_maxval, cl_offset);
451
for (i = 0; i < nb; i++)
452
lens[bitorder[i]] = grub_png_get_bits (data, 3);
454
for (; i < DEFLATE_HCLEN_MAX; i++)
455
lens[bitorder[i]] = 0;
457
for (i = 0; i < DEFLATE_HCLEN_MAX; i++)
458
grub_png_insert_huff_item (&cl, i, lens[i]);
460
grub_png_build_huff_table (&cl);
462
grub_png_init_huff_table (&data->code_table, DEFLATE_HUFF_LEN,
463
data->code_values, data->code_maxval,
466
grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN,
467
data->dist_values, data->dist_maxval,
471
for (i = 0; i < nl + nd; i++)
474
struct huff_table *ht;
481
ht = &data->code_table;
486
ht = &data->dist_table;
490
n = grub_png_get_huff_code (data, &cl);
493
grub_png_insert_huff_item (ht, code, n);
500
c = 3 + grub_png_get_bits (data, 2);
503
grub_png_insert_huff_item (ht, code++, prev);
510
i += 3 + grub_png_get_bits (data, 3) - 1;
512
i += 11 + grub_png_get_bits (data, 7) - 1;
515
grub_png_build_huff_table (&data->code_table);
516
grub_png_build_huff_table (&data->dist_table);
522
grub_png_output_byte (struct grub_png_data *data, grub_uint8_t n)
526
if (--data->raw_bytes < 0)
527
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "image size overflown");
529
if (data->cur_column == 0)
531
if (n >= PNG_FILTER_VALUE_LAST)
532
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "invalid filter value");
534
data->cur_filter = n;
537
*(data->cur_rgb++) = n;
540
row_bytes = data->image_width * data->bpp;
541
if (data->cur_column == row_bytes + 1)
543
grub_uint8_t *blank_line = NULL;
544
grub_uint8_t *cur = data->cur_rgb - row_bytes;
545
grub_uint8_t *left = cur;
548
if (data->first_line)
550
blank_line = grub_zalloc (row_bytes);
551
if (blank_line == NULL)
557
up = cur - row_bytes;
559
switch (data->cur_filter)
561
case PNG_FILTER_VALUE_SUB:
566
for (i = data->bpp; i < row_bytes; i++, cur++, left++)
571
case PNG_FILTER_VALUE_UP:
575
for (i = 0; i < row_bytes; i++, cur++, up++)
580
case PNG_FILTER_VALUE_AVG:
584
for (i = 0; i < data->bpp; i++, cur++, up++)
587
for (; i < row_bytes; i++, cur++, up++, left++)
588
*cur += ((int) *up + (int) *left) >> 1;
592
case PNG_FILTER_VALUE_PAETH:
595
grub_uint8_t *upper_left = up;
597
for (i = 0; i < data->bpp; i++, cur++, up++)
600
for (; i < row_bytes; i++, cur++, up++, left++, upper_left++)
602
int a, b, c, pa, pb, pc;
621
*cur += ((pa <= pb) && (pa <= pc)) ? a : (pb <= pc) ? b : c;
627
grub_free (blank_line);
629
data->cur_column = 0;
630
data->first_line = 0;
637
grub_png_read_dynamic_block (struct grub_png_data *data)
639
while (grub_errno == 0)
643
n = grub_png_get_huff_code (data, &data->code_table);
646
data->slide[data->wp] = n;
647
grub_png_output_byte (data, n);
650
if (data->wp >= WSIZE)
662
len += grub_png_get_bits (data, cplext[n]);
664
n = grub_png_get_huff_code (data, &data->dist_table);
667
dist += grub_png_get_bits (data, cpdext[n]);
669
pos = data->wp - dist;
675
data->slide[data->wp] = data->slide[pos];
676
grub_png_output_byte (data, data->slide[data->wp]);
679
if (data->wp >= WSIZE)
695
grub_png_decode_image_data (struct grub_png_data *data)
697
grub_uint8_t cmf, flg;
700
cmf = grub_png_get_byte (data);
701
flg = grub_png_get_byte (data);
703
if ((cmf & 0xF) != Z_DEFLATED)
704
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
705
"png: only support deflate compression method");
707
if (flg & Z_FLAG_DICT)
708
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
709
"png: dictionary not supported");
715
final = grub_png_get_bits (data, 1);
716
block_type = grub_png_get_bits (data, 2);
722
grub_uint16_t i, len;
725
len = grub_png_get_byte (data);
726
len += ((grub_uint16_t) grub_png_get_byte (data)) << 8;
728
/* Skip NLEN field. */
729
grub_png_get_byte (data);
730
grub_png_get_byte (data);
732
for (i = 0; i < len; i++)
733
grub_png_output_byte (data, grub_png_get_byte (data));
739
grub_png_init_fixed_block (data);
740
grub_png_read_dynamic_block (data);
743
case INFLATE_DYNAMIC:
744
grub_png_init_dynamic_block (data);
745
grub_png_read_dynamic_block (data);
749
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
750
"png: unknown block type");
753
while ((!final) && (grub_errno == 0));
755
/* Skip adler checksum. */
756
grub_png_get_dword (data);
758
/* Skip crc checksum. */
759
grub_png_get_dword (data);
764
static const grub_uint8_t png_magic[8] =
765
{ 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a };
768
grub_png_convert_image (struct grub_png_data *data)
771
grub_uint8_t *d1, *d2;
773
d1 = (*data->bitmap)->data;
774
d2 = data->image_data + 1;
776
/* Only copy the upper 8 bit. */
777
for (i = 0; i < (data->image_width * data->image_height * data->bpp >> 1);
783
grub_png_decode_png (struct grub_png_data *data)
785
grub_uint8_t magic[8];
787
if (grub_file_read (data->file, &magic[0], 8) != 8)
790
if (grub_memcmp (magic, png_magic, sizeof (png_magic)))
791
return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: not a png file");
795
grub_uint32_t len, type;
797
len = grub_png_get_dword (data);
798
type = grub_png_get_dword (data);
799
data->next_offset = data->file->offset + len + 4;
804
grub_png_decode_image_header (data);
808
data->inside_idat = 1;
809
data->idat_remain = len;
812
grub_png_decode_image_data (data);
814
data->inside_idat = 0;
819
grub_png_convert_image (data);
824
grub_file_seek (data->file, data->file->offset + len + 4);
830
if (data->file->offset != data->next_offset)
831
return grub_error (GRUB_ERR_BAD_FILE_TYPE,
832
"png: chunk size error");
839
grub_video_reader_png (struct grub_video_bitmap **bitmap,
840
const char *filename)
843
struct grub_png_data *data;
845
file = grub_buffile_open (filename, 0);
849
data = grub_zalloc (sizeof (*data));
853
data->bitmap = bitmap;
855
grub_png_decode_png (data);
857
grub_free (data->image_data);
861
if (grub_errno != GRUB_ERR_NONE)
863
grub_video_bitmap_destroy (*bitmap);
867
grub_file_close (file);
871
#if defined(PNG_DEBUG)
873
grub_cmd_pngtest (grub_command_t cmd __attribute__ ((unused)),
874
int argc, char **args)
876
struct grub_video_bitmap *bitmap = 0;
879
return grub_error (GRUB_ERR_BAD_ARGUMENT, "file name required");
881
grub_video_reader_png (&bitmap, args[0]);
882
if (grub_errno != GRUB_ERR_NONE)
885
grub_video_bitmap_destroy (bitmap);
887
return GRUB_ERR_NONE;
891
static struct grub_video_bitmap_reader png_reader = {
893
.reader = grub_video_reader_png,
899
grub_video_bitmap_reader_register (&png_reader);
900
#if defined(PNG_DEBUG)
901
cmd = grub_register_command ("pngtest", grub_cmd_pngtest,
903
"Tests loading of PNG bitmap.");
909
#if defined(PNG_DEBUG)
910
grub_unregister_command (cmd);
912
grub_video_bitmap_reader_unregister (&png_reader);