| 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,
 - };
 
 
  |