Resize比較
画像縮小方法色々です。
Bicubic、Bilinear、Nearest naibour、Lanczosと、Flashの普通の拡大縮小を比較してます。
それぞれ横にならんでいるので、画面外にはみ出ているものはドラッグアンドドロップで動かして確認してみてください。
Bicubic、Bilinear、Nearest naibourはPixelbenderを利用してます。
Pixel Benderを使ったBicubic法がよくわからなかったので、下記のBicubic resampling by Pixel Benderを使わせてもらってます。
http://blog.onthewings.net/2009/08/25/bicubic-resampling-by-pixel-bender/
LanczosはJozef Chutka氏のLanczos Resamplingを使わせて頂きました
http://wonderfl.net/c/dLf0
Jozef Chutka氏のLanczos法が一番綺麗に縮小できてる気がします。
元々Pixel Benderを使ったBicubicに期待して色々調べていたのですが、やってみるとイマイチな結果に…。
/**
* Copyright foka ( http://wonderfl.net/user/foka )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/dJ7M
*/
package
{
import com.bit101.components.InputText;
import com.bit101.components.Label;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.system.LoaderContext;
import flash.system.Security;
import com.bit101.components.PushButton;
import flash.net.URLRequest;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
/**
* --
* 画像縮小方法色々です。
* Bicubic、Bilinear、Nearest naibour、Lanczosと、Flashの普通の拡大縮小を比較してます。
* それぞれ横にならんでいるので、画面外にはみ出ているものはドラッグアンドドロップで動かして確認してみてください。
*
* Bicubic、Bilinear、Nearest naibourはPixelbenderを利用してます。
* Pixel Benderを使ったBicubic法がよくわからなかったので、下記のBicubic resampling by Pixel Benderを使わせてもらってます。
* http://blog.onthewings.net/2009/08/25/bicubic-resampling-by-pixel-bender/
*
* LanczosはJozef Chutka氏のLanczos Resamplingを使わせて頂きました
* http://wonderfl.net/c/dLf0
*
* Jozef Chutka氏のLanczos法が一番綺麗に縮小できてる気がします。
* 元々Pixel Benderを使ったBicubicに期待して色々調べていたのですが、やってみるとイマイチな結果に…。
*/
public class Main extends Sprite
{
private var bilinear:BilinearResampling = new BilinearResampling();
private var bicubic:BicubicResampling = new BicubicResampling();
private var neaest:NearestResampling = new NearestResampling();
private var souceBitmapData:BitmapData;
private var targetScale:Number = 0.3;
private var container:Sprite = new Sprite();
private var pushbutton:PushButton;
private var inputText:InputText;
private var loader:Loader = new Loader();
/*------------------------------------------------
コンストラクタ
------------------------------------------------*/
public function Main():void {
Security.loadPolicyFile("http://data.tsukuenoue.com/crossdomain.xml");
var context:LoaderContext = new LoaderContext(true);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest("http://data.tsukuenoue.com/resize/fusya.jpg"),context);
addChild(container);
}
/*------------------------------------------------
イベント
------------------------------------------------*/
private function onLoadComplete(e:Event):void
{
pushbutton = new PushButton(this, 170, 4, "resize", onClick);
var label:Label = new Label(this, 5, 5,"scale");
inputText = new InputText(this, 30, 6,"0.3");
loader.x = 8;
loader.y = 40;
addChild(loader);
souceBitmapData =Bitmap(loader.content).bitmapData;
container.x =8;
container.y =40;
container.addEventListener(MouseEvent.MOUSE_DOWN, onDn);
container.addEventListener(MouseEvent.ROLL_OVER, onOver);
container.addEventListener(MouseEvent.ROLL_OUT, onOut);
}
private function onOver(e:MouseEvent):void
{
Mouse.cursor = MouseCursor.HAND;
}
private function onOut(e:MouseEvent):void
{
Mouse.cursor = MouseCursor.AUTO;
}
private function onDn(e:MouseEvent):void
{
stage.addEventListener(MouseEvent.MOUSE_UP, onUP);
container.startDrag();
}
private function onUP(e:MouseEvent):void
{
stage.removeEventListener(MouseEvent.MOUSE_UP, onUP);
container.stopDrag();
}
private function onClick(e:MouseEvent):void
{
targetScale = Number(inputText.text);
if(targetScale>0){
reset();
loader.visible = true;
bicubicResampling();
bilinearResampling();
neaestResampling();
LanczosResampleing();
flashScaleResampling();
loader.visible = false;
}
}
/*------------------------------------------------
メソッド
------------------------------------------------*/
private function bicubicResampling():void {
var returnBmpData:BitmapData = bicubic.resample(souceBitmapData, targetScale, targetScale);
var bmp:Bitmap = new Bitmap(returnBmpData);
container.addChild(bmp);
var label:Label = new Label(container, bmp.x, bmp.height + 5,"Bicubic");
}
private function bilinearResampling():void {
var returnBmpData:BitmapData = bilinear.resample(souceBitmapData, targetScale);
var bmp:Bitmap = new Bitmap(returnBmpData);
container.addChild(bmp);
bmp.x = Math.round(loader.width * targetScale + 20);
var label:Label = new Label(container, bmp.x, bmp.height + 5,"Bilinear");
}
private function neaestResampling():void {
var returnBmpData:BitmapData = neaest.resample(souceBitmapData, targetScale);
var bmp:Bitmap = new Bitmap(returnBmpData);
container.addChild(bmp);
bmp.x = Math.round((loader.width * targetScale + 20)*2);
var label:Label = new Label(container, bmp.x, bmp.height + 5 ,"Neaest");
}
private function LanczosResampleing():void {
var returnBmpData:BitmapData = LanczosBitmapDataResizer.resize(souceBitmapData, Math.round(loader.width*targetScale), Math.round(loader.height*targetScale));
var bmp:Bitmap = new Bitmap(returnBmpData);
container.addChild(bmp);
bmp.x = Math.round((loader.width * targetScale + 20)*3);
var label:Label = new Label(container, bmp.x, bmp.height + 5,"Lanczos");
}
private function flashScaleResampling():void {
var bmp:Bitmap = new Bitmap(souceBitmapData);
bmp.smoothing = true;
var bmpContainer:Sprite = new Sprite();
container.addChild(bmpContainer);
bmpContainer.addChild(bmp);
bmp.scaleX = bmp.scaleY = targetScale;
bmpContainer.x = Math.round((loader.width * targetScale + 20) * 4);
var label:Label = new Label(container, bmpContainer.x, bmpContainer.height + 5,"FlashScale");
}
private function reset():void {
while (container.numChildren > 0) {
container.removeChildAt(0);
}
container.x =8;
container.y =40;
}
}
}
import flash.display.BitmapData;
import flash.display.Shader;
import flash.display.ShaderJob;
import flash.events.Event;
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.utils.Dictionary;
/*
* Bicubicは下記のBicubic resampling by Pixel Benderを使わせてもらってます。
* http://blog.onthewings.net/2009/08/25/bicubic-resampling-by-pixel-bender/
*
* */
class BicubicResampling
{
private var loader:URLLoader= new URLLoader;
/*------------------------------------------------
コンストラクタ
------------------------------------------------*/
public function BicubicResampling():void {
loader.addEventListener(Event.COMPLETE, onComplete);
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.load(new URLRequest("http://data.tsukuenoue.com/resize/bicubicResampling.pbj"));
}
/*------------------------------------------------
イベント
------------------------------------------------*/
private function onComplete(e:Event):void
{
loader.removeEventListener(Event.COMPLETE, onComplete);
}
/*------------------------------------------------
メソッド
------------------------------------------------*/
public function resample(srcBD:BitmapData,scaleX:Number,scaleY:Number):BitmapData {
var bicubicResamplingShader:Shader = new Shader(loader.data);
bicubicResamplingShader.data.src.input = srcBD;
bicubicResamplingShader.data.scale.value = [1/scaleX,1/scaleY];
var resultBD:BitmapData = new BitmapData(srcBD.width*scaleX,srcBD.height*scaleY);
var shaderJob:ShaderJob = new ShaderJob(bicubicResamplingShader);
shaderJob.target = resultBD;
shaderJob.start(true);
return resultBD;
}
}
/*
* Bilinear、Nearest neighbor はPixel Bender内に機能があるっぽいので、それを使ってます。
*
* */
class BilinearResampling
{
private var loader:URLLoader= new URLLoader;
/*------------------------------------------------
コンストラクタ
------------------------------------------------*/
public function BilinearResampling():void {
loader.addEventListener(Event.COMPLETE, onComplete);
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.load(new URLRequest("http://data.tsukuenoue.com/resize/bilinear.pbj"));
}
/*------------------------------------------------
イベント
------------------------------------------------*/
private function onComplete(e:Event):void
{
loader.removeEventListener(Event.COMPLETE, onComplete);
}
/*------------------------------------------------
メソッド
------------------------------------------------*/
public function resample(srcBD:BitmapData,scale:Number):BitmapData {
var shader:Shader = new Shader(loader.data);
shader.data.src.input = srcBD;
shader.data.scale.value = [1/scale];
var resultBD:BitmapData = new BitmapData(srcBD.width*scale,srcBD.height*scale);
var shaderJob:ShaderJob = new ShaderJob(shader);
shaderJob.target = resultBD;
shaderJob.start(true);
return resultBD;
}
}
class NearestResampling
{
private var loader:URLLoader= new URLLoader;
/*------------------------------------------------
コンストラクタ
------------------------------------------------*/
public function NearestResampling():void {
loader.addEventListener(Event.COMPLETE, onComplete);
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.load(new URLRequest("http://data.tsukuenoue.com/resize/nearest.pbj"));
}
/*------------------------------------------------
イベント
------------------------------------------------*/
private function onComplete(e:Event):void
{
loader.removeEventListener(Event.COMPLETE, onComplete);
}
/*------------------------------------------------
メソッド
------------------------------------------------*/
public function resample(srcBD:BitmapData,scale:Number):BitmapData {
var shader:Shader = new Shader(loader.data);
shader.data.src.input = srcBD;
shader.data.scale.value = [1/scale];
var resultBD:BitmapData = new BitmapData(srcBD.width*scale,srcBD.height*scale);
var shaderJob:ShaderJob = new ShaderJob(shader);
shaderJob.target = resultBD;
shaderJob.start(true);
return resultBD;
}
}
/*
* 以下、Jozef Chutka氏のLanczos Resamplingを使わせて頂きました
* http://wonderfl.net/c/dLf0
*
* */
class LanczosBitmapDataResizer
{
public static var CACHE:Dictionary;
public static var CACHE_PRECISION:uint = 100;
public static var FILTER_SIZE:uint = 1;
public function LanczosBitmapDataResizer()
{
}
public static function kernel(filterSize:uint, x:Number):Number
{
if(x >= filterSize || x <= -filterSize)
return 0;
if(x == 0)
return 1;
var xpi:Number = x * Math.PI;
return filterSize * Math.sin(xpi) * Math.sin(xpi / filterSize)
/ (xpi * xpi);
}
public static function createCache(kernel:Function,
cachePrecision:uint, filterSize:uint):Dictionary
{
var cache:Dictionary = new Dictionary();
var max:uint = filterSize * filterSize * cachePrecision;
var iPrecision:Number = 1 / cachePrecision;
var value:Number;
for(var cacheKey:uint = 0; cacheKey < max; cacheKey++)
{
value = kernel(filterSize, Math.sqrt(cacheKey * iPrecision));
cache[cacheKey] = value < 0 ? 0 : value;
}
return cache;
}
public static function resize(source:BitmapData, width:uint, height:uint, kernel:Function=null):BitmapData
{
var total:Number, distanceY:Number, value:Number;
var a:Number, r:Number, g:Number, b:Number;
var i:uint, color:uint, cacheKey:uint;
var x:int, x1:uint, x1b:int, x1e:int;
var y:int, y1:uint, y1b:int, y1e:int, y2:uint, y3:uint;
var y1et:Number, x1et:Number;
var values:Vector.<Number> = new Vector.<Number>();
var sx:Number = width / source.width;
var sy:Number = height / source.height;
var sw1:uint = source.width - 1;
var sh1:uint = source.height - 1;
var isx:Number = 1 / sx;
var isy:Number = 1 / sy;
var cw:Number = 1 / width;
var ch:Number = 1 / height;
var csx:Number = Math.min(1, sx) * Math.min(1, sx);
var csy:Number = Math.min(1, sy) * Math.min(1, sy);
var cx:Number, cy:Number;
var sourcePixelX:Number, sourcePixelY:Number;
var sourcePixels:Vector.<uint> = source.getVector(source.rect);
var output:BitmapData =
new BitmapData(width, height, source.transparent);
var outputPixels:Vector.<uint> =
new Vector.<uint>(width * height, true);
var cache:Dictionary = CACHE;
var cachePrecision:uint = CACHE_PRECISION;
var filterSize:uint = FILTER_SIZE;
var kernel:Function = kernel || public::kernel;
if(!cache)
CACHE = cache = createCache(kernel, cachePrecision, filterSize);
y = height;
while(y--)
{
sourcePixelY = (y + 0.5) * isy;
y1b = sourcePixelY - filterSize;
if(y1b < 0)
y1b = 0;
y1e = y1et = sourcePixelY + filterSize;
if(y1e != y1et)
y1e = y1et + 1;
if(y1e > sh1)
y1e = sh1;
cy = y * ch - sourcePixelY;
y3 = y * width;
x = width;
while(x--)
{
sourcePixelX = (x + 0.5) * isx;
x1b = sourcePixelX - filterSize;
if(x1b < 0)
x1b = 0;
x1e = x1et = sourcePixelX + filterSize;
if(x1e != x1et)
x1e = x1et + 1;
if(x1e > sw1)
x1e = sw1;
cx = x * cw - sourcePixelX;
values.length = i = total = 0;
for(y1 = y1b; y1 <= y1e; y1++)
{
distanceY = (y1 + cy) * (y1 + cy) * csy;
for(x1 = x1b; x1 <= x1e; x1++)
{
total += values[uint(i++)] = cache[uint(
((x1 + cx) * (x1 + cx) * csx + distanceY)
* cachePrecision)]||0;
}
}
total = 1 / total;
i = a = r = g = b = 0;
for(y1 = y1b; y1 <= y1e; y1++)
{
y2 = y1 * source.width;
for(x1 = x1b; x1 <= x1e; x1++)
{
color = sourcePixels[uint(y2 + x1)];
value = values[uint(i++)] * total;
a += (color >> 24 & 0xff) * value;
r += (color >> 16 & 0xff) * value;
g += (color >> 8 & 0xff) * value;
b += (color & 0xff) * value;
}
}
outputPixels[uint(x + y3)] = a << 24 | r << 16 | g << 8 | b;
}
}
output.setVector(output.rect, outputPixels);
return output;
}
}