├── .gitignore
├── README.md
├── demo
├── .actionScriptProperties
├── .project
├── .settings
│ └── org.eclipse.core.resources.prefs
├── assets
│ ├── bg.png
│ ├── particle.pex
│ ├── particle.png
│ ├── pixelmask.fla
│ ├── pixelmask.png
│ ├── pixelmask.swf
│ ├── pixelmask.xml
│ ├── shimmer.png
│ ├── shimmer.psd
│ ├── shimmer_button.png
│ └── shimmer_button.psd
├── bin-debug
│ ├── PixelMask_Shimmer.swf
│ └── org
│ │ └── axgl
│ │ └── resource
│ │ ├── button.png
│ │ ├── font.png
│ │ └── icon.png
└── src
│ ├── MaskSprite.as
│ ├── PixelMask.as
│ ├── PixelMaskScene.as
│ └── ShimmerButtonScene.as
└── src
└── starling
└── extensions
└── pixelmask
└── PixelMaskDisplayObject.as
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .DS_Store
3 |
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | pixelmask
2 | =========
3 |
4 | A Starling Extension to provide pixel based masking for Starling display objects
5 |
6 | For documentation and usage, please see the wiki:
7 |
8 | http://wiki.starling-framework.org/extensions/pixelmask
9 |
10 | /demo/ Notes:
11 |
12 | To run the demo, the following are required as linked source paths:
13 |
14 | - Pixelmask (found in the /src directory of this repository)
15 | - Starling (found here: https://github.com/PrimaryFeather/Starling-Framework)
16 | - Starling Particles Extension (found here: https://github.com/PrimaryFeather/Starling-Extension-Particle-System)
17 |
18 | IMPORTANT NOTE:
19 | Please use the latest version of Starling. There was a crucial performance improvement post 1.4's release that has a significantly positive impact on users of Pixelmask!
20 |
21 | Author:
22 | Jonathan Hart
23 | jonathan.hart@gmail.com
24 |
--------------------------------------------------------------------------------
/demo/.actionScriptProperties:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/demo/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | PixelMaskProject
4 |
5 |
6 |
7 |
8 |
9 | com.adobe.flexbuilder.project.flexbuilder
10 |
11 |
12 |
13 |
14 |
15 | com.adobe.flexbuilder.project.actionscriptnature
16 |
17 |
18 |
19 | [source path] Axel-src
20 | 2
21 | /Users/jonathan/Documents/git/Axel/src
22 |
23 |
24 | [source path] Starling-Extension-Particle-System-src
25 | 2
26 | /Users/jonathan/Documents/git/Starling-Extension-Particle-System/src
27 |
28 |
29 | [source path] pixelmask-src
30 | 2
31 | /Users/jonathan/Documents/git/pixelmask/src
32 |
33 |
34 | [source path] starling-src
35 | 2
36 | /Users/jonathan/Documents/git/Starling-Framework/starling/src
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/demo/.settings/org.eclipse.core.resources.prefs:
--------------------------------------------------------------------------------
1 | #Tue Nov 20 00:28:26 PST 2012
2 | eclipse.preferences.version=1
3 | encoding/=utf-8
4 |
--------------------------------------------------------------------------------
/demo/assets/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/bg.png
--------------------------------------------------------------------------------
/demo/assets/particle.pex:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/demo/assets/particle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/particle.png
--------------------------------------------------------------------------------
/demo/assets/pixelmask.fla:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/pixelmask.fla
--------------------------------------------------------------------------------
/demo/assets/pixelmask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/pixelmask.png
--------------------------------------------------------------------------------
/demo/assets/pixelmask.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/pixelmask.swf
--------------------------------------------------------------------------------
/demo/assets/pixelmask.xml:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/pixelmask.xml
--------------------------------------------------------------------------------
/demo/assets/shimmer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/shimmer.png
--------------------------------------------------------------------------------
/demo/assets/shimmer.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/shimmer.psd
--------------------------------------------------------------------------------
/demo/assets/shimmer_button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/shimmer_button.png
--------------------------------------------------------------------------------
/demo/assets/shimmer_button.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/assets/shimmer_button.psd
--------------------------------------------------------------------------------
/demo/bin-debug/PixelMask_Shimmer.swf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/bin-debug/PixelMask_Shimmer.swf
--------------------------------------------------------------------------------
/demo/bin-debug/org/axgl/resource/button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/bin-debug/org/axgl/resource/button.png
--------------------------------------------------------------------------------
/demo/bin-debug/org/axgl/resource/font.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/bin-debug/org/axgl/resource/font.png
--------------------------------------------------------------------------------
/demo/bin-debug/org/axgl/resource/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jonathanhart/pixelmask/9d0fdd570056108d22c69885bb007105c165119a/demo/bin-debug/org/axgl/resource/icon.png
--------------------------------------------------------------------------------
/demo/src/MaskSprite.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import starling.core.Starling;
4 | import starling.display.MovieClip;
5 | import starling.display.Sprite;
6 | import starling.textures.Texture;
7 | import starling.textures.TextureAtlas;
8 |
9 | public class MaskSprite extends Sprite
10 | {
11 | [Embed(source="../assets/pixelmask.xml",mimeType="application/octet-stream")]
12 | private var AnimData:Class;
13 | [Embed(source="../assets/pixelmask.png")]
14 | private var AnimTexture:Class;
15 |
16 | public function MaskSprite()
17 | {
18 | super();
19 |
20 | var heroTexture:Texture = Texture.fromEmbeddedAsset(AnimTexture);
21 | var heroXmlData:XML = XML(new AnimData());
22 | var heroTextureAtlas:TextureAtlas =
23 | new TextureAtlas(heroTexture, heroXmlData);
24 | //Fetch the sprite sequence form the texture using their name
25 | var _mc:MovieClip = new MovieClip(heroTextureAtlas.getTextures("mask_text"), 18);
26 | addChild(_mc);
27 | Starling.juggler.add(_mc);
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/demo/src/PixelMask.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import flash.display.Sprite;
4 | import flash.display.StageAlign;
5 | import flash.display.StageScaleMode;
6 |
7 | import starling.core.Starling;
8 |
9 | [SWF(frameRate=60,width=800,height=300)]
10 | public class PixelMask extends Sprite
11 | {
12 | private var _starling:Starling;
13 |
14 | public function PixelMask()
15 | {
16 | stage.align = StageAlign.TOP_LEFT;
17 | stage.scaleMode = StageScaleMode.NO_SCALE;
18 |
19 | _starling = new Starling(PixelMaskScene, stage);
20 | _starling.start();
21 | //_starling.showStats = true;
22 | _starling.stage.color = 0xff222222;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/demo/src/PixelMaskScene.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import starling.core.Starling;
4 | import starling.display.DisplayObjectContainer;
5 | import starling.display.Image;
6 | import starling.events.Event;
7 | import starling.events.Touch;
8 | import starling.events.TouchEvent;
9 | import starling.events.TouchPhase;
10 | import starling.extensions.PDParticleSystem;
11 | import starling.extensions.pixelmask.PixelMaskDisplayObject;
12 | import starling.textures.Texture;
13 |
14 | public class PixelMaskScene extends DisplayObjectContainer
15 | {
16 | // embed configuration XML
17 | [Embed(source="../assets/particle.pex", mimeType="application/octet-stream")]
18 | private static const ParticleConfig:Class;
19 |
20 | // embed particle texture
21 | [Embed(source = "../assets/particle.png")]
22 | private static const ParticleClass:Class;
23 |
24 | [Embed(source="../assets/bg.png")]
25 | private var BgTexture:Class;
26 |
27 | private var _particleContainer:PixelMaskDisplayObject;
28 |
29 | public function PixelMaskScene()
30 | {
31 | super();
32 | addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
33 | }
34 |
35 | private function handleAddedToStage(event:Event) : void
36 | {
37 | // background image
38 | var background:Image = new Image(Texture.fromEmbeddedAsset(BgTexture));
39 | addChild(background);
40 |
41 | // instantiate embedded objects
42 | var psConfig:XML = XML(new ParticleConfig());
43 | var psTexture:Texture = Texture.fromBitmap(new ParticleClass());
44 |
45 | // create particle system
46 | var ps:PDParticleSystem = new PDParticleSystem(psConfig, psTexture);
47 | ps.x = stage.stageWidth/2;
48 | ps.y = stage.stageHeight;
49 | ps.scaleY = -1;
50 | Starling.juggler.add(ps);
51 | ps.start();
52 |
53 | // create mask sprite
54 | var mask:MaskSprite = new MaskSprite();
55 | mask.x = (stage.stageWidth-mask.width)/2;
56 | mask.y = (stage.stageHeight-mask.height)/2;
57 | addEventListener(TouchEvent.TOUCH, handleClick);
58 |
59 | // apply the masking here:
60 | _particleContainer = new PixelMaskDisplayObject();
61 |
62 | // NOTE: to test non-animated mode, assign "false" to the second constructor parameter:
63 | //_particleContainer = new PixelMaskDisplayObject(-1, false);
64 |
65 | // uncomment this to see inverted mode:
66 | //_particleContainer.inverted = true;
67 |
68 | addChild(_particleContainer);
69 | _particleContainer.pixelMask = mask;
70 | _particleContainer.addChild(ps);
71 | //_particleContainer.scaleX = _particleContainer.scaleY = .5;
72 | //_particleContainer.x = _particleContainer.y = 100;
73 | }
74 |
75 | private function handleClick (e:TouchEvent) : void
76 | {
77 | var touch:Touch = e.getTouch(this, TouchPhase.ENDED);
78 | if (touch) _particleContainer.inverted = !_particleContainer.inverted;
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/demo/src/ShimmerButtonScene.as:
--------------------------------------------------------------------------------
1 | package
2 | {
3 | import flash.utils.setInterval;
4 | import flash.utils.setTimeout;
5 |
6 | import starling.animation.Tween;
7 | import starling.core.Starling;
8 | import starling.display.DisplayObjectContainer;
9 | import starling.display.Image;
10 | import starling.display.Sprite;
11 | import starling.events.Event;
12 | import starling.extensions.pixelmask.PixelMaskDisplayObject;
13 | import starling.text.TextField;
14 | import starling.textures.Texture;
15 |
16 | public class ShimmerButtonScene extends DisplayObjectContainer
17 | {
18 |
19 | // NOTE ON THIS DEMO:
20 | // This is a button shimmer demo that illustrates putting a sheen on any button and animating it.
21 | // For extra credit, it also has a shimmer on dynamic text that sits on top of the button
22 | // One shimmer is masked to the button, the other is masked to the text.
23 |
24 | // embed shimmer texture
25 | [Embed(source="../assets/shimmer.png")]
26 | private var ShimmerClass:Class;
27 |
28 | [Embed(source="../assets/bg.png")]
29 | private var BgTexture:Class;
30 |
31 | [Embed(source="../assets/shimmer_button.png")]
32 | private var ButtonTexture:Class;
33 |
34 | private var _shimmer:Image;
35 | private var _buttonShimmer:Image;
36 | private var _text:TextField;
37 | private var _buttonShimmerContainer:PixelMaskDisplayObject;
38 |
39 | private var _txt:String = "PIXELMASK";
40 | public function ShimmerButtonScene()
41 | {
42 | super();
43 | addEventListener(Event.ADDED_TO_STAGE, handleAddedToStage);
44 | }
45 |
46 | private function handleAddedToStage(event:Event) : void
47 | {
48 | // background image
49 | var bgTexture:Texture = Texture.fromEmbeddedAsset(BgTexture);
50 | addChild(new Image(bgTexture));
51 |
52 | // container
53 | var container:Sprite = new Sprite();
54 |
55 | var buttonTexture:Texture = Texture.fromEmbeddedAsset(ButtonTexture);
56 | var button:Image = new Image(buttonTexture);
57 | container.addChild(button);
58 |
59 | // textfield
60 | _text = new TextField(button.width, button.height, "P");
61 | _text.format.setTo("Helvetica Bold", 64, 0x222222);
62 |
63 | // a little nudge to center the text vertically
64 | _text.y += 5;
65 |
66 | _buttonShimmerContainer = new PixelMaskDisplayObject();
67 | _buttonShimmerContainer.pixelMask = button;
68 |
69 | var shimmerTexture:Texture = Texture.fromEmbeddedAsset(ShimmerClass);
70 | _buttonShimmer = new Image(shimmerTexture);
71 |
72 | _buttonShimmer.alpha = 0.4;
73 | _buttonShimmerContainer.addChild(_buttonShimmer);
74 | container.addChild(_text);
75 | container.addChild(_buttonShimmerContainer);
76 | // shimmer
77 | _shimmer = new Image(shimmerTexture);
78 |
79 | // apply the masking here:
80 | var textMaskContainer:PixelMaskDisplayObject = new PixelMaskDisplayObject();
81 | textMaskContainer.pixelMask = _text;
82 | textMaskContainer.addChild(_shimmer);
83 |
84 | container.addChild(textMaskContainer);
85 | container.x = (stage.stageWidth - button.width)/2;
86 | container.y = (stage.stageHeight - button.height)/2;
87 | addChild(container);
88 |
89 | setInterval(updateText, 500);
90 | fireShimmer();
91 | }
92 |
93 | private function updateText () : void
94 | {
95 | // this is just to illustrate that the text is dynamic
96 | _txt = _txt.substr(1) + _txt.substr(0,1);
97 | _text.text = _txt.substr(0,1);
98 | }
99 |
100 | private function fireShimmer() : void
101 | {
102 | _shimmer.visible = false;
103 | _buttonShimmerContainer.visible = false;
104 | setTimeout(performShimmer, 1000);
105 |
106 | function performShimmer() : void
107 | {
108 | _shimmer.visible = true;
109 | _buttonShimmerContainer.visible = true;
110 |
111 | _shimmer.x = _buttonShimmer.x = -200;
112 | var tween:Tween = new Tween(_shimmer, 2.5);
113 | tween.animate("x", _text.textBounds.width+200);
114 | tween.onComplete = fireShimmer;
115 | Starling.juggler.add(tween);
116 |
117 | var tweenButton:Tween = new Tween(_buttonShimmer, tween.totalTime);
118 | tweenButton.animate("x", _text.textBounds.width+200);
119 | Starling.juggler.add(tweenButton);
120 | }
121 | }
122 | }
123 | }
--------------------------------------------------------------------------------
/src/starling/extensions/pixelmask/PixelMaskDisplayObject.as:
--------------------------------------------------------------------------------
1 | package starling.extensions.pixelmask
2 | {
3 | import flash.display3D.Context3DBlendFactor;
4 | import flash.geom.Matrix;
5 | import flash.geom.Rectangle;
6 |
7 | import starling.core.Starling;
8 | import starling.display.BlendMode;
9 | import starling.display.DisplayObject;
10 | import starling.display.DisplayObjectContainer;
11 | import starling.display.Quad;
12 | import starling.events.Event;
13 | import starling.rendering.Painter;
14 | import starling.textures.RenderTexture;
15 | import starling.utils.Pool;
16 |
17 | public class PixelMaskDisplayObject extends DisplayObjectContainer
18 | {
19 | private static const MASK_MODE_NORMAL:String = "mask";
20 | private static const MASK_MODE_INVERTED:String = "maskInverted";
21 |
22 | private var _mask:DisplayObject;
23 | private var _renderTexture:RenderTexture;
24 | private var _maskRenderTexture:RenderTexture;
25 |
26 | private var _quad:Quad;
27 | private var _maskQuad:Quad;
28 |
29 | private var _superRenderFlag:Boolean = false;
30 | private var _scaleFactor:Number;
31 | private var _isAnimated:Boolean = true;
32 | private var _maskRendered:Boolean = false;
33 |
34 | private static var sIdentity:Matrix = new Matrix();
35 |
36 | public function PixelMaskDisplayObject(scaleFactor:Number=-1, isAnimated:Boolean=true)
37 | {
38 | super();
39 |
40 | BlendMode.register(MASK_MODE_NORMAL, Context3DBlendFactor.ZERO, Context3DBlendFactor.SOURCE_ALPHA);
41 | BlendMode.register(MASK_MODE_INVERTED, Context3DBlendFactor.ZERO, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
42 |
43 | _isAnimated = isAnimated;
44 | _scaleFactor = scaleFactor;
45 |
46 | _quad = new Quad(100, 100);
47 | _maskQuad = new Quad(100, 100);
48 | _maskQuad.blendMode = MASK_MODE_NORMAL;
49 |
50 | // Handle lost context. By using the conventional event, we can make a weak listener.
51 | // This avoids memory leaks when people forget to call "dispose" on the object.
52 | Starling.current.stage3D.addEventListener(Event.CONTEXT3D_CREATE,
53 | onContextCreated, false, 0, true);
54 | }
55 |
56 | override public function dispose():void
57 | {
58 | clearRenderTextures();
59 |
60 | _quad.dispose();
61 | _maskQuad.dispose();
62 |
63 | Starling.current.stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreated);
64 | super.dispose();
65 | }
66 |
67 | private function onContextCreated(event:Object):void
68 | {
69 | refreshRenderTextures();
70 | }
71 |
72 | public function get isAnimated():Boolean { return _isAnimated; }
73 | public function set isAnimated(value:Boolean):void { _isAnimated = value; }
74 |
75 | public function get inverted():Boolean { return _maskQuad.blendMode == MASK_MODE_INVERTED; }
76 | public function set inverted(value:Boolean):void
77 | {
78 | _maskQuad.blendMode = value ? MASK_MODE_INVERTED : MASK_MODE_NORMAL;
79 | }
80 |
81 | public function get pixelMask():DisplayObject { return _mask; }
82 | public function set pixelMask(value:DisplayObject):void
83 | {
84 | _mask = value;
85 |
86 | if (value)
87 | {
88 | if (_mask.width == 0 || _mask.height == 0)
89 | throw new Error ("Mask must have dimensions. Current dimensions are " +
90 | _mask.width + "x" + _mask.height + ".");
91 |
92 | refreshRenderTextures();
93 | }
94 | else
95 | {
96 | clearRenderTextures();
97 | }
98 | }
99 |
100 | private function clearRenderTextures():void
101 | {
102 | if (_maskRenderTexture) _maskRenderTexture.dispose();
103 | if (_renderTexture) _renderTexture.dispose();
104 | }
105 |
106 | private function refreshRenderTextures():void
107 | {
108 | if (_mask)
109 | {
110 | clearRenderTextures();
111 |
112 | var maskBounds:Rectangle = _mask.getBounds(_mask, Pool.getRectangle());
113 | var maskWidth:Number = maskBounds.width;
114 | var maskHeight:Number = maskBounds.height;
115 | Pool.putRectangle(maskBounds);
116 |
117 | _renderTexture = new RenderTexture(maskWidth, maskHeight, false, _scaleFactor);
118 | _maskRenderTexture = new RenderTexture(maskWidth, maskHeight, false, _scaleFactor);
119 |
120 | // quad using the new render texture
121 | _quad.texture = _renderTexture;
122 | _quad.readjustSize();
123 |
124 | // quad to blit the mask onto
125 | _maskQuad.texture = _maskRenderTexture;
126 | _maskQuad.readjustSize();
127 | }
128 |
129 | _maskRendered = false;
130 | }
131 |
132 | public override function render(painter:Painter):void
133 | {
134 | if (_isAnimated || (!_isAnimated && !_maskRendered))
135 | {
136 | painter.finishMeshBatch();
137 | painter.excludeFromCache(this);
138 |
139 | if (_superRenderFlag || !_mask)
140 | {
141 | super.render(painter);
142 | }
143 | else
144 | {
145 | if (_mask)
146 | {
147 | _maskRenderTexture.draw(_mask, sIdentity);
148 | _renderTexture.drawBundled(drawRenderTextures);
149 |
150 | painter.pushState();
151 | painter.state.transformModelviewMatrix(_mask.transformationMatrix);
152 |
153 | _quad.render(painter);
154 | _maskRendered = true;
155 |
156 | painter.popState();
157 | }
158 | }
159 | }
160 | else
161 | {
162 | _quad.render(painter);
163 | }
164 | }
165 |
166 | private function drawRenderTextures():void
167 | {
168 | var matrix:Matrix = Pool.getMatrix();
169 | matrix.copyFrom(_mask.transformationMatrix);
170 | matrix.invert();
171 |
172 | _superRenderFlag = true;
173 | _renderTexture.draw(this, matrix);
174 | _superRenderFlag = false;
175 | _renderTexture.draw(_maskQuad, sIdentity);
176 |
177 | Pool.putMatrix(matrix);
178 | }
179 | }
180 | }
--------------------------------------------------------------------------------