views:

233

answers:

3

Suppose I've converted a vector image file (.AI/.SVG) to .SWF for ease of importing dynamically as the source of an Image in Flex.

To count colors now, I have to create a "new BitMap", then ".draw()" and iterate over all pixels and use ".getPixel()" to retrieve the color.

Unfortunately, because of anti-aliasing, this seems to return several more colors than what are actually used to draw the image (say, a black logo).

Is there a better alternative to doing this in Flex?

A: 

Try using ConvolutionFilter to sharpen the original vector image. You may need to adjust the filter matrix to get the right values to sharpen out the anti-aliasing. Then perform the BitmapData.draw() method on the image to step through the pixels and the pixel data. Sharpening the original vector will allow very small colored areas to be detected; if you sharpen the Bitmap object after draw(), those small area colors may be rendered as a slightly different color than the original.

wmid
+1  A: 

I've got a solution, but it might not be the best one. Basically I'm just looping through the swf's bytes using as3swf and looking for shape definitions then getting to the fills. From there I push the colors(solid fills and colors found in gradients) into an array. Since there might be duplicates, I remove them at the end.

Here's the code:

import com.codeazur.as3swf.*;
import com.codeazur.as3swf.tags.*;

var colors:Array = [];
var colorsNum:int = 0;
var request:URLRequest = new URLRequest("plank.swf");
var loader:URLLoader = new URLLoader();
loader.dataFormat = URLLoaderDataFormat.BINARY;
loader.addEventListener(Event.COMPLETE, completeHandler);
loader.load(request);

function completeHandler(e:Event):void {
    var swf:SWF = new SWF(URLLoader(e.target).data as ByteArray);
    var tagsNum:int = swf.tags.length;
    for(var i:int = 0 ; i < tagsNum ; i++){
        if(swf.tags[i].name.substr(0,11) == 'DefineShape'){
            var fillStylesNum:int = TagDefineShape4(swf.tags[i]).shapes.fillStyles.length;
            if(fillStylesNum > 0){
                for(var j:int = 0 ; j < fillStylesNum ; j++){
                    if(TagDefineShape4(swf.tags[i]).shapes.fillStyles[j].gradient){
                        var recordsNum:int = TagDefineShape4(swf.tags[i]).shapes.fillStyles[j].gradient.records.length;
                        for(var k:int = 0 ; k < recordsNum; k++){}
                            colors.push(TagDefineShape4(swf.tags[i]).shapes.fillStyles[j].gradient.records[k].color)
                    }else{
                        colors.push(TagDefineShape4(swf.tags[i]).shapes.fillStyles[j].rgb);
                    }
                }
            }
        }
    }
    colors.sort(Array.NUMERIC);
    removeDuplicates(colors);
    trace('numColors: ' + colors.length + '\ncolors: ' + colors);
}
function removeDuplicates(array:Array):Array{
    var length:int = array.length;
    var i:int, j:int;
    var newLength:int = 1;

    for(i=1; i< length; i++){

       for(j=0; j< newLength ; j++)
       {

          if(array[i] == array[j])
          break;
       }
        if (j==newLength )
          array[newLength++] = array[i];
    }
    array.length = newLength;
    return array;
}

HTH

George Profenza
Surprisingly enough, this was actually much faster than processing each pixel in the BitMapData array. Thank you for the suggestion!
Eric
+1  A: 

To get rid of the vector anti-aliasing, you can set the stage.quality property to StageQuality.LOW before drawing it in a BitmapData, it might do the trick.

It'll surely be more complex, but to speed up your algorithm, you probably could get PixelBender or Alchemy to do the job.

Zed-K
Marked this as an answer as well, as it does work. Attempting to alleviate the issue where the user sees the quality change while the colors are counted, then reversed back.Thank you!
Eric