1 Reply Latest reply on Sep 22, 2013 4:59 AM by Prophessor Balthazar

    Blending two bitmaps into a new BitmapData, using a blend shader

    Prophessor Balthazar

      Hi

       

      I have:

       

      Two bitmap images of the same size, as BitmapData.

       

      I want:

       

      One larger BitmapData, with the two images blended together in a gradient, like this...

       

      Row 1: [ BitmapData 1 ]

      Row 2: [ BitmapData 1 with BitmapData 2 blended on top, alpha 0.1 ]

      Row 3: [ BitmapData 1 with BitmapData 2 blended on top, alpha 0.2 ]

      Row 4: [ BitmapData 1 with BitmapData 2 blended on top, alpha 0.3 ]

       

      ....

       

      Row n: [ BitmapData 2 ]

       

      ------------------------------------

       

      I can easily implement this using standard alpha blending. However, I would like to use my own blending algorithm, and doing it with actionscript is too slow. Therefore, I want to implement the blending function as a Pixel Bender shader. I have come so far as to make a function that works with standard alpha blending, and no custom shader involved:

       

      protected function createGradientLookupTable( bitmapAtBlend0:BitmapData, bitmapAtBlend1:BitmapData, steps:uint ):BitmapData {
          var gradient:BitmapData = new BitmapData( 256, bitmapAtBlend0.height * steps );
          var translationMatrix:Matrix = new Matrix();
          var alphaTransform:ColorTransform = new ColorTransform();

          for( var row:int = 0; row < steps; row++ ) {
              gradient.draw(bitmapAtBlend0, translationMatrix);
              alphaTransform.alphaMultiplier = row / ( steps - 1 );
              gradient.draw(bitmapAtBlend1, translationMatrix, alphaTransform );
              translationMatrix.translate( 0, bitmapAtBlend0.height );
          }

          return gradient;
      }

       

      This works. To begin with, I have made a standard alpha blending shader in Pixel Bender, just to check whether I can load it in my Flash Builder project and make it work. This filter works in Pixel Bender 2.5:

       

      <languageVersion : 1.0;>
      kernel BlendHCL
      <   namespace : "com.yadayada";
          vendor : "Yada Yada";
          version : 1;
          description : "Blends colors using hue chroma luma";
      >
      {
          input image4 src1;
          input image4 src2;
          output pixel4 dst;

          parameter float alpha;

          void
          evaluatePixel()
          {
              float4 pixelBottom = sampleNearest(src1,outCoord());
              float4 pixelTop = sampleNearest(src2,outCoord());

              dst = pixelBottom + ( pixelTop - pixelBottom ) * alpha;

          }
      }

       

      Don't mind the description etc, currently it's only supposed to take one alpha parameter, and blend using standard alpha blending. Just to see that I can get it to work. Here is my modified Actionscript code to use the custom blender, this code does NOT work:

       

      protected function createGradientLookupTable( bitmapAtBlend0:BitmapData, bitmapAtBlend1:BitmapData, steps:uint ):BitmapData {
          var gradient:BitmapData = new BitmapData( 256, bitmapAtBlend0.height * steps );
          var translationMatrix:Matrix = new Matrix();
          var bitmap1:Bitmap = new Bitmap( bitmapAtBlend1 );
          var blendShader:Shader = new Shader( blendShaderCode );

          for( var row:int = 0; row < steps; row++ ) {
              gradient.draw(bitmapAtBlend0, translationMatrix);
              blendShader.data.alpha.value = [ row / ( steps - 1 ) ];
              bitmap1.blendShader = blendShader;
              try {
      //            gradient.draw( bitmap1, translationMatrix ); // doesn't work
                  gradient.draw( bitmap1, translationMatrix, null, BlendMode.SHADER ); // doesn't work either
              }
              catch( e:Error ) {
                  trace( "Error happened! " + e.message ); // No error messages reported
              }
              translationMatrix.translate( 0, bitmapAtBlend0.height );
          }

          return gradient;
      }

       

      This modified code does not report any errors, it simply acts as if the alpha is always 1.0. The second image completely replaces the first. This is the expected result if the blend shader is not in effect at all. All the example code I find using a blend shader, simply blends the colors directly to the screen, not into a new Bitmap or BitmapData object. Can anyone help?

       

      PS: Debugging in Flash Builder 4.7 and inspecting the variables indicates I have a valid shader object (blendShader).

        • 1. Re: Blending two bitmaps into a new BitmapData, using a blend shader
          Prophessor Balthazar Level 1

          It looks to me I couldn't do it the way I wanted. Instead I created a shader that could create the entire gradient in one go, and made a ShaderJob instance in Flash to get the job done. Here is the resulting shader code:

           

           

          <languageVersion : 1.0;>
          
          kernel BlendHCL
          <   namespace : "com.yadayada";
              vendor : "Yada Yada";
              version : 1;
              description : "Blends colors using hue chroma luma";
          >
          {
          
              input image4 src1;
              input image4 src2;
              output pixel4 dst;
              
              parameter float imageHeight
              <
                  minValue:     1.0;
                  maxValue:    64.0;
                  defaultValue: 2.0;
                  description: "The height of the images that will be blended in the gradient result";
              >;
              
              parameter float steps
              <
                  minValue:      2.0;
                  maxValue:   1024.0;
                  defaultValue: 64.0;
                  description: "The number of shades in the gradient";
              >;
          
              void
              evaluatePixel()
              {
              
                  float2 sampleCoord = outCoord();
                  
                  sampleCoord.y = mod( sampleCoord.y, imageHeight );
              
                  float4 pixelBottom = sampleNearest(src1,sampleCoord);
                  float4 pixelTop = sampleNearest(src2,sampleCoord);
                  
                  float alpha = floor( outCoord().y / imageHeight ) / ( steps - 1.0 );
            
                  dst = pixelBottom + ( pixelTop - pixelBottom ) * alpha;
                  
              }
          }
          

           

           

          Here is the Actionscript code:

           

                    public function createGradientLookupTable(bitmapAtBlend0:BitmapData, bitmapAtBlend1:BitmapData, steps:uint):BitmapData {
                         var shader:Shader = new Shader( blendShaderCode );
                         
                         shader.data.imageHeight.value = [ Number( bitmapAtBlend0.height ) ];
                         shader.data.steps.value = [ Number( steps ) ];
                         
                         shader.data.src1.input = bitmapAtBlend0;
                         shader.data.src2.input = bitmapAtBlend1;
                         
                         var result:BitmapData = new BitmapData( 256, bitmapAtBlend0.height * steps );
                         
                         var shaderJob:ShaderJob = new ShaderJob(shader, result);
                         
                         shaderJob.start( true );
                         
                         return result;
                         
                    }
          
          

           

           

          It worked. Currently the shader still only does standard alpha blending, but now I can go on to do the actual shader coding.