Author Topic: Flixel + BitmapFilter  (Read 3447 times)

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Flixel + BitmapFilter
« on: Tue, May 18, 2010 »
One thing I kinda liked about a previous engine I used was the ability to apply arbitrary filters to sprites. For example, I could blur it, meddle with its colors, add a glow, drop shadow, stroke and what have you.

Flash has this same kind of functionality via BitmapFilters, so I thought it would be cool to integrate this into FlxSprite.

http://www.swfcabin.com/open/1274239500
(Instructions displayed in dialog in the demo)

It supports both single filters as well as arbitrarily nested arrays of filters, so you can combine things like blur and glow at the same time (press '3'). Not all filters are as easy as instantiating them - the HSB filters and the color swapping ones required some digging around with the Color Matrix stuff.

Is this something you guys would be interested in seeing more of?
« Last Edit: Tue, May 18, 2010 by Jon »

Dro

  • Member
  • **
  • Posts: 64
  • Karma: +0/-0
    • View Profile
Re: Flixel + BitmapFilter
« Reply #1 on: Tue, May 18, 2010 »
of course.
nice stuff  ;)

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Re: Flixel + BitmapFilter
« Reply #2 on: Tue, May 18, 2010 »
The gist of it is that I've added three functions and one new field to FlxSprite.

As a user, you use setFilter() and removeFilter(). You pass either a BitmapFilter or an Array of BitmapFilters to setFilter.

Then, you add applyFilter(filter) to the bottom of calcFrame(), so that it applies the filters after generating the bitmap for the sprite. There's one little bug right now where filters that expand the size of the input bitmap will get that extra data truncated off, but besides that, it works great so far.

Code: [Select]
               protected var filter:*;

public function setFilter(filter:*):void
{
if(filter != null && this.filter == filter)
{
return;
}

this.filter = filter;

calcFrame();
}

public function removeFilter(filter:*):void
{
if(this.filter == null || filter == null)
{
return;
}

if(this.filter is Array)
{
//Replace with your own Array.remove function.
ArrayUtil.removeValueFromArray(this.filter as Array, filter);
calcFrame();
}

else if(this.filter != null && this.filter == filter)
{
this.filter = null;
calcFrame();
}
}

protected function applyFilter(filter:*):void
{
if(filter != null)
{
_flashRect.x = _flashRect.y = 0;
_flashRect.width = _framePixels.width;
_flashRect.height = _framePixels.height;

_flashPointZero.x = 0;
_flashPointZero.y = 0;

if(filter is Array)
{
for each(var o:Object in filter)
{
applyFilter(o);
}
}

else if(filter is BitmapFilter)
{
_framePixels.applyFilter(_framePixels, _flashRect, _flashPointZero, filter as BitmapFilter);
}
}
}

Besides that, the rest is actually figuring out how to use the filter classes. ColorMatrixFilter is a little tricky, so I've provided my default implementations for some of the effects I was able to pull off.

Code: [Select]
               public static function createGrayscaleFilter():ColorMatrixFilter
{
var matrix:Array = new Array();

matrix = matrix.concat([0.5,0.5,0.5,0,0]);
matrix = matrix.concat([0.5,0.5,0.5,0,0]);
matrix = matrix.concat([0.5,0.5,0.5,0,0]);
matrix = matrix.concat([0,0,0,1,0]);

return new ColorMatrixFilter(matrix);
}

public static function createSepiaFilter():ColorMatrixFilter
{
var matrix:Array = new Array();

matrix = matrix.concat([0.34, 0.33, 0.33, 0.00, 30.00]);
matrix = matrix.concat([0.33, 0.34, 0.33, 0.00, 20.00]);
matrix = matrix.concat([0.33, 0.33, 0.34, 0.00, 0.00]);
matrix = matrix.concat([0.00, 0.00, 0.00, 1.00, 0.00]);

return new ColorMatrixFilter(matrix);
}

public static function createNegativeFilter():ColorMatrixFilter
{
var matrix:Array = new Array();

matrix = matrix.concat([-1, 0, 0, 0, 255]);
matrix = matrix.concat([0, -1, 0, 0, 255]);
matrix = matrix.concat([0, 0, -1, 0, 255]);
matrix = matrix.concat([0, 0, 0, 1, 0]);

return new ColorMatrixFilter(matrix);
}

Doing the color overlay (tint) and HSB was yet trickier, and I ended up using a 3rd party library to do that for me (look up Quasimondo's ColorMatrix).

Does anybody know if it would be possible to replicate something akin to Photoshop's "stroke" effect? Or a reflection effect? There probably are implementations of these, but I don't think I've seen them done in a BitmapFilter compatible form.
« Last Edit: Tue, May 18, 2010 by Jon »

hima

  • Member
  • **
  • Posts: 96
  • Karma: +0/-0
    • View Profile
    • My Dev Blog
Re: Flixel + BitmapFilter
« Reply #3 on: Tue, May 18, 2010 »
Lovely! I have always wanted to know how to do something like you did. Thank you so much!  :D

paul k

  • Member
  • **
  • Posts: 36
  • Karma: +0/-0
    • View Profile
Re: Flixel + BitmapFilter
« Reply #4 on: Thu, May 20, 2010 »
Thanks Jon, good stuff.  I'm sure I'll be using this.

Paul

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Re: Flixel + BitmapFilter
« Reply #5 on: Mon, Jun 14, 2010 »
I'm running into one issue where the effect is getting chopped off when the effect expands the size of the sprite (like a blur), but since the sprite is locked to the original size, it ends up truncating the effect.

For example, I have a 32x32 sprite. The blur needs 34x34 to do its work, but since it only gets 32x32, it ends up chopping the edges.

What's the recommended way of dealing with this?
« Last Edit: Mon, Jun 14, 2010 by Jon »

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
Re: Flixel + BitmapFilter
« Reply #6 on: Tue, Jun 15, 2010 »
Thanks, this is great.. Was looking for something like this !

Hectate

  • Active Member
  • ***
  • Posts: 192
  • Karma: +0/-0
    • View Profile
Re: Flixel + BitmapFilter
« Reply #7 on: Tue, Jun 15, 2010 »
I'm running into one issue where the effect is getting chopped off when the effect expands the size of the sprite (like a blur), but since the sprite is locked to the original size, it ends up truncating the effect.

For example, I have a 32x32 sprite. The blur needs 34x34 to do its work, but since it only gets 32x32, it ends up chopping the edges.

What's the recommended way of dealing with this?
My first thought is to pad your sprite with an additional 2 transparent pixels. Option two is to expand the sprite in the code somehow. I believe that Flixel does this when you use loadRotatedGraphic and bake the rotations into it (since the rotated sprite is larger than normal when the corners start going diagonal. I have no idea how that works but it might be worth looking at the function.
Patience is a Virtue,
But Haste is my Life.

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Re: Flixel + BitmapFilter
« Reply #8 on: Wed, Jun 16, 2010 »
Option one isn't feasible because it's a toolset, and I can't know ahead of time what effects will be applied to a sprite.

Your suggestion for looking at loadRotatedGraphic sounds plausible, so I'll have a look at that.

krix

  • Member
  • **
  • Posts: 61
  • Karma: +0/-0
    • View Profile
Re: Flixel + BitmapFilter
« Reply #9 on: Thu, Jun 17, 2010 »
Very nice!
This would be a great tutorial for the new http://www.flashgamedojo.com/ ;)
Besides it would be cool to see something like this implemented to flixel by default.

Great work!

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Re: Flixel + BitmapFilter
« Reply #10 on: Thu, Jun 17, 2010 »
I'd love to have this go into Flixel by default, once we can figure out the one issue a few posts above this.

I'll look into adding it to the Dojo, along with more discussion about how I arrived at the Color Matrices.

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Re: Flixel + BitmapFilter
« Reply #11 on: Thu, Jun 17, 2010 »
@ Hectate

I looked at loadRotatedGraphic and see what Adam did. It's quite interesting.

He takes the larger of the two dimensions and then multiplies that result by 1.5, which is correct in estimating the image size required to accommodate all cases. Then, he generates a bitmap, using a key that incorporates the width and height of the graphic.

It seems like this code is relevant in its approach, although the one difference is that filters are more flexible and there's no single constant I can multiply by to guarantee that the space will accommodate it, short of doing a chain of if(filter is FILTERTYPE) and special casing to handle each of the built-in ones.

Hectate

  • Active Member
  • ***
  • Posts: 192
  • Karma: +0/-0
    • View Profile
Re: Flixel + BitmapFilter
« Reply #12 on: Fri, Jun 18, 2010 »
It would be far easier for you to make the size increase of the sprite entirely up to the coder that calls your filters.
So if I put a blur (or whatever filter I need) on a sprite, I can tell the filter to add 0 (default) or more pixels to the bitmap. It'd be my responsibility to ensure that the end result matched what I desired - and all you'd have to do is let me decide how much extra space I actually need and provide the necessary code to expand it according to the parameter I supplied when calling it.
Patience is a Virtue,
But Haste is my Life.

datorum

  • Active Member
  • ***
  • Posts: 165
  • Karma: +0/-0
    • View Profile
Re: Flixel + BitmapFilter
« Reply #13 on: Sun, Jun 27, 2010 »
anyone did a fix on the "cutoff bug"?
I just added the feature myself and it looks great, except for the missing nose of my heli ;)

Jon

  • Member
  • **
  • Posts: 93
  • Karma: +0/-0
    • View Profile
    • Stencyl
Re: Flixel + BitmapFilter
« Reply #14 on: Sun, Jun 27, 2010 »
I haven't scrounged up the time to do that fix yet, so if anyone else wants to take a shot at it and is successful, please let us know!