绘制有机形状周围的轮廓

时间:2010-03-21 07:07:16

标签: flash actionscript-3 drawing

在Flash IDE中似乎特别容易做的一件事但是很难用代码来概括有机形状。在IDE中,您可以使用inkbucket工具在某些东西周围画一个笔划。除了代码之外什么也没用,似乎更棘手。我见过的一种方法是在所讨论的形状上添加一个发光滤光片,然后将其强度弄得一团糟。但是,如果我只想显示大纲呢?

我想要做的是收集构成边缘的所有点,然后连接点。实际上,我已经用我写的快速而脏的边缘检测脚本来收集所有的点。所以现在我有一个构成我的形状的所有点的向量。如何以正确的顺序连接它们,使它看起来像原始对象?

对于任何感兴趣的人都是我的边缘检测脚本:

         // Create a new sprite which we'll use for our outline
        var sp:Sprite = new Sprite();
        var radius:int = 50;
        sp.graphics.beginFill(0x00FF00, 1);
        sp.graphics.drawCircle(0, 0, radius);
        sp.graphics.endFill();
        sp.x = stage.stageWidth / 2;
        sp.y = stage.stageHeight / 2;

        // Create a bitmap data object to draw our vector data
        var bmd:BitmapData = new BitmapData(sp.width, sp.height, true, 0);

        // Use a transform matrix to translate the drawn clip so that none of its
        // pixels reside in negative space. The draw method will only draw starting
        // at 0,0
        var mat:Matrix = new Matrix(1, 0, 0, 1, radius, radius);
        bmd.draw(sp, mat);

        // Pass the bitmap data to an actual bitmap
        var bmp:Bitmap = new Bitmap(bmd);

        // Add the bitmap to the stage
        addChild(bmp);

        // Grab all of the pixel data from the bitmap data object
        var pixels:Vector.<uint> = bmd.getVector(bmd.rect);

        // Setup a vector to hold our stroke points
        var points:Vector.<Point> = new Vector.<Point>;

        // Loop through all of the pixels of the bitmap data object and
        // create a point instance for each pixel location that isn't
        // transparent.
        var l:int = pixels.length;
        for(var i:int = 0; i < l; ++i)
        {
            // Check to see if the pixel is transparent
            if(pixels[i] != 0)
            {
                var pt:Point;

                // Check to see if the pixel is on the first or last
                // row. We'll grab everything from these rows to close the outline
                if(i <= bmp.width || i >= (bmp.width * bmp.height) - bmp.width)
                {
                    pt = new Point();
                    pt.x = int(i % bmp.width);
                    pt.y = int(i / bmp.width);
                    points.push(pt);
                    continue;
                }

                // Check to see if the current pixel is on either extreme edge
                if(int(i % bmp.width) == 0 || int(i % bmp.width) == bmp.width - 1)
                {
                    pt = new Point();
                    pt.x = int(i % bmp.width);
                    pt.y = int(i / bmp.width);
                    points.push(pt);
                    continue;
                }

                // Check to see if the previous or next pixel are transparent,
                // if so save the current one.
                if(i > 0 && i < bmp.width * bmp.height)
                {
                    if(pixels[i - 1] == 0 || pixels[i + 1] == 0)
                    {
                        pt = new Point();
                        pt.x = int(i % bmp.width);
                        pt.y = int(i / bmp.width);
                        points.push(pt);
                    }
                }
            }
        }

// Create a new sprite which we'll use for our outline var sp:Sprite = new Sprite(); var radius:int = 50; sp.graphics.beginFill(0x00FF00, 1); sp.graphics.drawCircle(0, 0, radius); sp.graphics.endFill(); sp.x = stage.stageWidth / 2; sp.y = stage.stageHeight / 2; // Create a bitmap data object to draw our vector data var bmd:BitmapData = new BitmapData(sp.width, sp.height, true, 0); // Use a transform matrix to translate the drawn clip so that none of its // pixels reside in negative space. The draw method will only draw starting // at 0,0 var mat:Matrix = new Matrix(1, 0, 0, 1, radius, radius); bmd.draw(sp, mat); // Pass the bitmap data to an actual bitmap var bmp:Bitmap = new Bitmap(bmd); // Add the bitmap to the stage addChild(bmp); // Grab all of the pixel data from the bitmap data object var pixels:Vector.<uint> = bmd.getVector(bmd.rect); // Setup a vector to hold our stroke points var points:Vector.<Point> = new Vector.<Point>; // Loop through all of the pixels of the bitmap data object and // create a point instance for each pixel location that isn't // transparent. var l:int = pixels.length; for(var i:int = 0; i < l; ++i) { // Check to see if the pixel is transparent if(pixels[i] != 0) { var pt:Point; // Check to see if the pixel is on the first or last // row. We'll grab everything from these rows to close the outline if(i <= bmp.width || i >= (bmp.width * bmp.height) - bmp.width) { pt = new Point(); pt.x = int(i % bmp.width); pt.y = int(i / bmp.width); points.push(pt); continue; } // Check to see if the current pixel is on either extreme edge if(int(i % bmp.width) == 0 || int(i % bmp.width) == bmp.width - 1) { pt = new Point(); pt.x = int(i % bmp.width); pt.y = int(i / bmp.width); points.push(pt); continue; } // Check to see if the previous or next pixel are transparent, // if so save the current one. if(i > 0 && i < bmp.width * bmp.height) { if(pixels[i - 1] == 0 || pixels[i + 1] == 0) { pt = new Point(); pt.x = int(i % bmp.width); pt.y = int(i / bmp.width); points.push(pt); } } } }

1 个答案:

答案 0 :(得分:2)

目前无法反省您在设计时绘制的东西的形状(即找出所有线条和填充角落的位置),因此围绕任意形状绘制轮廓的唯一好方法是位图效果。但是,你的方式可能不会有成效。 Flash为您提供了几种处理位图的方法,但任何涉及在每个像素上循环的内容通常都不会足够快以便在运行时使用。

相反,我采取两种方法之一:最好和最简单的方法是使用内置过滤器。正如你所说,通常当人们想要绘制轮廓时,他们会使用强度大且半径短的辉光滤镜。如果您只想显示轮廓,请检查knockout属性。 (您可以在脚本或IDE中执行此操作。)如果您不喜欢使用此功能,可以尝试混合滤镜 - 例如,在发光之前或之后添加模糊。或者您可以使用代码将发光效果生成为空白位图,然后您可以通过各种方式处理结果 - 例如,使用BitmapData.threshold,我发现它非常强大。无论如何,我会玩这一段时间,并确保在你尝试别的东西之前它真的不会解决你的问题,因为它是迄今为止最简单的解决方案。

另一个选项是PixelBender。这个功能允许您使用类似C语言定义自定义过滤器,您可以预编译然后将其加载到Flash影片中,并应用于相关位图。该过程与编写photoshop过滤器非常相似。这使您可以完全控制要对位图执行何种处理,但它需要您在Flash外部以及外部动作脚本之外工作。

走你当前的路线并手动处理像素当然是第三种选择,但问题是你是否真的做了一些你在普通或自定义过滤器中做不到的事情。如果你真的想要完成你的工作,你可以尝试制作一个新的透明位图,对于你在上面的代码中捕获的每个点,在那一点绘制一个白点(带有setPixel32)。这会给你一个像素化的轮廓,你可能会想要模糊,或以其他方式处理。但实际上,你最终会得到与普通发光滤光片相似的东西,而且需要更长的时间。