In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Resize比較2

画像縮小検証その2です。
今回は2段階縮小法(正式名称不明)を主に検証。
結果は横にならんでいるので、画面外にはみ出てたらドラッグして確認してください。

前回↓ではpixelbender利用のリサイズはイマイチだったので省略。(縮小用に最適化されてないのかもしれませんが)
http://wonderfl.net/c/dJ7M

他にも処理速度の問題もあり、別を探っていたら2段階縮小という方法があったので試してみると、なかなか綺麗で、それなりに早く、仕組も簡単、と上々の結果になりました。
処理速度については、Lanczos法の有無を選べるようにしてあるので、scaleを大き目にして比べてみてください。

元々リサイズAIRアプリ↓を作ろうとして調べてたのですが、2段階縮小法を使ってようやく形になりました。
http://mrsz.tsukuenoue.com/
/**
 * Copyright foka ( http://wonderfl.net/user/foka )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/pXpl
 */

package
{
	import com.bit101.components.InputText;
	import com.bit101.components.Label;
	import com.bit101.components.PushButton;
	import com.bit101.components.HUISlider;
	import com.bit101.components.ComboBox;
	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.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.Security;
	import flash.ui.Mouse;
	import flash.ui.MouseCursor;


	 
	public class Main extends Sprite
	{
		private var souceBitmapData:BitmapData;
		private var targetScale:Number;
		private var container:Sprite = new Sprite();
		private var pushbutton:PushButton;
		private var loader:Loader = new Loader();
		private var slider:HUISlider;
		private var comboBox:ComboBox;
		private var LanchozFlg:Boolean = true;
		/*------------------------------------------------
			コンストラクタ
		------------------------------------------------*/
		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, 185, 4, "resize", onClick);
			slider = new HUISlider(this, 5, 5,"scale");
			slider.maximum = 1;
			slider.minimum = 0.01;
			slider.labelPrecision = 2;
			slider.value = 0.3;
			
			var selectArray:Array = ["TwoStep + Lanczos + Normal", "Twostep + Normal"];
			comboBox = new ComboBox(this, 300, 4, selectArray[0], selectArray);
			comboBox.width = 160;
			comboBox.addEventListener(Event.SELECT, onBoxSelect);
			
			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 onBoxSelect(e:Event):void 
		{
			if (comboBox.selectedIndex == 0) {
				LanchozFlg = true;
			}else {
				LanchozFlg = false;
			}
			resize();
		}
		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 
		{
			resize();
		}


		/*------------------------------------------------
			メソッド
		------------------------------------------------*/
		private function resize():void {
			//targetScale = Number(inputText.text);
			targetScale = slider.value;
			if(targetScale>0){
				reset();
				loader.visible = true;
				twoStepScaleResampling();
				if(LanchozFlg){
					LanczosResampleing();
				}
				flashScaleResampling();
				loader.visible = false;
			}
		}
		
		
		private function twoStepScaleResampling():void {
			var returnBmpData:BitmapData = TwoStepResizer.resize(souceBitmapData,  Math.round(loader.width*targetScale), Math.round(loader.height*targetScale));
			var bmp:Bitmap = new Bitmap(returnBmpData);
			container.addChild(bmp);
			var label:Label = new Label(container, bmp.x, bmp.height + 5,"TwoStepResize");
		}
		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)*1);
			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;
			if(LanchozFlg){
				bmpContainer.x = Math.round((loader.width * targetScale + 20) *2);
			}else {
				bmpContainer.x = Math.round((loader.width * targetScale + 20) * 1);
			}
			var label:Label = new Label(container, bmpContainer.x, bmpContainer.height + 5,"FlashNormalScale");
		}
		
		private function reset():void {
			while (container.numChildren > 0) {
				container.removeChildAt(0);
			}
			container.x =8;
			container.y =40;
		}
	}
}
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Sprite;
import flash.filters.BlurFilter;
import flash.utils.Dictionary;


class TwoStepResizer
{
	/*
	 * 2段階縮小
	 * 
	 * */

	//resize
	static public function resize(bmpdata:BitmapData, w:uint, h:uint,blur:Number=0,sizeChk:Boolean=true):BitmapData {
		if (sizeChk&&bmpdata.width > w * 2 || sizeChk&&bmpdata.height > h*2) {
			bmpdata = resize(bmpdata,w*2,h*2,0,false);
		}
		var targetScale:Number = Math.min(w / bmpdata.width,h / bmpdata.height);
		var bmp:Bitmap = new Bitmap(bmpdata.clone());
		bmp.smoothing = true;
		bmp.scaleX = bmp.scaleY = targetScale;
		
		//必要があればぼかして調整
		if(blur>0){
			bmp.filters = [new BlurFilter(1.05, 1.05, blur)];
		}
		
		var container:Sprite = new Sprite();
		container.addChild(bmp);

		var outputBmpData:BitmapData = new BitmapData(container.width, container.height,true,0);
		outputBmpData.draw(container);

		container.removeChild(bmp);
		container = null;
		bmp.bitmapData.dispose();
		bmp = null;

		return outputBmpData;
	}
	
	//BitmapData化
	static public function setBmpData(mc:DisplayObject):BitmapData {
		var bmpdata:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
		bmpdata.draw(mc);
		return bmpdata;
	}
	
}



class LanczosBitmapDataResizer
{
	/*
	 * 以下、Jozef Chutka氏のLanczos Resamplingを使わせて頂きました
	 * http://wonderfl.net/c/dLf0
	 * 
	 * */
	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;
	}
}