0

All the tutorials for using beginBitmapFill seem to center around dynamically drawing an object. Is it possible to simply apply a bitmap fill to an existing shape that's inside a movieclip? I know I can do this manually by selecting the shape and selecting a bitmap in the Color panel.. but is it possible to do it with code? This is what I'm trying and it's not giving errors, but it's also not working:

grl.tops.shapes.graphics.beginBitmapFill(new cubepattern(), null, true, false);
ola.rogula
  • 307
  • 1
  • 9
  • Probably, possible, but it is not a simple way. You can read and (probably) figure the shape of strokes already present on **Graphics** object, then replicate it with transparent lines applying the fill. Quite an endeavor of a task. https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/Graphics.html#readGraphicsData() – Organis Feb 08 '19 at 08:00
  • Better use a mask to draw that fill over an existing shape. Otherwise yes, fills are represented in a list returned by `readGraphicsData()` that you might decide to compile from two sources, one the shape you want to get filled, the other a shape that you fill with required bitmap, you get path elements from first source and fill elements from second source, then construct a new shape and feed its `graphics.drawGraphicsData` with the constructed list. – Vesper Feb 08 '19 at 09:06
  • 1
    What you have tried just adds the bitmap fill object to the *end* of that shape's graphics data, so should you decide to draw something on that graphics, then call `endFill()` it would get filled with your bitmap. Otherwise yes, it's just unused. – Vesper Feb 08 '19 at 09:07

1 Answers1

3

As others have commented, you can use readGraphicsData and drawGraphicsData respectively. It's even not over-complicated. Simply loop through the IGraphicsData of your desired shape, stop as soon as you've found an instance of the GraphicsBitmapFill class, create a new instance with another BitmapFill and finally apply the changes to the original shape.

Well, a picture is worth a thousand words. Here's an example. This might seem a bit long but there's just a whole lot of code to prepare a shape and load images to be used as bitmap fills.

Set your eyes on the process() function.

package
{
    import flash.display.Sprite;
    import flash.display.Shape;
    import flash.display.Graphics;
    import flash.display.Loader
    import flash.display.LoaderInfo;
    import flash.display.IGraphicsData;
    import flash.display.GraphicsBitmapFill;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.events.Event;
    import flash.net.URLRequest;
    import flash.utils.getQualifiedClassName;

    public class Main extends Sprite
    {
        private var bitmapData1:BitmapData;
        private var bitmapData2:BitmapData;
        private var masterSprite:Sprite = new Sprite();
        private var texturesLoaded:int = 0;
        private var loader:Loader = new Loader();

        public function Main():void
        {
            if (stage)
                init();
            else
                addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
            loader.load(new URLRequest("textureA.jpg"));
            addChild(masterSprite);
            masterSprite.x = masterSprite.y = 200;
        }

        private function onComplete(event:Event):void
        {
            switch (texturesLoaded)
            {
                case 0: 
                    bitmapData1 = Bitmap(LoaderInfo(event.target).content).bitmapData;
                    loader.load(new URLRequest("textureB.jpg"));
                    break;
                case 1: 
                    bitmapData2 = Bitmap(LoaderInfo(event.target).content).bitmapData;
                    loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onComplete);
                    drawStar();
                    process();
            }
            texturesLoaded++;
        }

        private function process():void
        {
            var tempShape:Shape = Shape(masterSprite.getChildAt(0));

            var graphicsData:Vector.<IGraphicsData> = tempShape.graphics.readGraphicsData();
            for (var a:int = 0; a < graphicsData.length; a++)
            {
                if (getQualifiedClassName(graphicsData[a]) == "flash.display::GraphicsBitmapFill")
                {
                    var bitmapFill:GraphicsBitmapFill = new GraphicsBitmapFill(bitmapData2);
                    graphicsData[a] = bitmapFill; break;
                }
            }
            tempShape.graphics.drawGraphicsData(graphicsData);
        }

        private function drawStar():void
        {
            var angles:Array = new Array(0, 36, 72, 108, 144, 180, 216, 252, 288, 324, 360);
            var innerRadius:int = 40;
            var outerRadius:int = 80;
            var shape:Shape = new Shape();
            shape.graphics.beginBitmapFill(bitmapData1);
            shape.graphics.moveTo(0 + Math.cos(angles[a] * (Math.PI / 180)) * outerRadius, 0 + Math.sin(angles[a] * (Math.PI / 180)) * outerRadius);
            for (var a:int = 0; a < angles.length; a++)
            {
                angles[a] -= 90;
                if (a % 2 == 0)
                {
                    shape.graphics.lineTo(0 + Math.cos(angles[a] * (Math.PI / 180)) * outerRadius, 0 + Math.sin(angles[a] * (Math.PI / 180)) * outerRadius);
                }
                else
                {
                    shape.graphics.lineTo(0 + Math.cos(angles[a] * (Math.PI / 180)) * innerRadius, 0 + Math.sin(angles[a] * (Math.PI / 180)) * innerRadius);
                }
            }
            shape.graphics.endFill();
            masterSprite.addChild(shape);
        }
    }
}
obscure
  • 11,916
  • 2
  • 17
  • 36
  • Wow, thanks! I'm trying to test this out but I don't know much about external as code. I've saved this into a .as file, then created an .fla in the same folder, and added the name of the as in the "Class" field and called Main(); Is that right? Now I'm getting an error that the location of the file isn't what it says in "Main" I dunno.. – ola.rogula Feb 08 '19 at 21:44
  • What if I don't need to do so much by code, just want to copy shape of movieclip "tops" and fill it with bitmap "cubepattern"? I mostly use the stuff in the process() function, right? – ola.rogula Feb 08 '19 at 21:46
  • Yes, exactly! Though you may need to import the required classes as above and reference **grl.tops.shapes** as the tempShape in the process() function. – obscure Feb 08 '19 at 22:26
  • I'm having trouble doing that. Most variations I try yield a "type coercion failed" error. I imagine the whole crux of the thing depends on making this line work. var tempShape:Shape = Shape(grl.tops.shapes); var tempShape:Shape = Shape(grl.tops.shapes.getChildAt(0)); – ola.rogula Feb 09 '19 at 00:39
  • 1
    Looks like your DisplayObject ain't a Shape. Unfortunately I can just guess what it really is. You could try **var tempShape:Sprite = Sprite(grl.tops.shapes);** or do a trace(grl.tops.shapes); and look at the debug panel what type it outputs. – obscure Feb 09 '19 at 11:59
  • Oh! You're right; it was further nested. Found the shape now, thank you. – ola.rogula Feb 09 '19 at 19:08
  • Last step, I'm just trying to sub in for my bitmap in the library. `var bitmapData2:BitmapData = new cubepattern();` cubepattern being the linkageId of my bitmap in the library. I'm trying variations of this and most don't give errors but I don't see anything happen. This code replaces the original shape or draws one above it? Do I need to further specify where to draw the new shape? – ola.rogula Feb 09 '19 at 19:12
  • 1
    Actually the code is looping through the IGraphicsData of your shape, stops if it finds a BitmapFill and replaces it. Maybe it still doesn't loop through the proper shape? Put a trace statement in the if() condition to see if it finds a BitmapFill at all or even a trace(graphicsData[a]) in the for loop. – obscure Feb 09 '19 at 19:53
  • Wow, thanks so much! Great explanation. It wasn't finding a BitmapFill because it traces back GraphicsSolidFill. Okay, I switched it to find a `"flash.display::GraphicsSolidFill"` and that worked, and it filled it with the bitmap! With minor tweaking I see that this will absolutely do what I need. Thank you!!! – ola.rogula Feb 10 '19 at 21:03
  • if I can bug you about one last thing, I amended the if statement to search for a Solid or Bitmap fill. In some cases of calling the process() function, it doesn't just override the current fill but seems to draw on top of it.. eg, the transparent bitmap seems to be overdrawn each time, making the lines thicker etc. Is it adding multiple fills and how can I prevent this? – ola.rogula Feb 11 '19 at 07:02
  • 1
    Yep, this needs to strip existing graphics data off `tempShape`, as `drawGraphicsData()` does not replace the entire set of graphics data existing on the shape, but rather adds to its tail, effectively overlapping whatever was there before. So, just call `graphics.clear()` before drawing new set of graphics data, should do. – Vesper Feb 11 '19 at 14:30
  • Can I do anything to manipulate the bitmap? It looks quite pixellated. I checked off Allow Smoothing in the bitmap's properties (does this utilize those settings?). I tried doubling the size of the shape then halving the size of the parent, to double the resolution, which worked, but it still pixellates badly at some zooms. – ola.rogula Feb 11 '19 at 18:37
  • I think I figured it out! Had to add `bitmapFill.smooth = true;` – ola.rogula Feb 12 '19 at 04:54
  • In case it helps anyone later, I was also able to increase the resolution of the bitmap by creating a matrix scaled to half, then applying it to the GraphicsBitmapFill – ola.rogula Feb 13 '19 at 17:25
  • Hopefully the last issue.. I'm trying to re-run the function whenever the movieclip goes to a new frame (and a new shape is presented) and the behaviour is curious.. It works when moving backwards through frames. But when I move forwards through frames (eg. from frame 3 to 7), it doesn't draw the shape again. It keeps the old shape. Why is this and how can I get around this? I'm so close to fully harnessing the power of this!!! – ola.rogula Feb 14 '19 at 06:24
  • I couldn't find a workaround for this so I had to switch to an everything-existing-at-once and toggling visibility hack (instead of switching frames) – ola.rogula Feb 16 '19 at 06:32