今天有位朋友给我发了邮件,说他按照《Flex2:使用BitmapData抓图》的方法,写了简单的Bitmap抓图程序,功能很简单,用Image载入图片。然后在Image上覆盖一个Canvas用户在载入的图片上作图,代码很简单,可是就是运行不正常,代码如下:

[xml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" >

<mx:Script>
    <![CDATA[

        import flash.display.Bitmap;
        import flash.display.BitmapData;
        import flash.display.Graphics;
        import flash.events.MouseEvent ;
        import flash.geom.Matrix;
        import flash.geom.Rectangle;
        import mx.controls.Alert;
        import mx.rpc.http.mxml.HTTPService;

        private var g:Graphics;
        private var backGroundImageData : BitmapData;
        private var mouseDown : Boolean = false;
        private var lineThickness : Number = 2;
        private var bitMap: Bitmap;   

        private function setUp():void
        {
            drawscreen.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownListener);
            drawscreen.addEventListener(MouseEvent.MOUSE_UP , mouseUpListener);
            drawscreen.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);
            g = drawscreen.graphics;
        }

        private function mouseDownListener(event:MouseEvent):void
        {
            mouseDown = true;
            g.moveTo(event.localX, event.localY);
        }

        private function mouseUpListener(event:MouseEvent):void
        {
            mouseDown = false;
        }

        private function mouseMoveListener(event:MouseEvent):void
        {
            if( mouseDown)
            {
                g.lineStyle(lineThickness, colorPicker.selectedColor, 1 );
                g.lineTo( event.localX, event.localY );
            }
        }           

        public function clearScribble():void
        {
            g.clear();
        }

        private function captureBitmap():void
        {
            backGroundImageData = new BitmapData( scribbleBoard.width, scribbleBoard.height);
            backGroundImageData.draw ( scribbleBoard, scribbleBoard.transform.matrix, scribbleBoard.transform.colorTransform );           

            var newBitmapData:BitmapData=new BitmapData( scribbleBoard.width, scribbleBoard.height);
            var bitmap:Bitmap=new Bitmap(newBitmapData);
            addChild(bitmap);

        }

    ]]>
</mx:Script>
    <mx:Canvas creationComplete="setUp()" >
        <mx:Panel height="100%" width="100%">

            <mx:VBox horizontalAlign="center" verticalAlign="middle" height="100%" width="100%">

             <mx:Canvas id="scribbleBoard" >
                 <mx:Image id="image" source="yourImage.jpg"/>
                 <mx:Canvas id="drawscreen"
                        height="{image.height}"
                        width="{image.width}"
                        backgroundAlpha="0" />
             </mx:Canvas>
             </mx:VBox>
             <mx:HBox  >
                 <mx:Button label="Clear" click="clearScribble()" />
                 <mx:Button label="Save" click="captureBitmap()" />
                 <mx:Label text="Line Thickness"/>
                 <mx:HSlider id="lineWidth"  minimum="1" maximum="10" mouseUp="lineThickness = lineWidth.value"/>
                 <mx:Label id="la" text="Change color:"/>
                 <mx:ColorPicker id="colorPicker"/>
             </mx:HBox>
         <mx:ControlBar/>
        </mx:Panel>
    </mx:Canvas>

</mx:Application>

以上程序和Bitmap有关的部分如下:

[java]
private function captureBitmap():void
{
  backGroundImageData = new BitmapData( scribbleBoard.width, scribbleBoard.height);
  backGroundImageData.draw ( scribbleBoard, scribbleBoard.transform.matrix, scribbleBoard.transform.colorTransform );           

  var newBitmapData:BitmapData=new BitmapData( scribbleBoard.width, scribbleBoard.height);
  var bitmap:Bitmap=new Bitmap(newBitmapData);
  addChild(bitmap);
}

这段代码中,有个错误:

[java]
backGroundImageData.draw ( scribbleBoard, scribbleBoard.transform.matrix, scribbleBoard.transform.colorTransform );

draw()方法不能直接使用scribbleBoard.transform.matrix和scribbleBoard.transform.colorTransform 。正确的用法是,如果你不想对BitmapData做任何处理的话,直接用null就可以了。所有正确的写法如下:

[java]
backGroundImageData.draw ( scribbleBoard);

为了演示方便我将上面的代码更改为如下:

[xml]
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" >
<mx:Script>
    <![CDATA[

        import flash.display.Bitmap;
        import flash.display.BitmapData;
        import flash.display.Graphics;
        import flash.events.MouseEvent ;
        import flash.geom.Matrix;
        import flash.geom.Rectangle;
        import mx.controls.Alert;
        import mx.core.UIComponent;

        private var g:Graphics;
        private var mouseDown : Boolean = false;
        private var lineThickness : Number = 2;   

        private function setUp():void
        {
            drawscreen.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownListener);
            drawscreen.addEventListener(MouseEvent.MOUSE_UP , mouseUpListener);
            drawscreen.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);
            g = drawscreen.graphics;
        }

        private function mouseDownListener(event:MouseEvent):void
        {
            mouseDown = true;
            g.moveTo(event.localX, event.localY);
        }

        private function mouseUpListener(event:MouseEvent):void
        {
            mouseDown = false;
        }

        private function mouseMoveListener(event:MouseEvent):void
        {
            if( mouseDown)
            {
                g.lineStyle(lineThickness, colorPicker.selectedColor, 1 );
                g.lineTo( event.localX, event.localY );
            }
        }           

        public function clearScribble():void
        {
            g.clear();
        }

        private function captureBitmap() : void
        {
           var bd : BitmapData = getBitmapData( UIComponent( scribbleBoard ) );
           //modify by feiy,20070523
           //the old code:
           //addChild(new Bitmap(bd));
           //the new code:
     	   var uiComp:UIComponent = new UIComponent();
           uiComp.addChild(new Bitmap(bd));
           this.addChild(uiComp);
        }
        private function getBitmapData( target : UIComponent ) : BitmapData
        {
          try{
           var bd : BitmapData = new BitmapData( target.width, target.height );
           bd.draw(target);
           return bd;
          }catch(exp:Error){
            Alert.show(exp.getStackTrace()+exp.message+exp.name+exp.toString()+exp.errorID);
          }
          return null;
        }

    ]]>
</mx:Script>
        <mx:Panel height="300" width="600">

            <mx:VBox horizontalAlign="center" verticalAlign="middle" height="100%" width="100%">

             <mx:Canvas id="scribbleBoard" creationComplete="setUp()">
                 <mx:Image id="image" source="init.jpg"/>
                 <mx:Canvas id="drawscreen"
                        height="{image.height}"
                        width="{image.width}"
                        backgroundAlpha="0" />
             </mx:Canvas>
             </mx:VBox>
             <mx:HBox  >
                 <mx:Button label="Clear" click="clearScribble()" />
                 <mx:Button label="Save" click="captureBitmap()" />
                 <mx:Label text="Line Thickness"/>
                 <mx:HSlider id="lineWidth"  minimum="1" maximum="10" mouseUp="lineThickness = lineWidth.value"/>
                 <mx:Label id="la" text="Change color:"/>
                 <mx:ColorPicker id="colorPicker"/>
             </mx:HBox>
         <mx:ControlBar/>
        </mx:Panel>
</mx:Application>

立刻运行示例

因为组件中有Image,有一点需要注意,Flex默认的编译设置中use-network=true,而对于Image组件来说,use-network为true就意味着如果是从本地载入图片,我们是不运行访问该Image的Bitmap数据的,所有如果本地调试以上代码会报安全错误的,如果要本地调试需要将use-network设置为false编译。对应的如果要在线调试又得将use-network设置为true,关于Image的的这个限制大家可以查看Flex官方文档,里面说的很详细。

另外,说句题外话,我们在Flex的开发过程中,最好使用带Debug功能的FlashPlayer调试程序,要不在碰到不解的问题时多使用try/catch,这样的好习惯能解决您的很多问题,比如上面说到的Image Bitmap数据的访问限制问题!