Posts tagged AlivePDF
Reinventing the wheel – PNG Encoder Transparency
Apr 20th
AlivePDF is an easy to use library to create PDF’s directly in Flash Player. There are several posts, i.e. here and here, describing how you can insert a hires image of any DisplayObject, i.e. a chart. These posts tell you how to modify a PNGEncoder in a way that it discards transparency – but in case your DisplayObject had some actual transparency, it won’t end up looking very good – transparent parts turn black, semi-transparent dark – what you really want is to give the DisplayObject a solid white background.
You can specify solid background colour on nearly any Flex component or you can always place the component in a white Canvas (or on white Rect in Flex 4).
Finally, the obvious approach (as we already had to hack it anyway) is to modify the mx.graphics.codec.PNGEncoder to replace transparency with white. There are no doubt great PNGEncoders out there, but I didn’t quickly find one that would discard transparency and extend IImageEncoder interface, so I jumped at the (rare) opportunity to have a play with >> and & operators – and have done one for you. The downside is that it’s not very fast – that’s likely caused by my poor coding, please feel free to suggest how to optimise it.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | //////////////////////////////////////////////////////////////////////////////// // // NonTransparentPNGEncoder.as // STEHIL.COM, 2010 // example usage with AlivePDF: // var p:PDF = new PDF( Orientation.PORTRAIT, Unit.MM, Size.A4 ); // p.addPage(); // var linechartSnap:ImageSnapshot = ImageSnapshot.captureImage(linechart, IMAGE_DPI, new NonTransparentPNGEncoder()); // p.addImageStream(linechartSnap.data, ColorSpace.DEVICE_RGB, null, 0, 150, 180, 80); // // based on mx.graphics.codec.PNGEncoder // // ADOBE SYSTEMS INCORPORATED // Copyright 2007 Adobe Systems Incorporated // All Rights Reserved. // // NOTICE: Adobe permits you to use, modify, and distribute this file // in accordance with the terms of the license agreement accompanying it. // //////////////////////////////////////////////////////////////////////////////// package com.stehil.util { import flash.display.BitmapData; import flash.utils.ByteArray; import mx.graphics.codec.IImageEncoder; /** * The PNGEncoder class converts raw bitmap images into encoded * images using Portable Network Graphics (PNG) lossless compression. * * <p>For the PNG specification, see http://www.w3.org/TR/PNG/</p>. */ public class NonTransparentPNGEncoder implements IImageEncoder { //-------------------------------------------------------------------------- // // Class constants // //-------------------------------------------------------------------------- /** * @private * The MIME type for a PNG image. */ private static const CONTENT_TYPE:String = "image/png"; //-------------------------------------------------------------------------- // // Constructor // //-------------------------------------------------------------------------- /** * Constructor. */ public function NonTransparentPNGEncoder() { super(); initializeCRCTable(); } //-------------------------------------------------------------------------- // // Variables // //-------------------------------------------------------------------------- /** * @private * Used for computing the cyclic redundancy checksum * at the end of each chunk. */ private var crcTable:Array; //-------------------------------------------------------------------------- // // Properties // //-------------------------------------------------------------------------- //---------------------------------- // contentType //---------------------------------- /** * The MIME type for the PNG encoded image. * The value is <code>"image/png"</code>. */ public function get contentType():String { return CONTENT_TYPE; } //-------------------------------------------------------------------------- // // Methods // //-------------------------------------------------------------------------- /** * Converts the pixels of a BitmapData object * to a PNG-encoded ByteArray object. * * @param bitmapData The input BitmapData object. * * @return Returns a ByteArray object containing PNG-encoded image data. */ public function encode(bitmapData:BitmapData):ByteArray { return internalEncode(bitmapData, bitmapData.width, bitmapData.height, bitmapData.transparent); } /** * Converts a ByteArray object containing raw pixels * in 32-bit ARGB (Alpha, Red, Green, Blue) format * to a new PNG-encoded ByteArray object. * The original ByteArray is left unchanged. * * @param byteArray The input ByteArray object containing raw pixels. * This ByteArray should contain * <code>4 * width * height</code> bytes. * Each pixel is represented by 4 bytes, in the order ARGB. * The first four bytes represent the top-left pixel of the image. * The next four bytes represent the pixel to its right, etc. * Each row follows the previous one without any padding. * * @param width The width of the input image, in pixels. * * @param height The height of the input image, in pixels. * * @param transparent If <code>false</code>, alpha channel information * is ignored but you still must represent each pixel * as four bytes in ARGB format. * * @return Returns a ByteArray object containing PNG-encoded image data. */ public function encodeByteArray(byteArray:ByteArray, width:int, height:int, transparent:Boolean = true):ByteArray { return internalEncode(byteArray, width, height, transparent); } /** * @private */ private function initializeCRCTable():void { crcTable = []; for (var n:uint = 0; n < 256; n++) { var c:uint = n; for (var k:uint = 0; k < 8; k++) { if (c & 1) c = uint(uint(0xedb88320) ^ uint(c >>> 1)); else c = uint(c >>> 1); } crcTable[n] = c; } } /** * @private */ private function internalEncode(source:Object, width:int, height:int, transparent:Boolean = true):ByteArray { // The source is either a BitmapData or a ByteArray. var sourceBitmapData:BitmapData = source as BitmapData; var sourceByteArray:ByteArray = source as ByteArray; if (sourceByteArray) sourceByteArray.position = 0; // Create output byte array var png:ByteArray = new ByteArray(); // Write PNG signature png.writeUnsignedInt(0x89504E47); png.writeUnsignedInt(0x0D0A1A0A); // Build IHDR chunk var IHDR:ByteArray = new ByteArray(); IHDR.writeInt(width); IHDR.writeInt(height); IHDR.writeByte(8); // bit depth per channel IHDR.writeByte(2); // color type: RGBA IHDR.writeByte(0); // compression method IHDR.writeByte(0); // filter method IHDR.writeByte(0); // interlace method writeChunk(png, 0x49484452, IHDR); // Build IDAT chunk var IDAT:ByteArray = new ByteArray(); var alpha:uint; var r:uint; var g:uint; var b:uint; for (var y:int = 0; y < height; y++) { IDAT.writeByte(0); // no filter var x:int; var pixel:uint; for (x = 0; x < width; x++) { pixel = sourceBitmapData.getPixel32(x, y); alpha = pixel >> 24 & 0xFF; //alpha=255 if no transparency r = pixel >> 16 & 0xFF; g = pixel >> 8 & 0xFF; b = pixel & 0xFF; alpha = 0xFF - alpha; //invert alpha value r+=alpha; g+=alpha; b+=alpha; IDAT.writeByte(limit8byte(r)); IDAT.writeByte(limit8byte(g)); IDAT.writeByte(limit8byte(b)); } } IDAT.compress(); writeChunk(png, 0x49444154, IDAT); // Build IEND chunk writeChunk(png, 0x49454E44, null); // return PNG png.position = 0; return png; } private function limit8byte(n:uint):uint { if (n>0xFF) return 0xFF; else return n; } /** * @private */ private function writeChunk(png:ByteArray, type:uint, data:ByteArray):void { // Write length of data. var len:uint = 0; if (data) len = data.length; png.writeUnsignedInt(len); // Write chunk type. var typePos:uint = png.position; png.writeUnsignedInt(type); // Write data. if (data) png.writeBytes(data); // Write CRC of chunk type and data. var crcPos:uint = png.position; png.position = typePos; var crc:uint = 0xFFFFFFFF; for (var i:uint = typePos; i < crcPos; i++) { crc = uint(crcTable[(crc ^ png.readUnsignedByte()) & uint(0xFF)] ^ uint(crc >>> 8)); } crc = uint(crc ^ uint(0xFFFFFFFF)); png.position = crcPos; png.writeUnsignedInt(crc); } } } |
