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:

12 Comments »

  1. [...] bookmarks tagged proud Active Window(S) Blur saved by 6 others     Ottsell bookmarked on 07/07/08 | [...]

  2. Tom Lee says:

    Very nice. A couple suggestions:

    1) When two windows are layered, it would be nice if the front window blurred the one behind it.
    2) Presently, the blur amount seems to be a global value. Can it be set for each window individually?

    Awesome job.

  3. Steve says:

    I appreciate you taking the time to create this example. I am trying to build this, but, as a newbie to AS, I am having some trouble. I can’t seem to resolve the Background or GraphicWindow data type. Any suggestions?

  4. Devon O. says:

    Hey Steve,
    Both items are MovieClips inside the .fla library. Once you have an object in the library, you can right click it and pick “linkage” from the context menu. Check the “Export for actionscript” box, leave the other boxes alone and in the Class textbox just put “Background” (for the image you want to be blurred) or “GraphicWindow” (for the window you want to drag around). Hope that helps out..

  5. Skye says:

    Devon,

    Could you please post a .ZIP with the ActiveBlur class, FLA, and its corresponding document class?

    Cheers!

  6. Devon O. says:

    Well, there’s really no more than what’s posted above, but since so many are asking, you can get the .fla that created the above .swf here. You’ll also need the ui package mentioned in this post to create the value slider control above – but the package isn’t necessary for general usage of the MWindowBlur class.

  7. Chrs says:

    Hi, I’ve put together a really simple timeline example to try and get this working (before trying it on a more complicated project), but it doesn’t seem to be working… can you see any reason why?

    ////////////////////////////////////////////////////////////////////////////////

    import com.onebyonedesign.extras.*;

    var bg:Sprite = new Sprite();
    var matr:Matrix = new Matrix();
    matr.createGradientBox(200, 50, 20, -50, 200);
    bg.graphics.beginGradientFill(“linear”, [0xff0000, 0x00ff00], [1,1], [150,255], matr);
    bg.graphics.drawRoundRect(100,20,200,250,20);
    bg.graphics.endFill();
    addChild(bg);

    var blur:MWindowBlur=new MWindowBlur(bg,100);

    var box:Sprite = new Sprite();
    box.graphics.beginFill(0x0000ff, 0.5);
    box.graphics.drawRect(0,0,100,100);
    box.graphics.endFill();
    addChild(box);

    box.addEventListener(MouseEvent.MOUSE_DOWN, drag);

    blur.addWindow(box);

    function drag(e:Event) {
    box.removeEventListener(MouseEvent.MOUSE_DOWN, drag);
    box.addEventListener(MouseEvent.MOUSE_UP, drop);
    box.startDrag();
    }//drag

    function drop(e:Event) {
    box.addEventListener(MouseEvent.MOUSE_DOWN, drag);
    box.removeEventListener(MouseEvent.MOUSE_UP, drop);
    box.stopDrag();
    }//drag

  8. Devon O. says:

    Hey Chris. There’s a couple things causing the problem. For some reason, the blur windows cannot be created programmatically – they have to be exported from the library or on the stage. So, if you make your box a class in the library, that will take care of the biggest problem. The other thing is that the background object should be aligned to the origin (0,0). Do those two things and it should work fine.

  9. vb says:

    Hey Devon’! This is awesome, but I don’t understand how I can use this, in my document? Should I’ve two movieclips, one background and one window-route in the .fla project? And how do I use the actions you have posted? Should I paste it in a normal keyframe in “actions” (by pressing F9)?
    Would you please make a short video how you do this? :)

  10. Pankaj Kumar says:

    I need same thing to be done in AS3

  11. am says:

    Pankaj Kumar really nice comment! Maybe you need someone to read this post for you. =P

RSS feed for comments on this post. / TrackBack URI

Leave a Reply

Devon O. Wolfgang

Technical Reviewer of “The Essential Guide to Flash CS4 AIR Development”

Contributing Author of “Flash AS3 for Interactive Agencies”

Senior Flash Engineer PopCap Games, International Ltd.

Portfolio

UV Scrolling in Starling


Obviously this could come in pretty darned handy for space games, side scrollers, etc, etc[...]

Drawing on Stuff in Away3D 4.0

So, Easter Day, I thought I’d sit down and make a little ‘Paint on an Egg and Send it to Your Friend’ app.

Santabot: A Unity3D Flash Game


All right, so a Christmas game like “Santabot vs. The Flying Saucers from Mars” may be a day late[...]

Magnify – a jQuery Plugin


Let me begin by saying right up front, I have not given up on Flash[...]

It’s a Starling Halloween


Getting some practice for the upcoming Zombie Apocalypse[...]

Getting Started with Proscenium

So I had a chance this weekend to sit down and play around with Adobe’s new 3D framework for Stage3D, Proscenium, and thought I’d share a few of the results (a word of caution, there are no preloaders for any of the examples and may load a bit slowly). The first shows some reflections and [...]

Particle Editor for Starling Framework

An in-browser particle editor for the Starling 2D Framework for Flash Player 11.

So Long and Thanks for all the Flash on the Beach

So, another Flash on the Beach has just has just drawn to a close[...]

Game Development Tips from the Trenches of PopCap

Well, this is a post that’s a bit overdue, but, thanks to a well timed bank holiday, I finally had the opportunity to sit down and type up what I’ve been meaning to for some time now…

Old Skool Demoscene FX as 3D Textures

Many moons ago, I got the idea to create some demoscene plane deformation effects in Flash based on the formulas found here: http://www.iquilezles.org/www/articles/deform/deform.htm. I posted my less than desired results up on wonderfl. Thankfully, fellow wonderfl user, Hasufel, forked my attempt and optimized the hell out of it coming up with this. Well, today, for no [...]

Making The Gaming Scene (A Change in Careers)

And for my second blog post of the day, a much more personal note. After about three years of working with, what I would consider as objectively as possible, the best digital agency in Ireland, vStream Digital Media, I have made the immense career decision to leave the agency world and enter the arena of [...]

Feeling Lucky?

Images to dice, kick ascii style [...]

Adventures in Playbook Land

Adventures in Playbook Land


Now that the ordeal is over, I thought I’d take the time to sit down and share my account of what it was like to develop a Blackberry Playbook application using the Adobe Flex SDK[...]

Flash

Draw it for Me

So many ideas – so little time….

Kinect Application Running in Dublin

So, on Friday I just wrapped up our latest project at vStream Digital Media, a Kinect powered flipbook that lets users flip through the hand written notebooks of Philip Lynott of Thin Lizzy. The app uses OpenNI, runs in Adobe AIR and is currently on display at a pretty bitchin’ Phil Lynott exhibition running in [...]

Beach Ball Kinect Party

So we finally got a Kinect camera hooked up to a pc at work and, while it doesn’t seem to be legal to use it for commercial projects (but, hey, I’m no lawyer), the boss asked me to get it figured out and come up with some ideas just in case it would be feasible [...]