﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-牛牛猪－研究所-文章分类-Flex / AS3</title><link>http://www.cnitblog.com/cmoron/category/7308.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 30 Sep 2011 14:01:20 GMT</lastBuildDate><pubDate>Fri, 30 Sep 2011 14:01:20 GMT</pubDate><ttl>60</ttl><item><title>Flash Player 10 Drawing API</title><link>http://www.cnitblog.com/cmoron/articles/44901.html</link><dc:creator>牛牛猪</dc:creator><author>牛牛猪</author><pubDate>Tue, 03 Jun 2008 16:00:00 GMT</pubDate><guid>http://www.cnitblog.com/cmoron/articles/44901.html</guid><wfw:comment>http://www.cnitblog.com/cmoron/comments/44901.html</wfw:comment><comments>http://www.cnitblog.com/cmoron/articles/44901.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/cmoron/comments/commentRss/44901.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/cmoron/services/trackbacks/44901.html</trackback:ping><description><![CDATA[<h3>Introduction</h3>
<p>Flash Player 10 expands greatly on the drawing API in ActionScript, more so than any other version of the Flash Player since the initial introduction of the drawing API in Flash Player 6. New features include:</p>
<ul>
    <li>Support for Pixel Bender (Hydra) filters
    <li>Use of vectors (typed arrays) for improved throughput and use of memory
    <li>Support for non-zero winding rules
    <li>An API for drawing triangles with support for 3D perspective rendering
    <li>Drawing API data objects </li>
</ul>
<p>Though the older APIs still exist, and to much of an extent work with the newer APIs, these new enhancements mostly render those old APIs obsolete. This is especially the case with the <code>IGraphicsData</code> objects which serve as data representations of all the available APIs.</p>
<div class=note>
<h4>Note: This is not an official reference</h4>
<p>The contents of this tutorial do not represent an official reference to the APIs in Flash Player 10. Those APIs are subject to change prior to a final Flash Player 10 public release.</p>
</div>
<!--
<ul class="file">
    <li class="zip"><a href="TBD">Download Source Files</a></li>
</ul>
-->
<h3>New Graphics Methods</h3>
<p>Drawing APIs are located within the <code>Graphics</code> class. Display objects which support the drawing APIs contain a <code>Graphics</code> instance from which these APIs are called. As of Flash Player 9, these APIs include:</p>
<ul>
    <li>beginBitmapFill()
    <li>beginFill()
    <li>beginGradientFill()
    <li>clear()
    <li>curveTo()
    <li>drawCircle()
    <li>drawEllipse()
    <li>drawRect()
    <li>drawRoundRect()
    <li>endFill()
    <li>lineGradientStyle()
    <li>lineStyle()
    <li>lineTo()
    <li>moveTo() </li>
</ul>
<p>These remain active and unchanged. Flash Player 10 introduces the following new <code>Graphics</code> APIs:</p>
<ul>
    <li>beginShaderFill()
    <li>drawGraphicsData()
    <li>drawPath()
    <li>drawTriangles()
    <li>lineBitmapStyle()
    <li>lineShaderStyle() </li>
</ul>
<p>Of the new APIs, <code>lineBitmapStyle</code> sticks out a little as it fills a hole in the older API set for strokes. </p>
<pre>public function lineBitmapStyle(bitmap:BitmapData, matrix:Matrix=null, repeat:Boolean=true, smooth:Boolean=false);</pre>
<p>The <code>beginShaderFill</code> and <code>lineShaderStyle</code> APIs are similar, but these utilize a new feature in Flash Player 10, support for Pixel Bender (code named Hydra) filters.</p>
<h4>Shader Strokes and Fills</h4>
<p>Pixel Bender support is another amazing feature of Flash Player 10. Pixel Bender (code named Hydra), if you're not familiar with it, is a programming language for image processing. Flash Player 10 will be able to use Pixel Bender for filters, blend modes, and not surprisingly, styles for the drawing API. </p>
<p>For the drawing API, there will be two new <code>Graphics</code> class methods, <code>beginShaderFill</code> and <code>lineShaderStyle</code>.</p>
<pre>public function lineShaderStyle(shader:Shader=null, matrix:Matrix=null);
public function beginShaderFill(shader:Shader=null, matrix:Matrix=null);
</pre>
<p>The new <code>Shader</code> class is a container for hosting and working with Pixel Bender bytecode. Pixel Bender bytecode (.pbj) is compiled from Pixel Bender source code (.pbk). You write Pixel Bender source code and compile it into bytecode using the <a href="http://labs.adobe.com/wiki/index.php/Pixel_Bender_Toolkit"><u><font color=#0000ff>Pixel Bender Toolkit</font></u></a>. Once you have bytecode, it needs to be loaded into the player and supplied to a <code>Shader</code> instance. That instance can then be used as a display object filter, a blend mode, or be used with the new shader drawing APIs.</p>
<p>When working with the drawing APIs, you'll need to be sure to supply an input image (or input images, as it's possible to have more than one) to your shader directly. This is not the case with filters since they use the target display object as the input. From there, additional values can be set as determined by the definition of the Pixel Bender file.</p>
<p>For an example I will use a slightly modified version of <a href="http://pixelero.wordpress.com/"><u><font color=#0000ff>Petri Leskinen</font></u></a>'s Kaleidoscope Pixel Bender filter (retrieved from <a href="http://labs.adobe.com/wiki/index.php/Pixel_Bender_Toolkit:Gallery"><u><font color=#0000ff>Adobe Labs Pixel Bender Gallery</font></u></a>). You can check out the source <a href="http://www.senocular.com/flash/tutorials/flash10drawingapi/demos/kaleidoscope.pbk"><u><font color=#0000ff>here</font></u></a>. It defines one input with the identifier <code>src</code>, and five parameters, <code>fadeColor</code>, <code>position</code>, <code>size</code>, <code>fade</code>, and <code>angle</code>. In ActionScript these are defined and modified through the shader's <code>data</code> property. Since filters created in Pixel Bender can define any properties they wish, these values will change with each filter. The <code>data</code> property in the a <code>Shader</code> instance will change to match those properties.</p>
<p>Because the shader (Pixel Bender) bytecode is located in a separate file, it will have to be loaded before used. <code>RoseImage</code> is an <code>BitmapData</code> image in the library.</p>
<pre><span class=comment>// image for shader fill</span>
var bitmapData:BitmapData = new RoseImage(0,0);
<span class=comment>// shader fill shader; it will use</span>
<span class=comment>// the image as an input</span>
var shader:Shader = new Shader();
<span class=comment>// loader to load pixel bender bytecode </span>
<span class=comment>// for shader instance</span>
var shaderLoader:URLLoader = new URLLoader();
shaderLoader.dataFormat = URLLoaderDataFormat.BINARY;
shaderLoader.addEventListener(Event.COMPLETE, shaderLoaded);
shaderLoader.load(new URLRequest("kaleidoscope.pbj"));
<span class=comment>// shaderLoader complete handler</span>
function shaderLoaded(event:Event):void {
try {
<span class=comment>// set up shader</span>
shader.byteCode = shaderLoader.data; <span class=comment>// error if invalid</span>
shader.data.src.input = bitmapData; <span class=comment>// image input</span>
shader.data.size.value = [100]; <span class=comment>// constant size</span>
shader.data.fade.value = [.5]; <span class=comment>// constant fade value</span>
<span class=comment>// draw the shader every frame</span>
addEventListener(Event.ENTER_FRAME, drawKaleidoscope);
}catch(error:Error){}
}
<span class=comment>// enterframe handler</span>
function drawKaleidoscope(event:Event):void {
<span class=comment>// base position on mouse location</span>
shader.data.position.value = [mouseX, mouseY];
<span class=comment>// rotate constantly over time</span>
shader.data.angle.value = [getTimer()/500];
<span class=comment>// draw a rectangle with the shader fill</span>
graphics.clear();
graphics.beginShaderFill(shader);
graphics.drawRect(0,0,300,250);
}</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/beginshaderfill_kaleidoscope_rose.png" width=312><br><a href="http://www.senocular.com/flash/tutorials/flash10drawingapi/demos/Kaleidoscope%20Rose.html" target=_blank><u><font color=#0000ff>Preview (Requires Flash Player 10)</font></u></a></p>
<p>&nbsp;</p>
<p>As the movie plays, the kaleidoscope effect is constantly being redrawn with new parameters based on time and the position of the mouse. Amazingly this is all happening in real time and performs well even without GPU acceleration.</p>
<h4>Drawing Paths Through Vectors</h4>
<p>Another new feature to ActionScript is typed arrays, or vectors. These are not to be confused with the vectors used to describe vector-based (non-bitmap) drawings. Here, vector refers to a kind of an array that can only store one type of value. Those familiar with C++ or Java may recognize the name. To others, it may just sound out of place. Rest assured, it's an established term.</p>
<p>Vectors are almost exactly like arrays at their core, having pretty much the same API. There are only a few real differences. These include:</p>
<ul>
    <li>Elements within a vector all share the same type (vectors instances define those types using the syntax <code>Vector.&lt;<em>Type</em>&gt;</code>)
    <li>Vectors have an additional property, <code>fixed</code>, which determines whether or not the length of the vector can change dynamically
    <li>The vector constructor allows for two optional arguments, <code>length</code> and <code>fixed</code>; vector values cannot be defined in the constructor
    <li>There is no equivalent to the bracket (<code>[]</code>) array constructor shorthand for vectors </li>
</ul>
<p>Additionally, there may be one or two array-specific APIs in ActionScript that do not carry over into vector instances. I think currently, that is limited to <code>sortOn</code>. For all other events and purposes, vectors are just like arrays.</p>
<pre><span class=comment>// creating a vector that contains int elements</span>
var primeNumbers:Vector.&lt;int&gt; = new Vector.&lt;int&gt;(4, true);
primeNumbers[0] = 2;
primeNumbers[1] = 3;
primeNumbers[2] = 5;
primeNumbers[3] = 7;</pre>
<p>So why use vectors? Given that vectors are dense, typed arrays, they offer a performance advantage over arrays - this especially for primitive values such as Numbers and ints. A few methods of the new drawing API such as <code>drawPath</code> and <code>drawTriangles</code> use vectors for this very reason.</p>
<p><strong>drawPath</strong></p>
<p>The <code>drawPath</code> method is the new drawing API workhorse for defining shapes and other path. </p>
<pre>public function drawPath(commands:Vector.&lt;int&gt;, data:Vector.&lt;Number&gt;, winding:String=<span class=code-quote>"evenOdd"</span>);</pre>
<p>It consolidates <code>moveTo</code>, <code>lineTo</code>, and <code>curveTo</code> into a single method that can handle each of those operations with a single call. Instead of each of those being called separately, they are abstracted into numeric identifiers. For example, a <code>moveTo</code> operation is signified by a 1, while a <code>lineTo</code> operation is a 2, etc. Those values are then stored in a commands <code>Vector.&lt;int&gt;</code>. Each command corresponds to position values stored into a data <code>Vector.&lt;Number&gt;</code> where two consecutive numbers define a point in the target coordinate space. Note that these are not Point objects; the vector is simply a series of numbers where each group of two numbers represents a point value. When both the command and data vectors are supplied to <code>drawPath</code>, each command is matched up with their respective point values (a collection of 2 or 4 numbers) to generate a path in the <code>Graphics</code> object from which <code>drawPath</code> was called.</p>
<p>The command values used by <code>drawPath</code> are stored in an enumeration class called <code>GraphicsPathCommand</code>. Its values are defined as:</p>
<pre>public static const NO_OP:int = 0;
public static const MOVE_TO:int = 1;
public static const LINE_TO:int = 2;
public static const CURVE_TO:int = 3;
public static const WIDE_MOVE_TO = 4;
public static const WIDE_LINE_TO:int = 5;</pre>
<p>The WIDE variants of <code>moveTo</code> and <code>lineTo</code> use an extra set of coordinates, relying on 4 number values instead of the standard 2. Because of this, it can be easy to quickly change a <code>moveTo</code> or <code>lineTo</code> command into a <code>curveTo</code> without having to add additional values to the data vector. The extra coordinate used by <code>curveTo</code> would already be there, just ignored by the wide <code>moveTo</code> and wide <code>lineTo</code> operations.</p>
<p>Example:</p>
<pre>var commands:Vector.&lt;int&gt; = new Vector.&lt;int&gt;(5, true);
commands[0] = GraphicsPathCommand.MOVE_TO;
commands[1] = GraphicsPathCommand.LINE_TO;
commands[2] = GraphicsPathCommand.LINE_TO;
commands[3] = GraphicsPathCommand.LINE_TO;
commands[4] = GraphicsPathCommand.LINE_TO;
var data:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;(10, true);
data[0] = 10; <span class=comment>// x</span>
data[1] = 10; <span class=comment>// y</span>
data[2] = 100;
data[3] = 10;
data[4] = 100;
data[5] = 100;
data[6] = 10;
data[7] = 100;
data[8] = 10;
data[9] = 10;
graphics.beginFill(0x800000);
graphics.drawPath(commands, data);</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawpath_square.png" width=312></p>
<p>Notice that there are twice as many data values as there are commands. For non-<code>curveTo</code> and non-wide commands, <code>commands.length = data.length/2</code>. When using <code>curveTo</code> and wide commands, <code>commands.length = data.length/4</code>. Combinations of each could result in different ratios, but <code>commands.length</code> should always be an even value.</p>
<p>I know what you're thinking, that's a lot of code just for a square. Keep in mind that once you have those commands and data vectors, they can easily be reused again. You'll see this even more with the <code>IGraphicsData</code> objects.</p>
<p>That's not to say that this code cannot be reduced; it can. You can use a couple of shortcuts such as starting with vectors without a fixed length and adding all values with a single push call, or using the a vector conversion function to convert an array to a vector directly within the <code>drawPath</code> call. Here is the same code condensed:</p>
<pre>graphics.beginFill(0x800000);
graphics.drawPath(
Vector.&lt;int&gt;([1,2,2,2,2]),
Vector.&lt;Number&gt;([10,10, 100,10, 100,100, 10,100, 10,10]));</pre>
<p><strong>winding</strong></p>
<p>The third parameter in <code>drawPath</code>, not used here, is the <code>winding</code> parameter. This is a string that specifies the winding or fill rule for intersecting or overlapping paths. Its values are defined in the <code>GraphicsPathWinding</code> enumeration class:</p>
<pre>public static const EVEN_ODD:String = "evenOdd";
public static const NON_ZERO:String = "nonZero";</pre>
<p>Winding refers to the direction in which a path is drawn. When paths are drawn clockwise, they're considered positively wound. Paths drawn counter-clockwise are considered negatively wound.</p>
<p class=image><img height=234 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/winding_positive_negative.png" width=353></p>
<p>Even-odd is the standard fill rule and is the rule used by all of the original drawing APIs. This is also the default for <code>drawPath</code>. The alternate rule is the non-zero rule.</p>
<p class=image><img height=247 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/winding_rules_evenodd_nonzero.png" width=339></p>
<p>With even-odd, any overlapping paths will alternate between open and closed fills. If two squares drawn with the same fill intersect, the area in which they intersection occurs will not be filled. No adjacent areas will be either both filled or both unfilled. Unlike the non-zero rule, winding with the even-odd rule is inconsequential.</p>
<p>The non-zero rule, on the other hand, depends on winding to determine whether or not areas defined by intersecting paths are filled. When paths of opposite winding intersect, the area defined is unfilled, much like with even-odd. For paths of the same winding, the area that would be unfilled gets filled.</p>
<p>The names even-odd and non-zero refer to a more specific rule that defines just how these fills are handled. Positively wound paths are given a weight of +1; negatively wound paths are given a weight of -1. Given a point within an enclosed area of a shape defined by one or multiple paths, when an arbitrary line is drawn from that point extending on into indefinitely, the number of times that line crosses a path, and the combined weights of those paths are used to determine the fill. For even-odd, the count is used. When the count is odd, the area is filled. For even counts, the area is unfilled. Non-zero uses the weights. When the combined weights are not 0, the area is filled. For 0 weights, the area is unfilled.</p>
<p class=image><img height=256 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/winding_rules_evenodd_nonzero_line.png" width=344></p>
<p>It may seem a little over-complicated to have these fill rules, but there are situations where, when understood, they can be of use. For example, consider drawing a star shape. With the standard even-odd rule, that shape would need to be drawn using 10 different lines. With non-zero, that can be reduced to just 5.</p>
<p class=image><img height=169 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/star_evenodd_vs_nonzero.png" width=362></p>
<p>Here is that star in ActionScript using <code>drawPath</code> with 5 lines and a non-zero fill.</p>
<pre>graphics.beginFill(0x60A0FF);
graphics.drawPath(
Vector.&lt;int&gt;([1,2,2,2,2]),
Vector.&lt;Number&gt;([66,10, 23,127, 122,50, 10,49, 109,127]),
GraphicsPathWinding.NON_ZERO);</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawpath_star.png" width=312></p>
<p>So far the shapes used in these last couple of examples have been fairly simple and could have, for the most part, just as easily been created with the original drawing APIs. As you work with move complicated paths, the advantages of the new APIs (namely <code>drawPath</code>) will be much more apparent, especially in terms of performance.</p>
<p>With <code>drawPath</code>, you're avoiding much of the function overhead that results from multiple, consecutive calls to <code>lineTo</code>, <code>curveTo</code>, etc. Now, a complicated path can be drawn with a single method call. Additionally, the use of vectors streamlines the data being sent to the renderer. In fact, a new pipeline is being used to handle the drawing data more efficiently and accurately than before. That means <code>drawPath</code> should not only work faster, but also be more precise than the path drawn from <code>lineTo</code> and <code>curveTo</code>. These enhancements can go a long way as you begin to work with more complicated, dynamic drawings in ActionScript.</p>
<h4>Drawing Triangles</h4>
<p>Along with the new <code>drawPath</code> method comes another addition to the <code>Graphics</code> class, the <code>drawTriangles</code> method.</p>
<pre>public function drawTriangles(vertices:Vector.&lt;Number&gt;, indices:Vector.&lt;int&gt;=null, uvtData:Vector.&lt;Number&gt;=null, culling:String="none");</pre>
<p>This method is somewhat similar to drawPath in that it too uses a <code>Vector.&lt;Number&gt;</code> to specify point locations for a path to be drawn, only with <code>drawTriangles</code>, there is no commands list. The commands are predefined to use the point locations to draw triangles. Every 3 points (6 numbers) represents a triangle path to be drawn.</p>
<p>Though I'm sure there are a lot of people who enjoy drawing a bunch of random triangles on the screen, the real purpose for the drawTriangles method is facilitate 3D rendering through ActionScript. Though Flash and ActionScript still does not directly support 3D per se, the drawing API can be used to take a 3D model stored in memory and draw it to the screen in Flash as 2D. Because 3D models (in the end) are represented by a collection of triangles in space, <code>drawTriangles</code> can be used to quickly make that translation from 3D to 2D. How does it do this?</p>
<ul>
    <li>All data points (vertices) inherently define triangle paths (no commands necessary)
    <li>Indices can be set to allow for the reuse of vertices reducing redundant data
    <li>UV texture mapping can be applied to each vertex
    <li>Perspective bitmap distortion can be applied to bitmap textures (T in UVT) </li>
</ul>
<p>For the most part, the advantages of drawTriangles only show when drawing textured meshes, notably with a 3D perspective. And that is really the big feature with <code>drawTriangles</code>, use of the new perspective renderer in Flash Player 10. This lets you take a bitmap and distort it in a way that makes it appear in 3D.</p>
<p class=image><img height=157 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/image_perspective_render.png" width=267></p>
<p>This may not exactly be entirely new to Flash. You've probably seen images like this appear in 3D using Flash before. Those examples, however, had to break up an image into many smaller pieces (triangles, in fact) in order to get that effect, and the quality wasn't always so great - often showing jagged straight edges or distortions in the image because of the affine transformations used to make it appear as though a perspective transform was being applied. Now, perspective handling can all be done through Flash Player with as little as one triangle drawn through <code>drawTriangles</code> (though it is more common to use 2 triangles to construct a single rectangular plane).</p>
<p><strong>drawTriangles</strong></p>
<p>The first parameter of drawTriangles is the only required parameter, the vertices parameter. This is a vector of numbers defining the points from which your triangles are drawn. Without using indices, the length of the vector used should always be a factor of 6 since each triangle requires 3 points (3 sets of two x,y values).</p>
<p>The following example draws two triangles:</p>
<pre>graphics.beginFill(0xFF8000);
graphics.drawTriangles(
Vector.&lt;Number&gt;([
10,10,  100,10,  10,100,
110,10, 110,100, 20,100]));</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawtriangles_solid_triangles.png" width=312></p>
<p>Neither of these triangles share any points, but if they did, the second <code>drawTriangles</code> parameter, <code>indices</code>, could be used to reuse values in the <code>vertices</code> vector for more than one triangle.</p>
<p>One thing to be careful with when dealing with <code>indices</code> is that the indices they represent are point indices, not indices that relate directly to the vertices elements. In other words, an index in the <code>vertices</code> vector as defined by <code>indices</code> is actually the real index divided 2. For the third point of a <code>vertices</code> vector, for example, an <code>indices</code> index of 2 would be used, even though the first numeric value of that point starts at the vector index of 4.</p>
<p>Merging the triangles in the example above to share the diagonal edge, a new example can be developed to utilize <code>indices</code>:</p>
<pre>graphics.beginFill(0xFF8000);
graphics.drawTriangles(
Vector.&lt;Number&gt;([10,10, 100,10, 10,100, 100,100]),
Vector.&lt;int&gt;([0,1,2, 1,3,2]));</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawtriangles_solid_triangles_indices.png" width=312></p>
<p>Notice that though a square has now been drawn using two triangles, only 4 points were specified in the vertices vector. Using indices, the two points shared by the two triangles are reused for each triangle reducing the overall vertices count from 6 (12 numbers) to 4 (8 numbers).</p>
<p class=image><img height=159 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/square_triangle_indices.png" width=107></p>
<p>This becomes particularly conservative when dealing with larger triangle meshes where most points are shared by multiple triangles.</p>
<p>Once you start working with textures, you'll want to make use of the <code>uvtData</code> parameter of <code>drawTriangles</code>. This parameter allows you to set up UV mapping for bitmap fills.</p>
<p>UV mapping is a method for texturing objects. It relies on two values, a U horizontal (x) and a V vertical (y). Rather than being based on pixel values, they're based on percentages, where 0 U and 0 V is the top left of an image and 1 U and 1 V is the bottom right.</p>
<p class=image><img height=181 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/uv_bitmap_coordinates.png" width=218></p>
<p>Vectors of a triangle can be given UV coordinates to associate themselves with the respective locations on an image.</p>
<p class=image><img height=184 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/uv_triangle_coordinates.png" width=229></p>
<ul>
    <li>Point A UV: (.25, .33)
    <li>Point B UV: (.25, .66)
    <li>Point C UV: (1, .66) </li>
</ul>
<p>As the triangle is transformed in space, the image is mapped to the triangle based on those coordinates.</p>
<p class=image><img height=234 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/uv_triangle_texture_mapping.png" width=222></p>
<p>You can imagine how well this approach will work with 3D models drawn in Flash. No longer will complicated matrix calculations be required to transform a bitmap to a pseudo-3D plane. Now UV values can be set once and work consistently throughout the lifetime of a model.</p>
<p>As with <code>vertices</code>, UV coordinates are stored in a <code>Vector.&lt;Number&gt;</code> vector named <code>uvtData</code> (the T value will be covered a little later). Each UV value relates directly to the corresponding vertex in the vertices vector at that same index. Taking the previous triangle example, we can now map a bitmap to the square using UV values. To mix things up a little, the bottom right corner can be extended to the bottom right to show how the UV values react. <code>BridgeImage</code> here is a <code>BitmapData</code> image in the library.</p>
<pre>graphics.beginBitmapFill(new BridgeImage(0,0));
graphics.drawTriangles(
Vector.&lt;Number&gt;([10,10, 100,10, 10,100, 150,150]),
Vector.&lt;int&gt;([0,1,2, 1,3,2]),
Vector.&lt;Number&gt;([0,0, 1,0, 0,1, 1,1]));</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawtriangles_bridge.png" width=312></p>
<p>At this point, there is no 3D perspective rendering being applied to the texture. All image manipulation is being done in 2D. When you want to provide a 3D perspective to a bitmap, you need to include a T value with the UV coordinates in <code>uvtData</code>.</p>
<p>The T value in <code>uvtData</code> represents the 3D perspective, or more specifically, the scale factor of the associated vertex. For example, if an object is positioned in 3D space off in the distance so that it is 50% it's "original" size, it's T value would be .5. As triangles are drawn to represent objects in 3D space, their locations along the z axis determine their T value. The magic equation that specifically determines this is:</p>
<pre>T = focalLength/(focalLength + z);</pre>
<p>Where <code>focalLength</code> represents a focal length or calculated "screen" location which dictates the amount of perspective provided in the view.</p>
<p class=image><img height=178 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/focallength_screen.png" width=264></p>
<p>The value of T here can be used to scale basic shapes to make them seem further in the distance and is usually the value used to convert 3D points to 2D points. In the case of UVT, it is also used to scale a bitmap between the points within a triangle with perspective.</p>
<p>When defining UVT values, the T value directly follows the UV values defined for a vertex. With the inclusion of T, every 3 values in <code>uvtData</code> (U, V, and T) match up with every 2 values in <code>vertices</code> (x, and y). With just UV, <code>uvtData.length == vertices.length</code>. The inclusion of of T means <code>uvtData.length = 1.5*vertices.length</code>.</p>
<p>Taking a step up in complexity, the following example shows a plane being rotated in 3D space using UVT data.</p>
<pre><span class=comment>// plane vertex coordinates (and t values)</span>
var x1:Number = -100,	y1:Number = -100,	z1:Number = 0,	t1:Number = 0;
var x2:Number = 100,	y2:Number = -100,	z2:Number = 0,	t2:Number = 0;
var x3:Number = 100,	y3:Number = 100,	z3:Number = 0,	t3:Number = 0;
var x4:Number = -100,	y4:Number = 100,	z4:Number = 0,	t4:Number = 0;
var focalLength:Number = 200; <span class=comment>// focal length</span>
var bitmapData:BitmapData = new BridgeImage(0,0); <span class=comment>// texture</span>
<span class=comment>// 2 triangles for 1 plane, indices will always be the same</span>
var indices:Vector.&lt;int&gt; = new Vector.&lt;int&gt;();
indices.push(0,1,3, 1,2,3);
var container:Sprite = new Sprite(); <span class=comment>// container to draw triangles in</span>
container.x = 200;
container.y = 200;
addChild(container);
function rotatePlane(event:Event):void {
<span class=comment>// rotate vertices over time</span>
var ticker = getTimer()/400;
z2 = z3 = -(z1 = z4 = 100*Math.sin(ticker));
x2 = x3 = -(x1 = x4 = 100*Math.cos(ticker));
<span class=comment>// calculate t values</span>
t1 = focalLength/(focalLength + z1);
t2 = focalLength/(focalLength + z2);
t3 = focalLength/(focalLength + z3);
t4 = focalLength/(focalLength + z4);
<span class=comment>// determine traingle vertices based on t values</span>
var vertices:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
vertices.push(x1*t1,y1*t1, x2*t2,y2*t2, x3*t3,y3*t3, x4*t4,y4*t4);
<span class=comment>// set T values allowing perspective to change</span>
<span class=comment>// as each vertex moves around in z space</span>
var uvtData:Vector.&lt;Number&gt; = new Vector.&lt;Number&gt;();
uvtData.push(0,0,t1, 1,0,t2, 1,1,t3, 0,1,t4);
<span class=comment>// draw</span>
container.graphics.clear();
container.graphics.beginBitmapFill(bitmapData);
container.graphics.drawTriangles(vertices, indices, uvtData);
}
<span class=comment>// animage every frame</span>
addEventListener(Event.ENTER_FRAME, rotatePlane);</pre>
<p class=image><img height=329 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawtriangles_rotating_bridge.png" width=296> <br><a href="http://www.senocular.com/flash/tutorials/flash10drawingapi/demos/Spinning%20Covered%20Bridge.html" target=_blank><u><font color=#0000ff>Preview (Requires Flash Player 10)</font></u></a></p>
<p>With this example, a number of 3D points are defined in simple timeline variables <code>x1</code>, <code>y1</code>, <code>z1</code>, <code>x2</code>, <code>y2</code>, etc. These are then transformed into 2D points based on T values calculated from their z position and the <code>focalLength</code> variable. Notice how the same T values used to calculate the position of each 3D point in 2D space directly related to the T value in UVT; quite simple. </p>
<p>There's one more aspect of <code>drawTriangles</code> not touched upon, back-face culling, or simply culling. Culling refers to the determination of a triangle (face) being visible during the render process. </p>
<p>Inherently all triangles are always rendered no matter their size, shape, or position. To save on rendering cycles, there are often times when you would want a number triangles to be skipped by the render. Consider a cube rotating in space. At any given time you'll never see more than 3 sides of that cube since the sides not in view would be facing the other direction on the other side of the cube.</p>
<p class=image><img height=179 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/cube_visible_sides.png" width=153></p>
<p>Since those sides are not going to be seen, the renderer shouldn't worry about drawing them. Using culling, you can do exactly that.</p>
<p>The way that <code>drawTriangles</code> determines culling is through winding. Assuming that all out-facing triangles of a 3D shape are drawn with the same winding, once a face is turned around, it's winding also changes. At that point it can be culled and removed from rendering.</p>
<p>The fourth parameter, <code>culling</code>, lets you set culling rules for triangles drawn with <code>drawTriangles</code>. It's a string with 3 possible values as defined in the <code>TriangleCulling</code> enumeration class:</p>
<pre>public static const NONE:String = "none";
public static const POSITIVE:String = "positive";
public static const NEGATIVE:String = "negative";</pre>
<p>A culling value of <code>"none"</code> (default) performs no culling. A setting of <code>"positive"</code> culls, or removes, triangles drawn positively wound while <code>"negative"</code> culls triangles negatively wound. When drawing the cube, if all of its out-facing triangles are drawn with positively wound, its culling should be set to <code>"negative"</code> so reversed faces not in view would not be rendered to the screen.</p>
<p class=image><img height=217 alt="" src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/cube_with_winding.png" width=375></p>
<p>The last <code>drawTriangles</code> example with the rotating image did not set a culling value. If set, only one side of the rotated image would be visible as it rotated. You can test this by adding in a <code>culling</code> value in the call to <code>drawTriangles</code>.</p>
<pre><span class=comment>// from earlier example</span>
container.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NEGATIVE);
</pre>
<p>Want to determine the winding of 3 points that make up a triangle? Use this function:</p>
<pre>function getWinding(path:Vector.&lt;Number&gt;):int {
if (path == null || path.length &lt; 6) return 0;
var winding:int = (path[0]-path[4])*(path[3]-path[5]) - (path[1]-path[5])*(path[2]-path[4]);
if (winding &lt; 0) return -1;
else if (winding &gt; 0) return 1;
return 0;
}</pre>
<p>It takes a <code>Vector.&lt;Number&gt;</code> of the 3 coordinate points of a triangle and will return 1 if winding is positive, -1 if winding is negative, and 0 if winding cannot be determined (if, for example, the triangle is flat).</p>
<p><strong>drawGraphicsData</strong></p>
<p>The final addition to the <code>Graphics</code> class is <code>drawGraphicsData</code>.</p>
<pre><span class=code-keyword>public</span> function drawGraphicsData(graphicsData:Vector.&lt;IGraphicsData&gt;):void</pre>
<p>This method is used to render drawings stored in the new graphics data classes.</p>
<h3>New Graphics Data Classes</h3>
<p>Along with new methods available in the <code>Graphics</code> class, a new collection of classes of the type <code>IGraphicsData</code> (an interface each of the classes implement) have been created to serve as data containers for the methods of the drawing API. These classes include:</p>
<ul>
    <li>GraphicsBitmapFill
    <li>GraphicsEndFill
    <li>GraphicsGradientFill
    <li>GraphicsPath
    <li>GraphicsShaderFill
    <li>GraphicsSolidFill
    <li>GraphicsStroke
    <li>GraphicsTrianglePath </li>
</ul>
<p>The idea behind these classes is that you can store complete drawings in list (<code>Vector.&lt;IGraphicsData&gt;</code>) that can be passed around as data and reused with any target. This also makes it really easy to save drawings for use at a later time.</p>
<p>You may notice that there are multiple fill classes for each style of fill, but only one stroke class. This is because the stroke class actually uses the fill classes to define its style. So every stroke is actually made up of the stroke class and a fill class. Other than that, the API for these classes mirror the methods they represent. The basic run down of <code>Graphics</code> methods to these classes is as follows:</p>
<table class=table cellPadding=5>
    <tbody>
        <tr>
            <th>Method</th>
            <th>Class</th>
        </tr>
        <tr>
            <td>beginFill</td>
            <td>GraphicsSolidFill</td>
        </tr>
        <tr>
            <td>beginBitmapFill</td>
            <td>GraphicsBitmapFill</td>
        </tr>
        <tr>
            <td>beginGradientFill</td>
            <td>GraphicsGradientFill</td>
        </tr>
        <tr>
            <td>beginShaderFill</td>
            <td>GraphicsShaderFill</td>
        </tr>
        <tr>
            <td>lineStyle</td>
            <td>GraphicsStroke + GraphicsSolidFill</td>
        </tr>
        <tr>
            <td>lineBitmapStyle</td>
            <td>GraphicsStroke + GraphicsBitmapFill</td>
        </tr>
        <tr>
            <td>lineGradientStyle</td>
            <td>GraphicsStroke + GraphicsGradientFill</td>
        </tr>
        <tr>
            <td>lineShaderStyle</td>
            <td>GraphicsStroke + GraphicsShaderFill</td>
        </tr>
        <tr>
            <td>moveTo <br>lineTo<br>curveTo<br>drawPath</td>
            <td>GraphicsPath</td>
        </tr>
        <tr>
            <td>drawTriangles</td>
            <td>GraphicsTrianglePath</td>
        </tr>
    </tbody>
</table>
<p>In addition, the <code>GraphicsPath</code> class has <code>moveTo</code>, <code>lineTo</code>, <code>curveTo</code>, <code>wideLineTo</code>, and <code>wideMoveTo</code> utility methods to easily define those commands for a <code>GraphicsPath</code> instance. This can ease the pain of trying to define or update the <code>commands</code> and <code>data</code> values directly.</p>
<p>Unfortunately, there are no equivalents to <code>drawCircle</code>, <code>drawEllipse</code>, <code>drawRect</code>, or <code>drawRoundedRect</code>. These would have to be drawn into a <code>GraphicsPath</code> instance using <code>lineTo</code> and <code>curveTo</code> or by supplying the data manually.</p>
<p>Once you have a collection of <code>IGraphicsData</code> instances, you can draw them through the <code>Graphics</code> method, <code>drawGraphicsData</code>. This method takes a vector of <code>IGraphicsData</code> instances and runs them through the drawing API in sequential order.</p>
<p>Example:</p>
<pre><span class=comment>// stroke object</span>
var stroke:GraphicsStroke = new GraphicsStroke(3);
stroke.joints = JointStyle.MITER;
stroke.fill = new GraphicsSolidFill(0x102020); // solid stroke
<span class=comment>// fill object</span>
var fill:GraphicsGradientFill = new GraphicsGradientFill();
fill.colors = [0x0000FF, 0xEEFFEE];
fill.matrix = new Matrix();
fill.matrix.createGradientBox(70,70, Math.PI/2);
<span class=comment>// path object</span>
var path:GraphicsPath = new GraphicsPath(new Vector.&lt;int&gt;(), new Vector.&lt;Number&gt;());
path.commands.push(1,2,2);
path.data.push(125,0, 50,100, 175,0);
<span class=comment>// combine objects for complete drawing</span>
var drawing:Vector.&lt;IGraphicsData&gt; = new Vector.&lt;IGraphicsData&gt;();
drawing.push(stroke, fill, path);
<span class=comment>// draw the drawing multiple times</span>
<span class=comment>// change one value to modify each variation</span>
graphics.drawGraphicsData(drawing);
path.data[2] += 200;
graphics.drawGraphicsData(drawing);
path.data[2] -= 150;
graphics.drawGraphicsData(drawing);
path.data[2] += 100;
graphics.drawGraphicsData(drawing);
path.data[2] -= 50;
graphics.drawGraphicsData(drawing);</pre>
<p class=image><img height=308 src="http://www.senocular.com/flash/tutorials/flash10drawingapi/images/drawgraphicsdata_icicles.png" width=312></p>
<p>In the example above, the original <code>drawing</code> definition only drew one of the icicle shapes. By modifying one value in the path used by the drawing, it could be redrawn multiple times for a more complex image.</p>
<p>Though <code>IGraphicsData</code> objects can define fill and stroke styles, they don't have to. <code>Graphics</code> class methods can be used to style while <code>IGraphicsData</code> objects can be used to draw a saved collection of paths, or vise versa. They work together all the same.</p>
<p>You may have also noticed that there is no <code>clear</code> object. The <code>clear</code> method remains solely as a method of the <code>Graphics</code> class. It is also still required to clear out a previous drawing before starting a new one, unless you're adding on to the original, as seen in the example above. Though you can change a single portion of a path or collection of <code>IGraphicsData</code> objects that make up a drawing, that entire drawing will have to be redrawn from scratch for those changes to be made visible. Nevertheless, having object representations of your drawing greatly simplify the ability to modify or even animate dynamic graphics.</p>
<h3>Conclusion</h3>
<p>With all of the new drawing API features in Flash Player 10, it might take a while to get caught up. Having 3D (or 2.5D) support is a big one, and the drawing API has been updated to be a part of that - a part outside of the <code>DisplayObject</code> integration (not covered here).</p>
<p>Other drawing API features challenge you to rethink how you draw images dynamically in Flash Player. With <code>IGraphicsData</code> objects, you now have a standardized data format for maintaining and reusing dynamic drawings whose usage is completely different from that of the older APIs. Existing systems may need to be ported, but the benefit may be well worth it.</p>
<p>One thing is for sure, with 3D, Pixel Bender integration, and the other enhancements, it's certainly an exciting time for Flash Player on the visual front. I'm excited to see how these features will be used to create totally new and compelling content.</p>
<br>出处：<a href="http://www.senocular.com/flash/tutorials/flash10drawingapi/">http://www.senocular.com/flash/tutorials/flash10drawingapi/</a><img src ="http://www.cnitblog.com/cmoron/aggbug/44901.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/cmoron/" target="_blank">牛牛猪</a> 2008-06-04 00:00 <a href="http://www.cnitblog.com/cmoron/articles/44901.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>