skip to main |skip to sidebar
显示标签为“actionscript”的博文。显示所有博文
显示标签为“actionscript”的博文。显示所有博文

2008-06-22

[AS3]详解E4X的变量展开(3)——效率与实用性

从上一篇文章可以看出使用 E4X 变量展开要慢于 new String。因为 E4X 是先把字符串转换为 XML(XMLList) 再转换为字符串。在解析字符串时开销很大。下面是测试的结果:

方式
第1次
第2次
第3次
第4次
第5次
平均
E4X 变量展开
1008
1490
959
1037
959
1091
new String
135
110
125
111
117
120

(E4X 变量展开的代码为:<>abc{1+2}def</>.toString(),new String 的代码为:"abc" + (1+2) + "def";循环次数为10000次;单位:ms。)

由此可见 E4X 变量展开的效率要差一些,但不在大量的循环里的时候差别不是很明显。因为这里的差别是循环 10000 次的结果,执行一次的差别仅仅为 0.097(ms),是可以忽略不计的。因此 E4X 变量展开还是很有用的。

比如说以下代码(预载入文本):

E4X 变量展开的格式:

e4xStr(<>Loading... {loaded}K/{total}K ({loaded/total*100}%)</>)
//e4xStr 为
function e4xStr(e4x:XMLList):String {
  return e4x.toString();
}

new String 的格式:

"Loading... " + loaded + "K/" + total + "K (" + (loaded/total*100) + "%)"

由此可见 E4X 变量展开的可读性要大大高于new String 的格式。因为预载入文本一般为每一帧执行一次,所以说一般不会遇到效率的瓶颈。

2008-06-15

[AS3]详解E4X的变量展开(2)——内部实现

[AS3]E4X的变量展开详解(1)——基础,下面来研究E4X变量展开的内部实现。

源文件:

var a = <a>abc{123}def</a>;

使用 abcdump 反编译后的结果:

 0        getlocal0
1 pushscope
2 findpropstrict XML
4 getproperty XML
6 pushstring "<a>abc"
8 pushbyte 123
10 esc_xelem
11 add
12 pushstring "def</a>"
14 add
15 construct (1)
17 getglobalscope
18 swap
19 setslot 1
21 returnvoid

new String() 做比较

源文件:

var a = new String("a")

使用 abcdump 反编译后的结果:

 0        getlocal0
1 pushscope
2 findpropstrict String
4 pushstring "a"
6 constructprop String (1)
9 getglobalscope
10 swap
11 setslot 1
13 returnvoid

会发现 E4X 与 new String() 很相似。

先来看压栈的部分

new String() 的为:

4        pushstring          "a"
6 constructprop String (1)

new String() 是先把 "a" 压入堆栈,再调用构造函数。

而 E4X 的为:

 6        pushstring          "<a>abc"
8 pushbyte 123
10 esc_xelem
11 add
12 pushstring "def</a>"
14 add
15 construct (1)

有点复杂,过程为:

1.把 "<a>abc" 压入堆栈(第6行)

"<a>abc"
XML

2.把 123 压入堆栈(第8行)

123
"<a>abc"
XML

3.把 栈顶(这里为 123)转换为 String。(第10行) esc_xelem 相当于 toXMLString(123) *注意 toXMLString 为内部函数,无法调用。

"123"
"<a>abc"
XML

4.把堆栈的栈顶的两项相加。(第11行)

"<a>abc123"
XML

5.把 "def</a>" 压入堆栈(第12行)

"def</a>"
"<a>abc123"
XML

6.把堆栈的栈顶的两项相加。(第14行)

"<a>abc123def</a>"
XML

7.调用构造函数。(第15行)即返回 new XML("<a>abc123def</a>")

总结

<a>abc{123}def</a>;

就相当于

new XML("<a>abc" + toXMLString(123) + "def</a>");

最后,关于AVM2的格式可以参照AVM2 Instructions,不明白的语句可以去查一查。

未完待续,下一篇详解效率与实用性

2008-06-14

[AS3]E4X的变量展开详解(1)——基础

在 E4X 里可以使用 {} 来进行变量展开。比如下以下代码:

var foo:String = "123";
trace(<>abc{foo}def</>.toString());// -> abc123def

{} 里面可以为在执行期才知道的变量:

var foo:String = Math.random().toSTring();
trace(<>abc{foo}def</>.toString());

{} 还可以使用表达式(运算符、调用函数,etc.):

var foo:String = "123";
trace(<>{foo + foo}</>.toString());// -> 123123
//比较复杂一点的
trace(<>{foo += "456", foo+"789"}</>.toString());// -> 123456789
//调用函数
trace(<>{foo.substr(1)}</>.toString());// -> 23

{} 里无法使用语句,但可以使用匿名函数来代替:

var foo:String = "123";
trace(<>{(function(a){return a + a})(foo)}</>.toString());// -> 123123

使用 E4X 会减少一些代码,方便书写,但注意 <></> 的类型为 XMLList

最后给一个方便的函数:

public function e4xstr(e4x:XMLList):String {
  return e4x.toString();
}
//用法
var a:int = 3;
var b:int = 5;
trace(e4xstr(<>a+b={a+b}, a*b={a*b}</>)); // -> a+b=8, a*b=15

未完待续,下一篇详解 E4X 的内部实现

2008-06-07

[AS3]逻辑和(&&)与逻辑或(||)的妙用

使用 &&|| 可以简化代码,提高可读性。比如以下代码:

var foo:Boolean = true;
var bar:Object = {};
trace(foo && bar);  // -> [object Object]
trace(foo || bar);  // -> true

&&|| 的结果是由运算符的左边的项目(这里是 foo )来决定的。简单地说,规则为:

A && B : 如果 Boolean(A) 为 false 则返回 A,否则返回 B。
A || B : 如果 Boolean(A) 为 true 则返回 A,否则返回 B。

所以实际上并没有进行真正数学意义上的逻辑运算,返回值也不一定是 Boolean 值。

Boolean(A) 的转换规则如下:

Undefiend  : false
Null   : false
Boolean  : 与转换前相同
Number   : 0 或 NaN 为 false 替其他的为 true
String   : 空串为 false  其他的为 true
Object   : true

使用逻辑运算符简化代码

使用逻辑运算符可以简化简单的逻辑判断(if)。

//1. 使用 if
if (foo) {
  doSomething();
}
if (!bar) {
  doSomething2();
}

//2. 使用逻辑运算符
foo && doSomething();
bar || doSomething2();

多重 if 的写法:

//1. 使用 if
if (foo) {
  if (bar) {
    sayHello();
  }
}
 
//2. 使用 if 与逻辑运算符
if (foo && bar) {
  sayHello();
}

//3. 只使用逻辑运算符
foo && bar && sayHello();     

虽然 else 也可以实现,但代码有可能变得难一閲读。

&&= 与 ||=

在需要代入结果时,使用 &&=||= 更方便。

//1. 使用 if
if (foo) {
  foo =  "<" + foo + "/>";
}

//2. 使用逻辑运算
foo &&= "<" + foo + "/>";

2008-04-27

[AS3]ActionScript 中数组的访问的 BUG

重要度:(3/5) 这个问题看似简单,但 debug 时却难以发现。

数组访问时如果下标使用了函数并且使用了 +=-=*= 等运算符时需要特别地注意,函数会被调用两次!

比如以下代码:

var i:int = 0;
var foo = function():int {
trace("foo");
return ++i;
}
var arr:Array = [1,2,3];
arr[foo()] += 10;
trace(arr);

将会输出:

foo;
foo;
1,13,3

而正确的输出为:

foo;
1,12,3

这是因为在编译时编译器只做了一下简单的操作:

(a += b) --> (a) = (a) + (b)

以至于将 arr[foo()] += 10; 编译成 arr[foo()] = arr[foo()] + 10;

2008-04-06

[AS3]mxmlc 编译器的 BUG

PS:因为最近很忙,好久没有更新 Blog 了。

最近发现了一个 mxmlc 编译器的 BUG,当含有 -(true ? 1 : 0) 的代码编译会出错。

比如以下代码:

package {
import flash.display.Sprite;
public class TestBUG extends Sprite {

public function TestBUG() {
trace('Test');
var foo:Number = -(true ? 1 : 0);
}
}
}

使用 mxmlc 编译时会弹出错误:

Error: null
java.lang.NullPointerException
  at macromedia.asc.semantics.ConstantEvaluator.evaluate(ConstantEvaluator.java:1168)
  at macromedia.asc.parser.UnaryExpressionNode.evaluate(UnaryExpressionNode.java:33)
  at macromedia.asc.semantics.ConstantEvaluator.evaluate(ConstantEvaluator.java:1805)
......

但把 -(true ? 1 : 0)- 号去掉后编译却可以正常进行,真奇怪!

影响的版本:

  • 2.0
  • 2.0.1
  • 3.0.0

2008-03-16

[AS3] ActionScript 3 的代码规范

Adobe 推出了AS3 的代码的书写规范。详情参见:Coding Conventions

它推出的代码规范并不意味着开发者必须严格地遵行,仅仅是建议罢了(开发 Flex SDK 的除外)

以下为我推荐的一些内容:

数组的声明格式

var a:Array /* of String */ = [];
var a:Array /* of int */ = [ 1, 2, 3 ];
function f(a:Array /* of Number */):Array /* of Object */  {
  //...
}

不要写成

var a:Array = [];
var a:Array = new Array();
var a:Array = new Array(1, 2, 3);
function f(a:Array):Array {
  //...
|

Object 的声明格式

var o:Object = {};
var o:Object = { a: 1, b: 2, c: 3 };

不要写成

var o:Object = new Object();
var o:Object = new Object();
o.a = 1;
o.b = 2;
o.c = 3

比较

if (n == 3) // "如果 n 是 3"

不要写成

if (3 == n) // "如果 3 是 n"

2008-02-07

[AS3]让构造函数不执行 super()

在构造函数里一般 super() 必须执行。如果不写,在编译时会在构造函数的最上面添加 super()。但有时又需要不执行。比如以下代码:

class AbstractClass {
  public function AbstractClass() {
    throw new Error("Abstract class");
  }
}
class SubClass extends AbstractClass {
  public function SubClass() {
    trace("sub class");
  }
}

SubClass 对象会因为超类弹出错误而创建失败。但如果让 super() 不执行就可以了。

class SubClass extends AbstractClass {
  public function SubClass() {
    if(0) { //这里必须为返回 false 的表达式。但不能为 "false"(false 会被编译器忽视)。
      super();
    }
    trace("sub class");
  }
}

测试代码:

package {
  import flash.display.*;
  public class Test extends Sprite {
    public function Test() {
      new AbstractClass(); // -> Super CLass
      new SubClass1(); // -> Super CLass / Sub CLass1 start / Sub CLass1 end
      new SubClass2(); // -> Sub CLass2 start / Sub CLass2 end
    }
  }
}
class AbstractClass {
  public function AbstractClass() {
    trace("Super CLass");
  }
}
class SubClass1 {
  public function AbstractClass() {
    trace("Sub CLass1 start");
    super();
    trace("Sub CLass1 end");
  }
}
class SubClass2 {
  public function AbstractClass() {
    trace("Sub CLass2 start");
    if(0) {
      super();
    }
    trace("Sub CLass2 end");
  }
}

2008-01-20

[AS3]小心使用连续赋值

连续赋值即为 a = b = 1 的形式的赋值。这样写有时候会很方便,但有一个陷阱!

比如以下代码:

var a:Array, b:String;
a = b = null;

会报类型错误。因为在内部是编译成以下形式的。

var a:Array;
var b:String;
b = null;
a = b;

理所当然会报错了。以后可要注意。

2008-01-14

[AS3]for 循环数组的效率测试

循环 ("abcdefghijklmnopqrstuvwxyz").split("") 这个数组 100000 次的结果:(单位:ms)

No.for 循环for..in 循环for each 循环
1
17  
8213  
2165  
2
19  
8126  
2188  
3
18  
8338  
2128  
4
18  
7100  
2409  
5
17  
8054  
2945  
平均
17.8
7900.2
2367.0
偏差(越小越稳定)
0.748
443.326
305.225

测试程序:

源码:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
 scriptTimeLimit="10"
 width="520" height="490">
 <mx:Script>
 <![CDATA[
 
 private static const FOR:String = "for 循环";
 private static const FOR_IN:String = "for..in 循环";
 private static const FOR_EACH:String = "for each 循环"
 private static var LOOP_SRC:Array = ("abcdefghijklmnopqrstuvwxyz").split("");
 
 private function test(testCase:String):void {
  var loopCount:int = int(count_ns.value);
  var start:int, now:int;
  var i:int;
  output("@" + testCase);
  try {
   switch (testCase) {
    case FOR:
     start = getTimer();
     for (i = 0; i < loopCount; i++) {
      //var length:int = LOOP_SRC.length;
      for(var j:int; j<LOOP_SRC.length; j++) {
       LOOP_SRC[j].toUpperCase();
      }
     }
     now = getTimer();
     break;
    case FOR_IN:
     start = getTimer();
     for (i = 0; i < loopCount; i++) {
      for(var j2:String in LOOP_SRC) {
       LOOP_SRC[j2].toUpperCase();
      }
     }
     now = getTimer();
     break;
    case FOR_EACH:
     start = getTimer();
     for (i = 0; i < loopCount; i++) {
      for each(var v:String in LOOP_SRC) {
       v.toUpperCase();
      }
     }
     now = getTimer();
     break;
   }
  }
  catch (e:Error) {
   now = getTimer();
   output("  " + i + "次时弹出错误: " + e);
  }
  var time:int = now - start;
  output("  经过时间(ms): " + nf.format(time));
  output("  1秒中的循环次数: " + nf.format(i * 1000 / time));
  output("----------------------------------------");
 }

 private function output(msg:String):void {
  output_ta.text += msg + "\n";
 }
 ]]>
 </mx:Script>
 
 <mx:NumberFormatter id="nf" precision="0" />
 <mx:Panel title="循环数组的压力测试"
  paddingTop="8" paddingRight="8" paddingBottom="8" paddingLeft="8">
  <mx:HBox>
   <mx:Label text="循环回数:" />
   <mx:NumericStepper id="count_ns" value="100000" maximum="10000000" />
  </mx:HBox>
  <mx:HBox>
   <mx:Button label="{FOR}" click="test(FOR)" />
   <mx:Button label="{FOR_IN}" click="test(FOR_IN)" />
   <mx:Button label="{FOR_EACH}" click="test(FOR_EACH)" />
  </mx:HBox>
  <mx:TextArea id="output_ta" width="400" height="300" />
  <mx:Label color="red" text="*Application.scriptTimeLimit设为了10秒。" />
 </mx:Panel>
</mx:Application>

2008-01-13

[AS3]trace() 与 logging 的压力测试

trace() 其实很慢!以后再发布时,一定要注意去掉 trace()mx.logging.Target

源代码:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
scriptTimeLimit="5"
width="520" height="490">
<mx:Script>
<![CDATA[
import mx.logging.ILogger;
import mx.logging.Log;
import mx.logging.targets.TraceTarget;

private static const logger:ILogger = Log.getLogger("TraceTest");

private static const NONE:String = "无 trace()";
private static const TRACE:String = "有 trace()";
private static const LOGGER:String = "logger.debug()"

private var tt:TraceTarget;

private function test(testCase:String):void {
var loopCount:int = int(count_ns.value);
var start:int, now:int;
var i:int;
output("@" + testCase);
try {
switch (testCase) {
case NONE:
start = getTimer();
for (i = 0; i < loopCount; i++) {
"abcdefghijklmnopqrstuvwxyz";
}
now = getTimer();
break;
case TRACE:
start = getTimer();
for (i = 0; i < loopCount; i++) {
trace("abcdefghijklmnopqrstuvwxyz");
}
now = getTimer();
break;
case LOGGER:
start = getTimer();
for (i = 0; i < loopCount; i++) {
logger.debug("abcdefghijklmnopqrstuvwxyz");
}
now = getTimer();
break;
}
}
catch (e:Error) {
now = getTimer();
output(" " + i + "次时弹出错误: " + e);
}
var time:int = now - start;
output(" 经过时间(ms): " + nf.format(time));
output(" 1秒中的循环次数: " + nf.format(i * 1000 / time));
output("----------------------------------------");
}

private function updateTraceTarget():void {
if (!tt) {
tt = new TraceTarget();
tt.includeDate = false;
tt.includeTime = false;
tt.includeLevel = true;
tt.includeCategory = true;
}

if (traceTarget_ch.selected) {
Log.addTarget(tt);
} else {
Log.removeTarget(tt);
}
}

private function output(msg:String):void {
output_ta.text += msg + "\n";
}
]]>
</mx:Script>

<mx:NumberFormatter id="nf" precision="0" />
<mx:Panel title="trace()与logger.debug()的压力测试"
paddingTop="8" paddingRight="8" paddingBottom="8" paddingLeft="8">
<mx:HBox>
<mx:Label text="循环回数:" />
<mx:NumericStepper id="count_ns" value="1000000" maximum="10000000" />
</mx:HBox>
<mx:HBox>
<mx:Button label="{NONE}" click="test(NONE)" />
<mx:Button label="{TRACE}" click="test(TRACE)" />
<mx:Button label="{LOGGER}" click="test(LOGGER)" />
<mx:CheckBox id="traceTarget_ch" label="添加TraceTarget" change="updateTraceTarget()"/>
</mx:HBox>
<mx:TextArea id="output_ta" width="400" height="300" />
<mx:Label color="red" text="*Application.scriptTimeLimit设为了5秒。" />
</mx:Panel>
</mx:Application>

2008-01-07

[AS3]pixelDissolve 有 Bug?

最近发现使用 BitmapData.pixelDissolve 溶解像素时,无论 randomSeednumOfPixels 参数是多少,sourceRect 指定的矩形的左上角一定会被溶解!

比如设定为溶解 10 个像素时,左上角 + 剩下的像素中取 10 个会变化。设为溶解 0 个时,只有左上角会变化。

例子: 可以看到左上角很不自然。

例子的代码:

package {
import flash.display.*;
import flash.events.Event;
import flash.geom.*;

[SWF(width="32",height="32")]
public class Test2 extends Sprite{
public function Test2():void {
var bitmap:BitmapData = new BitmapData(32, 32, false,0);
var seed:int = 0;
addChild(new Bitmap(bitmap));
addEventListener(Event.ENTER_FRAME,
function (event:Event):void {
seed = bitmap.pixelDissolve(bitmap, bitmap.rect, new Point(0,0),
seed, 5, Math.random()*0x1000000);
}
);
}
}
}

我用的 Flash Player 的版本是 9.0.115.0。

2008-01-06

[AS3]加载大于 2880px 的图片的方法

Flash Player 在加载大于 2880px 的图片时,大于 2880px 的区域将会被剪掉!2880px 这个限度虽然一般不小,但对某些开发来说,却是一堵又高又大的墙。

但在 AS3.0 里,却是可以办到的!使用 URLStream 加载后,用 Loader.loadBytes 来显示

通过二进制的加载,会忽视外部域名的图片的安全沙箱。也就是说可以随便 draw 了。但注意加载本身是需要 crossdomain.xm了的。

2007-12-24

[AS3.0]使用":?"运算符来代替"if..else"的函数型编程

在函数型语言中,所有的语句都有返回值,当然 if..else 也不例外。这个功能在命令型语言中是没有的。

比如以下代码:

public function foo(a:int):int {
if(a<0) {
return -1
}else if(a==0) {
return 0
}else{
return 1
}
}

既难读也麻烦。如果使用 haskell(一种函数型语言) 来写的话:

foo a = if a<0 then -1 
else if a==0 then 0
else 1

十分简练。注意,AS3.0 有一个 :? 运算符,可以用它来实现,如果把格式写好的话,可读性会比 if..else 还要好。

public function foo(a:int):int {
return a<0 ? -1 :
a==0 ? 0 :
1;
}

if..else 相套时,可以这样写:

public function foo2(x:int, y:int):int {
return x>0 ? (
y>0 ? 1 :
y<0 ? 4 :
-1
): x<0 ? (
y>0 ? 2 :
y<0 ? 3 :
-1
):
-1;
}

2007-11-18

[AS3]高级图形处理(2)——老照片滤镜

照片的时间久了,就会出现褪色、模糊、杂色

实现方法

褪色
使用ColorTransform。参数为new ColorTransform(0.7, 0.7, 0.7, 0x44, 0x33, 0x22)
模糊
使用BlurFilter。参数为new BlurFilter(0.2, 0.2)
添加杂色
使用BitmapData.noise($randomSeed, 0xee, 0xff, 7, true)

核心代码

//Color Transform
outputBitmapData.colorTransform( sourceRect, new ColorTransform(0.7, 0.7, 0.7, 0x44, 0x33, 0x22) );
//Blur
outputBitmapData.applyFilter( outputBitmapData, sourceRect, destPoint, new BlurFilter(0.2, 0.2) );
//Noise
var noise:BitmapData = new BitmapData($width, $height, false);
noise.noise(0, 0xee, 0xff, 7, true);
outputBitmapData.draw(
noise,
new Matrix(1, 0, 0, 1, destPoint.x, destPoint.y),
null,
BlendMode.MULTIPLY
);

完整代码下载

2007-10-28

[AS3]高级图形处理(1)——自定义颜色比率的噪音(Noise)

一般制作线性噪音(0~255之间呈线性分布的噪音)很简单。使用BitmapData.noise()即可。

但要求产生的噪音当中,红色占10%、绿色占10%、蓝色占80%该怎么办?没有现成的方法可以用。但这类噪声却很有用,比如游戏地图的随机生成、制作宇宙的繁星等。

其实很简单!可以使用BitmapData.pixelDissolve()BitmapData.pixelDissolve()是用来溶解像素,制造马赛克的过渡效果的。但它还可以制造噪音!

代码:

package advancedflex.display.images {
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;

public class ImageRandom {

public static function customColorNoise(
src:BitmapData,
sourceRect:Rectangle,
colors:Array,
ratios:Array,
randomSeed:int = 0):int
{
if(colors.length != ratios.length) {
throw new ArgumentError("colors.length != ratios.length.")
}
if(!src) {
throw new ArgumentError("src is null.")
}
var length:int = colors.length;
var s:int = src.width * src.height;
for(var i:int = 0; i < length; i++) {
ratios[i] = s*ratios[i];
}
return customColorNoize2(src, sourceRect, colors, ratios, randomSeed);
}

public static function customColorNoize2(
src:BitmapData,
sourceRect:Rectangle,
colors:Array,
numPixels:Array,
randomSeed:int = 0):int
{
if(colors.length != numPixels.length) {
throw new ArgumentError("colors.length != numPixels.length.")
}
if(!src) {
throw new TypeError("Src must not be null.");
}
var length:int = colors.length;
var point:Point = new Point(sourceRect.x, sourceRect.y)
for(var i:int = 0; i < length; i++) {
randomSeed = src.pixelDissolve(src, sourceRect, point, randomSeed, numPixels[i], colors[i]);
}
return randomSeed;
}
}
}

2007-10-21

[AS3]位图的色阶控制(2) -- 伽玛校正(Gamma Correction)

[AS3]位图的色阶控制(1) -- 得到色阶

位图调节色阶时,在内部使用的是曲线校正中的伽玛校正。

伽玛校正为调整亮度的方法,它只有一个参数即γ(Gamma)。伽玛校正的变换公式为:

X'=X_max*(x/X_max)^(1/γ)

x'为变换后的亮度。xmax为x的最大值。

γ = 1 x' = x (没有变化)
γ = 0.5 x' = x2(变暗)
γ = 2 x' = √x(变亮)

调整色阶的三个滑条的意思

Plotoshop的调整色阶里有黑,灰,白三个滑条,黑为阴影,白为高亮。从0到黑均为黑色;从白到255均为白色;从黑到白为以灰为中值的伽玛校正。(xb为黑,xg为灰,xw为白)

Actionscript 3.0的实现

实现曲线、伽玛校正等可以使用BitmapData.paletteMap。

三个滑条的值分别为xb、xg、xw (0≤xb≤xg≤xw≤255)。Xmax = xb - xw。当x = xg - xb时,因为想x' / xmax = 0.5,所以

g=( log(Xg-Xb / Xw-Xb) ) / (log(0.5)

var gamma:Number = Math.log((xg - xb) / (xw - xb)) / Math.log(0.5);
var mapR:Array = [], mapG:Array = [], mapB:Array = [];
for(var i:int = 0; i < 256; i++) {
 mapB[i] = i < xb ? 0 : i > xw ? 0xff : 255 * Math.pow((i - xb) / (xw - xb), 1 / gamma);
 mapG[i] = mapB[i] << 8;
 mapR[i] = mapB[i] << 16;
}
image.paletteMap(bmdOrigin, bmd.rect, new Point(), mapR, mapG, mapB);

完整的实现代码:


package bitmap {
 import flash.display.*;
 import flash.filters.*;
 import flash.geom.*;
 import flash.events.Event;

 [SWF(width="256", height="410")]
 public class Histogram3 extends Sprite {
  [Embed(source="023.jpg")]
  private var SampleImage:Class;

  private var dragging:Sprite;
  private var h2pos:Number = 0.5;
  private var h1:Sprite;
  private var h2:Sprite;
  private var h3:Sprite;

  public function Histogram3() {
   stage.scaleMode = "noScale";

   var bmd:BitmapData = Bitmap(addChild(new SampleImage())).bitmapData;

   var s:Sprite = new Sprite();
   addChild(s).y = bmd.height + 10;
   createHistogram(bmd, s);

   s = new Sprite();
   addChild(s).y = bmd.height + 140;
   createHistogram(bmd, s);

   addChild(createSlider()).y = bmd.height + 115;

   var bmdOrigin:BitmapData = bmd.clone();
   addEventListener(
    Event.ENTER_FRAME, 
    function(e:*):void {
     if(dragging) {
      var gamma:Number = Math.log((h2.x - h1.x) / (h3.x - h1.x)) / Math.log(0.5);
      var mapR:Array = [], mapG:Array = [], mapB:Array = [];
      for(var i:int = 0; i < 0x100; i++) {
       mapB[i] = i < h1.x ? 0 : i > h3.x ? 0xff : 255 * Math.pow((i - h1.x) / (h3.x - h1.x), 1 / gamma);
       mapG[i] = mapB[i] << 8;
       mapR[i] = mapB[i] << 16;
      }
      bmd.paletteMap(bmdOrigin, bmd.rect, new Point(), mapR, mapG, mapB);
      
      s.graphics.clear();
      createHistogram(bmd, s);
     }
    }
   );
  }

  // 生成色阶
  private function createHistogram(bmd:BitmapData, s:Sprite):void {
   // 灰度化
   var cmf:ColorMatrixFilter = new ColorMatrixFilter(
    [1 / 3, 1 / 3, 1 / 3, 0, 0, 
     1 / 3, 1 / 3, 1 / 3, 0, 0, 
     1 / 3, 1 / 3, 1 / 3, 0, 0]
   );
   var bmd2:BitmapData = bmd.clone();
   bmd2.applyFilter(bmd2, bmd2.rect, new Point(), cmf);

   // 用threshold来得到颜色分布
   var values:Array = [];
   for(var i:int = 0; i < 0x100; i++) {
    values[i] = bmd2.threshold(bmd2, bmd2.rect, new Point(), "==", 
     i + (i << 8) + (i << 16), 0, 0xffffff, false);
   }
   bmd2.dispose();

   // 画色阶
   var max:int = bmd.width * bmd.height / 50;
   s.graphics.lineStyle(1);
   for(i = 0; i < 0x100; i++) {
    s.graphics.moveTo(i, 100);
    s.graphics.lineTo(i, Math.max(0, 100 - values[i] / max * 100));
   }
  }

  // 生成滑条
  private function createSlider():Sprite {
   // 画滑条可移动的范围
   var slider:Sprite = new Sprite();
   slider.graphics.beginFill(0xffffff);
   slider.graphics.drawRect(0, 0, 256, 10);
   slider.graphics.endFill();
   slider.graphics.lineStyle(1, 0);
   slider.graphics.lineTo(255, 0);
   slider.buttonMode = true;
   slider.useHandCursor = true;

   // 画滑条
   h1 = Sprite(slider.addChild(createButton(0x000000))); h1.x = 0;
   h2 = Sprite(slider.addChild(createButton(0x999999))); h2.x = 128;
   h3 = Sprite(slider.addChild(createButton(0xffffff))); h3.x = 255;

   // mouseDown
   slider.addEventListener("mouseDown", function(e:*):void {
    var localX:Number = slider.globalToLocal(new Point(mouseX, mouseY)).x;

    // 滑动滑条
    var d1:Number = Math.abs(localX - h1.x);
    var d2:Number = Math.abs(localX - h2.x);
    var d3:Number = Math.abs(localX - h3.x);
    var max:Number = Math.min(d1, d2, d3);
    dragging = (max == d1 ? h1 : max == d2 ? h2 : h3);

    // 验证滑条是否超出范围
    var bounds:Rectangle = getDraggableBounds(dragging);
    dragging.x = Math.max(Math.min(localX, bounds.right), bounds.x);
    updateH2(null);

    dragging.startDrag(false, bounds);
   });

   // mouseMove
   stage.addEventListener("mouseMove", updateH2);

   // mouseUp
   stage.addEventListener("mouseUp", function(e:*):void {
    if(dragging) {
     dragging.stopDrag();
     dragging = null;
    }
   });

   return slider;
  }

  // 生成滑条
  private function createButton(color:int):Sprite {
   var s:Sprite = new Sprite();
   s.graphics.lineStyle(1, 0);
   s.graphics.beginFill(color);
   s.graphics.lineTo(5, 8.6);
   s.graphics.lineTo(-5, 8.6);
   s.graphics.endFill();
   return s;
  }

  // 计算滑条的移动范围
  private function getDraggableBounds(s:Sprite):Rectangle {
   if(s == h1) return new Rectangle(0, 0, h3.x - 4, 0);
   if(s == h2) return new Rectangle(h1.x + 2, 0, h3.x - h1.x - 4, 0);
   if(s == h3) return new Rectangle(h1.x + 4, 0, 255 - h1.x - 4, 0);
   return null;
  }

  // 计算灰滑条的移动
  private function updateH2(e:*):void {
   if(dragging && dragging != h2) {
    h2.x = (h3.x - h1.x) * h2pos + h1.x;
    h2.x = Math.max(Math.min(h2.x, h3.x - 2), h1.x + 2);
   }
   else if(dragging == h2){
    h2pos = (h2.x - h1.x) / (h3.x - h1.x);
   }
  }
 }
}

最终效果下载:Histogram2.swf

2007-10-20

[AS3]RegExp的命名捕获组

命名捕获组为正则表现的捕获组的一种。它利用名字来指定捕获组(当然也可以用下标引用)。它经常使用在层次复杂、难以使用下表的表达式里。

定义命名捕获组使用(?P<名字>)的格式。

比如以下将会定义命名为digits的命名捕获组:

var myPattern:RegExp = /(?P<digits>\d+)/g; //命名捕获组
var str:String = "abc123def";
var result:Array = myPattern.exec(str);
trace(result.digits); //123

再如以下将会定义name与dom两个命名捕获组分别匹配用户名与域名:

var emailPattern:RegExp =
/(?P<name>(\w|[_.¥-])+)@(?P<dom>((\w|-)+))+\.\w{2,4}+/;
var address:String = "bob@example.com";
var result:Array = emailPattern.exec(address);
trace(result.name); // bob
trace(result.dom); // example

注意,命名捕获组不是ECMA-Script定义的。为Actionscript自行扩展的。

2007-10-14

[AS3]位图的色阶控制(1) -- 得到色阶

色阶为位图的颜色分布的图。做过图形处理的应该很熟悉。

比如下图:上面为原图,下面为色阶。

Historgram

得到方法:

  1. 灰色化。把各个通道求平均。用ColorMatrixFilter即可。
  2. 用threshold来得到颜色分布。threshold的返回值为匹配的像素的个数。

代码:


package {
    import flash.display.*;
    import flash.filters.ColorMatrixFilter;
    import flash.geom.Point;

    public class TestBitmap extends Sprite {
        [Embed(source="023.jpg")]
        private var SampleImage:Class;

        public function TestBitmap() {
            var bmd:BitmapData = Bitmap(addChild(new SampleImage())).bitmapData;
            var s:Sprite = new Sprite();
            createHistogram(bmd,s);
            addChild(s).y = bmd.height + 10;
        }

        //生成色阶
        private function createHistogram(bmd:BitmapData, s:Sprite):void {
            //灰度画
            var cmf:ColorMatrixFilter = new ColorMatrixFilter(
                [1 / 3, 1 / 3, 1 / 3, 0, 0, 
                 1 / 3, 1 / 3, 1 / 3, 0, 0, 
                 1 / 3, 1 / 3, 1 / 3, 0, 0]
            );
            var bmd2:BitmapData = bmd.clone();
            bmd2.applyFilter(bmd2, bmd2.rect, new Point(), cmf);

            //用threshold来得到颜色分布
            var values:Array = [];
            for(var i:int = 0; i < 0x256; i++) {
                values[i] = bmd2.threshold(bmd2, bmd2.rect, new Point(), "==", i, 0, 0xff, false);
            }
            bmd2.dispose();

            //画色阶
            var max:int = bmd.width * bmd.height / 50;
            s.graphics.lineStyle(1);
            for(i = 0; i < 256; i++) {
                s.graphics.moveTo(i, 100);
                s.graphics.lineTo(i, Math.max(0, 100 - values[i] / max * 100));
            }
        }
    }
}

[AS3]位图过度之模板过度

先来看效果:

它使用的模板为:

运行原理很简单,主要是运用阈值,再设置原图的Alpha通道。详细看源码

源码下载BitmapPatternTween.fla