Skip to content

Instantly share code, notes, and snippets.

@tzenkner
Last active August 29, 2015 13:58
Show Gist options
  • Save tzenkner/10073078 to your computer and use it in GitHub Desktop.
Save tzenkner/10073078 to your computer and use it in GitHub Desktop.
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