AS3 - 位图数据边缘透明度问题

3
我正在使用AS3构建一个绘图应用程序,但是在画笔的边缘上出现了羽化或模糊的问题。我使用bitmapData.draw()来绘制画笔,但是当慢慢绘制时,我看到边缘周围有较暗的颜色区域。
我尝试了许多选项,包括将所有涉及的显示对象设置为cacheAsBitmap=true,使用copyPixels代替draw,模糊滤镜与渐变填充等等...但都没有成功解决问题。
下面的类说明了我的问题。我已经包括了一个solid()方法,它可以正确地工作,但没有羽化的边缘,还有一个gradient()方法和一个filter()方法,它们都显示了相同的问题,而onMove2使用copyPixels()也有同样的问题。
有什么办法可以解决这个问题吗?我真的不想为这么简单的东西使用Pixelbender着色器...
package test {
    import flash.display.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.geom.*;

    public class Sucks extends Sprite {

        private var brush:Sprite;
        private var brushMap:BitmapData;
        private var bmd:BitmapData;
        private var radius:Number = 50;
        private var color:uint = 0x990000;


        public function Sucks() {
            brush = new Sprite();
            brushMap = new BitmapData(radius*2, radius*2, true, 0x00ffffff);
            bmd = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0x0);
            var bmp:Bitmap = new Bitmap(bmd, PixelSnapping.ALWAYS, true);
            addChild(bmp);

            //solid();         
            gradient();
            //filter();
            brushMap.draw(brush, new Matrix(1, 0, 0, 1, radius, radius));

            stage.addEventListener(Event.ENTER_FRAME, onMove);
        }

        private function solid():void {
            brush.graphics.beginFill(color, 1);
            brush.graphics.drawCircle(0, 0, radius);
            brush.graphics.endFill();
        }

        private function gradient():void {
            var m:Matrix = new Matrix();
            m.createGradientBox(radius*2, radius*2, 0, -radius, -radius);
            brush.graphics.beginGradientFill(GradientType.RADIAL, [color, color], [1, 0], [0, 250], m);
            brush.graphics.drawCircle(0, 0, radius);
            brush.graphics.endFill();
        }

        private function filter():void {
            solid();
            brush.filters = [new BlurFilter(8, 8, 3)];
        }

        private function onMove(e:Event):void {
            var mp:Matrix = new Matrix();
            mp.tx = mouseX;
            mp.ty = mouseY;
            bmd.draw(brush, mp);
            //bmd.applyFilter(bmd, new Rectangle(0, 0, stage.stageWidth, stage.stageHeight), new Point(), new BlurFilter(2, 2, 3));
        }

        private function onMove2(e:Event):void {
            bmd.copyPixels(brushMap, new Rectangle(0, 0, radius*2, radius*2), new Point(mouseX-radius, mouseY-radius), null, null, true);
        }

    }

}
6个回答

2
你的问题是由于Flash使用了预乘alpha引起的。通常这不是一个大问题,但如果你使用模糊的软羽化刷子时有非常低的alpha值的像素值,那么这将变得非常明显。
预乘会导致像素信息丢失 - 当你有低alpha值时,颜色值实际上会被舍入,当你在应用程序中缓慢绘制并且相同的像素重复绘制时,区域将变得越来越暗。
关于预乘问题的更多详细信息,请查看我的旧文章:http://www.quasimondo.com/archives/000665.php
除非你单独处理alpha通道和rgb通道,否则你无法解决这个问题。但是,这样做也意味着你必须自己处理所有合成,这不是一个简单的任务,可能会使你的应用程序变慢。

1
我很惊讶BlurFilter没有起作用。我过去使用这个的时候还挺成功的。也许值得尝试一下Flex Filter Explorer看看是否需要调整设置。
另外一个你可以尝试的是绘制几个同心圆,半透明并且半径逐渐减小,以模拟模糊边缘的效果。

这是一个好主意,但也有一些不规则形状的画笔,很难创造同心圆。似乎任何模糊的形状在绘制到位图时都会受到这些小的伪像的影响。 - James H

0

我不知道它是否与此有关,但您正在设置透明度,但是您的BitmapData并不透明。

brushMap = new BitmapData(radius*2, radius*2, true, 0x00ffffff);

brushMap = new BitmapData(radius*2, radius*2, true, 0);

另外,尝试将PixelSnapping.ALWAYS的值更改为PixelSnapping.NEVER,但我猜你已经尝试过了。


brushMap仅在onMove2的copyPixels()调用中使用。由于它是ARGB,因此0x00ffffff和0x0基本相同。无论如何,这对问题没有影响。对于像素捕捉,我也没有运气,我尝试了所有设置...真希望我能找到一个简单的解决方案。 - James H

0
如果刷子颜色是单色的,你也可以尝试使用光晕滤镜,在精确的设置下它也可以被实现。

0

我认为这更多是一个enterframe的问题,而不是你的逻辑问题。我注意到,如果你将enter_frame处理程序更改为mouse_move处理程序,它会更好地工作。使用enter_frame的问题在于,你一遍又一遍地绘制同一个项目--自然会有自己的伪影--因此,在绘制约600次后,它将开始显示这些伪影。我认为最好使用mouse_move。

如果你必须使用enter_frame,我建议同时监听enter_frame和mouse_move,并显著降低enter_frames绘制的不透明度。

这对我很有效:

stage.addEventListener(MouseEvent.MOUSE_MOVE, onMove);

监听鼠标移动存在相同的问题,只是程度较轻。在我的实际代码中,我会在上一个鼠标点和当前点之间进行插值,以便正确地分隔时间间隔。但无论间隔多大,这些伪像总是存在的,只是程度较轻。 - James H
我看到你的问题了——那真糟糕。看起来 Flash 真的让你很困扰——模糊滤镜好像有问题/或者是预乘 alpha?试着把模糊质量改成 1——这样似乎会减少很多伪影……但我认为,最终你还是得自己创建一个着色器…… - ansiart

0

将画笔的cacheAsBitmap属性更改为true。

brush.cacheAsBitmap = true;

精灵将作为位图而不是矢量图保留在内存中,因此绘制时它将更精确地映射像素。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接