123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011 |
- /*
- * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
- * 02110-1301, USA.
- *
- * You can also choose to distribute this program under the terms of
- * the Unmodified Binary Distribution Licence (as given in the file
- * COPYING.UBDL), provided that you have satisfied its requirements.
- */
-
- FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
- #include <stdint.h>
- #include <stdlib.h>
- #include <string.h>
- #include <errno.h>
- #include <byteswap.h>
- #include <ipxe/umalloc.h>
- #include <ipxe/pixbuf.h>
- #include <ipxe/deflate.h>
- #include <ipxe/png.h>
-
- /** @file
- *
- * Portable Network Graphics (PNG) format
- *
- * The PNG format is defined in RFC 2083.
- */
-
- /** PNG context */
- struct png_context {
- /** Offset within image */
- size_t offset;
-
- /** Pixel buffer */
- struct pixel_buffer *pixbuf;
-
- /** Bit depth */
- unsigned int depth;
- /** Colour type */
- unsigned int colour_type;
- /** Number of channels */
- unsigned int channels;
- /** Number of interlace passes */
- unsigned int passes;
- /** Palette, in iPXE's pixel buffer format */
- uint32_t palette[PNG_PALETTE_COUNT];
-
- /** Decompression buffer for raw PNG data */
- struct deflate_chunk raw;
- /** Decompressor */
- struct deflate deflate;
- };
-
- /** A PNG interlace pass */
- struct png_interlace {
- /** Pass number */
- unsigned int pass;
- /** X starting indent */
- unsigned int x_indent;
- /** Y starting indent */
- unsigned int y_indent;
- /** X stride */
- unsigned int x_stride;
- /** Y stride */
- unsigned int y_stride;
- /** Width */
- unsigned int width;
- /** Height */
- unsigned int height;
- };
-
- /** PNG file signature */
- static struct png_signature png_signature = PNG_SIGNATURE;
-
- /** Number of interlacing passes */
- static uint8_t png_interlace_passes[] = {
- [PNG_INTERLACE_NONE] = 1,
- [PNG_INTERLACE_ADAM7] = 7,
- };
-
- /**
- * Transcribe PNG chunk type name (for debugging)
- *
- * @v type Chunk type
- * @ret name Chunk type name
- */
- static const char * png_type_name ( uint32_t type ) {
- static union {
- uint32_t type;
- char name[ sizeof ( uint32_t ) + 1 /* NUL */ ];
- } u;
-
- u.type = type;
- return u.name;
- }
-
- /**
- * Calculate PNG interlace pass parameters
- *
- * @v png PNG context
- * @v pass Pass number (0=first pass)
- * @v interlace Interlace pass to fill in
- */
- static void png_interlace ( struct png_context *png, unsigned int pass,
- struct png_interlace *interlace ) {
- unsigned int grid_width_log2;
- unsigned int grid_height_log2;
- unsigned int x_indent;
- unsigned int y_indent;
- unsigned int x_stride_log2;
- unsigned int y_stride_log2;
- unsigned int x_stride;
- unsigned int y_stride;
- unsigned int width;
- unsigned int height;
-
- /* Sanity check */
- assert ( png->passes > 0 );
-
- /* Store pass number */
- interlace->pass = pass;
-
- /* Calculate interlace grid dimensions */
- grid_width_log2 = ( png->passes / 2 );
- grid_height_log2 = ( ( png->passes - 1 ) / 2 );
-
- /* Calculate starting indents */
- interlace->x_indent = x_indent =
- ( ( pass & 1 ) ?
- ( 1 << ( grid_width_log2 - ( pass / 2 ) - 1 ) ) : 0 );
- interlace->y_indent = y_indent =
- ( ( pass && ! ( pass & 1 ) ) ?
- ( 1 << ( grid_height_log2 - ( ( pass - 1 ) / 2 ) - 1 ) ) : 0);
-
- /* Calculate strides */
- x_stride_log2 = ( grid_width_log2 - ( pass / 2 ) );
- y_stride_log2 =
- ( grid_height_log2 - ( pass ? ( ( pass - 1 ) / 2 ) : 0 ) );
- interlace->x_stride = x_stride = ( 1 << x_stride_log2 );
- interlace->y_stride = y_stride = ( 1 << y_stride_log2 );
-
- /* Calculate pass dimensions */
- width = png->pixbuf->width;
- height = png->pixbuf->height;
- interlace->width =
- ( ( width - x_indent + x_stride - 1 ) >> x_stride_log2 );
- interlace->height =
- ( ( height - y_indent + y_stride - 1 ) >> y_stride_log2 );
- }
-
- /**
- * Calculate PNG pixel length
- *
- * @v png PNG context
- * @ret pixel_len Pixel length
- */
- static unsigned int png_pixel_len ( struct png_context *png ) {
-
- return ( ( ( png->channels * png->depth ) + 7 ) / 8 );
- }
-
- /**
- * Calculate PNG scanline length
- *
- * @v png PNG context
- * @v interlace Interlace pass
- * @ret scanline_len Scanline length (including filter byte)
- */
- static size_t png_scanline_len ( struct png_context *png,
- struct png_interlace *interlace ) {
-
- return ( 1 /* Filter byte */ +
- ( ( interlace->width * png->channels * png->depth ) + 7 ) / 8);
- }
-
- /**
- * Handle PNG image header chunk
- *
- * @v image PNG image
- * @v png PNG context
- * @v len Chunk length
- * @ret rc Return status code
- */
- static int png_image_header ( struct image *image, struct png_context *png,
- size_t len ) {
- struct png_image_header ihdr;
- struct png_interlace interlace;
- unsigned int pass;
-
- /* Sanity check */
- if ( len != sizeof ( ihdr ) ) {
- DBGC ( image, "PNG %s invalid IHDR length %zd\n",
- image->name, len );
- return -EINVAL;
- }
- if ( png->pixbuf ) {
- DBGC ( image, "PNG %s duplicate IHDR\n", image->name );
- return -EINVAL;
- }
-
- /* Extract image header */
- copy_from_user ( &ihdr, image->data, png->offset, len );
- DBGC ( image, "PNG %s %dx%d depth %d type %d compression %d filter %d "
- "interlace %d\n", image->name, ntohl ( ihdr.width ),
- ntohl ( ihdr.height ), ihdr.depth, ihdr.colour_type,
- ihdr.compression, ihdr.filter, ihdr.interlace );
-
- /* Sanity checks */
- if ( ihdr.compression >= PNG_COMPRESSION_UNKNOWN ) {
- DBGC ( image, "PNG %s unknown compression method %d\n",
- image->name, ihdr.compression );
- return -ENOTSUP;
- }
- if ( ihdr.filter >= PNG_FILTER_UNKNOWN ) {
- DBGC ( image, "PNG %s unknown filter method %d\n",
- image->name, ihdr.filter );
- return -ENOTSUP;
- }
- if ( ihdr.interlace >= PNG_INTERLACE_UNKNOWN ) {
- DBGC ( image, "PNG %s unknown interlace method %d\n",
- image->name, ihdr.interlace );
- return -ENOTSUP;
- }
-
- /* Allocate pixel buffer */
- png->pixbuf = alloc_pixbuf ( ntohl ( ihdr.width ),
- ntohl ( ihdr.height ) );
- if ( ! png->pixbuf ) {
- DBGC ( image, "PNG %s could not allocate pixel buffer\n",
- image->name );
- return -ENOMEM;
- }
-
- /* Extract bit depth */
- png->depth = ihdr.depth;
- if ( ( png->depth == 0 ) ||
- ( ( png->depth & ( png->depth - 1 ) ) != 0 ) ) {
- DBGC ( image, "PNG %s invalid depth %d\n",
- image->name, png->depth );
- return -EINVAL;
- }
-
- /* Calculate number of channels */
- png->colour_type = ihdr.colour_type;
- png->channels = 1;
- if ( ! ( ihdr.colour_type & PNG_COLOUR_TYPE_PALETTE ) ) {
- if ( ihdr.colour_type & PNG_COLOUR_TYPE_RGB )
- png->channels += 2;
- if ( ihdr.colour_type & PNG_COLOUR_TYPE_ALPHA )
- png->channels += 1;
- }
-
- /* Calculate number of interlace passes */
- png->passes = png_interlace_passes[ihdr.interlace];
-
- /* Calculate length of raw data buffer */
- for ( pass = 0 ; pass < png->passes ; pass++ ) {
- png_interlace ( png, pass, &interlace );
- if ( interlace.width == 0 )
- continue;
- png->raw.len += ( interlace.height *
- png_scanline_len ( png, &interlace ) );
- }
-
- /* Allocate raw data buffer */
- png->raw.data = umalloc ( png->raw.len );
- if ( ! png->raw.data ) {
- DBGC ( image, "PNG %s could not allocate data buffer\n",
- image->name );
- return -ENOMEM;
- }
-
- return 0;
- }
-
- /**
- * Handle PNG palette chunk
- *
- * @v image PNG image
- * @v png PNG context
- * @v len Chunk length
- * @ret rc Return status code
- */
- static int png_palette ( struct image *image, struct png_context *png,
- size_t len ) {
- size_t offset = png->offset;
- struct png_palette_entry palette;
- unsigned int i;
-
- /* Populate palette */
- for ( i = 0 ; i < ( sizeof ( png->palette ) /
- sizeof ( png->palette[0] ) ) ; i++ ) {
-
- /* Stop when we run out of palette data */
- if ( len < sizeof ( palette ) )
- break;
-
- /* Extract palette entry */
- copy_from_user ( &palette, image->data, offset,
- sizeof ( palette ) );
- png->palette[i] = ( ( palette.red << 16 ) |
- ( palette.green << 8 ) |
- ( palette.blue << 0 ) );
- DBGC2 ( image, "PNG %s palette entry %d is %#06x\n",
- image->name, i, png->palette[i] );
-
- /* Move to next entry */
- offset += sizeof ( palette );
- len -= sizeof ( palette );
- }
-
- return 0;
- }
-
- /**
- * Handle PNG image data chunk
- *
- * @v image PNG image
- * @v png PNG context
- * @v len Chunk length
- * @ret rc Return status code
- */
- static int png_image_data ( struct image *image, struct png_context *png,
- size_t len ) {
- struct deflate_chunk in;
- int rc;
-
- /* Deflate this chunk */
- deflate_chunk_init ( &in, image->data, png->offset,
- ( png->offset + len ) );
- if ( ( rc = deflate_inflate ( &png->deflate, &in, &png->raw ) ) != 0 ) {
- DBGC ( image, "PNG %s could not decompress: %s\n",
- image->name, strerror ( rc ) );
- return rc;
- }
-
- return 0;
- }
-
- /**
- * Unfilter byte using the "None" filter
- *
- * @v current Filtered current byte
- * @v left Unfiltered left byte
- * @v above Unfiltered above byte
- * @v above_left Unfiltered above-left byte
- * @ret current Unfiltered current byte
- */
- static unsigned int png_unfilter_none ( unsigned int current,
- unsigned int left __unused,
- unsigned int above __unused,
- unsigned int above_left __unused ) {
-
- return current;
- }
-
- /**
- * Unfilter byte using the "Sub" filter
- *
- * @v current Filtered current byte
- * @v left Unfiltered left byte
- * @v above Unfiltered above byte
- * @v above_left Unfiltered above-left byte
- * @ret current Unfiltered current byte
- */
- static unsigned int png_unfilter_sub ( unsigned int current,
- unsigned int left,
- unsigned int above __unused,
- unsigned int above_left __unused ) {
-
- return ( current + left );
- }
-
- /**
- * Unfilter byte using the "Up" filter
- *
- * @v current Filtered current byte
- * @v left Unfiltered left byte
- * @v above Unfiltered above byte
- * @v above_left Unfiltered above-left byte
- * @ret current Unfiltered current byte
- */
- static unsigned int png_unfilter_up ( unsigned int current,
- unsigned int left __unused,
- unsigned int above,
- unsigned int above_left __unused ) {
-
- return ( current + above );
- }
-
- /**
- * Unfilter byte using the "Average" filter
- *
- * @v current Filtered current byte
- * @v left Unfiltered left byte
- * @v above Unfiltered above byte
- * @v above_left Unfiltered above-left byte
- * @ret current Unfiltered current byte
- */
- static unsigned int png_unfilter_average ( unsigned int current,
- unsigned int left,
- unsigned int above,
- unsigned int above_left __unused ) {
-
- return ( current + ( ( above + left ) >> 1 ) );
- }
-
- /**
- * Paeth predictor function (defined in RFC 2083)
- *
- * @v a Pixel A
- * @v b Pixel B
- * @v c Pixel C
- * @ret predictor Predictor pixel
- */
- static unsigned int png_paeth_predictor ( unsigned int a, unsigned int b,
- unsigned int c ) {
- unsigned int p;
- unsigned int pa;
- unsigned int pb;
- unsigned int pc;
-
- /* Algorithm as defined in RFC 2083 section 6.6 */
- p = ( a + b - c );
- pa = abs ( p - a );
- pb = abs ( p - b );
- pc = abs ( p - c );
- if ( ( pa <= pb ) && ( pa <= pc ) ) {
- return a;
- } else if ( pb <= pc ) {
- return b;
- } else {
- return c;
- }
- }
-
- /**
- * Unfilter byte using the "Paeth" filter
- *
- * @v current Filtered current byte
- * @v above_left Unfiltered above-left byte
- * @v above Unfiltered above byte
- * @v left Unfiltered left byte
- * @ret current Unfiltered current byte
- */
- static unsigned int png_unfilter_paeth ( unsigned int current,
- unsigned int left,
- unsigned int above,
- unsigned int above_left ) {
-
- return ( current + png_paeth_predictor ( left, above, above_left ) );
- }
-
- /** A PNG filter */
- struct png_filter {
- /**
- * Unfilter byte
- *
- * @v current Filtered current byte
- * @v left Unfiltered left byte
- * @v above Unfiltered above byte
- * @v above_left Unfiltered above-left byte
- * @ret current Unfiltered current byte
- */
- unsigned int ( * unfilter ) ( unsigned int current,
- unsigned int left,
- unsigned int above,
- unsigned int above_left );
- };
-
- /** PNG filter types */
- static struct png_filter png_filters[] = {
- [PNG_FILTER_BASIC_NONE] = { png_unfilter_none },
- [PNG_FILTER_BASIC_SUB] = { png_unfilter_sub },
- [PNG_FILTER_BASIC_UP] = { png_unfilter_up },
- [PNG_FILTER_BASIC_AVERAGE] = { png_unfilter_average },
- [PNG_FILTER_BASIC_PAETH] = { png_unfilter_paeth },
- };
-
- /**
- * Unfilter one interlace pass of PNG raw data
- *
- * @v image PNG image
- * @v png PNG context
- * @v interlace Interlace pass
- * @ret rc Return status code
- *
- * This routine may assume that it is impossible to overrun the raw
- * data buffer, since the size is determined by the image dimensions.
- */
- static int png_unfilter_pass ( struct image *image, struct png_context *png,
- struct png_interlace *interlace ) {
- size_t offset = png->raw.offset;
- size_t pixel_len = png_pixel_len ( png );
- size_t scanline_len = png_scanline_len ( png, interlace );
- struct png_filter *filter;
- unsigned int scanline;
- unsigned int byte;
- uint8_t filter_type;
- uint8_t left;
- uint8_t above;
- uint8_t above_left;
- uint8_t current;
-
- /* On the first scanline of a pass, above bytes are assumed to
- * be zero.
- */
- above = 0;
-
- /* Iterate over each scanline in turn */
- for ( scanline = 0 ; scanline < interlace->height ; scanline++ ) {
-
- /* Extract filter byte and determine filter type */
- copy_from_user ( &filter_type, png->raw.data, offset++,
- sizeof ( filter_type ) );
- if ( filter_type >= ( sizeof ( png_filters ) /
- sizeof ( png_filters[0] ) ) ) {
- DBGC ( image, "PNG %s unknown filter type %d\n",
- image->name, filter_type );
- return -ENOTSUP;
- }
- filter = &png_filters[filter_type];
- assert ( filter->unfilter != NULL );
- DBGC2 ( image, "PNG %s pass %d scanline %d filter type %d\n",
- image->name, interlace->pass, scanline, filter_type );
-
- /* At the start of a line, both above-left and left
- * bytes are taken to be zero.
- */
- left = 0;
- above_left = 0;
-
- /* Iterate over each byte (not pixel) in turn */
- for ( byte = 0 ; byte < ( scanline_len - 1 ) ; byte++ ) {
-
- /* Extract predictor bytes, if applicable */
- if ( byte >= pixel_len ) {
- copy_from_user ( &left, png->raw.data,
- ( offset - pixel_len ),
- sizeof ( left ) );
- }
- if ( scanline > 0 ) {
- copy_from_user ( &above, png->raw.data,
- ( offset - scanline_len ),
- sizeof ( above ) );
- }
- if ( ( scanline > 0 ) && ( byte >= pixel_len ) ) {
- copy_from_user ( &above_left, png->raw.data,
- ( offset - scanline_len -
- pixel_len ),
- sizeof ( above_left ) );
- }
-
- /* Unfilter current byte */
- copy_from_user ( ¤t, png->raw.data,
- offset, sizeof ( current ) );
- current = filter->unfilter ( current, left, above,
- above_left );
- copy_to_user ( png->raw.data, offset++,
- ¤t, sizeof ( current ) );
- }
- }
-
- /* Update offset */
- png->raw.offset = offset;
-
- return 0;
- }
-
- /**
- * Unfilter PNG raw data
- *
- * @v image PNG image
- * @v png PNG context
- * @ret rc Return status code
- *
- * This routine may assume that it is impossible to overrun the raw
- * data buffer, since the size is determined by the image dimensions.
- */
- static int png_unfilter ( struct image *image, struct png_context *png ) {
- struct png_interlace interlace;
- unsigned int pass;
- int rc;
-
- /* Process each interlace pass */
- png->raw.offset = 0;
- for ( pass = 0 ; pass < png->passes ; pass++ ) {
-
- /* Calculate interlace pass parameters */
- png_interlace ( png, pass, &interlace );
-
- /* Skip zero-width rows (which have no filter bytes) */
- if ( interlace.width == 0 )
- continue;
-
- /* Unfilter this pass */
- if ( ( rc = png_unfilter_pass ( image, png,
- &interlace ) ) != 0 )
- return rc;
- }
- assert ( png->raw.offset == png->raw.len );
-
- return 0;
- }
-
- /**
- * Calculate PNG pixel component value
- *
- * @v raw Raw component value
- * @v alpha Alpha value
- * @v max Maximum raw/alpha value
- * @ret value Component value in range 0-255
- */
- static inline unsigned int png_pixel ( unsigned int raw, unsigned int alpha,
- unsigned int max ) {
-
- /* The basic calculation is 255*(raw/max)*(value/max). We use
- * fixed-point arithmetic (scaling up to the maximum range for
- * a 32-bit integer), in order to get the same results for
- * alpha blending as the test cases (produced using
- * ImageMagick).
- */
- return ( ( ( ( ( 0xff00 * raw * alpha ) / max ) / max ) + 0x80 ) >> 8 );
- }
-
- /**
- * Fill one interlace pass of PNG pixels
- *
- * @v image PNG image
- * @v png PNG context
- * @v interlace Interlace pass
- *
- * This routine may assume that it is impossible to overrun either the
- * raw data buffer or the pixel buffer, since the sizes of both are
- * determined by the image dimensions.
- */
- static void png_pixels_pass ( struct image *image,
- struct png_context *png,
- struct png_interlace *interlace ) {
- size_t raw_offset = png->raw.offset;
- uint8_t channel[png->channels];
- int is_indexed = ( png->colour_type & PNG_COLOUR_TYPE_PALETTE );
- int is_rgb = ( png->colour_type & PNG_COLOUR_TYPE_RGB );
- int has_alpha = ( png->colour_type & PNG_COLOUR_TYPE_ALPHA );
- size_t pixbuf_y_offset;
- size_t pixbuf_offset;
- size_t pixbuf_x_stride;
- size_t pixbuf_y_stride;
- size_t raw_stride;
- unsigned int y;
- unsigned int x;
- unsigned int c;
- unsigned int bits;
- unsigned int depth;
- unsigned int max;
- unsigned int alpha;
- unsigned int raw;
- unsigned int value;
- uint8_t current = 0;
- uint32_t pixel;
-
- /* We only ever use the top byte of 16-bit pixels. Model this
- * as a bit depth of 8 with a stride of more than one.
- */
- depth = png->depth;
- raw_stride = ( ( depth + 7 ) / 8 );
- if ( depth > 8 )
- depth = 8;
- max = ( ( 1 << depth ) - 1 );
-
- /* Calculate pixel buffer offset and strides */
- pixbuf_y_offset = ( ( ( interlace->y_indent * png->pixbuf->width ) +
- interlace->x_indent ) * sizeof ( pixel ) );
- pixbuf_x_stride = ( interlace->x_stride * sizeof ( pixel ) );
- pixbuf_y_stride = ( interlace->y_stride * png->pixbuf->width *
- sizeof ( pixel ) );
- DBGC2 ( image, "PNG %s pass %d %dx%d at (%d,%d) stride (%d,%d)\n",
- image->name, interlace->pass, interlace->width,
- interlace->height, interlace->x_indent, interlace->y_indent,
- interlace->x_stride, interlace->y_stride );
-
- /* Iterate over each scanline in turn */
- for ( y = 0 ; y < interlace->height ; y++ ) {
-
- /* Skip filter byte */
- raw_offset++;
-
- /* Iterate over each pixel in turn */
- bits = depth;
- pixbuf_offset = pixbuf_y_offset;
- for ( x = 0 ; x < interlace->width ; x++ ) {
-
- /* Extract sample value */
- for ( c = 0 ; c < png->channels ; c++ ) {
-
- /* Get sample value into high bits of current */
- current <<= depth;
- bits -= depth;
- if ( ! bits ) {
- copy_from_user ( ¤t,
- png->raw.data,
- raw_offset,
- sizeof ( current ) );
- raw_offset += raw_stride;
- bits = 8;
- }
-
- /* Extract sample value */
- channel[c] = ( current >> ( 8 - depth ) );
- }
-
- /* Convert to native pixel format */
- if ( is_indexed ) {
-
- /* Indexed */
- pixel = png->palette[channel[0]];
-
- } else {
-
- /* Determine alpha value */
- alpha = ( has_alpha ?
- channel[ png->channels - 1 ] : max );
-
- /* Convert to RGB value */
- pixel = 0;
- for ( c = 0 ; c < 3 ; c++ ) {
- raw = channel[ is_rgb ? c : 0 ];
- value = png_pixel ( raw, alpha, max );
- assert ( value <= 255 );
- pixel = ( ( pixel << 8 ) | value );
- }
- }
-
- /* Store pixel */
- copy_to_user ( png->pixbuf->data, pixbuf_offset,
- &pixel, sizeof ( pixel ) );
- pixbuf_offset += pixbuf_x_stride;
- }
-
- /* Move to next output row */
- pixbuf_y_offset += pixbuf_y_stride;
- }
-
- /* Update offset */
- png->raw.offset = raw_offset;
- }
-
- /**
- * Fill PNG pixels
- *
- * @v image PNG image
- * @v png PNG context
- *
- * This routine may assume that it is impossible to overrun either the
- * raw data buffer or the pixel buffer, since the sizes of both are
- * determined by the image dimensions.
- */
- static void png_pixels ( struct image *image, struct png_context *png ) {
- struct png_interlace interlace;
- unsigned int pass;
-
- /* Process each interlace pass */
- png->raw.offset = 0;
- for ( pass = 0 ; pass < png->passes ; pass++ ) {
-
- /* Calculate interlace pass parameters */
- png_interlace ( png, pass, &interlace );
-
- /* Skip zero-width rows (which have no filter bytes) */
- if ( interlace.width == 0 )
- continue;
-
- /* Unfilter this pass */
- png_pixels_pass ( image, png, &interlace );
- }
- assert ( png->raw.offset == png->raw.len );
- }
-
- /**
- * Handle PNG image end chunk
- *
- * @v image PNG image
- * @v png PNG context
- * @v len Chunk length
- * @ret rc Return status code
- */
- static int png_image_end ( struct image *image, struct png_context *png,
- size_t len ) {
- int rc;
-
- /* Sanity checks */
- if ( len != 0 ) {
- DBGC ( image, "PNG %s invalid IEND length %zd\n",
- image->name, len );
- return -EINVAL;
- }
- if ( ! png->pixbuf ) {
- DBGC ( image, "PNG %s missing pixel buffer (no IHDR?)\n",
- image->name );
- return -EINVAL;
- }
- if ( ! deflate_finished ( &png->deflate ) ) {
- DBGC ( image, "PNG %s decompression not complete\n",
- image->name );
- return -EINVAL;
- }
- if ( png->raw.offset != png->raw.len ) {
- DBGC ( image, "PNG %s incorrect decompressed length (expected "
- "%zd, got %zd)\n", image->name, png->raw.len,
- png->raw.offset );
- return -EINVAL;
- }
-
- /* Unfilter raw data */
- if ( ( rc = png_unfilter ( image, png ) ) != 0 )
- return rc;
-
- /* Fill pixel buffer */
- png_pixels ( image, png );
-
- return 0;
- }
-
- /** A PNG chunk handler */
- struct png_chunk_handler {
- /** Chunk type */
- uint32_t type;
- /**
- * Handle chunk
- *
- * @v image PNG image
- * @v png PNG context
- * @v len Chunk length
- * @ret rc Return status code
- */
- int ( * handle ) ( struct image *image, struct png_context *png,
- size_t len );
- };
-
- /** PNG chunk handlers */
- static struct png_chunk_handler png_chunk_handlers[] = {
- { htonl ( PNG_TYPE_IHDR ), png_image_header },
- { htonl ( PNG_TYPE_PLTE ), png_palette },
- { htonl ( PNG_TYPE_IDAT ), png_image_data },
- { htonl ( PNG_TYPE_IEND ), png_image_end },
- };
-
- /**
- * Handle PNG chunk
- *
- * @v image PNG image
- * @v png PNG context
- * @v type Chunk type
- * @v len Chunk length
- * @ret rc Return status code
- */
- static int png_chunk ( struct image *image, struct png_context *png,
- uint32_t type, size_t len ) {
- struct png_chunk_handler *handler;
- unsigned int i;
-
- DBGC ( image, "PNG %s chunk type %s offset %zd length %zd\n",
- image->name, png_type_name ( type ), png->offset, len );
-
- /* Handle according to chunk type */
- for ( i = 0 ; i < ( sizeof ( png_chunk_handlers ) /
- sizeof ( png_chunk_handlers[0] ) ) ; i++ ) {
- handler = &png_chunk_handlers[i];
- if ( handler->type == type )
- return handler->handle ( image, png, len );
- }
-
- /* Fail if unknown chunk type is critical */
- if ( ! ( type & htonl ( PNG_CHUNK_ANCILLARY ) ) ) {
- DBGC ( image, "PNG %s unknown critical chunk type %s\n",
- image->name, png_type_name ( type ) );
- return -ENOTSUP;
- }
-
- /* Ignore non-critical unknown chunk types */
- return 0;
- }
-
- /**
- * Convert PNG image to pixel buffer
- *
- * @v image PNG image
- * @v pixbuf Pixel buffer to fill in
- * @ret rc Return status code
- */
- static int png_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) {
- struct png_context *png;
- struct png_chunk_header header;
- struct png_chunk_footer footer;
- size_t remaining;
- size_t chunk_len;
- int rc;
-
- /* Allocate and initialise context */
- png = zalloc ( sizeof ( *png ) );
- if ( ! png ) {
- rc = -ENOMEM;
- goto err_alloc;
- }
- png->offset = sizeof ( struct png_signature );
- deflate_init ( &png->deflate, DEFLATE_ZLIB );
-
- /* Process chunks */
- do {
-
- /* Extract chunk header */
- remaining = ( image->len - png->offset );
- if ( remaining < sizeof ( header ) ) {
- DBGC ( image, "PNG %s truncated chunk header at offset "
- "%zd\n", image->name, png->offset );
- rc = -EINVAL;
- goto err_truncated;
- }
- copy_from_user ( &header, image->data, png->offset,
- sizeof ( header ) );
- png->offset += sizeof ( header );
-
- /* Validate chunk length */
- chunk_len = ntohl ( header.len );
- if ( remaining < ( sizeof ( header ) + chunk_len +
- sizeof ( footer ) ) ) {
- DBGC ( image, "PNG %s truncated chunk data/footer at "
- "offset %zd\n", image->name, png->offset );
- rc = -EINVAL;
- goto err_truncated;
- }
-
- /* Handle chunk */
- if ( ( rc = png_chunk ( image, png, header.type,
- chunk_len ) ) != 0 )
- goto err_chunk;
-
- /* Move to next chunk */
- png->offset += ( chunk_len + sizeof ( footer ) );
-
- } while ( png->offset < image->len );
-
- /* Check that we finished with an IEND chunk */
- if ( header.type != htonl ( PNG_TYPE_IEND ) ) {
- DBGC ( image, "PNG %s did not finish with IEND\n",
- image->name );
- rc = -EINVAL;
- goto err_iend;
- }
-
- /* Return pixel buffer */
- *pixbuf = pixbuf_get ( png->pixbuf );
-
- /* Success */
- rc = 0;
-
- err_iend:
- err_chunk:
- err_truncated:
- pixbuf_put ( png->pixbuf );
- ufree ( png->raw.data );
- free ( png );
- err_alloc:
- return rc;
- }
-
- /**
- * Probe PNG image
- *
- * @v image PNG image
- * @ret rc Return status code
- */
- static int png_probe ( struct image *image ) {
- struct png_signature signature;
-
- /* Sanity check */
- if ( image->len < sizeof ( signature ) ) {
- DBGC ( image, "PNG %s is too short\n", image->name );
- return -ENOEXEC;
- }
-
- /* Check signature */
- copy_from_user ( &signature, image->data, 0, sizeof ( signature ) );
- if ( memcmp ( &signature, &png_signature, sizeof ( signature ) ) != 0 ){
- DBGC ( image, "PNG %s has invalid signature\n", image->name );
- return -ENOEXEC;
- }
-
- return 0;
- }
-
- /** PNG image type */
- struct image_type png_image_type __image_type ( PROBE_NORMAL ) = {
- .name = "PNG",
- .probe = png_probe,
- .pixbuf = png_pixbuf,
- };
|