Active Window(S) Blur

After taking another look at this post, I came up with a way of making multiple windows that will all blur the same image. I have to admit, though, I’m not particularly fond nor proud of the way I did it. The trouble is this: a masked display object can only have a single mask – so all items which will be used as a mask must be added to a single encompassing parent. But you would also like each mask item to be draggable along with the window it “rides behind”. Therefore, ideally, you’d like each mask item to be a child of the window instance (as I did it in the previous post). But there lies the rub. A display object can have only a single parent, hence you can’t add the child to both the mask and the window.

The solution I came up with was this: add the masking item to the mask instance and move the masking item with the window using an ENTER_FRAME event (rather than a parent-child relationship). If anyone has a more elegant solution, please post a comment. I’m probably overlooking something simple, but brilliant…

The MWindowBlur (Multiple Window Blur) class:

package com.onebyonedesign.extras {
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.BlurFilter;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	
	/**
	* Allows creation of multiple windows that blur the same background image
	* @author Devon O.
	*/
	public class MWindowBlur {
		
		private var _background:DisplayObjectContainer;
		private var _blurData:BitmapData;
		private var _blurImage:Bitmap;
		private var _blurAmount:int;
		private var _blur:BlurFilter;
		
		private var _mask:Sprite = new Sprite();
		private var _point:Point = new Point();
		private var _windows:Array = [];
		private var _maskDictionary:Dictionary = new Dictionary(true);
		
		public function MWindowBlur(background:DisplayObjectContainer, blurAmount:int = 8) {
			_background = background;
			_blurAmount = (blurAmount >= 0 && blurAmount <= 255) ? blurAmount : 8;
			_blur = new BlurFilter(_blurAmount, _blurAmount, 3);
			createBlur();
		}
		
		public function addWindow(window:DisplayObjectContainer):Boolean {
			if (_windows.indexOf(window) > -1) return false;
			
			var WindowClass:Class = Object(window).constructor;
			var m:DisplayObjectContainer = new WindowClass();
			m.transform = window.transform;
			m.filters = window.filters;
			if (window.scale9Grid) {
				m.scale9Grid = window.scale9Grid;
			}
			_maskDictionary[window] = m;
			_windows.push(window);
			
			if (!_mask.willTrigger(Event.ENTER_FRAME))
				_mask.addEventListener(Event.ENTER_FRAME, moveMask);

			_mask.addChild(m);
			
			if (!_background.contains(_mask))
				_background.addChild(_mask);
			
			return true;
		}
		
		public function removeWindow(window:DisplayObjectContainer = null):Boolean {
			if (!_windows.length) return false;
			
			if (!window) return removeWindow(_windows[_windows.length - 1]);
			
			var index:int = _windows.indexOf(window);
			if (index < 0) return false;
			
			_windows.splice(index, 1);
			_mask.removeChild(_maskDictionary[window]);
			if (!_windows.length)
				_mask.removeEventListener(Event.ENTER_FRAME, moveMask);
			
			return true;
		}
		
		public function kill():void {
			if (_mask.willTrigger(Event.ENTER_FRAME))
				_mask.removeEventListener(Event.ENTER_FRAME, moveMask);
			
			if (_background.contains(_mask))
				_background.removeChild(_mask);
				
			if (_background.contains(_blurImage))
				_background.removeChild(_blurImage);
				
			_blurData.dispose();
			
			_blurImage = null;
			_blurData = null;
			_maskDictionary = null;
			_background = null;
			_blur = null;
		}
		
		public function get blurAmount():int { return _blurAmount; }
		
		public function set blurAmount(value:int):void {
			_blurAmount = value;
			createBlur();
		}
		
		private function createBlur():void {
			if (_blurData) _blurData.dispose();
			_blur.blurX = _blur.blurY = _blurAmount;
			_blurData = new BitmapData(_background.width, _background.height, false);
			_blurData.draw(_background);
			_blurData.applyFilter(_blurData, _blurData.rect, _point, _blur);
			_blurImage = new Bitmap(_blurData);
			_blurImage.mask = _mask;
			_background.addChild(_blurImage);
		}
		
		private function moveMask(event:Event):void {
			var i:int = _windows.length;
			while (i--) {
				var w:DisplayObjectContainer = _windows[i];
				var m:DisplayObjectContainer = _maskDictionary[w];
				m.transform = w.transform;
			}
		}
	}
}

The Flash document class for a test ride:

package {
	
	import com.onebyonedesign.extras.MWindowBlur;
	import com.onebyonedesign.ui.OBO_ValueSlider;
	import com.onebyonedesign.ui.events.ValueSliderEvent;
	import fl.controls.Button;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	import flash.filters.DropShadowFilter;
	import flash.text.AntiAliasType;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormat;
	
	/**
	* Demonstrates MWindowBlur (for multiple windows) Class
	* @author Devon O.
	*/
	public class Main extends Sprite {
		
		// GraphicWindow is MovieClip in .fla library
		private var _activeWindow:GraphicWindow;
		// Background is MovieClip in .fla library
		private var _bg:Background;
		private var _windowBlur:MWindowBlur;
		private var _shadow:DropShadowFilter = new DropShadowFilter(2, 90, 0x000000, 1, 2, 2, 1, 3);
		private var _windowArray:Array = [];
		
		public function Main():void {
			// Background is MovieClip in .fla library
			_bg = new Background();
			addChild(_bg);
			init();
		}
		
		private function init():void {
			initBlur();
			initControlPanel();
			addWindow();
		}
		
		private function initBlur():void {
			_windowBlur = new MWindowBlur(_bg);
		}
		
		private function initControlPanel():void {
			var cp:Sprite = new Sprite();
			cp.graphics.beginFill(0xEAEAEA);
			cp.graphics.drawRoundRect(0, 0, 222, 70, 10, 10);
			cp.graphics.endFill();
			cp.filters = [_shadow];
			cp.x = 10;
			cp.y = stage.stageHeight - cp.height - 10;
			
			var tf:TextField = new TextField();
			var fmt:TextFormat = new TextFormat("_sans", 11);
			tf.defaultTextFormat = fmt;
			tf.autoSize = TextFieldAutoSize.LEFT;
			tf.selectable = false;
			tf.mouseEnabled = false;
			tf.antiAliasType = AntiAliasType.ADVANCED;
			tf.text = "Blur amount:";
			tf.x = 5;
			tf.y = 5;
			cp.addChild(tf);
			
			var blurSlider:OBO_ValueSlider = new OBO_ValueSlider(139, 0, 32, _windowBlur.blurAmount);
			blurSlider.x = tf.x + tf.textWidth + 8;
			blurSlider.y = 13;
			blurSlider.addEventListener(ValueSliderEvent.DRAG, blurChangeHandler);
			cp.addChild(blurSlider);
			
			var b1:Button = new Button();
			b1.label = "Add Window";
			b1.x = 5;
			b1.y = 40;
			b1.addEventListener(MouseEvent.CLICK, addWindow);
			cp.addChild(b1);
			
			var b2:Button = new Button();
			b2.label = "Remove Window";
			b2.x = b1.x + b1.width + 10;
			b2.y = 40;
			b2.addEventListener(MouseEvent.CLICK, removeWindow);
			cp.addChild(b2);
			
			addChild(cp);
		}
		
		private function addWindow(event:MouseEvent = null):void {
			// GraphicWindow is MovieClip in library
			var window:GraphicWindow = new GraphicWindow();
			window.width = randRange(50, 300);
			window.height = randRange(50, 200);
			
			window.x = randRange(0, 500 - window.width);
			window.y = randRange(0, 500 - window.height);
			window.filters = [_shadow];
			
			_windowArray.push(window);
			
			addChild(window);
			
			_windowBlur.addWindow(window);
			window.addEventListener(MouseEvent.MOUSE_DOWN, pressHandler);
		}
		
		private function removeWindow(event:MouseEvent):void {
			if (_windowArray.length) {
				_windowBlur.removeWindow();
				removeChild(_windowArray.pop());
			}
		}
		
		private function blurChangeHandler(event:ValueSliderEvent):void {
			_windowBlur.blurAmount = int(event.value);
		}
		
		private function pressHandler(event:MouseEvent):void {
			_activeWindow = event.currentTarget as GraphicWindow;
			addChild(_activeWindow);
			stage.addEventListener(MouseEvent.MOUSE_UP, releaseHandler);
			_activeWindow.startDrag(false);
		}
		
		private function releaseHandler(event:MouseEvent):void {
			stage.removeEventListener(MouseEvent.MOUSE_UP, releaseHandler);
			_activeWindow.stopDrag();
		}
		
		private function randRange(min:Number, max:Number):Number {
			return Math.floor(Math.random() * (max - min + 1)) + min;
		}
	}
}

And a quick example:

[kml_flashembed movie="http://blog.onebyonedesign.com/wp-content/uploads/2008/07/windowblur2.swf" height="500" width="500" /]

Date: