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