Jan
04
2010

Pixel Bender Blend Modes

In my ActionScript 3.0 Image Effects book I have a chapter in which I discuss Pixel Bender and demonstrate how to create a blending mode for the Flash Player. I also suggest the possibility of rewriting the standard Flash Player blend modes so that a percent parameter might be supported, as you find in Photoshop. Then you could apply a multiply blend mode at 50% as opposed to always 100%. In a previous chapter in the book I present the blending mode formulas to make doing that easier.

So I finally took my own suggestion :) I have written Pixel Bender shaders not just for the standard Flash Player blend modes (multiply, screen, overlay…), but for the standard Photoshop blend modes as well (color dodge, vivid light, hue…). Here’s an example of them at work:

Pixel Bender blend modes in Flash Player

I incorporated the blend modes into the aether effects library. To apply one of these blend modes at runtime, you need to either embed the shader byte code in the SWF or load in the shader at runtime. Embedding would look like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package {

    import aether.blendModes.HardMixBlendMode;
    import aether.blendModes.ShaderBlendMode;

    import flash.display.Bitmap;
    import flash.display.Sprite;

    [SWF(width=400, height=602)]

    public class EmbeddingBlendMode extends Sprite {

        [Embed(source='/assets/hardMix.pbj', mimeType='application/octet-stream')]
        private static const HardMixKernel:Class;

        [Embed(source='/assets/hydrant.jpg')]
        private static const ForegroundImage:Class;

        [Embed(source='/assets/fungus.jpg')]
        private static const BackgroundImage:Class;

        public function EmbeddingBlendMode() {
            var backgroundImage:Bitmap = new BackgroundImage() as Bitmap;
            addChild(backgroundImage);

            var foregroundImage:Bitmap = new ForegroundImage() as Bitmap;
            addChild(foregroundImage);

            ShaderBlendMode.setShaderClassPath(this);
            foregroundImage.blendShader = new HardMixBlendMode(.5).shader;
        }

    }

}

In this case, the shader byte code is embedded into this class, and so I need to tell the ShaderBlendMode class where to find the shader. This is done through the static setShaderClassPath() method. All blend mode shaders will then look to this class for the byte code. By default, each BlendModeShader child class has an expected name of the shader that is assigned to a static shaderClass property. For HardMixBlendMode, it is “HardMixKernel”. For AddBlendMode it is “AddKernel”. Etc. So here in this class I embed the byte code with the class name HardMixKernel. If I wanted to use a different name, I could assign a new value to HardMixBlendMode.shaderClass.

Finally, I need to assign the actual Shader instance to the blendShader property of the bitmap, and this can be accessed through the shader property of a BlendModeShader instance. Since I am not looking to alter the 50% hard mix blend, I do not even need to save a reference to the blend mode shader, I just assign it directly to the bitmap.

If you are not embedding the byte code, you will need to load it at runtime. The following code does that (though I still embed the images just to save space here to focus on the blend mode code — if you are not using the mxmlc compiler obviously you will need to load the images or use another type of display object).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package {

    import aether.blendModes.HardMixBlendMode;
    import aether.blendModes.ShaderBlendMode;

    import flash.display.Bitmap;
    import flash.display.Sprite;

    [SWF(width=400, height=602)]

    public class LoadingBlendMode extends Sprite {

        [Embed(source='/assets/hydrant.jpg')]
        private static const ForegroundImage:Class;

        [Embed(source='/assets/fungus.jpg')]
        private static const BackgroundImage:Class;

        private var _blendMode:ShaderBlendMode;

        public function LoadingBlendMode() {
            var backgroundImage:Bitmap = new BackgroundImage() as Bitmap;
            addChild(backgroundImage);

            var foregroundImage:Bitmap = new ForegroundImage() as Bitmap;
            addChild(foregroundImage);

            ShaderBlendMode.shaderFilePath = "/pbjs/";
            _blendMode = new HardMixBlendMode(.5, foregroundImage);
        }

    }

}

In this case I must specify through the static shaderFilePath where the shader will be loaded from. Just as with the shaderClass property, there is a shaderFile static property of each ShaderBlendMode that determines the name of the shader file to load. By default, the HardMixBlendMode uses “hardMix.pbj”. The MultiplyBlendMode uses “multiply.pbj”. I hope the pattern is obvious :). If you want to rename the shader file, you simply have to update the shaderFile value for the associated class.

One of the cool things about these blend modes is that if you assign a display object as the second parameter, as I did in this example, then the shader will reapply itself to the display object if its percentage ever changes (by default in the Flash Player, a shader is copied to the display object, there is no object reference that remains). So if you wanted to change the percentage of the blend mode applied, you would simply need to have:

1
_blendMode.percent = 0.8;

The display object would then be automatically updated with the new shader value.

To use these shaders, simply grab the aether effects library. There is a precompiled SWC as well as the original source for both the ActionScript and the Pixel Bender shaders. There are no dependencies in the shader blend modes on other pieces of the aether library, so if all you want is to use a blend mode you don’t get any excess baggage along with it.

  • Share/Bookmark

13 Responses to “Pixel Bender Blend Modes”

  1. Just found this great post on pretty much the same thing, posted back in May (unfortunately posted after I had researched and written my book — it would have been useful!):

    http://www.lostinactionscript.com/blog/index.php/2009/05/26/custom-blend-modes-for-flash-10/

    This has quite a few additional blend modes and some great tips. I certainly do not currently handle alpha properly with the shaders I presented, so that might be something I iterate on (I did originally code a few of those other modes like “reflect” and “phoenix”, but left them out in the end, wanting to concentrate on just the Flash and Photoshop default blend modes).

    And this blog has some additional blend modes for you, also from May:

    http://www.rphelan.com/?p=272

  2. Please update your Color, it’s alpha is quite off.

    try this:

    kernel Color

    {

    parameter float percent
    ;

    input image4 background;
    input image4 source;
    output pixel4 result;

    void
    evaluatePixel()
    {
    float2 coord = outCoord();

    pixel4 sourcePx = sampleNearest(source, coord);
    float sourceLuminance = sourcePx.r * 0.3 +
    sourcePx.g * 0.59 +
    sourcePx.b * 0.11;
    pixel4 backgroundPx = sampleNearest(background, coord);
    float backgroundLuminance = backgroundPx.r * 0.3 +
    backgroundPx.g * 0.59 +
    backgroundPx.b * 0.11;

    float luminance = backgroundLuminance – sourceLuminance;
    pixel4 fullBlend = sourcePx;
    fullBlend.r += luminance;
    fullBlend.g += luminance;
    fullBlend.b += luminance;
    result = mix(backgroundPx, fullBlend, percent);
    }

    }

  3. What you have there is what I currently have for Luminosity. How do you mean the alpha is off (I haven’t taken alpha into account in any of the blend modes, which is something I hope to rectify)?

    Currently, the Color blend mode as it stands produces the exact same effect as in Photoshop when I compare the same images in my test app with the blend mode applied in Photoshop with the same stacking. But that is when I have amount set to 1. Perhaps you are pointing out that the “percent” in the blend mode doesn’t work like the “opacity” setting in Photoshop? If so, I see your point there.

    I’d like to keep the percent setting as it is, though, which is the percentage to apply the blend mode, but I’d also like to allow for transparency in the two images. Again, that’s something I’d like to address for the next iteration.

    Or am I completely off with what you were saying?

  4. ps filter: http://img709.imageshack.us/i/20100210105803.jpg/
    current Color: http://img51.imageshack.us/i/20100210110737.jpg/
    how I think it should be: http://img109.imageshack.us/i/20100210110818.jpg/

    I think I understood what you’re doing: 0 is “no blend”, 1 is “full blend”. In that case, I wanted a full blend on an element with alpha applied. But the filter doesn’t work with transparent objects, am I right?

  5. Correct. I need to rework the shaders to work properly with transparent objects, but right now the “percent” is the amount of the blend mode to apply, not the alpha of the display object. That won’t change — I think I will keep the single parameter for the shader. What WILL change is that you will be able to set the alpha of the display objects and the blend modes will work with this transparency properly.

  6. hello I’m happy to find this great blog and post.I’m a great newbie but always can get all my jobs done. My problem is when I import .png file in flash then use one of your pixel bender blend modes like luminosity, color or hue the transparent part of the image goes strait black.. I figured this is what you guys where talking about above. Is there a fix that I can adjust manually? I know nothing about coding language in Pixel bender so is there anyway you guys can tell me what to copy and paste or send me updated luminosity color an hue Pixel Bender files?? Thank guys

  7. Your demo doesn’t work anymore ?

  8. Huh. Did the last time I checked. What an odd error. I’ll take a look and put up a fix shortly. Thanks for letting me know.

  9. This completely slipped my mind. The issue was that I developed this using the Flash Builder and Flex 4 beta, and now even the local files I created are showing this error, and unfortunately the syntax is different enough that I couldn’t simply just quickly update the files.

    I’ll see if I can fix them this weekend and will post back with the updated (if anyone is still reading this :)).

  10. hi todd,
    i need to ask something from book.
    you always use onImageLoaded event like this

    private function onImageLoaded(e:Event):void{
    var loaderInfo:LoaderInfo = e.target as LoaderInfo;
    var bitmap:Bitmap = loaderInfo.content as Bitmap;
    }

    i tried this one and it is shorter, if is there something i dont know, light me.

    private function onImageLoaded(e:Event):void{
    var bitmap:Bitmap = e.target.content as Bitmap;
    }

  11. No, that’s fine. It’s the same code, just done on one line instead of two. Sometimes breaking it up into multiple lines is a little clearer, which is why I did what I did (especially for the book) but there’s nothing wrong with using either version.

  12. thanks for reply

  13. Hello mate I really appreciate what u do, I also have your book :)
    I’ve tried to use ur library and it works perfectly ty so much !!

Leave a Reply