scaffold.libs_as.starling.rendering.VertexData.as Maven / Gradle / Ivy
// =================================================================================================
//
// Starling Framework
// Copyright 2011-2015 Gamua. All Rights Reserved.
//
// This program is free software. You can redistribute and/or modify it
// in accordance with the terms of the accompanying license agreement.
//
// =================================================================================================
package starling.rendering
{
import flash.display3D.Context3D;
import flash.display3D.VertexBuffer3D;
import flash.errors.IllegalOperationError;
import flash.geom.Matrix;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.utils.ByteArray;
import flash.utils.Endian;
import starling.core.Starling;
import starling.errors.MissingContextError;
import starling.utils.MathUtil;
import starling.utils.MatrixUtil;
import starling.utils.StringUtil;
/** The VertexData class manages a raw list of vertex information, allowing direct upload
* to Stage3D vertex buffers. You only have to work with this class if you're writing
* your own rendering code (e.g. if you create custom display objects).
*
* To render objects with Stage3D, you have to organize vertices and indices in so-called
* vertex- and index-buffers. Vertex buffers store the coordinates of the vertices that make
* up an object; index buffers reference those vertices to determine which vertices spawn
* up triangles. Those buffers reside in graphics memory and can be accessed very
* efficiently by the GPU.
*
* Before you can move data into the buffers, you have to set it up in conventional
* memory — that is, in a Vector or a ByteArray. Since it's quite cumbersome to manually
* create and manipulate those data structures, the IndexData and VertexData classes provide
* a simple way to do just that. The data is stored in a ByteArray (one index or vertex after
* the other) that can easily be uploaded to a buffer.
*
* Vertex Format
*
* The VertexData class requires a custom format string on initialization, or an instance
* of the VertexDataFormat class. Here is an example:
*
*
* vertexData = new VertexData("position:float2, color:bytes4");
* vertexData.setPoint(0, "position", 320, 480);
* vertexData.setColor(0, "color", 0xff00ff);
*
* This instance is set up with two attributes: "position" and "color". The keywords
* after the colons depict the format and size of the data that each property uses; in this
* case, we store two floats for the position (for the x- and y-coordinates) and four
* bytes for the color. Please refer to the VertexDataFormat documentation for details.
*
* The attribute names are then used to read and write data to the respective positions
* inside a vertex. Furthermore, they come in handy when copying data from one VertexData
* instance to another: attributes with equal name and data format may be transferred between
* different VertexData objects, even when they contain different sets of attributes or have
* a different layout.
*
* Colors
*
* Always use the format bytes4
for color data. The color access methods
* expect that format, since it's the most efficient way to store color data. Furthermore,
* you should always include the string "color" (or "Color") in the name of color data;
* that way, it will be recognized as such and will always have its alpha value pre-filled
* with the value "1.0".
*
* Premultiplied Alpha
*
* Per default, color values are stored with premultiplied alpha values, which
* means that the rgb
values were multiplied with the alpha
values
* before saving them. You can change this behavior with the premultipliedAlpha
* property.
*
* Beware: with premultiplied alpha, the alpha value always effects the resolution of
* the RGB channels. A small alpha value results in a lower accuracy of the other channels,
* and if the alpha value reaches zero, the color information is lost altogether.
*
* @see VertexDataFormat
* @see IndexData
*/
public class VertexData
{
private var _rawData:ByteArray;
private var _numVertices:int;
private var _format:VertexDataFormat;
private var _attributes:Vector.;
private var _numAttributes:int;
private var _premultipliedAlpha:Boolean;
private var _posOffset:int; // in bytes
private var _colOffset:int; // in bytes
private var _vertexSize:int; // in bytes
// helper objects
private static var sHelperPoint:Point = new Point();
private static var sHelperPoint3D:Vector3D = new Vector3D();
private static var sBytes:ByteArray = new ByteArray();
/** Creates an empty VertexData object with the given format and initial capacity.
*
* @param format
*
* Either a VertexDataFormat instance or a String that describes the data format.
* Refer to the VertexDataFormat class for more information. If you don't pass a format,
* the default MeshStyle.VERTEX_FORMAT
will be used.
*
* @param initialCapacity
*
* The initial capacity affects just the way the internal ByteArray is allocated, not the
* numIndices
value, which will always be zero when the constructor returns.
* The reason for this behavior is the peculiar way in which ByteArrays organize their
* memory:
*
* The first time you set the length of a ByteArray, it will adhere to that:
* a ByteArray with length 20 will take up 20 bytes (plus some overhead). When you change
* it to a smaller length, it will stick to the original value, e.g. with a length of 10
* it will still take up 20 bytes. However, now comes the weird part: change it to
* anything above the original length, and it will allocate 4096 bytes!
*
* Thus, be sure to always make a generous educated guess, depending on the planned
* usage of your VertexData instances.
*/
public function VertexData(format:*=null, initialCapacity:int=32)
{
if (format == null) _format = MeshStyle.VERTEX_FORMAT;
else if (format is VertexDataFormat) _format = format;
else if (format is String) _format = VertexDataFormat.fromString(format as String);
else throw new ArgumentError("'format' must be String or VertexDataFormat");
_attributes = _format.attributes;
_numAttributes = _attributes.length;
_posOffset = _format.hasAttribute("position") ? _format.getOffsetInBytes("position") : 0;
_colOffset = _format.hasAttribute("color") ? _format.getOffsetInBytes("color") : 0;
_vertexSize = _format.vertexSizeInBytes;
_numVertices = 0;
_premultipliedAlpha = true;
_rawData = new ByteArray();
_rawData.endian = sBytes.endian = Endian.LITTLE_ENDIAN;
_rawData.length = initialCapacity * _vertexSize; // just for the initial allocation
_rawData.length = 0; // changes length, but not memory!
}
/** Explicitly frees up the memory used by the ByteArray. */
public function clear():void
{
_rawData.clear();
_numVertices = 0;
}
/** Creates a duplicate of the vertex data object. */
public function clone():VertexData
{
var clone:VertexData = new VertexData(_format, _numVertices);
clone._rawData.writeBytes(_rawData);
clone._numVertices = _numVertices;
clone._premultipliedAlpha = _premultipliedAlpha;
return clone;
}
/** Copies the vertex data (or a range of it, defined by 'vertexID' and 'numVertices')
* of this instance to another vertex data object, starting at a certain target index.
* If the target is not big enough, it will be resized to fit all the new vertices.
*
* If you pass a non-null matrix, the 2D position of each vertex will be transformed
* by that matrix before storing it in the target object. (The position being either an
* attribute with the name "position" or, if such an attribute is not found, the first
* attribute of each vertex. It must consist of two float values containing the x- and
* y-coordinates of the vertex.)
*
* Source and target do not need to have the exact same format. Only properties that
* exist in the target will be copied; others will be ignored. If a property with the
* same name but a different format exists in the target, an exception will be raised.
* Beware, though, that the copy-operation becomes much more expensive when the formats
* differ.
*/
public function copyTo(target:VertexData, targetVertexID:int=0, matrix:Matrix=null,
vertexID:int=0, numVertices:int=-1):void
{
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
if (_format === target._format)
{
if (target._numVertices < targetVertexID + numVertices)
target._numVertices = targetVertexID + numVertices;
// In this case, it's fastest to copy the complete range in one call
// and then overwrite only the transformed positions.
var targetRawData:ByteArray = target._rawData;
targetRawData.position = targetVertexID * _vertexSize;
targetRawData.writeBytes(_rawData, vertexID * _vertexSize, numVertices * _vertexSize);
if (matrix)
{
var x:Number, y:Number;
var position:int = targetVertexID * _vertexSize + _posOffset;
var endPosition:int = position + (numVertices * _vertexSize);
while (position < endPosition)
{
targetRawData.position = position;
x = targetRawData.readFloat();
y = targetRawData.readFloat();
targetRawData.position = position;
targetRawData.writeFloat(matrix.a * x + matrix.c * y + matrix.tx);
targetRawData.writeFloat(matrix.d * y + matrix.b * x + matrix.ty);
position += _vertexSize;
}
}
}
else
{
if (target._numVertices < targetVertexID + numVertices)
target.numVertices = targetVertexID + numVertices; // ensure correct alphas!
for (var i:int=0; i<_numAttributes; ++i)
{
var srcAttr:VertexDataAttribute = _attributes[i];
var tgtAttr:VertexDataAttribute = target.getAttribute(srcAttr.name);
if (tgtAttr) // only copy attributes that exist in the target, as well
{
if (srcAttr.offset == _posOffset)
copyAttributeTo_internal(target, targetVertexID, matrix,
srcAttr, tgtAttr, vertexID, numVertices);
else
copyAttributeTo_internal(target, targetVertexID, null,
srcAttr, tgtAttr, vertexID, numVertices);
}
}
}
}
/** Copies a specific attribute of all contained vertices (or a range of them, defined by
* 'vertexID' and 'numVertices') to another VertexData instance. Beware that both name
* and format of the attribute must be identical in source and target.
* If the target is not big enough, it will be resized to fit all the new vertices.
*
* If you pass a non-null matrix, the specified attribute will be transformed by
* that matrix before storing it in the target object. It must consist of two float
* values.
*/
public function copyAttributeTo(target:VertexData, targetVertexID:int, attrName:String,
matrix:Matrix=null, vertexID:int=0, numVertices:int=-1):void
{
var sourceAttribute:VertexDataAttribute = getAttribute(attrName);
var targetAttribute:VertexDataAttribute = target.getAttribute(attrName);
if (sourceAttribute == null)
throw new ArgumentError("Attribute '" + attrName + "' not found in source data");
if (targetAttribute == null)
throw new ArgumentError("Attribute '" + attrName + "' not found in target data");
copyAttributeTo_internal(target, targetVertexID, matrix,
sourceAttribute, targetAttribute, vertexID, numVertices);
}
private function copyAttributeTo_internal(
target:VertexData, targetVertexID:int, matrix:Matrix,
sourceAttribute:VertexDataAttribute, targetAttribute:VertexDataAttribute,
vertexID:int, numVertices:int):void
{
if (sourceAttribute.format != targetAttribute.format)
throw new IllegalOperationError("Attribute formats differ between source and target");
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
if (target._numVertices < targetVertexID + numVertices)
target._numVertices = targetVertexID + numVertices;
var i:int, j:int, x:Number, y:Number;
var sourceData:ByteArray = _rawData;
var targetData:ByteArray = target._rawData;
var sourceDelta:int = _vertexSize - sourceAttribute.size;
var targetDelta:int = target._vertexSize - targetAttribute.size;
var attributeSizeIn32Bits:int = sourceAttribute.size / 4;
sourceData.position = vertexID * _vertexSize + sourceAttribute.offset;
targetData.position = targetVertexID * target._vertexSize + targetAttribute.offset;
if (matrix)
{
for (i=0; i> 8) & 0xffffff;
}
/** Writes the RGB color to the specified vertex and attribute (alpha is not changed). */
public function setColor(vertexID:int, attrName:String, color:uint):void
{
if (_numVertices < vertexID + 1)
numVertices = vertexID + 1;
var alpha:Number = getAlpha(vertexID, attrName);
colorize(attrName, color, alpha, vertexID, 1);
}
/** Reads the alpha value from the specified vertex and attribute. */
public function getAlpha(vertexID:int, attrName:String="color"):Number
{
var offset:int = attrName == "color" ? _colOffset : getAttribute(attrName).offset;
_rawData.position = vertexID * _vertexSize + offset;
var rgba:uint = switchEndian(_rawData.readUnsignedInt());
return (rgba & 0xff) / 255.0;
}
/** Writes the given alpha value to the specified vertex and attribute (range 0-1). */
public function setAlpha(vertexID:int, attrName:String, alpha:Number):void
{
if (_numVertices < vertexID + 1)
numVertices = vertexID + 1;
var color:uint = getColor(vertexID, attrName);
colorize(attrName, color, alpha, vertexID, 1);
}
// bounds helpers
/** Calculates the bounds of the 2D vertex positions identified by the given name.
* The positions may optionally be transformed by a matrix before calculating the bounds.
* If you pass an 'out' Rectangle, the result will be stored in this rectangle
* instead of creating a new object. To use all vertices for the calculation, set
* 'numVertices' to '-1'. */
public function getBounds(attrName:String="position", matrix:Matrix=null,
vertexID:int=0, numVertices:int=-1, out:Rectangle=null):Rectangle
{
if (out == null) out = new Rectangle();
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
if (numVertices == 0)
{
if (matrix == null)
out.setEmpty();
else
{
MatrixUtil.transformCoords(matrix, 0, 0, sHelperPoint);
out.setTo(sHelperPoint.x, sHelperPoint.y, 0, 0);
}
}
else
{
var minX:Number = Number.MAX_VALUE, maxX:Number = -Number.MAX_VALUE;
var minY:Number = Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE;
var offset:int = attrName == "position" ? _posOffset : getAttribute(attrName).offset;
var position:int = vertexID * _vertexSize + offset;
var x:Number, y:Number, i:int;
if (matrix == null)
{
for (i=0; i x) minX = x;
if (maxX < x) maxX = x;
if (minY > y) minY = y;
if (maxY < y) maxY = y;
}
}
else
{
for (i=0; i sHelperPoint.x) minX = sHelperPoint.x;
if (maxX < sHelperPoint.x) maxX = sHelperPoint.x;
if (minY > sHelperPoint.y) minY = sHelperPoint.y;
if (maxY < sHelperPoint.y) maxY = sHelperPoint.y;
}
}
out.setTo(minX, minY, maxX - minX, maxY - minY);
}
return out;
}
/** Calculates the bounds of the 2D vertex positions identified by the given name,
* projected into the XY-plane of a certain 3D space as they appear from the given
* camera position. Note that 'camPos' is expected in the target coordinate system
* (the same that the XY-plane lies in).
*
* If you pass an 'out' Rectangle, the result will be stored in this rectangle
* instead of creating a new object. To use all vertices for the calculation, set
* 'numVertices' to '-1'.
*/
public function getBoundsProjected(attrName:String, matrix:Matrix3D,
camPos:Vector3D, vertexID:int=0, numVertices:int=-1,
out:Rectangle=null):Rectangle
{
if (out == null) out = new Rectangle();
if (camPos == null) throw new ArgumentError("camPos must not be null");
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
if (numVertices == 0)
{
if (matrix)
MatrixUtil.transformCoords3D(matrix, 0, 0, 0, sHelperPoint3D);
else
sHelperPoint3D.setTo(0, 0, 0);
MathUtil.intersectLineWithXYPlane(camPos, sHelperPoint3D, sHelperPoint);
out.setTo(sHelperPoint.x, sHelperPoint.y, 0, 0);
}
else
{
var minX:Number = Number.MAX_VALUE, maxX:Number = -Number.MAX_VALUE;
var minY:Number = Number.MAX_VALUE, maxY:Number = -Number.MAX_VALUE;
var offset:int = attrName == "position" ? _posOffset : getAttribute(attrName).offset;
var position:int = vertexID * _vertexSize + offset;
var x:Number, y:Number, i:int;
for (i=0; i sHelperPoint.x) minX = sHelperPoint.x;
if (maxX < sHelperPoint.x) maxX = sHelperPoint.x;
if (minY > sHelperPoint.y) minY = sHelperPoint.y;
if (maxY < sHelperPoint.y) maxY = sHelperPoint.y;
}
out.setTo(minX, minY, maxX - minX, maxY - minY);
}
return out;
}
/** Indicates if color attributes should be stored premultiplied with the alpha value.
* Changing this value does not modify any existing color data.
* If you want that, use the setPremultipliedAlpha
method instead.
* @default true */
public function get premultipliedAlpha():Boolean { return _premultipliedAlpha; }
public function set premultipliedAlpha(value:Boolean):void
{
setPremultipliedAlpha(value, false);
}
/** Changes the way alpha and color values are stored. Optionally updates all existing
* vertices. */
public function setPremultipliedAlpha(value:Boolean, updateData:Boolean):void
{
if (updateData && value != _premultipliedAlpha)
{
for (var i:int=0; i<_numAttributes; ++i)
{
var attribute:VertexDataAttribute = _attributes[i];
if (attribute.isColor)
{
var offset:int = attribute.offset;
var oldColor:uint;
var newColor:uint;
for (var j:int=0; j<_numVertices; ++j)
{
_rawData.position = offset;
oldColor = switchEndian(_rawData.readUnsignedInt());
newColor = value ? premultiplyAlpha(oldColor) : unmultiplyAlpha(oldColor);
_rawData.position = offset;
_rawData.writeUnsignedInt(switchEndian(newColor));
offset += _vertexSize;
}
}
}
}
_premultipliedAlpha = value;
}
/** Indicates if any vertices have a non-white color or are not fully opaque. */
public function isTinted(attrName:String="color"):Boolean
{
var offset:int = attrName == "color" ? _colOffset : getAttribute(attrName).offset;
for (var i:int=0; i<_numVertices; ++i)
{
_rawData.position = offset;
if (_rawData.readUnsignedInt() != 0xffffffff)
return true;
offset += _vertexSize;
}
return false;
}
// modify multiple attributes
/** Transforms the 2D positions of subsequent vertices by multiplication with a
* transformation matrix. */
public function transformPoints(attrName:String, matrix:Matrix,
vertexID:int=0, numVertices:int=-1):void
{
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
var x:Number, y:Number;
var offset:int = attrName == "position" ? _posOffset : getAttribute(attrName).offset;
var position:int = vertexID * _vertexSize + offset;
var endPosition:int = position + (numVertices * _vertexSize);
while (position < endPosition)
{
_rawData.position = position;
x = _rawData.readFloat();
y = _rawData.readFloat();
_rawData.position = position;
_rawData.writeFloat(matrix.a * x + matrix.c * y + matrix.tx);
_rawData.writeFloat(matrix.d * y + matrix.b * x + matrix.ty);
position += _vertexSize;
}
}
/** Translates the 2D positions of subsequent vertices by a certain offset. */
public function translatePoints(attrName:String, deltaX:Number, deltaY:Number,
vertexID:int=0, numVertices:int=-1):void
{
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
var x:Number, y:Number;
var offset:int = attrName == "position" ? _posOffset : getAttribute(attrName).offset;
var position:int = vertexID * _vertexSize + offset;
var endPosition:int = position + (numVertices * _vertexSize);
while (position < endPosition)
{
_rawData.position = position;
x = _rawData.readFloat();
y = _rawData.readFloat();
_rawData.position = position;
_rawData.writeFloat(x + deltaX);
_rawData.writeFloat(y + deltaY);
position += _vertexSize;
}
}
/** Multiplies the alpha values of subsequent vertices by a certain factor. */
public function scaleAlphas(attrName:String, factor:Number,
vertexID:int=0, numVertices:int=-1):void
{
if (factor == 1.0) return;
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
var i:int;
var offset:int = attrName == "color" ? _colOffset : getAttribute(attrName).offset;
var colorPos:int = vertexID * _vertexSize + offset;
var alphaPos:int, alpha:Number, rgba:uint;
for (i=0; i 1.0) alpha = 1.0;
else if (alpha < 0.0) alpha = 0.0;
if (alpha == 1.0 || !_premultipliedAlpha)
{
_rawData[alphaPos] = int(alpha * 255.0);
}
else
{
_rawData.position = colorPos;
rgba = unmultiplyAlpha(switchEndian(_rawData.readUnsignedInt()));
rgba = (rgba & 0xffffff00) | (int(alpha * 255.0) & 0xff);
rgba = premultiplyAlpha(rgba);
_rawData.position = colorPos;
_rawData.writeUnsignedInt(switchEndian(rgba));
}
colorPos += _vertexSize;
}
}
/** Writes the given RGB and alpha values to the specified vertices. */
public function colorize(attrName:String, color:uint, alpha:Number=1.0,
vertexID:int=0, numVertices:int=-1):void
{
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
var offset:int = attrName == "color" ? _colOffset : getAttribute(attrName).offset;
var position:int = vertexID * _vertexSize + offset;
var endPosition:int = position + (numVertices * _vertexSize);
if (alpha > 1.0) alpha = 1.0;
else if (alpha < 0.0) alpha = 0.0;
var rgba:uint = ((color << 8) & 0xffffff00) | (int(alpha * 255.0) & 0xff);
if (_premultipliedAlpha && alpha != 1.0) rgba = premultiplyAlpha(rgba);
_rawData.position = vertexID * _vertexSize + offset;
_rawData.writeUnsignedInt(switchEndian(rgba));
while (position < endPosition)
{
_rawData.position = position;
_rawData.writeUnsignedInt(switchEndian(rgba));
position += _vertexSize;
}
}
// format helpers
/** Returns the format of a certain vertex attribute, identified by its name.
* Typical values: float1, float2, float3, float4, bytes4
. */
public function getFormat(attrName:String):String
{
return getAttribute(attrName).format;
}
/** Returns the size of a certain vertex attribute in bytes. */
public function getSizeInBytes(attrName:String):int
{
return getAttribute(attrName).size;
}
/** Returns the size of a certain vertex attribute in 32 bit units. */
public function getSizeIn32Bits(attrName:String):int
{
return getAttribute(attrName).size / 4;
}
/** Returns the offset (in bytes) of an attribute within a vertex. */
public function getOffsetInBytes(attrName:String):int
{
return getAttribute(attrName).offset;
}
/** Returns the offset (in 32 bit units) of an attribute within a vertex. */
public function getOffsetIn32Bits(attrName:String):int
{
return getAttribute(attrName).offset / 4;
}
/** Indicates if the VertexData instances contains an attribute with the specified name. */
public function hasAttribute(attrName:String):Boolean
{
return getAttribute(attrName) != null;
}
// VertexBuffer helpers
/** Creates a vertex buffer object with the right size to fit the complete data.
* Optionally, the current data is uploaded right away. */
public function createVertexBuffer(upload:Boolean=false,
bufferUsage:String="staticDraw"):VertexBuffer3D
{
var context:Context3D = Starling.context;
if (context == null) throw new MissingContextError();
if (_numVertices == 0) return null;
var buffer:VertexBuffer3D = context.createVertexBuffer(
_numVertices, _vertexSize / 4, bufferUsage);
if (upload) uploadToVertexBuffer(buffer);
return buffer;
}
/** Specifies the attribute to use at a certain register (identified by its index)
* in the vertex shader. */
public function setVertexBufferAttribute(buffer:VertexBuffer3D, index:int, attrName:String):void
{
var attribute:VertexDataAttribute = getAttribute(attrName);
Starling.context.setVertexBufferAt(index, buffer, attribute.offset / 4, attribute.format);
}
/** Uploads the complete data (or a section of it) to the given vertex buffer. */
public function uploadToVertexBuffer(buffer:VertexBuffer3D, vertexID:int=0, numVertices:int=-1):void
{
if (numVertices < 0 || vertexID + numVertices > _numVertices)
numVertices = _numVertices - vertexID;
if (numVertices > 0)
buffer.uploadFromByteArray(_rawData, 0, vertexID, numVertices);
}
[Inline]
private final function getAttribute(attrName:String):VertexDataAttribute
{
var i:int, attribute:VertexDataAttribute;
for (i=0; i<_numAttributes; ++i)
{
attribute = _attributes[i];
if (attribute.name == attrName) return attribute;
}
return null;
}
[Inline]
private static function switchEndian(value:uint):uint
{
return ( value & 0xff) << 24 |
((value >> 8) & 0xff) << 16 |
((value >> 16) & 0xff) << 8 |
((value >> 24) & 0xff);
}
private static function premultiplyAlpha(rgba:uint):uint
{
var alpha:uint = rgba & 0xff;
if (alpha == 0xff) return rgba;
else
{
var factor:Number = alpha / 255.0;
var r:uint = ((rgba >> 24) & 0xff) * factor;
var g:uint = ((rgba >> 16) & 0xff) * factor;
var b:uint = ((rgba >> 8) & 0xff) * factor;
return (r & 0xff) << 24 |
(g & 0xff) << 16 |
(b & 0xff) << 8 | alpha;
}
}
private static function unmultiplyAlpha(rgba:uint):uint
{
var alpha:uint = rgba & 0xff;
if (alpha == 0xff || alpha == 0x0) return rgba;
else
{
var factor:Number = alpha / 255.0;
var r:uint = ((rgba >> 24) & 0xff) / factor;
var g:uint = ((rgba >> 16) & 0xff) / factor;
var b:uint = ((rgba >> 8) & 0xff) / factor;
return (r & 0xff) << 24 |
(g & 0xff) << 16 |
(b & 0xff) << 8 | alpha;
}
}
// properties
/** The total number of vertices. If you make the object bigger, it will be filled up with
* 1.0
for all alpha values and zero for everything else. */
public function get numVertices():int { return _numVertices; }
public function set numVertices(value:int):void
{
if (_numVertices == value) return;
_rawData.length = value * _vertexSize;
for (var i:int=0; i<_numAttributes; ++i)
{
var attribute:VertexDataAttribute = _attributes[i];
if (attribute.isColor)
{
// alpha values of all color-properties must be initialized with "1.0"
var offset:int = attribute.offset + 3;
for (var j:int=_numVertices; j