package { import flash.display.*; import com.adobe.images.*; import flash.utils.*; import flash.net.*; import flash.geom.Matrix; import flash.geom.Rectangle; import flash.geom.Point; public class PNGExportHelper { public static var UseExpandedBitmaps = false; public static var AlphaThreshold = 5; private static function ExpandBitmapBorders(bd:BitmapData, bgColor = 0x0, passes = 1):BitmapData { /* var thresh:BitmapData = new BitmapData(bd.width, bd.height, true, bgColor); thresh.threshold(bd, bd.rect, new Point(), "<", (20 << 24), bgColor, 0xFF000000, true); */ var sampled:BitmapData = new BitmapData(bd.width, bd.height, true, bgColor); bd.lock(); sampled.lock(); for (var y = 0; y < bd.height; y++) { for (var x = 0; x < bd.width; x++) { var oldVal:uint = bd.getPixel32(x, y); var a:uint = (oldVal & 0xFF000000) >>> 24; var rgb:uint = (oldVal & 0xFFFFFF); if (a > AlphaThreshold) { sampled.setPixel32(x, y, (0xFF << 24) | rgb); } else { sampled.setPixel32(x, y, bgColor); } } } sampled.unlock(); bd.unlock(); return ExpandBitmapBordersInternal(sampled, bgColor, passes); } private static function ExpandBitmapBordersInternal(bd:BitmapData, bgColor, passes):BitmapData { var output:BitmapData = new BitmapData(bd.width, bd.height, true, bgColor); var scale = 16; var scaled:BitmapData = new BitmapData(bd.width/scale, bd.height/scale, true, bgColor); var mat:Matrix = new Matrix(); mat.scale(1/scale, 1/scale); scaled.draw(bd, mat, null, null, null, true); output.lock(); var colors:Array = new Array(); var weights:Array = new Array(); for (var oy = 0; oy < scaled.height; oy++) { for (var ox = 0; ox < scaled.width; ox++) { var scaledVal:uint = scaled.getPixel32(ox, oy); var scaleda:uint = (scaledVal & 0xFF000000) >>> 24; if (scaleda == 0 || scaleda == 255) { output.copyPixels(bd, new Rectangle(ox * scale, oy * scale, scale, scale), new Point(ox * scale, oy * scale)); continue; } else { //_strace(scaleda+""); } for (var y = oy * scale; y < oy * scale + scale; y++) { for (var x = ox * scale; x < ox * scale + scale; x++) { var oldVal:uint = bd.getPixel32(x, y); var a:uint = (oldVal & 0xFF000000) >>> 24; var rgb:uint = (oldVal & 0xFFFFFF); if (a == 0 && rgb == bgColor) { colors.length = 0; weights.length = 0; for (var y1 = y - 2; y1 <= y + 2; y1++) { for (var x1 = x - 2; x1 <= x + 2; x1++) { var dist = Math.abs(x1 - x) + Math.abs(y1 - y); if (dist >= 3) continue; if (x1 >= 0 && x1 < bd.width && y1 >= 0 && y1 < bd.height) { colors.push(bd.getPixel(x1, y1)); weights.push(1 - dist * 0.2); } } } var newVal = AveragePixels(colors, weights, bgColor); if (newVal == null) { newVal = oldVal; } else { var newR:uint = (newVal & 0xFF0000) >> 16; var newG:uint = (newVal & 0xFF00) >> 8; var newB:uint = (newVal & 0xFF); if (newVal != 0 && colors.length >= 4) { var bp = true; } var sorted = colors.sort(function(a, b) { var aR:uint = (a & 0xFF0000) >> 16; var aG:uint = (a & 0xFF00) >> 8; var aB:uint = (a & 0xFF); var bR:uint = (b & 0xFF0000) >> 16; var bG:uint = (b & 0xFF00) >> 8; var bB:uint = (b & 0xFF); var diffA = Math.abs(aR - newR) + Math.abs(aG - newG) + Math.abs(aB - newB); var diffB = Math.abs(bR - newR) + Math.abs(bG - newG) + Math.abs(bB - newB); return diffA - diffB; }, Array.RETURNINDEXEDARRAY | Array.DESCENDING); var toRemove = Math.min(2, colors.length - 4); var removed = 0; for (var i = 0; i < colors.length; i++) { var index = sorted.indexOf(i + removed); if (index < toRemove) { colors.splice(i, 1); weights.splice(i, 1); removed++; i--; if (removed == toRemove) break; } } var fixOutliersVal = AveragePixels(colors, weights, bgColor); if (fixOutliersVal == null) newVal = (0xFF << 24) | newVal; newVal = (0xFF << 24) | fixOutliersVal; } output.setPixel32(x, y, newVal); } else { output.setPixel32(x, y, oldVal); } } } }//end outer scaled x }//End outer scaled y bd.unlock(); output.unlock(); passes--; if (passes > 0) { return ExpandBitmapBordersInternal(output, bgColor, passes); } else { return output; } } private static function AveragePixels(colors:Array, weights:Array, bgColor:uint):uint { var rTotal = 0; var gTotal = 0; var bTotal = 0; var wTotal = 0; for (var i = 0; i < colors.length; i++) { var color:uint = colors[i]; var a:uint = (color & 0xFF000000) >>> 24; var rgb:uint = (color & 0xFFFFFF); if (a == 0 && rgb == bgColor) { colors.splice(i, 1); weights.splice(i, 1); i--; continue; } var r:uint = (rgb & 0xFF0000) >>> 16; var g:uint = (rgb & 0xFF00) >>> 8; var b:uint = (rgb & 0xFF); var w = weights[i]; rTotal += w * r; gTotal += w * g; bTotal += w * b; wTotal += w; } if (wTotal == 0) { return null; } rTotal /= wTotal; gTotal /= wTotal; bTotal /= wTotal; var rFinal = Math.max(0, Math.min(255, Math.round(rTotal))); var gFinal = Math.max(0, Math.min(255, Math.round(gTotal))); var bFinal = Math.max(0, Math.min(255, Math.round(bTotal))); return (uint(rFinal) << 16) | (uint(gFinal) << 8) | (uint(bFinal)); } public static function GetExportPNG(bd:BitmapData):ByteArray { if (!UseExpandedBitmaps) return PNGEncoder.encode(bd); var expanded:BitmapData = ExpandBitmapBorders(bd, 0x0, 2); //var fr:FileReference = new FileReference(); //fr.save(PNGEncoder.encode(expanded), "output.png"); return PNGEncoderWithAlpha.encode(expanded, bd); } } }