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:

9 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.

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 Software Developer in Dublin, Ireland

Portfolio

Bayer Pixel Bender Filter

Bayer Mosaic Filter in Pixel Bender


Now, seeing as how I’ve been trying to learn some Pixel Bender coding lately[...]

Liquitext

Liquid Text Effect


Haven’t been too active around here lately due to a major on going project at work lately as well as the fact that my wife and I are in the slow process of moving homes. Really, the only free time it seems I have these days is during the lunch hour. So, over the past two days, I came up with this fun little toy during lunches[...]

Google’s Text To Speech Engine in Flash

I’m not sure how I managed to miss this, but I just happened to run across a ‘new’ (well, newish), albeit still unofficial, offering of Google today: text-to-speech. You can see what few details there are on this Techcrunch post. Basically, it just boils down to this though – you send your phrase to be [...]

Quick Sound Effects Generator


Need some beeps, boops, or bops, to go with your UI or games [...]

Quick QuickBox2D Tip II – Collision Detection

Custom collision detection/handling in QuickBox2D [...]

None of This Runs Eternal


No Flash/Actionscript stuff here. Just me rambling about the upcoming Current 93 concert [...]

Playing Around with the New UndoManager

Included in the Flex 4.0 SDK and the, just released, Flash Professional CS5 lies a new hidden little gem of a class: flashx.undo.UndoManager (although the Flex 4.0 SDK’s been out for awhile, I have to admit I didn’t even notice this until I installed Flash CS5 and started poking around the documentation looking for new [...]

Making Waves

In a previous post, “Digging into the Microphone in Flash Player 10.1″, reader David Law asked in the comments how it would be possible to save .wav files to the server. I wasn’t sure right offhand, but thought I’d spend my lunch hour yesterday looking into it. Well, after reading this quick tutorial on Adobe [...]

Some Drawing Fun and a Bad Movie You’ll Never See

Earlier today, Dave Gillem posted a link on Facebook to an incredible Processing based drawing application. Thought I’d have a go at reproducing something similar in Flash. Well, I failed miserably, but the results were still interesting enough to check out. You can play around with it below. Just mouse down to draw in the [...]

Animating Bezier Curves


The other day I got the notion in my head that I wanted to draw some bezier curves in an animated fashion. I’m sure this has been done a million times before, but sometimes reinventing the wheel can be a good learning experience [...]

My God, It’s Full of Stars…


Just a quick little thing I got an idea for (i.e. pretty much ripped off) while browsing Processing examples.

Another Year, Another Look

As a few folks may have noticed, the blog is looking a little bit different as of today [...]

The Webcam Warholizer


Just a little bit of quick Friday fun with webcam and bitmapata…

More With the JiglibFlash Terrain

Playing around a bit more with the animated JiglibFlash terrain idea from my last post, I wanted to see how it would work in conjunction with a webcam. So, needless to say, you’ll need a webcam to check out the results below.

Rockin and Rollin with the JiglibFlash Terrain


Finally got the chance to play around with the new JiglibFlash height map terrain, today, and have to say I am very impressed [...]