make the linux-ppc packags be in synch with other platforms
[tangerine.git] / arch / common / boot / grub2 / video / readers / png.c
blob608fa5e4a849ac15d9d93fd0bab6df44d31bccd4
1 /*
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>
22 #include <grub/dl.h>
23 #include <grub/mm.h>
24 #include <grub/misc.h>
25 #include <grub/arg.h>
26 #include <grub/file.h>
28 /* Uncomment following define to enable PNG debug. */
29 //#define 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
59 #define Z_DEFLATED 8
60 #define Z_FLAG_DICT 32
62 #define INFLATE_STORED 0
63 #define INFLATE_FIXED 1
64 #define INFLATE_DYNAMIC 2
66 #define WSIZE 0x8000
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
77 struct huff_table
79 int *values, *maxval, *offset;
80 int num_values, max_length;
83 struct grub_png_data
85 grub_file_t file;
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];
109 int wp;
111 grub_uint8_t *cur_rgb;
113 int cur_colume, cur_filter, first_line;
116 static grub_uint32_t
117 grub_png_get_dword (struct grub_png_data *data)
119 grub_uint32_t r;
121 r = 0;
122 grub_file_read (data->file, (char *) &r, sizeof (grub_uint32_t));
124 return grub_be_to_cpu32 (r);
127 static grub_uint8_t
128 grub_png_get_byte (struct grub_png_data *data)
130 grub_uint8_t r;
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");
145 return 0;
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");
154 return 0;
157 data->next_offset = data->file->offset + len + 4;
159 while (len == 0);
160 data->idat_remain = len;
163 r = 0;
164 grub_file_read (data->file, (char *) &r, 1);
166 if (data->inside_idat)
167 data->idat_remain--;
169 return r;
172 static int
173 grub_png_get_bits (struct grub_png_data *data, int num)
175 int code, shift;
177 if (data->bit_count == 0)
179 data->bit_save = grub_png_get_byte (data);
180 data->bit_count = 8;
183 code = 0;
184 shift = 0;
185 while (grub_errno == 0)
187 int n;
189 n = data->bit_count;
190 if (n > num)
191 n = num;
193 code += (int) (data->bit_save & ((1 << n) - 1)) << shift;
194 num -= n;
195 if (!num)
197 data->bit_count -= n;
198 data->bit_save >>= n;
199 break;
202 shift += n;
204 data->bit_save = grub_png_get_byte (data);
205 data->bit_count = 8;
208 return code;
211 static grub_err_t
212 grub_png_decode_image_header (struct grub_png_data *data)
214 int color_type;
215 int color_bits;
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,
233 data->image_height,
234 GRUB_VIDEO_BLIT_FORMAT_R8G8B8))
235 return grub_errno;
236 data->bpp = 3;
238 else if (color_type == PNG_COLOR_TYPE_RGBA)
240 if (grub_video_bitmap_create (data->bitmap, data->image_width,
241 data->image_height,
242 GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8))
243 return grub_errno;
244 data->bpp = 4;
246 else
247 return grub_error (GRUB_ERR_BAD_FILE_TYPE,
248 "png: color type not supported");
250 if (data->is_16bit)
252 data->bpp <<= 1;
254 data->image_data = grub_malloc (data->image_height *
255 data->image_width * data->bpp);
256 if (grub_errno)
257 return grub_errno;
259 data->cur_rgb = data->image_data;
261 else
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);
287 return grub_errno;
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
305 }; /* 99==invalid */
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,
318 12, 12, 13, 13
321 static void
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;
328 ht->num_values = 0;
329 ht->max_length = cur_maxlen;
330 grub_memset (cur_maxval, 0, sizeof (int) * cur_maxlen);
333 static void
334 grub_png_insert_huff_item (struct huff_table *ht, int code, int len)
336 int i, n;
338 if (len == 0)
339 return;
341 if (len > ht->max_length)
343 grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: invalid code length");
344 return;
347 n = 0;
348 for (i = len; i < ht->max_length; i++)
349 n += ht->maxval[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;
355 ht->num_values++;
356 ht->maxval[len - 1]++;
359 static void
360 grub_png_build_huff_table (struct huff_table *ht)
362 int base, ofs, i;
364 base = 0;
365 ofs = 0;
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;
374 base <<= 1;
378 static int
379 grub_png_get_huff_code (struct grub_png_data *data, struct huff_table *ht)
381 int code, i;
383 code = 0;
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]];
390 return 0;
393 static grub_err_t
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)];
399 int cl_maxval[8];
400 int cl_offset[8];
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,
426 data->code_offset);
428 grub_png_init_huff_table (&data->dist_table, DEFLATE_HUFF_LEN,
429 data->dist_values, data->dist_maxval,
430 data->dist_offset);
432 prev = 0;
433 for (i = 0; i < nl + nd; i++)
435 int n, code;
436 struct huff_table *ht;
438 if (grub_errno)
439 return grub_errno;
441 if (i < nl)
443 ht = &data->code_table;
444 code = i;
446 else
448 ht = &data->dist_table;
449 code = i - nl;
452 n = grub_png_get_huff_code (data, &cl);
453 if (n < 16)
455 grub_png_insert_huff_item (ht, code, n);
456 prev = n;
458 else if (n == 16)
460 int c;
462 c = 3 + grub_png_get_bits (data, 2);
463 while (c > 0)
465 grub_png_insert_huff_item (ht, code++, prev);
466 i++;
467 c--;
469 i--;
471 else if (n == 17)
472 i += 3 + grub_png_get_bits (data, 3) - 1;
473 else
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);
480 return grub_errno;
483 static grub_err_t
484 grub_png_output_byte (struct grub_png_data *data, grub_uint8_t n)
486 int row_bytes;
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;
498 else
499 *(data->cur_rgb++) = n;
501 data->cur_colume++;
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;
508 grub_uint8_t *up;
510 if (data->first_line)
512 blank_line = grub_malloc (row_bytes);
513 if (blank_line == NULL)
514 return grub_errno;
516 grub_memset (blank_line, 0, row_bytes);
517 up = blank_line;
519 else
520 up = cur - row_bytes;
522 switch (data->cur_filter)
524 case PNG_FILTER_VALUE_SUB:
526 int i;
528 cur += data->bpp;
529 for (i = data->bpp; i < row_bytes; i++, cur++, left++)
530 *cur += *left;
532 break;
534 case PNG_FILTER_VALUE_UP:
536 int i;
538 for (i = 0; i < row_bytes; i++, cur++, up++)
539 *cur += *up;
541 break;
543 case PNG_FILTER_VALUE_AVG:
545 int i;
547 for (i = 0; i < data->bpp; i++, cur++, up++)
548 *cur += *up >> 1;
550 for (; i < row_bytes; i++, cur++, up++, left++)
551 *cur += ((int) *up + (int) *left) >> 1;
553 break;
555 case PNG_FILTER_VALUE_PAETH:
557 int i;
558 grub_uint8_t *upper_left = up;
560 for (i = 0; i < data->bpp; i++, cur++, up++)
561 *cur += *up;
563 for (; i < row_bytes; i++, cur++, up++, left++, upper_left++)
565 int a, b, c, pa, pb, pc;
567 a = *left;
568 b = *up;
569 c = *upper_left;
571 pa = b - c;
572 pb = a - c;
573 pc = pa + pb;
575 if (pa < 0)
576 pa = -pa;
578 if (pb < 0)
579 pb = -pb;
581 if (pc < 0)
582 pc = -pc;
584 *cur += ((pa <= pb) && (pa <= pc)) ? a : (pb <= pc) ? b : c;
589 if (blank_line)
590 grub_free (blank_line);
592 data->cur_colume = 0;
593 data->first_line = 0;
596 return grub_errno;
599 static grub_err_t
600 grub_png_read_dynamic_block (struct grub_png_data *data)
602 while (grub_errno == 0)
604 int n;
606 n = grub_png_get_huff_code (data, &data->code_table);
607 if (n < 256)
609 data->slide[data->wp] = n;
610 grub_png_output_byte (data, n);
612 data->wp++;
613 if (data->wp >= WSIZE)
614 data->wp = 0;
616 else if (n == 256)
617 break;
618 else
620 int len, dist, pos;
622 n -= 257;
623 len = cplens[n];
624 if (cplext[n])
625 len += grub_png_get_bits (data, cplext[n]);
627 n = grub_png_get_huff_code (data, &data->dist_table);
628 dist = cpdist[n];
629 if (cpdext[n])
630 dist += grub_png_get_bits (data, cpdext[n]);
632 pos = data->wp - dist;
633 if (pos < 0)
634 pos += WSIZE;
636 while (len > 0)
638 data->slide[data->wp] = data->slide[pos];
639 grub_png_output_byte (data, data->slide[data->wp]);
641 data->wp++;
642 if (data->wp >= WSIZE)
643 data->wp = 0;
645 pos++;
646 if (pos >= WSIZE)
647 pos = 0;
649 len--;
654 return grub_errno;
657 static grub_err_t
658 grub_png_decode_image_data (struct grub_png_data *data)
660 grub_uint8_t cmf, flg;
661 int final;
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");
676 int block_type;
678 final = grub_png_get_bits (data, 1);
679 block_type = grub_png_get_bits (data, 2);
681 switch (block_type)
683 case INFLATE_STORED:
685 grub_uint16_t i, len;
687 data->bit_count = 0;
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));
698 break;
701 case INFLATE_FIXED:
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);
708 break;
710 default:
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);
723 return grub_errno;
726 static const grub_uint8_t png_magic[8] =
727 { 0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0x0a };
729 static void
730 grub_png_convert_image (struct grub_png_data *data)
732 int i;
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);
740 i++, d1++, d2+=2)
741 *d1 = *d2;
744 static grub_err_t
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)
750 return grub_errno;
752 if (grub_memcmp (magic, png_magic, sizeof (png_magic)))
753 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "png: not a png file");
755 while (1)
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;
763 switch (type)
765 case PNG_CHUNK_IHDR:
766 grub_png_decode_image_header (data);
767 break;
769 case PNG_CHUNK_IDAT:
770 data->inside_idat = 1;
771 data->idat_remain = len;
772 data->bit_count = 0;
774 grub_png_decode_image_data (data);
776 data->inside_idat = 0;
777 break;
779 case PNG_CHUNK_IEND:
780 if (data->is_16bit)
781 grub_png_convert_image (data);
783 return grub_errno;
785 default:
786 grub_file_seek (data->file, data->file->offset + len + 4);
789 if (grub_errno)
790 break;
792 if (data->file->offset != data->next_offset)
793 return grub_error (GRUB_ERR_BAD_FILE_TYPE,
794 "png: chunk size error");
797 return grub_errno;
800 static grub_err_t
801 grub_video_reader_png (struct grub_video_bitmap **bitmap,
802 const char *filename)
804 grub_file_t file;
805 struct grub_png_data *data;
807 file = grub_file_open (filename);
808 if (!file)
809 return grub_errno;
811 data = grub_malloc (sizeof (*data));
812 if (data != NULL)
814 grub_memset (data, 0, sizeof (*data));
815 data->file = file;
816 data->bitmap = bitmap;
818 grub_png_decode_png (data);
820 grub_free (data->image_data);
821 grub_free (data);
824 if (grub_errno != GRUB_ERR_NONE)
826 grub_video_bitmap_destroy (*bitmap);
827 *bitmap = 0;
830 grub_file_close (file);
831 return grub_errno;
834 #if defined(PNG_DEBUG)
835 static grub_err_t
836 grub_cmd_pngtest (struct grub_arg_list *state __attribute__ ((unused)),
837 int argc, char **args)
839 struct grub_video_bitmap *bitmap = 0;
841 if (argc != 1)
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)
846 return grub_errno;
848 grub_video_bitmap_destroy (bitmap);
850 return GRUB_ERR_NONE;
852 #endif
854 static struct grub_video_bitmap_reader png_reader = {
855 .extension = ".png",
856 .reader = grub_video_reader_png,
857 .next = 0
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);
867 #endif
870 GRUB_MOD_FINI (video_reader_png)
872 #if defined(PNG_DEBUG)
873 grub_unregister_command ("pngtest");
874 #endif
875 grub_video_bitmap_reader_unregister (&png_reader);