Last active
August 29, 2015 13:58
-
-
Save tzenkner/10073078 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package application.popup.view{ | |
import com.adobe.utils.AGALMiniAssembler; | |
import flash.display3D.*; | |
import flash.geom.*; | |
import starling.core.RenderSupport; | |
import starling.core.Starling; | |
import starling.display.DisplayObject; | |
import starling.errors.MissingContextError; | |
import starling.events.Event; | |
import starling.textures.Texture; | |
import starling.utils.VertexData; | |
/** | |
* Custom Image with included circular mask | |
* setters for scaling, alpha, etc need to be implemented | |
* @author Thomas Zenkner | |
*/ | |
public class CustomImage extends DisplayObject{ | |
private var _texture:Texture; | |
private var _region:Rectangle; | |
private var _color:uint; | |
private var _circleData:Vector.<Number>; | |
private var _borderData:Vector.<Number>; | |
private var _programName:String; | |
private const INVERTED_FILL:String = "INVERTED_FILL"; | |
private const NORMAL_FILL:String = "NORMAL_FILL"; | |
private var mHeight:Number; | |
private var mWidth:Number; | |
private var _radius:Number; | |
private var _outerRadius:Number; | |
private var _invertedFill:Boolean = false; | |
private var _useColor:Boolean = false; | |
private var _tint:Boolean = false; | |
private var _masked:Boolean = false; | |
private var _useBorder:Boolean = false; | |
private var mVertexData:VertexData; | |
private var mVertexBuffer:VertexBuffer3D; | |
private var mIndexData:Vector.<uint>; | |
private var mIndexBuffer:IndexBuffer3D; | |
private static var sHelperMatrix:Matrix = new Matrix(); | |
private var _context:Context3D; | |
/** When width and height stay at zero it takes the dimension of the texture | |
* @param radius the radius of the circle | |
* @param outerRadius invert + outerRadius = donut fill between radius and outerRadius | |
* @param invert set to true for filling ouside of the circle | |
* @param region needed when texture comes from an atlas, eg provided | |
* through Atlas.getRegion("textureName") | |
**/ | |
public function CustomImage(texture:Texture = null, region:Rectangle = null, width:Number = 0, height:Number = 0, radius:Number = -1, invert:Boolean = false, outerRadius:Number = -1){ | |
mWidth = width == 0 ? texture.width : width; | |
mHeight = height == 0 ? texture.height : height; | |
_radius = radius; | |
_outerRadius = outerRadius > -1 ? outerRadius : Math.max(mWidth/2, mHeight/2); | |
_texture = texture; | |
_region = region; | |
_invertedFill = invert; | |
_masked = _radius > -1 ? true : false; | |
_useBorder = outerRadius > -1 ? true : false; | |
_circleData = Vector.<Number>([2, radius*radius, mWidth/2, mHeight/2]); | |
_borderData = Vector.<Number>([_outerRadius*_outerRadius, 0, 0, 0]); | |
setupVertices(); | |
createBuffers(); | |
registerPrograms(); | |
Starling.current.addEventListener(Event.CONTEXT3D_CREATE, onContextCreated); | |
} | |
public override function dispose():void{ | |
Starling.current.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreated); | |
if (mVertexBuffer){ | |
mVertexBuffer.dispose(); | |
} | |
if (mIndexBuffer){ | |
mIndexBuffer.dispose(); | |
} | |
super.dispose(); | |
} | |
private function onContextCreated(event:Event):void{ | |
createBuffers(); | |
registerPrograms(); | |
} | |
public override function getBounds(targetSpace:DisplayObject, resultRect:Rectangle=null):Rectangle{ | |
if (resultRect == null) resultRect = new Rectangle(); | |
var transformationMatrix:Matrix = targetSpace == this ? null : getTransformationMatrix(targetSpace, sHelperMatrix); | |
return mVertexData.getBounds(transformationMatrix, 0, -1, resultRect); | |
} | |
private function setupVertices():void{ | |
mVertexData = new VertexData(4); | |
mVertexData.setPosition(0, 0.0, 0.0); | |
mVertexData.setPosition(1, mWidth, 0.0); | |
mVertexData.setPosition(2, 0.0, mHeight); | |
mVertexData.setPosition(3, mWidth, mHeight); | |
var u0:Number = _region ? _region.x / _texture.root.width : 0.0; | |
var v0:Number = _region ? _region.y / _texture.root.height : 0.0; | |
var u1:Number = _region ? (_region.x + _region.width) / _texture.root.width : 1.0; | |
var v1:Number = _region ? (_region.y + _region.height) / _texture.root.height : 1.0; | |
mVertexData.setTexCoords(0, u0, v0); | |
mVertexData.setTexCoords(1, u1, v0); | |
mVertexData.setTexCoords(2, u0, v1); | |
mVertexData.setTexCoords(3, u1, v1); | |
mIndexData = new <uint>[]; | |
mIndexData.push(0, 1, 2); | |
mIndexData.push(1, 2, 3); | |
} | |
private function createBuffers():void{ | |
_context = Starling.context; | |
// _context.enableErrorChecking = true; | |
if (_context == null){ | |
throw new MissingContextError(); | |
} | |
if (mVertexBuffer){ | |
mVertexBuffer.dispose(); | |
} | |
if (mIndexBuffer){ | |
mIndexBuffer.dispose(); | |
} | |
mVertexBuffer = _context.createVertexBuffer(mVertexData.numVertices, VertexData.ELEMENTS_PER_VERTEX); | |
mVertexBuffer.uploadFromVector(mVertexData.rawData, 0, mVertexData.numVertices); | |
mIndexBuffer = _context.createIndexBuffer(mIndexData.length); | |
mIndexBuffer.uploadFromVector(mIndexData, 0, mIndexData.length); | |
} | |
public override function render(support:RenderSupport, alpha:Number):void{ | |
support.finishQuadBatch(); | |
support.raiseDrawCount(); | |
if (_context == null){ | |
throw new MissingContextError(); | |
} | |
var alphaVector:Vector.<Number> = new <Number>[1.0, 1.0, 1.0, alpha * this.alpha]; | |
support.applyBlendMode(false); | |
_context.setProgram(Starling.current.getProgram(_programName)); | |
_context.setTextureAt(0, _texture.base); //fs0 | |
_context.setVertexBufferAt(0, mVertexBuffer, VertexData.POSITION_OFFSET, Context3DVertexBufferFormat.FLOAT_2);//va0 | |
_context.setVertexBufferAt(1, mVertexBuffer, VertexData.COLOR_OFFSET, Context3DVertexBufferFormat.FLOAT_4);//va1 | |
_context.setVertexBufferAt(2, mVertexBuffer, VertexData.TEXCOORD_OFFSET, Context3DVertexBufferFormat.FLOAT_2);//va2 | |
_context.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, support.mvpMatrix3D, true); //vc0 | |
_context.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, alphaVector, 1);//vc4 | |
_context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, _circleData); //fc0 | |
_context.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, _borderData); //fc1 | |
_context.drawTriangles(mIndexBuffer); | |
_context.setTextureAt(0, null); | |
_context.setVertexBufferAt(0, null); | |
_context.setVertexBufferAt(1, null); | |
_context.setVertexBufferAt(2, null); | |
} | |
private function registerPrograms():void{ | |
var st:Starling = Starling.current; | |
var vertexCode:String = | |
"m44 vt0, va0, vc0 \n" + // 4×4 matrix transform to output clipspace (vc0 = mvpMatrix (4 vectors, vc0 - vc3)) | |
"mov v0, va0 \n" + // v0 = position (va0) | |
"mov v1, va2 \n" + // v1 = uv (va2) | |
"mov v2, va1 \n" + // v2 = color (va1) | |
"mov op, vt0 \n"; // vt0 --> output pixel | |
var fragmentCode:String = | |
"tex ft1, v1, fs0 <2d, clamp, linear, mipnone>\n"; //sample texture fs0 to ft1 | |
if (_masked){ | |
fragmentCode += "sub ft4, v0.y, fc0.w \n"; // ft4 = y - center.y | |
fragmentCode += "pow ft4, ft4, fc0.x \n"; // ft4 = ft4^2 | |
fragmentCode += "sub ft3, v0.x, fc0.z \n"; // ft3 = x - center.x | |
fragmentCode += "pow ft3, ft3, fc0.x \n"; // ft3 = ft3^2 | |
fragmentCode += "add ft3, ft3, ft4 \n"; // ft3 = ft3 + ft4 | |
if (_invertedFill){ | |
if (_useBorder){ | |
fragmentCode += "slt ft5, ft3, fc1.x \n"; // ft2 = ft3 > borderRadius^2 ? 1 : 0; | |
fragmentCode += "mul ft1.w, ft1.w, ft5 \n"; // multiply output alpha with ft5; | |
} | |
fragmentCode += "sge ft2, ft3, fc0.y \n"; // ft2 = ft3 > radius^2 ? 1 : 0; (a^2 + b^2 = c^2); | |
}else{ | |
fragmentCode += "slt ft2, ft3, fc0.y \n"; // ft2 = ft3 <= radius^2 ? 1 : 0; (a^2 + b^2 = c^2) | |
} | |
} | |
if (_useColor){ | |
fragmentCode += "mov ft6 , ft1.w \n"; //store orig alpha | |
fragmentCode += "add ft1, ft1, v2 \n"; // add color to output; | |
if (_tint){ | |
fragmentCode += "mov ft1, v2 \n"; // override output with color; | |
} | |
fragmentCode += "mov ft1.w, ft6 \n"; // restore orig alpha | |
} | |
if (_masked){ | |
fragmentCode += "mul ft1.w, ft1.w, ft2 \n"; // multiply output alpha with ft2; | |
} | |
fragmentCode += "mov oc, ft1"; // output color = ft1 | |
var vertexAssembler:AGALMiniAssembler = new AGALMiniAssembler(); | |
vertexAssembler.assemble(Context3DProgramType.VERTEX, vertexCode); | |
var fragmentAssembler:AGALMiniAssembler = new AGALMiniAssembler(); | |
fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, fragmentCode); | |
_programName = _invertedFill ? INVERTED_FILL : NORMAL_FILL; | |
st.registerProgram(_programName, vertexAssembler.agalcode, fragmentAssembler.agalcode); | |
} | |
public function get radius():Number{ | |
return _radius; | |
} | |
public function set radius(value:Number):void{ | |
_radius = value; | |
_circleData[1] = value * value; | |
} | |
public function get outerRadius():Number{ | |
return _outerRadius; | |
} | |
public function set outerRadius(value:Number):void{ | |
_outerRadius = value; | |
_borderData[0] = value * value; | |
} | |
public function get color():uint{ | |
return _color; | |
} | |
public function setColor(value:uint, overDraw:Boolean = false):void{ | |
_color = value; | |
_tint = overDraw; | |
_useColor = true; | |
mVertexData.setUniformColor(value); | |
registerPrograms(); | |
createBuffers(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment