MEGA Search
20.3 Million


Sign Up
From: Henry A. Eckstein  
Subject: Here is a FAST Alpha Blend Routine
NewsGroup: borland.public.delphi.graphics
Date Posted: 2-Dec-2003 at 16:46:48 PST
Enjoy this FAST alpha blend routine which uses optimized
pixel by pixel SCANLINE accesses...

I do have a test images and code snippet archive
with 1600 by 1200 pixel test images for you
to test this system with. It's a 6.7 megabyte file.
Email me at henry@comwave.com and i'll send it to you.
It is too big to post in this forum.

If the code looks a little messy in this post, cut & paste the code
to your Delphi Editor and get rid of the 80 character line wrap.
Set line wrap to 255 characters or more.

This routine will work on Delphi Versions 5, 6 & 7
I haven't tested it with Delphi Version 2, 3 or 4.

The image files currently MUST be of Windows BMP format
but some enterprising programmer can change teh code to work with
JPEG or other file formats.

{
ALPHA BLEND AND SUPPORT ROUTINES DESIGNED AND CODED BY:
==============================================================
Henry A. Eckstein
Triad Communications Ltd.
2751 Oxford Street
Vancouver, British Columbia, Canada
V5K 1N5

Telephone: 604-253-3990
Fax:       604-253-0770

Toll Free: 1-800-600-9762 (1-800-OZZY-ROC)

Website: www.triadcommunications.ca

Email: henry@comwave.com

Copyright (C) 2003 - All Rights Reserved.

These Code Snippets are hereby donated to the
PUBLIC DOMAIN for use in any which way you desire.

Please include an acknowledgement of my authorship
if you do happen to use the Alpha Blend code.


THANK YOU and GOOD CODING

P.S. If you like skiing and snowboarding
      goto the website: www.whistler-blackcomb.com
      to see the two biggest ski mountains
      in North America, 160 km north of
      Vancouver, BC, Canada

P.S.2. The test photos are of my hometown in Vancouver, BC, Canada
}


uses
   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, 
Forms,
   Dialogs, StdCtrls, MPlayer;


Type
   {$R-} // Turn range checking OFF so that arrays can be accessed 
outside of their range limits

   // Assumes a 32 bit TBitmap pixel type  i.e. TBitmap.PixelFormat := 
pf32bit;
   RGB_Pixel = Packed Record  // Variant record allow two separate 
fields to occupy the same memory space
                 Case LongWord of
                   0 : ( Blue, Green, Red, Alpha : Byte );  // This is 
Windows specific as an RGB pixel has its colour channels ordered in a 
specific way i.e. ABGR rather than the usual RGBA
                   1 : ( All_Channels : LongWord );         // The above 
colour components can be accessed as a single unsigned 32-bit integer 
when All_Channels field is accessed.
                 End;                                       // This is 
useful for performing AND, XOR, OR and other bitwise manipulations on an 
entire pixel

   // A pointer to the above pixel record type
   RGB_Pixel_Pointer = ^RGB_Pixel;

   {
   An array of the above RGB pixel type which makes it easy to access a 
region
   of memory containing a series of RGB pixels. This is used by many 
graphics
   filters or rendering routines.

   Packed Array forces the individual fields to be aligned close 
together in memory.
   Some operating systems or compilers, if not declared as packed, would 
align fields on 16-bit or 32-bit boundaries
   depending upon the size of an individual field.  The Delphi specific 
identifier "Packed"
   gives us the ability to force record fields to be close together with 
no free byte space in between.

   This array has been declared with 0..1 range to save heap and stack 
memory space.
   But if you turn range checking off i.e. $R- then the array can be 
accessed
   outside of the declared boundaries.
   }
   Array_of_RGB_Pixels = Packed Array[ 0..1 ] of RGB_Pixel;

   {
   A pointer to an Array of RGB pixels which makes it easy to access a 
region
   of memory containing a series of RGB pixels. This is used by many 
graphics
   filters or rendering routines that need to facilitate pointer arithmetic
   or memory address manipulation on a whole series of RGB pixels.
   }
   Pointer_to_Array_of_RGB_Pixels = ^Array_
of_RGB_Pixels;


{
For speed reasons make sure your TBitmap.PixelFormat := pf32bit is set,
as this routine assumes all pixels in the Tbitmap parameters are bitmaps
composed of 8-bit Red, Green, Blue, Alpha channels.
Accessing pixels as 32 bit integers really speeds things up.

Using scanline is one of the fastest ways in software to access the
TBitmap pixels without using assembler or MMX instructions.

Alpha Blending two 1600 by 1200 pixel 32 bit bitmaps using a 1600 by 
1200 pixel 32 bit greyscale alpha channel bitamp
on a 450 MHZ AMD K3 will be finshed in 1.5 seconds (1576 milliseconds)
On other Pentium 500 MHZ systems I have been able to get alpha blend 
times downto
600 milliseconds on 1600 by 1200 pixel bitmaps
(my graphics card is a Matrox G550 with 32 megs of Video RAM and I have 
an AMD K3 running at 450 MHZ and 378 megs of system RAM)

Please NOTE that the Foreground and Alpha Channel bitmaps
MUST be of the same width and height so that the alpha blend operation
can correctly combine the foreground with the background
}
Procedure Alpha_Blend_Bitmap_Over_Background( Starting_X_Coordinate, 
Starting_Y_Coordinate: Integer; Var Foreground_Bitmap, 
Background_Bitmap, Alpha_Channel_Bitmap: TBitmap );

   Var
     x, y,

     Last_X,
     Last_Y,
     Current_X_Coordinate : Large_Integer;

     Line_of_Foreground_Pixels,
     Line_of_Background_Pixels,
     Line_of_Alpha_Channel_Pixels : Pointer_to_Array_of_RGB_Pixels;

     Foreground_Pixel,
     Background_Pixel,
     Alpha_Channel_Pixel : RGB_Pixel;

   Begin
     Try
       // if alpha channel does not exist then just try and draw the
       // foreground bitmap onto the background using the Delphi Draw() 
command.
       if Alpha_Channel_Bitmap = NIL then
         Background_Bitmap.Canvas.Draw( Starting_X_Coordinate, 
Starting_Y_Coordinate, Foreground_Bitmap );

       with Foreground_Bitmap do
         Begin
           Last_X := Foreground_Bitmap.Width - 1;
           Last_Y := Foreground_Bitmap.Height - 1;

           for y := ZERO to Last_Y do
             Begin
               // get the pointer to each row of pixel on all the bitmaps
               Line_of_Foreground_Pixels    := 
Foreground_Bitmap.ScanLine[ y ];
               Line_of_Background_Pixels    := 
Background_Bitmap.ScanLine[ Starting_Y_Coordinate + y ];
               Line_of_Alpha_Channel_Pixels := 
Alpha_Channel_Bitmap.ScanLine[ y ];

               for x := ZERO to Last_X do
                 Begin
                   Current_X_Coordinate := Starting_X_Coordinate + x;

                   // retrieve the relevant pixel on each row of pixels
                   Foreground_Pixel    := Line_of_Foreground_Pixels[ x ];
                   Background_Pixel    := Line_of_Background_Pixels[ 
Current_X_Coordinate ];
                   Alpha_Channel_Pixel := Line_of_Alpha_Channel_Pixels[ x ];

                   // Perform Alpha blend oepration
                   Line_of_Background_Pixels[ Current_X_Coordinate 
].Blue  := ( ( Alpha_Channel_Pixel.Blue  * ( ForeGround_Pixel.Blue  - 
Background_Pixel.Blue  ) ) SHR 8 ) + Background_Pixel.Blue;
                   Line_of_Background_Pixels[ Current_X_Coordinate 
].Green := ( ( Alpha_Channel_Pixel.Green * ( ForeGround_Pixel.Green - 
Background_Pixel.Green ) ) SHR 8 ) + Background_Pixel.Green;
                   Line_of_Background_Pixels[ Current_X_Coordinate ].Red 
   := ( ( Alpha_Channel_Pixel.Red   * ( ForeGround_Pixel.Red   - 
Background_Pixel.Red   ) ) SHR 8 ) + Background_Pixel.Red;

                   // The Alpha channel of a four-colour-channel RGB 
pixel can be safely ignored since we don't need to access it
                 End;
             End;
         End;
     Except
       // on any errors (i.e. if Foreground or Background bitmaps are 
invalid) display this message
       ShowMessage( 'ERROR: Unable to Draw Foreground Bitmap onto 
Background Bitmap using an Alpha Channel' );
     End;
   End;

{
This routine is used for timing operations so that we
 can figure
out how many milliseconds a specific operation takes.
It's accurate down to the millisecond level

The difference between two calls of this routine will give us the
number of milliseconds an operation takes.
}
Function Get_Clock_Count_in_Milliseconds: Floating_Point;

   Var
     Current_Date_And_Time: TDateTime; // Delphi specific Date & Time 
Record which is a 64 bit floating point number

     Number_of_Milliseconds_Since_Midnight : Double;

     Current_Hour,
     Current_Minute,
     Current_Second,
     Current_MilliSecond: Word;

   Begin
     Try
       Current_Date_And_Time := Now;   // NOW is a Delphi specific 
function part of the SysUtils unit
                                       // Get the current date and time 
in Floating Point format

       { DecodeTime is a SysUtils unit function - Get the component 
parts of the date and time and put each element into the desired variables }
       DecodeTime( Current_Date_And_Time, Current_Hour, Current_Minute, 
Current_Second, Current_MilliSecond );

       Number_of_Milliseconds_Since_Midnight := ( Current_Hour * 3600000 ) +
                                                ( Current_Minute * 60000 ) +
                                                ( Current_Second * 1000 ) +
                                                  Current_MilliSecond;
     Except
       Showmessage( 'Unable to obtain the system clock count in 
milliseconds since midnight' );
       Get_Clock_Count_in_Milliseconds := 0.0;
       Exit;
     End;
                                           { Number of days past since 
December 30, 1899 - Number of Milliseconds in a day }
     Get_Clock_Count_in_Milliseconds := ( Trunc( Current_Date_And_Time ) 
* 86400000 ) + Number_of_Milliseconds_Since_Midnight;
   End;

{
A general example procedure which a user can call to see how long an 
alpha blend operation takes
It will loads 3 standard Windows Bitmaps and alpha blend them together.
one of the bitmaps MUST be a greyscale image which called the Alpha Channel

An Alpha channel can have feathered or soft edges and the closer
to 100% FULL WHITE (i.e. R:255, G:255, B:255) then the more that the 
FOREGROUND bitmap will show through.

The closer to ZERO percent Black (i.e. R:0, G:0, B:0) the more the
BACKGROUND will show through.

Any mix of grey in between will give you a blend where you see BOTH images.
(i.e. R:127, G:127, B:127) is a 50% blend

Setting the individual colour channels of an Alpha pixel allows you to 
target
the specific colour channel of an Alpha Blend   i.e. (i.e. R:0, G:255, B:0)
would give have ONLY the green channel of the foreground show through.

The alpha channel bitmap can be filled with a single solid colour
or can contain any shape and any degree of soft or hard eges on those 
shapes.

Example of use:  Alpha_Blend_Example( TForm1.Canvas );
}
Procedure Alpha_Blend_Example( Canvas_of_Form: TCanvas );  // 
Canvas_of_Form is usually the canvas of the TForm where you wish to draw 
the final output

   Var
     Foreground_Image,
     Background_Image,
     Alpha_Channel_Image : TBitmap;  // If you get a stack overflow or 
other type of error increase the stack size to the following
                                     // values using the $M compiler 
directive {$M 16384,18000000} This set maximum stack to 18 megabytes
                                     // You can also move these 3 bitmap 
variables out of this procedure declaration into Heap memory
                                     // You will need to do this if your 
images are 1600 by 1200 pixels or larger

     Elapsed_Time_Message : String;

     Start_Time,
     Finish_Time,
     Total_Elapsed_Time : Extended;  // Holds how many milliseconds 
since midnight the system clock has been run - used for timing purposes

   Begin
     Foreground_Image    := TBitmap.Create;
     Background_Image    := TBitmap.Create;
     Alpha_Channel_Image := TBitmap.Create;

     // load bitmaps from files
     Foreground_Image.LoadFromFile    ( 'Foreground Image.bmp'    );
     Background_Image.LoadFromFile    ( 'Background Image.bmp'    );
     Alpha_Channel_Image.LoadFromFile ( 'Alpha Channel Image.bmp' ); // 
if you change the name of this file to
                                                                     // 
'Alpha Channel Image 50 Percent.bmp' then
                                                                     // 
you'll see a 50% blend of both foreground and background
                                                                     // 
also try the file 'Alpha Channel Image Left to Right Ramp.bmp'

     // set bitmaps to 32 bit pixels per pixel with full 8-bits of Red, 
Green Blue and Alpha channel
     Foreground_Image.PixelFormat    := pf32bit;
     Background_Image.PixelFormat    := pf32bit;
     Alpha_Channel_Image.PixelFormat := pf32bit;

     Canvas_of_Form.Draw( 0, 0, Alpha_Channel_Image );
     Showmessage( 'Here is the Alpha Channel Image which will be used in 
the Alpha Blend...' );

     Canvas_of_Form.Draw( 0, 0, Foreground_Image );
     Showmessage( 'Here is the Foreground Image which will be used in 
the Alpha Blend...' );

     Canvas_of_Form.Draw( 0, 0, Background_Image );
     Showmessage( 'Here is the Background Image which will be used in 
the Alpha Blend...' + #10 + #13 +
                  'Click on OK to begin Alpha Blend Test...' );


     // ******************* Clock 32 bit integer array access time
     Start_Time := Get_Clock_Count_in_Milliseconds;

     Alpha_Blend_Bitmap_Over_Background( ZERO, ZERO, Foreground_Image, 
Background_Image, Alpha_Channel_Image );

     Finish_Time := Get_Clock_Count_in_Milliseconds;

     Total_Elapsed_Time := Finish_Time - Start_Time;

     Elapsed_Time_Message := 'Clocking Total Pixel by Pixel Alpha Blend: 
' + FloatToStr( Total_Elapsed_Time ) + ' milliseconds.' + #10 + #13 +
                             'Note the soft edges on the images 
boundaries.';

     Canvas_of_Form.Draw( 0, 0, Background_Image );

     Showmessage( Elapsed_Time_Message );

     Foreground_Image.Free;
     Background_Image.Free;
     Alpha_Channel_Image.Free;
   End;



From: Nils Haeck  
Subject: Re: Is it possible to get a copy of an Optimized MMX Alpha B
NewsGroup: borland.public.delphi.graphics
Date Posted: 5-Dec-2003 at 2:5:25 PST
Hi Jens,

Mike's MMX alphablend code is part of his (free) VirtualTreeview code so you
can also download it here:
http://www.delphi-gems.com

The procedures are in VirtualTrees.pas

Kind regards,

Nils

"Jens Gruschel"  wrote in message
news:3fcfbf3d$1@newsgroups.borland.com...
> > Well then, here is my version (taken from Virtual Treeview). It allows
> four different kinds of alpha blending and does not require any
> premultiplication of color values (as the Win API AlphaBlend).
>
> Could you post it to the attachments newsgroup please? It seems to be too
> long for my newsreader. But looks interesting. Thank you for sharing it
with
> us!
>
> Jens
>
>
>




From: Jens Gruschel  
Subject: Re: Is it possible to get a copy of an Optimized MMX Alpha B
NewsGroup: borland.public.delphi.graphics
Date Posted: 5-Dec-2003 at 0:14:6 PST
> Well then, here is my version (taken from Virtual Treeview). It allows
four different kinds of alpha blending and does not require any
premultiplication of color values (as the Win API AlphaBlend).

Could you post it to the attachments newsgroup please? It seems to be too
long for my newsreader. But looks interesting. Thank you for sharing it with
us!

Jens




From: Mike Lischke  
Subject: Re: Is it possible to get a copy of an Optimized MMX Alpha B
NewsGroup: borland.public.delphi.graphics
Date Posted: 4-Dec-2003 at 10:52:15 PST
user@domain.invalid wrote:

> Would it be possible for me to get a copy of your MMX optimized
> Alpha Blend, I would really appreciate it ...

Well then, here is my version (taken from Virtual Treeview). It allows four different kinds of alpha blending and does not require any premultiplication of color values (as the Win API AlphaBlend).

type
  // Describes the mode how to blend pixels.
  TBlendMode = (
    bmConstantAlpha,         // apply given constant alpha
    bmPerPixelAlpha,         // use alpha value of the source pixel
    bmMasterAlpha,           // use alpha value of source pixel and multiply it with the constant alpha value
    bmConstantAlphaAndColor  // blend the destination color with the given constant color und the constant alpha value
  );

procedure AlphaBlendLineConstant(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer);

// Blends a line of Count pixels from Source to Destination using a constant alpha value.
// The layout of a pixel must be BGRA where A is ignored (but is calculated as the other components).
// ConstantAlpha must be in the range 0..255 where 0 means totally transparent (destination pixel only)
// and 255 totally opaque (source pixel only).
// Bias is an additional value which gets added to every component and must be in the range -128..127
//
// EAX contains Source
// EDX contains Destination
// ECX contains Count
// ConstantAlpha and Bias are on the stack

asm
        PUSH    ESI                    // save used registers
        PUSH    EDI

        MOV     ESI, EAX               // ESI becomes the actual source pointer
        MOV     EDI, EDX               // EDI becomes the actual target pointer

        // Load MM6 with the constant alpha value (replicate it for every component).
        // Expand it to word size.
        MOV     EAX, [ConstantAlpha]
        DB      $0F, $6E, $F0          /// MOVD      MM6, EAX
        DB      $0F, $61, $F6          /// PUNPCKLWD MM6, MM6
        DB      $0F, $62, $F6          /// PUNPCKLDQ MM6, MM6

        // Load MM5 with the bias value.
        MOV     EAX, [Bias]
        DB      $0F, $6E, $E8          /// MOVD      MM5, EAX
        DB      $0F, $61, $ED          /// PUNPCKLWD MM5, MM5
        DB      $0F, $62, $ED          /// PUNPCKLDQ MM5, MM5

        // Load MM4 with 128 to allow for saturated biasing.
        MOV     EAX, 128
        DB      $0F, $6E, $E0          /// MOVD      MM4, EAX
        DB      $0F, $61, $E4          /// PUNPCKLWD MM4, MM4
        DB      $0F, $62, $E4          /// PUNPCKLDQ MM4, MM4

@1:     // The pixel loop calculates an entire pixel in one run.
        // Note: The pixel byte values are expanded into the higher bytes of a word due
        //       to the way unpacking works. We compensate for this with an extra shift.
        DB      $0F, $EF, $C0          /// PXOR      MM0, MM0,   clear source pixel register for unpacking
        DB      $0F, $60, $06          /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8,     move higher bytes to lower bytes
        DB      $0F, $EF, $C9          /// PXOR      MM1, MM1,   clear target pixel register for unpacking
        DB      $0F, $60, $0F          /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words
        DB      $0F, $6F, $D1          /// MOVQ      MM2, MM1,   make a copy of the shifted values, we need them again
        DB      $0F, $71, $D1, $08     /// PSRLW     MM1, 8,     move higher bytes to lower bytes

        // calculation is: target = (alpha * (source - target) + 256 * target) / 256
        DB      $0F, $F9, $C1          /// PSUBW     MM0, MM1,   source - target
        DB      $0F, $D5, $C6          /// PMULLW    MM0, MM6,   alpha * (source - target)
        DB      $0F, $FD, $C2          /// PADDW     MM0, MM2,   add target (in shifted form)
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8,     divide by 256

        // Bias is accounted for by 
conversion of range 0..255 to -128..127,
        // doing a saturated add and convert back to 0..255.
        DB      $0F, $F9, $C4          /// PSUBW     MM0, MM4
        DB      $0F, $ED, $C5          /// PADDSW    MM0, MM5
        DB      $0F, $FD, $C4          /// PADDW     MM0, MM4
        DB      $0F, $67, $C0          /// PACKUSWB  MM0, MM0,   convert words to bytes with saturation
        DB      $0F, $7E, $07          /// MOVD      [EDI], MM0, store the result
@3:
        ADD     ESI, 4
        ADD     EDI, 4
        DEC     ECX
        JNZ     @1
        POP     EDI
        POP     ESI
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AlphaBlendLinePerPixel(Source, Destination: Pointer; Count, Bias: Integer);

// Blends a line of Count pixels from Source to Destination using the alpha value of the source pixels.
// The layout of a pixel must be BGRA.
// Bias is an additional value which gets added to every component and must be in the range -128..127
//
// EAX contains Source
// EDX contains Destination
// ECX contains Count
// Bias is on the stack

asm
        PUSH    ESI                    // save used registers
        PUSH    EDI

        MOV     ESI, EAX               // ESI becomes the actual source pointer
        MOV     EDI, EDX               // EDI becomes the actual target pointer

        // Load MM5 with the bias value.
        MOV     EAX, [Bias]
        DB      $0F, $6E, $E8          /// MOVD      MM5, EAX
        DB      $0F, $61, $ED          /// PUNPCKLWD MM5, MM5
        DB      $0F, $62, $ED          /// PUNPCKLDQ MM5, MM5

        // Load MM4 with 128 to allow for saturated biasing.
        MOV     EAX, 128
        DB      $0F, $6E, $E0          /// MOVD      MM4, EAX
        DB      $0F, $61, $E4          /// PUNPCKLWD MM4, MM4
        DB      $0F, $62, $E4          /// PUNPCKLDQ MM4, MM4

@1:     // The pixel loop calculates an entire pixel in one run.
        // Note: The pixel byte values are expanded into the higher bytes of a word due
        //       to the way unpacking works. We compensate for this with an extra shift.
        DB      $0F, $EF, $C0          /// PXOR      MM0, MM0,   clear source pixel register for unpacking
        DB      $0F, $60, $06          /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8,     move higher bytes to lower bytes
        DB      $0F, $EF, $C9          /// PXOR      MM1, MM1,   clear target pixel register for unpacking
        DB      $0F, $60, $0F          /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words
        DB      $0F, $6F, $D1          /// MOVQ      MM2, MM1,   make a copy of the shifted values, we need them again
        DB      $0F, $71, $D1, $08     /// PSRLW     MM1, 8,     move higher bytes to lower bytes

        // Load MM6 with the source alpha value (replicate it for every component).
        // Expand it to word size.
        DB      $0F, $6F, $F0          /// MOVQ MM6, MM0
        DB      $0F, $69, $F6          /// PUNPCKHWD MM6, MM6
        DB      $0F, $6A, $F6          /// PUNPCKHDQ MM6, MM6

        // calculation is: target = (alpha * (source - target) + 256 * target) / 256
        DB      $0F, $F9, $C1          /// PSUBW     MM0, MM1,   source - target
        DB      $0F, $D5, $C6          /// PMULLW    MM0, MM6,   alpha * (source - target)
        DB      $0F, $FD, $C2          /// PADDW     MM0, MM2,   add target (in shifted form)
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8,     divide by 256

        // Bias is accounted for by conversion of range 0..255 to -128..127,
        // doing a saturated add and convert back to 0..255.
        DB      $0F, $F9, $C4          /// PSUBW     MM0, MM4
        DB      $0F, $ED, $C5          /// PADDSW    MM0, MM5
        DB      $0F, $FD, $C4          /// PADDW     MM0, MM4
        DB      $0F, $67, $C0      
    /// PACKUSWB  MM0, MM0,   convert words to bytes with saturation
        DB      $0F, $7E, $07          /// MOVD      [EDI], MM0, store the result
@3:
        ADD     ESI, 4
        ADD     EDI, 4
        DEC     ECX
        JNZ     @1
        POP     EDI
        POP     ESI
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AlphaBlendLineMaster(Source, Destination: Pointer; Count: Integer; ConstantAlpha, Bias: Integer);

// Blends a line of Count pixels from Source to Destination using the source pixel and a constant alpha value.
// The layout of a pixel must be BGRA.
// ConstantAlpha must be in the range 0..255.
// Bias is an additional value which gets added to every component and must be in the range -128..127
//
// EAX contains Source
// EDX contains Destination
// ECX contains Count
// ConstantAlpha and Bias are on the stack

asm
        PUSH    ESI                    // save used registers
        PUSH    EDI

        MOV     ESI, EAX               // ESI becomes the actual source pointer
        MOV     EDI, EDX               // EDI becomes the actual target pointer

        // Load MM6 with the constant alpha value (replicate it for every component).
        // Expand it to word size.
        MOV     EAX, [ConstantAlpha]
        DB      $0F, $6E, $F0          /// MOVD      MM6, EAX
        DB      $0F, $61, $F6          /// PUNPCKLWD MM6, MM6
        DB      $0F, $62, $F6          /// PUNPCKLDQ MM6, MM6

        // Load MM5 with the bias value.
        MOV     EAX, [Bias]
        DB      $0F, $6E, $E8          /// MOVD      MM5, EAX
        DB      $0F, $61, $ED          /// PUNPCKLWD MM5, MM5
        DB      $0F, $62, $ED          /// PUNPCKLDQ MM5, MM5

        // Load MM4 with 128 to allow for saturated biasing.
        MOV     EAX, 128
        DB      $0F, $6E, $E0          /// MOVD      MM4, EAX
        DB      $0F, $61, $E4          /// PUNPCKLWD MM4, MM4
        DB      $0F, $62, $E4          /// PUNPCKLDQ MM4, MM4

@1:     // The pixel loop calculates an entire pixel in one run.
        // Note: The pixel byte values are expanded into the higher bytes of a word due
        //       to the way unpacking works. We compensate for this with an extra shift.
        DB      $0F, $EF, $C0          /// PXOR      MM0, MM0,   clear source pixel register for unpacking
        DB      $0F, $60, $06          /// PUNPCKLBW MM0, [ESI], unpack source pixel byte values into words
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8,     move higher bytes to lower bytes
        DB      $0F, $EF, $C9          /// PXOR      MM1, MM1,   clear target pixel register for unpacking
        DB      $0F, $60, $0F          /// PUNPCKLBW MM1, [EDI], unpack target pixel byte values into words
        DB      $0F, $6F, $D1          /// MOVQ      MM2, MM1,   make a copy of the shifted values, we need them again
        DB      $0F, $71, $D1, $08     /// PSRLW     MM1, 8,     move higher bytes to lower bytes

        // Load MM7 with the source alpha value (replicate it for every component).
        // Expand it to word size.
        DB      $0F, $6F, $F8          /// MOVQ      MM7, MM0
        DB      $0F, $69, $FF          /// PUNPCKHWD MM7, MM7
        DB      $0F, $6A, $FF          /// PUNPCKHDQ MM7, MM7
        DB      $0F, $D5, $FE          /// PMULLW    MM7, MM6,   source alpha * master alpha
        DB      $0F, $71, $D7, $08     /// PSRLW     MM7, 8,     divide by 256

        // calculation is: target = (alpha * master alpha * (source - target) + 256 * target) / 256
        DB      $0F, $F9, $C1          /// PSUBW     MM0, MM1,   source - target
        DB      $0F, $D5, $C7          /// PMULLW    MM0, MM7,   alpha * (source - target)
        DB      $0F, $FD, $C2          /// PADDW     MM0, MM2,   add target (in shifted form)
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8,     divide by 256

        // Bias is accounted for by conversion of r
ange 0..255 to -128..127,
        // doing a saturated add and convert back to 0..255.
        DB      $0F, $F9, $C4          /// PSUBW     MM0, MM4
        DB      $0F, $ED, $C5          /// PADDSW    MM0, MM5
        DB      $0F, $FD, $C4          /// PADDW     MM0, MM4
        DB      $0F, $67, $C0          /// PACKUSWB  MM0, MM0,   convert words to bytes with saturation
        DB      $0F, $7E, $07          /// MOVD      [EDI], MM0, store the result
@3:
        ADD     ESI, 4
        ADD     EDI, 4
        DEC     ECX
        JNZ     @1
        POP     EDI
        POP     ESI
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AlphaBlendLineMasterAndColor(Destination: Pointer; Count: Integer; ConstantAlpha, Color: Integer);

// Blends a line of Count pixels in Destination against the given color using a constant alpha value.
// The layout of a pixel must be BGRA and Color must be rrggbb00 (as stored by a COLORREF).
// ConstantAlpha must be in the range 0..255.
//
// EAX contains Destination
// EDX contains Count
// ECX contains ConstantAlpha
// Color is passed on the stack

asm
        // The used formula is: target = (alpha * color + (256 - alpha) * target) / 256.
        // alpha * color (factor 1) and 256 - alpha (factor 2) are constant values which can be calculated in advance.
        // The remaining calculation is therefore: target = (F1 + F2 * target) / 256

        // Load MM3 with the constant alpha value (replicate it for every component).
        // Expand it to word size. (Every calculation here works on word sized operands.)
        DB      $0F, $6E, $D9          /// MOVD      MM3, ECX
        DB      $0F, $61, $DB          /// PUNPCKLWD MM3, MM3
        DB      $0F, $62, $DB          /// PUNPCKLDQ MM3, MM3

        // Calculate factor 2.
        MOV     ECX, $100
        DB      $0F, $6E, $D1          /// MOVD      MM2, ECX
        DB      $0F, $61, $D2          /// PUNPCKLWD MM2, MM2
        DB      $0F, $62, $D2          /// PUNPCKLDQ MM2, MM2
        DB      $0F, $F9, $D3          /// PSUBW     MM2, MM3             // MM2 contains now: 255 - alpha = F2

        // Now calculate factor 1. Alpha is still in MM3, but the r and b components of Color must be swapped.
        MOV     ECX, [Color]
        BSWAP   ECX
        ROR     ECX, 8
        DB      $0F, $6E, $C9          /// MOVD      MM1, ECX             // Load the color and convert to word sized values.
        DB      $0F, $EF, $E4          /// PXOR      MM4, MM4
        DB      $0F, $60, $CC          /// PUNPCKLBW MM1, MM4
        DB      $0F, $D5, $CB          /// PMULLW    MM1, MM3             // MM1 contains now: color * alpha = F1

@1:     // The pixel loop calculates an entire pixel in one run.
        DB      $0F, $6E, $00          /// MOVD      MM0, [EAX]
        DB      $0F, $60, $C4          /// PUNPCKLBW MM0, MM4

        DB      $0F, $D5, $C2          /// PMULLW    MM0, MM2             // calculate F1 + F2 * target
        DB      $0F, $FD, $C1          /// PADDW     MM0, MM1
        DB      $0F, $71, $D0, $08     /// PSRLW     MM0, 8               // divide by 256

        DB      $0F, $67, $C0          /// PACKUSWB  MM0, MM0             // convert words to bytes with saturation
        DB      $0F, $7E, $00          /// MOVD      [EAX], MM0           // store the result

        ADD     EAX, 4
        DEC     EDX
        JNZ     @1
end;

//----------------------------------------------------------------------------------------------------------------------

procedure EMMS;

// Reset MMX state to use the FPU for other tasks again.

asm
        DB      $0F, $77               /// EMMS
end;

//----------------------------------------------------------------------------------------------------------------------

function GetBitmapBitsFromDeviceContext(DC: HDC; var Width, Height: Integer): Pointer;

// Helper function used to retrieve the bitmap selected into the give
n device context. If there is a bitmap then
// the function will return a pointer to its bits otherwise nil is returned.
// Additionally the dimensions of the bitmap are returned. 

var
  Bitmap: HBITMAP;
  DIB: TDIBSection;

begin
  Result := nil;
  Width := 0;
  Height := 0;

  Bitmap := GetCurrentObject(DC, OBJ_BITMAP);
  if Bitmap <> 0 then
  begin
    if GetObject(Bitmap, SizeOf(DIB), @DIB) = SizeOf(DIB) then
    begin
      Assert(DIB.dsBm.bmPlanes * DIB.dsBm.bmBitsPixel = 32, 'Alpha blending error: bitmap must use 32 bpp.');
      Result := DIB.dsBm.bmBits;
      Width := DIB.dsBmih.biWidth;
      Height := DIB.dsBmih.biHeight;
    end;
  end;
  Assert(Result <> nil, 'Alpha blending DC error: no bitmap available.');
end;

//----------------------------------------------------------------------------------------------------------------------

function CalculateScanline(Bits: Pointer; Width, Height, Row: Integer): Pointer;

// Helper function to calculate the start address for the given row.

begin
  if Height > 0 then  // bottom-up DIB
    Row := Height - Row - 1;
  // Return DWORD aligned address of the requested scanline.
  Integer(Result) := Integer(Bits) + Row * ((Width * 32 + 31) and not 31) div 8;
end;

//----------------------------------------------------------------------------------------------------------------------

procedure AlphaBlend(Source, Destination: HDC; R: TRect; Target: TPoint; Mode: TBlendMode; ConstantAlpha, Bias: Integer);

// Optimized alpha blend procedure using MMX instructions to perform as quick as possible.
// For this procedure to work properly it is important that both source and target bitmap use the 32 bit color format.
// R describes the source rectangle to work on.
// Target is the place (upper left corner) in the target bitmap where to blend to. Note that source width + X offset
// must be less or equal to the target width. Similar for the height.
// If Mode is bmConstantAlpha then the blend operation uses the given ConstantAlpha value for all pixels.
// If Mode is bmPerPixelAlpha then each pixel is blended using its individual alpha value (the alpha value of the source).
// If Mode is bmMasterAlpha then each pixel is blended using its individual alpha value multiplied by ConstantAlpha.
// If Mode is bmConstantAlphaAndColor then each destination pixel is blended using ConstantAlpha but also a constant
// color which will be obtained from Bias. In this case no offset value is added, otherwise Bias is used as offset.
// Blending of a color into target only (bmConstantAlphaAndColor) ignores Source (the DC) and Target (the position).
// CAUTION: This procedure does not check whether MMX instructions are actually available! Call it only if MMX is really
//          usable.

var
  Y: Integer;
  SourceRun,
  TargetRun: PByte;

  SourceBits,
  DestBits: Pointer;
  SourceWidth,
  SourceHeight,
  DestWidth,
  DestHeight: Integer;
  
begin                              
  if not IsRectEmpty(R) then
  begin
    // Note: it is tempting to optimize the special cases for constant alpha 0 and 255 by just ignoring soure
    //       (alpha = 0) or simply do a blit (alpha = 255). But this does not take the bias into account.
    case Mode of
      bmConstantAlpha:
        begin
          // Get a pointer to the bitmap bits for the source and target device contexts.
          // Note: this supposes that both contexts do actually have bitmaps assigned!
          SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight);
          DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
          if Assigned(SourceBits) and Assigned(DestBits) then
          begin
            for Y := 0 to R.Bottom - R.Top - 1 do
            begin
              SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top);
              Inc(SourceRun, 4 * R.Left);
              TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y);
              Inc(TargetRun, 4 * Target.X);
              AlphaBlendLineConstant(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias);
            end;
          end;
          EMMS;
        end;
      bmPerPixelAlpha:
        begin
          SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight);
          DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
          if Assigned(SourceBits) and Assigned(DestBits) then
          begin
            for Y := 0 to R.Bottom - R.Top - 1 do
            begin
              SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top);
              Inc(SourceRun, 4 * R.Left);
              TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y);
              Inc(TargetRun, 4 * Target.X);
              AlphaBlendLinePerPixel(SourceRun, TargetRun, R.Right - R.Left, Bias);
            end;
          end;
          EMMS;
        end;
      bmMasterAlpha:
        begin
          SourceBits := GetBitmapBitsFromDeviceContext(Source, SourceWidth, SourceHeight);
          DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
          if Assigned(SourceBits) and Assigned(DestBits) then
          begin
            for Y := 0 to R.Bottom - R.Top - 1 do
            begin
              SourceRun := CalculateScanline(SourceBits, SourceWidth, SourceHeight, Y + R.Top);
              Inc(SourceRun, 4 * Target.X);
              TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + Target.Y);
              AlphaBlendLineMaster(SourceRun, TargetRun, R.Right - R.Left, ConstantAlpha, Bias);
            end;
          end;
          EMMS;
        end;
      bmConstantAlphaAndColor:
        begin
          // Source is ignored since there is a constant color value.
          DestBits := GetBitmapBitsFromDeviceContext(Destination, DestWidth, DestHeight);
          if Assigned(DestBits) then
          begin
            for Y := 0 to R.Bottom - R.Top - 1 do
            begin
              TargetRun := CalculateScanline(DestBits, DestWidth, DestHeight, Y + R.Top);
              Inc(TargetRun, 4 * R.Left);
              AlphaBlendLineMasterAndColor(TargetRun, R.Right - R.Left, ConstantAlpha, Bias);
            end;
          end;
          EMMS;
        end;
    end;
  end;
end;

Mike
-- 
www.delphi-gems.com
www.delphi-unicode.net
www.lischke-online.de

From: Tomaz Koritnik  
Subject: Re: Is it possible to get a copy of an Optimized MMX Alpha B
NewsGroup: borland.public.delphi.graphics
Date Posted: 4-Dec-2003 at 9:50:8 PST
Ni

    No problem. Below are two functions. Both work on 32bit images only! One
using global alpha and second using per-pixel alpha value. I have not yet
written the function which uses mask for alpha values.

//**************************************************************************
****
//*
//*    FUNCTION   : CopyScanLineBlendGlobal
//*
//*    PURPOSE    : Combines pixels of source and dest scanline using
//*                 specified blending value. Result is stored in dest.
//*                 BlendValue 0 means source only whereas BlendValue 255
//*                 means dest only.
//*
//*    VARIABLES  : SourceScanLine : source scanline
//*                 DestScanLine   : dest scanline
//*                 PixelCount     : number of pixels
//*                 BlendValue     : blend value
//*
//*    RETURNS    : none
//*
//**************************************************************************
****
procedure CopyScanLineBlendGlobal( SourceScanLine, DestScanLine: Pointer;
  PixelCount: Integer; BlendValue: Byte );
const
  add256: Int64 = $00FF00FF00FF00FF;
var
  alph: Int64;
begin
  // initialize intermediate values
  Alph := Round( BlendValue * 128 / 255 );
  Alph := ( Alph shl 16 ) or Alph;
  Alph := ( Alph shl 32 ) or Alph;
  Alph := ( Alph shl 48 ) or Alph;

  asm
    push esi
    push edi

    emms

    // save intermediate values to registers
    movq mm5, add256 // 256
    movq mm6, alph // alpha
    movq mm7, mm6 // alpha * 2
    psllw mm7, 1
    pxor mm2, mm2 // empty register value

    // source and destination pixels
    mov esi, SourceScanLine // foreground
    mov edi, DestScanLine // background

    mov ecx, PixelCount
@loop:
    mov eax, [esi]
    cmp eax, [edi]
    je @next

    movd mm0, [esi]
    movd mm1, [edi]
    movd [edi], mm0

    // unpack bytes to words
    punpcklbw mm0, mm2
    punpcklbw mm1, mm2

    // calculate resulting color
//  C = ( ( F + 256 - B ) * A / 128 ) + B - ( 2 * A )
// F = foreground, B = background, A = alpha (0..128)

    paddw mm1, mm5 // + 256
    psubw mm1, mm0 // - B
    pmullw mm1, mm6 // * A
    psrlw mm1, 7 // / 128
    paddw mm1, mm0 // + B
    psubw mm1, mm7 // - 2 * A

    // pack words to bytes
    packuswb mm1, mm2
    movd [edi], mm1

@next:
    add esi, 4
    add edi, 4

    dec ecx
    jnz @loop

    emms

    pop edi
    pop esi
  end;
end;

//**************************************************************************
****
//*
//*    FUNCTION   : CopyScanLineBlendPixel
//*
//*    PURPOSE    : Combines pixels of source and dest using blend value
//*                 stored in source pixel
//*
//*    VARIABLES  : SourceScanLine : source scanline
//*                 DestScanLine   : dest scanline
//*                 PixelCount     : number of pixels
//*
//*    RETURNS    : none
//*
//**************************************************************************
****
procedure CopyScanLineBlendPixel( SourceScanLine, DestScanLine: Pointer;
  PixelCount: Integer );
const
  add256: Int64 = $0100010001000100;
var
  x: Int64;
begin
  if PixelCount > 0 then
  begin
    x := $00ff000000ff0000;
    asm
      push esi
      push edi

      emms

      // save intermediate values to registers
      pxor mm2, mm2 // empty register value
      movq mm7, add256
      movq mm6, x

      // X = F + 256
      //  C = ( ( X - B ) * A / 128 ) + B - 2A

      mov esi, SourceScanLine // source
      mov edi, DestScanLine // dest

      mov ecx, PixelCount
    @loop:

      movd mm1, [esi] // source : foreground
      movd mm0, [edi] // dest : background

      // unpack bytes to words
      punpcklbw mm1, mm2 // foreground
      punpcklbw mm0, mm2 // background

      // extract alpha value from foreground color
      movq mm3, mm1
      punpckhdq mm3, mm1
      pand mm3, mm6
      packuswb mm3, mm3
      psrlw mm3, 9

      // add 256 to foreground
      paddw mm1, mm7

      psubw mm1, mm0 // - B
      pmullw mm1, m
m3 // * A
      psrlw mm1, 7 // / 128
      paddw mm1, mm0 // + B

      psllw mm3, 1
      psubw mm1, mm3 // - 2A

      // pack words to bytes
      packuswb mm1, mm2
      movd [edi], mm1

      add esi, 4
      add edi, 4

      dec ecx
      jnz @loop

      emms

      pop edi
      pop esi
    end;
  end;
end;

regards,
Tomaz


 wrote in message
news:3fce62ba$2@newsgroups.borland.com...
> Much appreciated regarding the feedback on my Delphi Scanline version of
> an alpha blend routine.
>
> Would it be possible for me to get a copy of your MMX optimized
> Alpha Blend, I would really appreciate it ...
>
> Thank You,
>
> Henry Eckstein
> email it to henry@comwave.com
>
>
>
> Tomaz Koritnik wrote:
> > Hi
> >
> >     Your code has several disadvantages over MMX version of blending
> > algorythm.
> >         - MMX version is much faster simply because it speeds up basic
math
> > functions like multiplication and all the rest
> >         - MMX is present from P1 166 on and there are probably no
computers
> > left that don't have MMX so why bother with non-MMX versions :).
> >         - You are using TBitmap.Scanline which is not as fast as you
think.
> > TBitmap does some extra operations when you use Scanline property which
is
> > slower. Best thing is to cache scanline pointers on first usage and then
use
> > lookup table instead of TBitmap.Scanline. Believe me, it's much faster.
> >
> > I also don't think of MMX as hardware acceleration. It basically just a
new
> > low-level (ok, not so new) instruction set which is based on SIMD model.
> >
> > I tested my MMX blending function which is not as yours but uses same
> > blending factor for all pixels. I combines two 1600x1200 bitmaps and
time
> > needed to blend two images is 25ms. I have AthlonXP 2400+ with DDR RAM.
I
> > also tested same program using same images on old P1-133 or 166 computer
(I
> > don't know exactly but looks like it has MMX) and blend speed was 170ms.
> >
> > regards,
> > Tomaz
> >
> >
> > "Henry A. Eckstein"  wrote in message
> > news:3fcd327b$1@newsgroups.borland.com...
> >
> >>Enjoy this FAST alpha blend routine which uses optimized
> >>pixel by pixel SCANLINE accesses...
> >>
> >>I do have a test images and code snippet archive
> >>with 1600 by 1200 pixel test images for you
> >>to test this system with. It's a 6.7 megabyte file.
> >>Email me at henry@comwave.com and i'll send it to you.
> >>It is too big to post in this forum.
> >>
> >>If the code looks a little messy in this post, cut & paste the code
> >>to your Delphi Editor and get rid of the 80 character line wrap.
> >>Set line wrap to 255 characters or more.
> >>
> >>This routine will work on Delphi Versions 5, 6 & 7
> >>I haven't tested it with Delphi Version 2, 3 or 4.
> >>
> >>The image files currently MUST be of Windows BMP format
> >>but some enterprising programmer can change teh code to work with
> >>JPEG or other file formats.
> >>
> >>{
> >>ALPHA BLEND AND SUPPORT ROUTINES DESIGNED AND CODED BY:
> >>==============================================================
> >>Henry A. Eckstein
> >>Triad Communications Ltd.
> >>2751 Oxford Street
> >>Vancouver, British Columbia, Canada
> >>V5K 1N5
> >>
> >>Telephone: 604-253-3990
> >>Fax:       604-253-0770
> >>
> >>Toll Free: 1-800-600-9762 (1-800-OZZY-ROC)
> >>
> >>Website: www.triadcommunications.ca
> >>
> >>Email: henry@comwave.com
> >>
> >>Copyright (C) 2003 - All Rights Reserved.
> >>
> >>These Code Snippets are hereby donated to the
> >>PUBLIC DOMAIN for use in any which way you desire.
> >>
> >>Please include an acknowledgement of my authorship
> >>if you do happen to use the Alpha Blend code.
> >>
> >>
> >>THANK YOU and GOOD CODING
> >>
> >>P.S. If you like skiing and snowboarding
> >>      goto the website: www.whistler-blackcomb.com
> >>      to see the two biggest ski mountains
> >>      in North America, 160 km north of
> >>      Vancouver, BC, Canada
> >
>
> >>P.S.2. The test photos are of my hometown in Vancouver, BC, Canada
> >>}
> >>
> >>
> >>uses
> >>   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
> >>Forms,
> >>   Dialogs, StdCtrls, MPlayer;
> >>
> >>
> >>Type
> >>   {$R-} // Turn range checking OFF so that arrays can be accessed
> >>outside of their range limits
> >>
> >>   // Assumes a 32 bit TBitmap pixel type  i.e. TBitmap.PixelFormat :=
> >>pf32bit;
> >>   RGB_Pixel = Packed Record  // Variant record allow two separate
> >>fields to occupy the same memory space
> >>                 Case LongWord of
> >>                   0 : ( Blue, Green, Red, Alpha : Byte );  // This is
> >>Windows specific as an RGB pixel has its colour channels ordered in a
> >>specific way i.e. ABGR rather than the usual RGBA
> >>                   1 : ( All_Channels : LongWord );         // The above
> >>colour components can be accessed as a single unsigned 32-bit integer
> >>when All_Channels field is accessed.
> >>                 End;                                       // This is
> >>useful for performing AND, XOR, OR and other bitwise manipulations on an
> >>entire pixel
> >>
> >>   // A pointer to the above pixel record type
> >>   RGB_Pixel_Pointer = ^RGB_Pixel;
> >>
> >>   {
> >>   An array of the above RGB pixel type which makes it easy to access a
> >>region
> >>   of memory containing a series of RGB pixels. This is used by many
> >>graphics
> >>   filters or rendering routines.
> >>
> >>   Packed Array forces the individual fields to be aligned close
> >>together in memory.
> >>   Some operating systems or compilers, if not declared as packed, would
> >>align fields on 16-bit or 32-bit boundaries
> >>   depending upon the size of an individual field.  The Delphi specific
> >>identifier "Packed"
> >>   gives us the ability to force record fields to be close together with
> >>no free byte space in between.
> >>
> >>   This array has been declared with 0..1 range to save heap and stack
> >>memory space.
> >>   But if you turn range checking off i.e. $R- then the array can be
> >>accessed
> >>   outside of the declared boundaries.
> >>   }
> >>   Array_of_RGB_Pixels = Packed Array[ 0..1 ] of RGB_Pixel;
> >>
> >>   {
> >>   A pointer to an Array of RGB pixels which makes it easy to access a
> >>region
> >>   of memory containing a series of RGB pixels. This is used by many
> >>graphics
> >>   filters or rendering routines that need to facilitate pointer
> >
> > arithmetic
> >
> >>   or memory address manipulation on a whole series of RGB pixels.
> >>   }
> >>   Pointer_to_Array_of_RGB_Pixels = ^Array_of_RGB_Pixels;
> >>
> >>
> >>{
> >>For speed reasons make sure your TBitmap.PixelFormat := pf32bit is set,
> >>as this routine assumes all pixels in the Tbitmap parameters are bitmaps
> >>composed of 8-bit Red, Green, Blue, Alpha channels.
> >>Accessing pixels as 32 bit integers really speeds things up.
> >>
> >>Using scanline is one of the fastest ways in software to access the
> >>TBitmap pixels without using assembler or MMX instructions.
> >>
> >>Alpha Blending two 1600 by 1200 pixel 32 bit bitmaps using a 1600 by
> >>1200 pixel 32 bit greyscale alpha channel bitamp
> >>on a 450 MHZ AMD K3 will be finshed in 1.5 seconds (1576 milliseconds)
> >>On other Pentium 500 MHZ systems I have been able to get alpha blend
> >>times downto
> >>600 milliseconds on 1600 by 1200 pixel bitmaps
> >>(my graphics card is a Matrox G550 with 32 megs of Video RAM and I have
> >>an AMD K3 running at 450 MHZ and 378 megs of system RAM)
> >>
> >>Please NOTE that the Foreground and Alpha Channel bitmaps
> >>MUST be of the same width and height so that the alpha blend operation
> >>can correctly combine the foreground with the background
> >>}
> >>Procedure Alpha_Blend_Bitmap_Over_Background( Starting_X_Coordinate,
> >>Starting_Y_Coordinate: Integer; Var Foreground_Bitmap,
> >>Background_Bitmap, Alpha_Channel_Bitmap: TBitmap );
> >>
> >>   Var
> >>     x, y,
> >>
> >>     Last_X,
> >
>     Last_Y,
> >>     Current_X_Coordinate : Large_Integer;
> >>
> >>     Line_of_Foreground_Pixels,
> >>     Line_of_Background_Pixels,
> >>     Line_of_Alpha_Channel_Pixels : Pointer_to_Array_of_RGB_Pixels;
> >>
> >>     Foreground_Pixel,
> >>     Background_Pixel,
> >>     Alpha_Channel_Pixel : RGB_Pixel;
> >>
> >>   Begin
> >>     Try
> >>       // if alpha channel does not exist then just try and draw the
> >>       // foreground bitmap onto the background using the Delphi Draw()
> >>command.
> >>       if Alpha_Channel_Bitmap = NIL then
> >>         Background_Bitmap.Canvas.Draw( Starting_X_Coordinate,
> >>Starting_Y_Coordinate, Foreground_Bitmap );
> >>
> >>       with Foreground_Bitmap do
> >>         Begin
> >>           Last_X := Foreground_Bitmap.Width - 1;
> >>           Last_Y := Foreground_Bitmap.Height - 1;
> >>
> >>           for y := ZERO to Last_Y do
> >>             Begin
> >>               // get the pointer to each row of pixel on all the
bitmaps
> >>               Line_of_Foreground_Pixels    :=
> >>Foreground_Bitmap.ScanLine[ y ];
> >>               Line_of_Background_Pixels    :=
> >>Background_Bitmap.ScanLine[ Starting_Y_Coordinate + y ];
> >>               Line_of_Alpha_Channel_Pixels :=
> >>Alpha_Channel_Bitmap.ScanLine[ y ];
> >>
> >>               for x := ZERO to Last_X do
> >>                 Begin
> >>                   Current_X_Coordinate := Starting_X_Coordinate + x;
> >>
> >>                   // retrieve the relevant pixel on each row of pixels
> >>                   Foreground_Pixel    := Line_of_Foreground_Pixels[
x ];
> >>                   Background_Pixel    := Line_of_Background_Pixels[
> >>Current_X_Coordinate ];
> >>                   Alpha_Channel_Pixel := Line_of_Alpha_Channel_Pixels[
> >
> > x ];
> >
> >>                   // Perform Alpha blend oepration
> >>                   Line_of_Background_Pixels[ Current_X_Coordinate
> >>].Blue  := ( ( Alpha_Channel_Pixel.Blue  * ( ForeGround_Pixel.Blue  -
> >>Background_Pixel.Blue  ) ) SHR 8 ) + Background_Pixel.Blue;
> >>                   Line_of_Background_Pixels[ Current_X_Coordinate
> >>].Green := ( ( Alpha_Channel_Pixel.Green * ( ForeGround_Pixel.Green -
> >>Background_Pixel.Green ) ) SHR 8 ) + Background_Pixel.Green;
> >>                   Line_of_Background_Pixels[ Current_X_Coordinate ].Red
> >>   := ( ( Alpha_Channel_Pixel.Red   * ( ForeGround_Pixel.Red   -
> >>Background_Pixel.Red   ) ) SHR 8 ) + Background_Pixel.Red;
> >>
> >>                   // The Alpha channel of a four-colour-channel RGB
> >>pixel can be safely ignored since we don't need to access it
> >>                 End;
> >>             End;
> >>         End;
> >>     Except
> >>       // on any errors (i.e. if Foreground or Background bitmaps are
> >>invalid) display this message
> >>       ShowMessage( 'ERROR: Unable to Draw Foreground Bitmap onto
> >>Background Bitmap using an Alpha Channel' );
> >>     End;
> >>   End;
> >>
> >>{
> >>This routine is used for timing operations so that we can figure
> >>out how many milliseconds a specific operation takes.
> >>It's accurate down to the millisecond level
> >>
> >>The difference between two calls of this routine will give us the
> >>number of milliseconds an operation takes.
> >>}
> >>Function Get_Clock_Count_in_Milliseconds: Floating_Point;
> >>
> >>   Var
> >>     Current_Date_And_Time: TDateTime; // Delphi specific Date & Time
> >>Record which is a 64 bit floating point number
> >>
> >>     Number_of_Milliseconds_Since_Midnight : Double;
> >>
> >>     Current_Hour,
> >>     Current_Minute,
> >>     Current_Second,
> >>     Current_MilliSecond: Word;
> >>
> >>   Begin
> >>     Try
> >>       Current_Date_And_Time := Now;   // NOW is a Delphi specific
> >>function part of the SysUtils unit
> >>                                       // Get the current date and time
> >>in Floating Point format
> >>
> >>       { DecodeTime is a SysUtils unit function - Get the component
> >>parts of the date and time and pu
t each element into the desired
> >
> > variables }
> >
> >>       DecodeTime( Current_Date_And_Time, Current_Hour, Current_Minute,
> >>Current_Second, Current_MilliSecond );
> >>
> >>       Number_of_Milliseconds_Since_Midnight := ( Current_Hour *
3600000 )
> >
> > +
> >
> >>                                                ( Current_Minute *
60000 )
> >
> > +
> >
> >>                                                ( Current_Second *
1000 )
> >
> > +
> >
> >>                                                  Current_MilliSecond;
> >>     Except
> >>       Showmessage( 'Unable to obtain the system clock count in
> >>milliseconds since midnight' );
> >>       Get_Clock_Count_in_Milliseconds := 0.0;
> >>       Exit;
> >>     End;
> >>                                           { Number of days past since
> >>December 30, 1899 - Number of Milliseconds in a day }
> >>     Get_Clock_Count_in_Milliseconds := ( Trunc( Current_Date_And_Time )
> >>* 86400000 ) + Number_of_Milliseconds_Since_Midnight;
> >>   End;
> >>
> >>{
> >>A general example procedure which a user can call to see how long an
> >>alpha blend operation takes
> >>It will loads 3 standard Windows Bitmaps and alpha blend them together.
> >>one of the bitmaps MUST be a greyscale image which called the Alpha
> >
> > Channel
> >
> >>An Alpha channel can have feathered or soft edges and the closer
> >>to 100% FULL WHITE (i.e. R:255, G:255, B:255) then the more that the
> >>FOREGROUND bitmap will show through.
> >>
> >>The closer to ZERO percent Black (i.e. R:0, G:0, B:0) the more the
> >>BACKGROUND will show through.
> >>
> >>Any mix of grey in between will give you a blend where you see BOTH
> >
> > images.
> >
> >>(i.e. R:127, G:127, B:127) is a 50% blend
> >>
> >>Setting the individual colour channels of an Alpha pixel allows you to
> >>target
> >>the specific colour channel of an Alpha Blend   i.e. (i.e. R:0, G:255,
> >
> > B:0)
> >
> >>would give have ONLY the green channel of the foreground show through.
> >>
> >>The alpha channel bitmap can be filled with a single solid colour
> >>or can contain any shape and any degree of soft or hard eges on those
> >>shapes.
> >>
> >>Example of use:  Alpha_Blend_Example( TForm1.Canvas );
> >>}
> >>Procedure Alpha_Blend_Example( Canvas_of_Form: TCanvas );  //
> >>Canvas_of_Form is usually the canvas of the TForm where you wish to draw
> >>the final output
> >>
> >>   Var
> >>     Foreground_Image,
> >>     Background_Image,
> >>     Alpha_Channel_Image : TBitmap;  // If you get a stack overflow or
> >>other type of error increase the stack size to the following
> >>                                     // values using the $M compiler
> >>directive {$M 16384,18000000} This set maximum stack to 18 megabytes
> >>                                     // You can also move these 3 bitmap
> >>variables out of this procedure declaration into Heap memory
> >>                                     // You will need to do this if your
> >>images are 1600 by 1200 pixels or larger
> >>
> >>     Elapsed_Time_Message : String;
> >>
> >>     Start_Time,
> >>     Finish_Time,
> >>     Total_Elapsed_Time : Extended;  // Holds how many milliseconds
> >>since midnight the system clock has been run - used for timing purposes
> >>
> >>   Begin
> >>     Foreground_Image    := TBitmap.Create;
> >>     Background_Image    := TBitmap.Create;
> >>     Alpha_Channel_Image := TBitmap.Create;
> >>
> >>     // load bitmaps from files
> >>     Foreground_Image.LoadFromFile    ( 'Foreground Image.bmp'    );
> >>     Background_Image.LoadFromFile    ( 'Background Image.bmp'    );
> >>     Alpha_Channel_Image.LoadFromFile ( 'Alpha Channel Image.bmp' ); //
> >>if you change the name of this file to
> >>                                                                     //
> >>'Alpha Channel Image 50 Percent.bmp' then
> >>                                                                     //
> >>you'll see a 50% blend of both foreground and background
> >>                                                                     //
> >>also try the file 'Alpha Channel Image Left to Right Ramp.bmp'
> >>
> >>     // set bitmaps to 32 bit pixels per pixel with full 8-bits of Red,
> >>Green Blue and Alpha channel
> >>     Foreground_Image.PixelFormat    := pf32bit;
> >>     Background_Image.PixelFormat    := pf32bit;
> >>     Alpha_Channel_Image.PixelFormat := pf32bit;
> >>
> >>     Canvas_of_Form.Draw( 0, 0, Alpha_Channel_Image );
> >>     Showmessage( 'Here is the Alpha Channel Image which will be used in
> >>the Alpha Blend...' );
> >>
> >>     Canvas_of_Form.Draw( 0, 0, Foreground_Image );
> >>     Showmessage( 'Here is the Foreground Image which will be used in
> >>the Alpha Blend...' );
> >>
> >>     Canvas_of_Form.Draw( 0, 0, Background_Image );
> >>     Showmessage( 'Here is the Background Image which will be used in
> >>the Alpha Blend...' + #10 + #13 +
> >>                  'Click on OK to begin Alpha Blend Test...' );
> >>
> >>
> >>     // ******************* Clock 32 bit integer array access time
> >>     Start_Time := Get_Clock_Count_in_Milliseconds;
> >>
> >>     Alpha_Blend_Bitmap_Over_Background( ZERO, ZERO, Foreground_Image,
> >>Background_Image, Alpha_Channel_Image );
> >>
> >>     Finish_Time := Get_Clock_Count_in_Milliseconds;
> >>
> >>     Total_Elapsed_Time := Finish_Time - Start_Time;
> >>
> >>     Elapsed_Time_Message := 'Clocking Total Pixel by Pixel Alpha Blend:
> >>' + FloatToStr( Total_Elapsed_Time ) + ' milliseconds.' + #10 + #13 +
> >>                             'Note the soft edges on the images
> >>boundaries.';
> >>
> >>     Canvas_of_Form.Draw( 0, 0, Background_Image );
> >>
> >>     Showmessage( Elapsed_Time_Message );
> >>
> >>     Foreground_Image.Free;
> >>     Background_Image.Free;
> >>     Alpha_Channel_Image.Free;
> >>   End;
> >>
> >>
> >
> >
> >
>



From: Henry A. Eckstein  
Subject: Re: FAST Alpha Blend Routine - Borland says don't do pointer
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 16:41:33 PST
This article was originally published at this delphi code website:

    http://www.efg2.com/Lab/ImageProcessing/Scanline.htm

And details possible optimizations and hazards when using Scanline
to access pixels via pointer arithmetic.

However, this article DOES NOT discuss the hazards of multi-threaded
processes that access the same Tbitmaps or TCanvas as another process.
I have personally found that the Canvas.Lock facility does NOT always
work because many coders are bypassing the built-in safeguards of
the TCanvas routines and directly accessing the image pixels via 
assembler or
esoteric pointers. Although MUCH faster, it's a dangerous way to go when
dealing with multi-threaded code. Thus if I mix and match components 
from different coders I cannot be sure they are using thread-safe code
in their graphics routines.

The article below is direct from the horse's mouth at Borland...

Minimizing Scanline Accesses

Several times in the various Delphi newsgroups someone suggests that
the number of Scanline accesses should be minimized as an optimization
technique since considerable overhead is thought to exist with each call.
For example, instead of accessing Scanline once per row
(as in many of the examples here), with some extra work Scanline can
be accessed only twice at the beginning of the loop stepping through the 
rows.
  A "pointer" can be incremented using integer arithmetic instead of a 
Scanline
call for each row.


.....On the question of performance impact of calling Scanlines[] a lot:
Yes, there is a potential performance hit.  The DIBSection's memory buffer
is not guaranteed to be coherent (reflect the most recent GDI operations)
unless you call GDIFlush before accessing the pointer.  The ScanLines[] 
property
has to call GDIFlush to ensure the buffer contents are in sync.  If the 
bitmap
has been modified by GDI calls, then the call to GDIFlush could block 
waiting
for the GDI pipeline to empty.   If no modifications are still pending, 
GDIFlush
should return immediately, but there will always be the overhead of 
making an additional call.

Additionally, TBitmap.GetScanlines() calls TBitmap.Changing, which may have
to create a completely new, unshared copy of the image if the bitmap is 
shared.
That will completely blow out performance on the first touch of Scanlines,
and it chips away a little even when the bitmap isn't shared.

The technique shown in Listing 13 for minimizing time lost to ScanLines[]
was developed by me while writing the TJPEGImage class.  There is more at
work here than meets the eye, though, which should be mentioned with that
listing.   The easiest way to eliminate calls to ScanLines[] is to simply
do your own pointer arithmetic to advance the start-of-scanline buffer
pointer to the next scanline.


***** VERY VERY IMPORTANT ******
There are two hazards in doing that:
=====================================

1) correctly dealing with the DWORD alignment of scan lines, and

2) the  physical layout of scanlines in the memory buffer.

DIBs can be oriented as "top-down", where the first row of pixels
in the bitmap reside in the first bytes of memory in the buffer,
or as "bottom-up", where the first row of pixels reside in the last
bytes of memory and grow upward in memory.  Oddly enough, "bottom-up"
is the more common DIB orientation, possibly due to BMP's OS/2 origins.

The technique of subtracting the address of Scanline[0] from the
address of Scanline[1] solves both problems very nicely.  It gives you
the distance between scanlines including DWORD padding, if any, and
the sign of the delta implicitly indicates the DIB memory orientation.
This eliminates the need for performance-robbing conditional statements
in the critical pointer advancement loop.   Just increment the pointer
by that delta, and it will put you on the next scanline, be it above
or below the current address.

Comments from Nono40 (Nov 2003):

I have one note about the "Optimization" (Accessing ScanLines only twice 
for a bitmap ).
We usually use (0,0) at the top left corner of a bitmap. But in that 
case points like
( Cos(t) , Sin(t) ) will go clockwise.

If we want to use a "normal" drawing with ( Cos(t) , Sin(t) ) going 
anti-clockwise
( and (0,0) pixel at bottom left ). There is no need to change "y" by 
"Height-Y-1"
each time. There is only to change line 0 pointer and Delta value 
between two lines:

PtrBmp:=BitMap.ScanLine[BitMap.Height-1]; 
Delta:=Integer(BitMap.ScanLine[BitMap.Height-2])-Integer(PtrBmp);

Of course, after the "Y" signification is not the same in ScanLine 
access and Canvas access.
But it's not a problem if we use only ScanLine access.

Just a personnal point of view: I prefere pf32bit, as i use assembly 
language with ScanLine.
It's easier to access pixels with a complete Integer, even if 25% of 
space is lost.
With ScanLine if one register olds the ScanLine pointer of one Line ( ex 
EDI ),
and another register olds "X" value ( ex EAX). Read/Write of the pixel 
is very
simple using bulit-un index multiplier:

MOV Dword Ptr [EDI+EAX*4],clYellow




RandomAccess wrote:
>  wrote in message
> news:3fcdb434$1@newsgroups.borland.com...
> 
> 
> 
>>...but Borland said not to do that because the bitmaps may be accessed
>>by other routines at the same time as my blend routine. (even if
> 
> LOCKED)....
> 
> 
> I'd very much like to know exactly where you got this information as I've
> been having some problems with locked Bitmap canvases accessed in background
> threads.  My code runs very quickly processing many bitmaps in several
> threads simultaneously and occasionally I get errors that totally mystify
> me - and the exceptions are always coming out of code working directly on
> the bitmaps and/or their canvas and there is never anything wrong with the
> input into these routines.  It's just like the canvas handle gets toasted or
> something like that - and for no apparent reason.
> 
> Best Regards
> 
> 

-- 

==============================================================
Henry A. Eckstein
Triad Communications Ltd.
2751 Oxford Street
Vancouver, British Columbia, Canada
V5K 1N5

Telephone: 604-253-3990
Fax:       604-253-0770

Toll Free: 1-800-600-9762 (1-800-OZZY-ROC)

Website: www.triadcommunications.ca

Email: henry@comwave.com

General Inquiries: triadc@comwave.com

We Produce Training, Promotional and Information Videos
for distribution on Tape, DVD or via Internet.

We integrated high quality digital video into
custom designed Computer-based Training Systems with
embedded test and evaluation components.

We are an authorized distributor of Maxell brand
professional blank media.

We Offer Duplication, Conversion services for
NTSC, PAL & SECAM format videos and audio programs.

We Digitize and Compress to MPEG-1, MPEG-2, Quicktime, AVI,
Windows Media, Real Video, Custom Streaming formats and Audio MP3
and then save onto DVD, CD-Recordable and other digital media.

We Transfer 8mm and 16mm film to Video, DVD and CD-ROM

We provide Legal Video Services including the video recording
of depositions, day-in-the-life, accident reconstructions.

We are a full-service, one-stop video and multimedia production house 
that can provide you
with initial scripts to complete video production services, high quality 
post-production
and computer graphics to final packaged copies and all steps in between.

Established 1973.




From: RandomAccess  
Subject: Re: Here is a FAST Alpha Blend Routine - Borland said don't
NewsGroup: borland.public.delphi.graphics
Date Posted: 4-Dec-2003 at 0:52:19 PST
 wrote in message
news:3fcdb434$1@newsgroups.borland.com...


> ...but Borland said not to do that because the bitmaps may be accessed
> by other routines at the same time as my blend routine. (even if
LOCKED)....


I'd very much like to know exactly where you got this information as I've
been having some problems with locked Bitmap canvases accessed in background
threads.  My code runs very quickly processing many bitmaps in several
threads simultaneously and occasionally I get errors that totally mystify
me - and the exceptions are always coming out of code working directly on
the bitmaps and/or their canvas and there is never anything wrong with the
input into these routines.  It's just like the canvas handle gets toasted or
something like that - and for no apparent reason.

Best Regards



From: Eric Grange  
Subject: Re: Here is a FAST Alpha Blend Routine - Borland said don't
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 11:19:55 PST
> [...] because the bitmaps may be accessed
> by other routines at the same time as my blend routine.

If that's the case, use of ScanLine is also invalid anyway
(the pointer could become invalid just as well while altering
the line's data), so that point is moot.
Invoking ScanLine for each line won't shield you any better,
and funnily enough, it actually makes you more vulnerable, since 
it will then take more time to blend, you're increasing the 
probability of a Bad Thing (TM) happening during the blend.

> and because Bitmaps may have pixel coordinate 0,0 in lower left corner
> rather than upper left corner 

That's why the offset is computed from the first two scanlines.

Eric

From: Tomaz Koritnik  
Subject: Re: Here is a FAST Alpha Blend Routine
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 10:37:2 PST
Hi

    Your code has several disadvantages over MMX version of blending
algorythm.
        - MMX version is much faster simply because it speeds up basic math
functions like multiplication and all the rest
        - MMX is present from P1 166 on and there are probably no computers
left that don't have MMX so why bother with non-MMX versions :).
        - You are using TBitmap.Scanline which is not as fast as you think.
TBitmap does some extra operations when you use Scanline property which is
slower. Best thing is to cache scanline pointers on first usage and then use
lookup table instead of TBitmap.Scanline. Believe me, it's much faster.

I also don't think of MMX as hardware acceleration. It basically just a new
low-level (ok, not so new) instruction set which is based on SIMD model.

I tested my MMX blending function which is not as yours but uses same
blending factor for all pixels. I combines two 1600x1200 bitmaps and time
needed to blend two images is 25ms. I have AthlonXP 2400+ with DDR RAM. I
also tested same program using same images on old P1-133 or 166 computer (I
don't know exactly but looks like it has MMX) and blend speed was 170ms.

regards,
Tomaz


"Henry A. Eckstein"  wrote in message
news:3fcd327b$1@newsgroups.borland.com...
>
> Enjoy this FAST alpha blend routine which uses optimized
> pixel by pixel SCANLINE accesses...
>
> I do have a test images and code snippet archive
> with 1600 by 1200 pixel test images for you
> to test this system with. It's a 6.7 megabyte file.
> Email me at henry@comwave.com and i'll send it to you.
> It is too big to post in this forum.
>
> If the code looks a little messy in this post, cut & paste the code
> to your Delphi Editor and get rid of the 80 character line wrap.
> Set line wrap to 255 characters or more.
>
> This routine will work on Delphi Versions 5, 6 & 7
> I haven't tested it with Delphi Version 2, 3 or 4.
>
> The image files currently MUST be of Windows BMP format
> but some enterprising programmer can change teh code to work with
> JPEG or other file formats.
>
> {
> ALPHA BLEND AND SUPPORT ROUTINES DESIGNED AND CODED BY:
> ==============================================================
> Henry A. Eckstein
> Triad Communications Ltd.
> 2751 Oxford Street
> Vancouver, British Columbia, Canada
> V5K 1N5
>
> Telephone: 604-253-3990
> Fax:       604-253-0770
>
> Toll Free: 1-800-600-9762 (1-800-OZZY-ROC)
>
> Website: www.triadcommunications.ca
>
> Email: henry@comwave.com
>
> Copyright (C) 2003 - All Rights Reserved.
>
> These Code Snippets are hereby donated to the
> PUBLIC DOMAIN for use in any which way you desire.
>
> Please include an acknowledgement of my authorship
> if you do happen to use the Alpha Blend code.
>
>
> THANK YOU and GOOD CODING
>
> P.S. If you like skiing and snowboarding
>       goto the website: www.whistler-blackcomb.com
>       to see the two biggest ski mountains
>       in North America, 160 km north of
>       Vancouver, BC, Canada
>
> P.S.2. The test photos are of my hometown in Vancouver, BC, Canada
> }
>
>
> uses
>    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
> Forms,
>    Dialogs, StdCtrls, MPlayer;
>
>
> Type
>    {$R-} // Turn range checking OFF so that arrays can be accessed
> outside of their range limits
>
>    // Assumes a 32 bit TBitmap pixel type  i.e. TBitmap.PixelFormat :=
> pf32bit;
>    RGB_Pixel = Packed Record  // Variant record allow two separate
> fields to occupy the same memory space
>                  Case LongWord of
>                    0 : ( Blue, Green, Red, Alpha : Byte );  // This is
> Windows specific as an RGB pixel has its colour channels ordered in a
> specific way i.e. ABGR rather than the usual RGBA
>                    1 : ( All_Channels : LongWord );         // The above
> colour components can be accessed as a single unsigned 32-bit integer
> when All_Channels field is accessed.
>                  End;                                       // 
This is
> useful for performing AND, XOR, OR and other bitwise manipulations on an
> entire pixel
>
>    // A pointer to the above pixel record type
>    RGB_Pixel_Pointer = ^RGB_Pixel;
>
>    {
>    An array of the above RGB pixel type which makes it easy to access a
> region
>    of memory containing a series of RGB pixels. This is used by many
> graphics
>    filters or rendering routines.
>
>    Packed Array forces the individual fields to be aligned close
> together in memory.
>    Some operating systems or compilers, if not declared as packed, would
> align fields on 16-bit or 32-bit boundaries
>    depending upon the size of an individual field.  The Delphi specific
> identifier "Packed"
>    gives us the ability to force record fields to be close together with
> no free byte space in between.
>
>    This array has been declared with 0..1 range to save heap and stack
> memory space.
>    But if you turn range checking off i.e. $R- then the array can be
> accessed
>    outside of the declared boundaries.
>    }
>    Array_of_RGB_Pixels = Packed Array[ 0..1 ] of RGB_Pixel;
>
>    {
>    A pointer to an Array of RGB pixels which makes it easy to access a
> region
>    of memory containing a series of RGB pixels. This is used by many
> graphics
>    filters or rendering routines that need to facilitate pointer
arithmetic
>    or memory address manipulation on a whole series of RGB pixels.
>    }
>    Pointer_to_Array_of_RGB_Pixels = ^Array_of_RGB_Pixels;
>
>
> {
> For speed reasons make sure your TBitmap.PixelFormat := pf32bit is set,
> as this routine assumes all pixels in the Tbitmap parameters are bitmaps
> composed of 8-bit Red, Green, Blue, Alpha channels.
> Accessing pixels as 32 bit integers really speeds things up.
>
> Using scanline is one of the fastest ways in software to access the
> TBitmap pixels without using assembler or MMX instructions.
>
> Alpha Blending two 1600 by 1200 pixel 32 bit bitmaps using a 1600 by
> 1200 pixel 32 bit greyscale alpha channel bitamp
> on a 450 MHZ AMD K3 will be finshed in 1.5 seconds (1576 milliseconds)
> On other Pentium 500 MHZ systems I have been able to get alpha blend
> times downto
> 600 milliseconds on 1600 by 1200 pixel bitmaps
> (my graphics card is a Matrox G550 with 32 megs of Video RAM and I have
> an AMD K3 running at 450 MHZ and 378 megs of system RAM)
>
> Please NOTE that the Foreground and Alpha Channel bitmaps
> MUST be of the same width and height so that the alpha blend operation
> can correctly combine the foreground with the background
> }
> Procedure Alpha_Blend_Bitmap_Over_Background( Starting_X_Coordinate,
> Starting_Y_Coordinate: Integer; Var Foreground_Bitmap,
> Background_Bitmap, Alpha_Channel_Bitmap: TBitmap );
>
>    Var
>      x, y,
>
>      Last_X,
>      Last_Y,
>      Current_X_Coordinate : Large_Integer;
>
>      Line_of_Foreground_Pixels,
>      Line_of_Background_Pixels,
>      Line_of_Alpha_Channel_Pixels : Pointer_to_Array_of_RGB_Pixels;
>
>      Foreground_Pixel,
>      Background_Pixel,
>      Alpha_Channel_Pixel : RGB_Pixel;
>
>    Begin
>      Try
>        // if alpha channel does not exist then just try and draw the
>        // foreground bitmap onto the background using the Delphi Draw()
> command.
>        if Alpha_Channel_Bitmap = NIL then
>          Background_Bitmap.Canvas.Draw( Starting_X_Coordinate,
> Starting_Y_Coordinate, Foreground_Bitmap );
>
>        with Foreground_Bitmap do
>          Begin
>            Last_X := Foreground_Bitmap.Width - 1;
>            Last_Y := Foreground_Bitmap.Height - 1;
>
>            for y := ZERO to Last_Y do
>              Begin
>                // get the pointer to each row of pixel on all the bitmaps
>                Line_of_Foreground_Pixels    :=
> Foreground_Bitmap.ScanLine[ y ];
>                Line_of_Background_Pixels    :=
> Background_Bitmap.ScanLine[ Starting_Y_Coordinate + y ];
>                Line_of_Alpha_Channel_Pixels :=
> Alpha_Channel_Bitmap.ScanLine[ y ];
>
>     
           for x := ZERO to Last_X do
>                  Begin
>                    Current_X_Coordinate := Starting_X_Coordinate + x;
>
>                    // retrieve the relevant pixel on each row of pixels
>                    Foreground_Pixel    := Line_of_Foreground_Pixels[ x ];
>                    Background_Pixel    := Line_of_Background_Pixels[
> Current_X_Coordinate ];
>                    Alpha_Channel_Pixel := Line_of_Alpha_Channel_Pixels[
x ];
>
>                    // Perform Alpha blend oepration
>                    Line_of_Background_Pixels[ Current_X_Coordinate
> ].Blue  := ( ( Alpha_Channel_Pixel.Blue  * ( ForeGround_Pixel.Blue  -
> Background_Pixel.Blue  ) ) SHR 8 ) + Background_Pixel.Blue;
>                    Line_of_Background_Pixels[ Current_X_Coordinate
> ].Green := ( ( Alpha_Channel_Pixel.Green * ( ForeGround_Pixel.Green -
> Background_Pixel.Green ) ) SHR 8 ) + Background_Pixel.Green;
>                    Line_of_Background_Pixels[ Current_X_Coordinate ].Red
>    := ( ( Alpha_Channel_Pixel.Red   * ( ForeGround_Pixel.Red   -
> Background_Pixel.Red   ) ) SHR 8 ) + Background_Pixel.Red;
>
>                    // The Alpha channel of a four-colour-channel RGB
> pixel can be safely ignored since we don't need to access it
>                  End;
>              End;
>          End;
>      Except
>        // on any errors (i.e. if Foreground or Background bitmaps are
> invalid) display this message
>        ShowMessage( 'ERROR: Unable to Draw Foreground Bitmap onto
> Background Bitmap using an Alpha Channel' );
>      End;
>    End;
>
> {
> This routine is used for timing operations so that we can figure
> out how many milliseconds a specific operation takes.
> It's accurate down to the millisecond level
>
> The difference between two calls of this routine will give us the
> number of milliseconds an operation takes.
> }
> Function Get_Clock_Count_in_Milliseconds: Floating_Point;
>
>    Var
>      Current_Date_And_Time: TDateTime; // Delphi specific Date & Time
> Record which is a 64 bit floating point number
>
>      Number_of_Milliseconds_Since_Midnight : Double;
>
>      Current_Hour,
>      Current_Minute,
>      Current_Second,
>      Current_MilliSecond: Word;
>
>    Begin
>      Try
>        Current_Date_And_Time := Now;   // NOW is a Delphi specific
> function part of the SysUtils unit
>                                        // Get the current date and time
> in Floating Point format
>
>        { DecodeTime is a SysUtils unit function - Get the component
> parts of the date and time and put each element into the desired
variables }
>        DecodeTime( Current_Date_And_Time, Current_Hour, Current_Minute,
> Current_Second, Current_MilliSecond );
>
>        Number_of_Milliseconds_Since_Midnight := ( Current_Hour * 3600000 )
+
>                                                 ( Current_Minute * 60000 )
+
>                                                 ( Current_Second * 1000 )
+
>                                                   Current_MilliSecond;
>      Except
>        Showmessage( 'Unable to obtain the system clock count in
> milliseconds since midnight' );
>        Get_Clock_Count_in_Milliseconds := 0.0;
>        Exit;
>      End;
>                                            { Number of days past since
> December 30, 1899 - Number of Milliseconds in a day }
>      Get_Clock_Count_in_Milliseconds := ( Trunc( Current_Date_And_Time )
> * 86400000 ) + Number_of_Milliseconds_Since_Midnight;
>    End;
>
> {
> A general example procedure which a user can call to see how long an
> alpha blend operation takes
> It will loads 3 standard Windows Bitmaps and alpha blend them together.
> one of the bitmaps MUST be a greyscale image which called the Alpha
Channel
>
> An Alpha channel can have feathered or soft edges and the closer
> to 100% FULL WHITE (i.e. R:255, G:255, B:255) then the more that the
> FOREGROUND bitmap will show through.
>
> The closer to ZERO percent Black (i.e. R:0, G:0, B:0) the mor
e the
> BACKGROUND will show through.
>
> Any mix of grey in between will give you a blend where you see BOTH
images.
> (i.e. R:127, G:127, B:127) is a 50% blend
>
> Setting the individual colour channels of an Alpha pixel allows you to
> target
> the specific colour channel of an Alpha Blend   i.e. (i.e. R:0, G:255,
B:0)
> would give have ONLY the green channel of the foreground show through.
>
> The alpha channel bitmap can be filled with a single solid colour
> or can contain any shape and any degree of soft or hard eges on those
> shapes.
>
> Example of use:  Alpha_Blend_Example( TForm1.Canvas );
> }
> Procedure Alpha_Blend_Example( Canvas_of_Form: TCanvas );  //
> Canvas_of_Form is usually the canvas of the TForm where you wish to draw
> the final output
>
>    Var
>      Foreground_Image,
>      Background_Image,
>      Alpha_Channel_Image : TBitmap;  // If you get a stack overflow or
> other type of error increase the stack size to the following
>                                      // values using the $M compiler
> directive {$M 16384,18000000} This set maximum stack to 18 megabytes
>                                      // You can also move these 3 bitmap
> variables out of this procedure declaration into Heap memory
>                                      // You will need to do this if your
> images are 1600 by 1200 pixels or larger
>
>      Elapsed_Time_Message : String;
>
>      Start_Time,
>      Finish_Time,
>      Total_Elapsed_Time : Extended;  // Holds how many milliseconds
> since midnight the system clock has been run - used for timing purposes
>
>    Begin
>      Foreground_Image    := TBitmap.Create;
>      Background_Image    := TBitmap.Create;
>      Alpha_Channel_Image := TBitmap.Create;
>
>      // load bitmaps from files
>      Foreground_Image.LoadFromFile    ( 'Foreground Image.bmp'    );
>      Background_Image.LoadFromFile    ( 'Background Image.bmp'    );
>      Alpha_Channel_Image.LoadFromFile ( 'Alpha Channel Image.bmp' ); //
> if you change the name of this file to
>                                                                      //
> 'Alpha Channel Image 50 Percent.bmp' then
>                                                                      //
> you'll see a 50% blend of both foreground and background
>                                                                      //
> also try the file 'Alpha Channel Image Left to Right Ramp.bmp'
>
>      // set bitmaps to 32 bit pixels per pixel with full 8-bits of Red,
> Green Blue and Alpha channel
>      Foreground_Image.PixelFormat    := pf32bit;
>      Background_Image.PixelFormat    := pf32bit;
>      Alpha_Channel_Image.PixelFormat := pf32bit;
>
>      Canvas_of_Form.Draw( 0, 0, Alpha_Channel_Image );
>      Showmessage( 'Here is the Alpha Channel Image which will be used in
> the Alpha Blend...' );
>
>      Canvas_of_Form.Draw( 0, 0, Foreground_Image );
>      Showmessage( 'Here is the Foreground Image which will be used in
> the Alpha Blend...' );
>
>      Canvas_of_Form.Draw( 0, 0, Background_Image );
>      Showmessage( 'Here is the Background Image which will be used in
> the Alpha Blend...' + #10 + #13 +
>                   'Click on OK to begin Alpha Blend Test...' );
>
>
>      // ******************* Clock 32 bit integer array access time
>      Start_Time := Get_Clock_Count_in_Milliseconds;
>
>      Alpha_Blend_Bitmap_Over_Background( ZERO, ZERO, Foreground_Image,
> Background_Image, Alpha_Channel_Image );
>
>      Finish_Time := Get_Clock_Count_in_Milliseconds;
>
>      Total_Elapsed_Time := Finish_Time - Start_Time;
>
>      Elapsed_Time_Message := 'Clocking Total Pixel by Pixel Alpha Blend:
> ' + FloatToStr( Total_Elapsed_Time ) + ' milliseconds.' + #10 + #13 +
>                              'Note the soft edges on the images
> boundaries.';
>
>      Canvas_of_Form.Draw( 0, 0, Background_Image );
>
>      Showmessage( Elapsed_Time_Message );
>
>      Foreground_Image.Free;
>      Background_Image.Free;
>      Alpha_Channel_Image.Free;
>    End;
>
>



From: Eric Grange  
Subject: Re: Here is a FAST Alpha Blend Routine
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 10:9:15 PST
You could probably get a boost by not calling ScanLine for each Y,
but only twice, for Y=0 and Y=1 and then compute the next values
from these two:

	ScanLine[n]:=ScanLine[0]+(ScanLine[1]-ScanLine[0])*n

(of course, place the values in variables!)
Once your raster loop gets efficient, ScanLine will become the limitting
factor.

Eric

From:  
Subject: Re: Is it possible to get a copy of an Optimized MMX Alpha B
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 17:24:59 PST
Much appreciated regarding the feedback on my Delphi Scanline version of 
an alpha blend routine.

Would it be possible for me to get a copy of your MMX optimized
Alpha Blend, I would really appreciate it ...

Thank You,

Henry Eckstein
email it to henry@comwave.com



Tomaz Koritnik wrote:
> Hi
> 
>     Your code has several disadvantages over MMX version of blending
> algorythm.
>         - MMX version is much faster simply because it speeds up basic math
> functions like multiplication and all the rest
>         - MMX is present from P1 166 on and there are probably no computers
> left that don't have MMX so why bother with non-MMX versions :).
>         - You are using TBitmap.Scanline which is not as fast as you think.
> TBitmap does some extra operations when you use Scanline property which is
> slower. Best thing is to cache scanline pointers on first usage and then use
> lookup table instead of TBitmap.Scanline. Believe me, it's much faster.
> 
> I also don't think of MMX as hardware acceleration. It basically just a new
> low-level (ok, not so new) instruction set which is based on SIMD model.
> 
> I tested my MMX blending function which is not as yours but uses same
> blending factor for all pixels. I combines two 1600x1200 bitmaps and time
> needed to blend two images is 25ms. I have AthlonXP 2400+ with DDR RAM. I
> also tested same program using same images on old P1-133 or 166 computer (I
> don't know exactly but looks like it has MMX) and blend speed was 170ms.
> 
> regards,
> Tomaz
> 
> 
> "Henry A. Eckstein"  wrote in message
> news:3fcd327b$1@newsgroups.borland.com...
> 
>>Enjoy this FAST alpha blend routine which uses optimized
>>pixel by pixel SCANLINE accesses...
>>
>>I do have a test images and code snippet archive
>>with 1600 by 1200 pixel test images for you
>>to test this system with. It's a 6.7 megabyte file.
>>Email me at henry@comwave.com and i'll send it to you.
>>It is too big to post in this forum.
>>
>>If the code looks a little messy in this post, cut & paste the code
>>to your Delphi Editor and get rid of the 80 character line wrap.
>>Set line wrap to 255 characters or more.
>>
>>This routine will work on Delphi Versions 5, 6 & 7
>>I haven't tested it with Delphi Version 2, 3 or 4.
>>
>>The image files currently MUST be of Windows BMP format
>>but some enterprising programmer can change teh code to work with
>>JPEG or other file formats.
>>
>>{
>>ALPHA BLEND AND SUPPORT ROUTINES DESIGNED AND CODED BY:
>>==============================================================
>>Henry A. Eckstein
>>Triad Communications Ltd.
>>2751 Oxford Street
>>Vancouver, British Columbia, Canada
>>V5K 1N5
>>
>>Telephone: 604-253-3990
>>Fax:       604-253-0770
>>
>>Toll Free: 1-800-600-9762 (1-800-OZZY-ROC)
>>
>>Website: www.triadcommunications.ca
>>
>>Email: henry@comwave.com
>>
>>Copyright (C) 2003 - All Rights Reserved.
>>
>>These Code Snippets are hereby donated to the
>>PUBLIC DOMAIN for use in any which way you desire.
>>
>>Please include an acknowledgement of my authorship
>>if you do happen to use the Alpha Blend code.
>>
>>
>>THANK YOU and GOOD CODING
>>
>>P.S. If you like skiing and snowboarding
>>      goto the website: www.whistler-blackcomb.com
>>      to see the two biggest ski mountains
>>      in North America, 160 km north of
>>      Vancouver, BC, Canada
>>
>>P.S.2. The test photos are of my hometown in Vancouver, BC, Canada
>>}
>>
>>
>>uses
>>   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls,
>>Forms,
>>   Dialogs, StdCtrls, MPlayer;
>>
>>
>>Type
>>   {$R-} // Turn range checking OFF so that arrays can be accessed
>>outside of their range limits
>>
>>   // Assumes a 32 bit TBitmap pixel type  i.e. TBitmap.PixelFormat :=
>>pf32bit;
>>   RGB_Pixel = Packed Record  // Variant record allow two separate
>>fields to occupy the same memory space
>>                 Case LongWord of
>>                   0 : ( Blue, Green, Red, Alpha : Byte );  // This 
is
>>Windows specific as an RGB pixel has its colour channels ordered in a
>>specific way i.e. ABGR rather than the usual RGBA
>>                   1 : ( All_Channels : LongWord );         // The above
>>colour components can be accessed as a single unsigned 32-bit integer
>>when All_Channels field is accessed.
>>                 End;                                       // This is
>>useful for performing AND, XOR, OR and other bitwise manipulations on an
>>entire pixel
>>
>>   // A pointer to the above pixel record type
>>   RGB_Pixel_Pointer = ^RGB_Pixel;
>>
>>   {
>>   An array of the above RGB pixel type which makes it easy to access a
>>region
>>   of memory containing a series of RGB pixels. This is used by many
>>graphics
>>   filters or rendering routines.
>>
>>   Packed Array forces the individual fields to be aligned close
>>together in memory.
>>   Some operating systems or compilers, if not declared as packed, would
>>align fields on 16-bit or 32-bit boundaries
>>   depending upon the size of an individual field.  The Delphi specific
>>identifier "Packed"
>>   gives us the ability to force record fields to be close together with
>>no free byte space in between.
>>
>>   This array has been declared with 0..1 range to save heap and stack
>>memory space.
>>   But if you turn range checking off i.e. $R- then the array can be
>>accessed
>>   outside of the declared boundaries.
>>   }
>>   Array_of_RGB_Pixels = Packed Array[ 0..1 ] of RGB_Pixel;
>>
>>   {
>>   A pointer to an Array of RGB pixels which makes it easy to access a
>>region
>>   of memory containing a series of RGB pixels. This is used by many
>>graphics
>>   filters or rendering routines that need to facilitate pointer
> 
> arithmetic
> 
>>   or memory address manipulation on a whole series of RGB pixels.
>>   }
>>   Pointer_to_Array_of_RGB_Pixels = ^Array_of_RGB_Pixels;
>>
>>
>>{
>>For speed reasons make sure your TBitmap.PixelFormat := pf32bit is set,
>>as this routine assumes all pixels in the Tbitmap parameters are bitmaps
>>composed of 8-bit Red, Green, Blue, Alpha channels.
>>Accessing pixels as 32 bit integers really speeds things up.
>>
>>Using scanline is one of the fastest ways in software to access the
>>TBitmap pixels without using assembler or MMX instructions.
>>
>>Alpha Blending two 1600 by 1200 pixel 32 bit bitmaps using a 1600 by
>>1200 pixel 32 bit greyscale alpha channel bitamp
>>on a 450 MHZ AMD K3 will be finshed in 1.5 seconds (1576 milliseconds)
>>On other Pentium 500 MHZ systems I have been able to get alpha blend
>>times downto
>>600 milliseconds on 1600 by 1200 pixel bitmaps
>>(my graphics card is a Matrox G550 with 32 megs of Video RAM and I have
>>an AMD K3 running at 450 MHZ and 378 megs of system RAM)
>>
>>Please NOTE that the Foreground and Alpha Channel bitmaps
>>MUST be of the same width and height so that the alpha blend operation
>>can correctly combine the foreground with the background
>>}
>>Procedure Alpha_Blend_Bitmap_Over_Background( Starting_X_Coordinate,
>>Starting_Y_Coordinate: Integer; Var Foreground_Bitmap,
>>Background_Bitmap, Alpha_Channel_Bitmap: TBitmap );
>>
>>   Var
>>     x, y,
>>
>>     Last_X,
>>     Last_Y,
>>     Current_X_Coordinate : Large_Integer;
>>
>>     Line_of_Foreground_Pixels,
>>     Line_of_Background_Pixels,
>>     Line_of_Alpha_Channel_Pixels : Pointer_to_Array_of_RGB_Pixels;
>>
>>     Foreground_Pixel,
>>     Background_Pixel,
>>     Alpha_Channel_Pixel : RGB_Pixel;
>>
>>   Begin
>>     Try
>>       // if alpha channel does not exist then just try and draw the
>>       // foreground bitmap onto the background using the Delphi Draw()
>>command.
>>       if Alpha_Channel_Bitmap = NIL then
>>         Background_Bitmap.Canvas.Draw( Starting_X_Coordinate,
>>Starting_Y_Coordinate, Foreground_Bitmap );
>>
>>       with Foreground_Bitmap do
>>         Begin
>>           Last_X := Foreground_Bitmap.Width - 1;
>>           Last_Y := Foreground_Bitmap.Height - 1;
>>
>>           for y := Z
ERO to Last_Y do
>>             Begin
>>               // get the pointer to each row of pixel on all the bitmaps
>>               Line_of_Foreground_Pixels    :=
>>Foreground_Bitmap.ScanLine[ y ];
>>               Line_of_Background_Pixels    :=
>>Background_Bitmap.ScanLine[ Starting_Y_Coordinate + y ];
>>               Line_of_Alpha_Channel_Pixels :=
>>Alpha_Channel_Bitmap.ScanLine[ y ];
>>
>>               for x := ZERO to Last_X do
>>                 Begin
>>                   Current_X_Coordinate := Starting_X_Coordinate + x;
>>
>>                   // retrieve the relevant pixel on each row of pixels
>>                   Foreground_Pixel    := Line_of_Foreground_Pixels[ x ];
>>                   Background_Pixel    := Line_of_Background_Pixels[
>>Current_X_Coordinate ];
>>                   Alpha_Channel_Pixel := Line_of_Alpha_Channel_Pixels[
> 
> x ];
> 
>>                   // Perform Alpha blend oepration
>>                   Line_of_Background_Pixels[ Current_X_Coordinate
>>].Blue  := ( ( Alpha_Channel_Pixel.Blue  * ( ForeGround_Pixel.Blue  -
>>Background_Pixel.Blue  ) ) SHR 8 ) + Background_Pixel.Blue;
>>                   Line_of_Background_Pixels[ Current_X_Coordinate
>>].Green := ( ( Alpha_Channel_Pixel.Green * ( ForeGround_Pixel.Green -
>>Background_Pixel.Green ) ) SHR 8 ) + Background_Pixel.Green;
>>                   Line_of_Background_Pixels[ Current_X_Coordinate ].Red
>>   := ( ( Alpha_Channel_Pixel.Red   * ( ForeGround_Pixel.Red   -
>>Background_Pixel.Red   ) ) SHR 8 ) + Background_Pixel.Red;
>>
>>                   // The Alpha channel of a four-colour-channel RGB
>>pixel can be safely ignored since we don't need to access it
>>                 End;
>>             End;
>>         End;
>>     Except
>>       // on any errors (i.e. if Foreground or Background bitmaps are
>>invalid) display this message
>>       ShowMessage( 'ERROR: Unable to Draw Foreground Bitmap onto
>>Background Bitmap using an Alpha Channel' );
>>     End;
>>   End;
>>
>>{
>>This routine is used for timing operations so that we can figure
>>out how many milliseconds a specific operation takes.
>>It's accurate down to the millisecond level
>>
>>The difference between two calls of this routine will give us the
>>number of milliseconds an operation takes.
>>}
>>Function Get_Clock_Count_in_Milliseconds: Floating_Point;
>>
>>   Var
>>     Current_Date_And_Time: TDateTime; // Delphi specific Date & Time
>>Record which is a 64 bit floating point number
>>
>>     Number_of_Milliseconds_Since_Midnight : Double;
>>
>>     Current_Hour,
>>     Current_Minute,
>>     Current_Second,
>>     Current_MilliSecond: Word;
>>
>>   Begin
>>     Try
>>       Current_Date_And_Time := Now;   // NOW is a Delphi specific
>>function part of the SysUtils unit
>>                                       // Get the current date and time
>>in Floating Point format
>>
>>       { DecodeTime is a SysUtils unit function - Get the component
>>parts of the date and time and put each element into the desired
> 
> variables }
> 
>>       DecodeTime( Current_Date_And_Time, Current_Hour, Current_Minute,
>>Current_Second, Current_MilliSecond );
>>
>>       Number_of_Milliseconds_Since_Midnight := ( Current_Hour * 3600000 )
> 
> +
> 
>>                                                ( Current_Minute * 60000 )
> 
> +
> 
>>                                                ( Current_Second * 1000 )
> 
> +
> 
>>                                                  Current_MilliSecond;
>>     Except
>>       Showmessage( 'Unable to obtain the system clock count in
>>milliseconds since midnight' );
>>       Get_Clock_Count_in_Milliseconds := 0.0;
>>       Exit;
>>     End;
>>                                           { Number of days past since
>>December 30, 1899 - Number of Milliseconds in a day }
>>     Get_Clock_Count_in_Milliseconds := ( Trunc( Current_Date_And_Time )
>>* 86400000 ) + Number_of_Milliseconds_Since_Midnight;
>>   End;
>>
>>{
>>A general example procedure which a
 user can call to see how long an
>>alpha blend operation takes
>>It will loads 3 standard Windows Bitmaps and alpha blend them together.
>>one of the bitmaps MUST be a greyscale image which called the Alpha
> 
> Channel
> 
>>An Alpha channel can have feathered or soft edges and the closer
>>to 100% FULL WHITE (i.e. R:255, G:255, B:255) then the more that the
>>FOREGROUND bitmap will show through.
>>
>>The closer to ZERO percent Black (i.e. R:0, G:0, B:0) the more the
>>BACKGROUND will show through.
>>
>>Any mix of grey in between will give you a blend where you see BOTH
> 
> images.
> 
>>(i.e. R:127, G:127, B:127) is a 50% blend
>>
>>Setting the individual colour channels of an Alpha pixel allows you to
>>target
>>the specific colour channel of an Alpha Blend   i.e. (i.e. R:0, G:255,
> 
> B:0)
> 
>>would give have ONLY the green channel of the foreground show through.
>>
>>The alpha channel bitmap can be filled with a single solid colour
>>or can contain any shape and any degree of soft or hard eges on those
>>shapes.
>>
>>Example of use:  Alpha_Blend_Example( TForm1.Canvas );
>>}
>>Procedure Alpha_Blend_Example( Canvas_of_Form: TCanvas );  //
>>Canvas_of_Form is usually the canvas of the TForm where you wish to draw
>>the final output
>>
>>   Var
>>     Foreground_Image,
>>     Background_Image,
>>     Alpha_Channel_Image : TBitmap;  // If you get a stack overflow or
>>other type of error increase the stack size to the following
>>                                     // values using the $M compiler
>>directive {$M 16384,18000000} This set maximum stack to 18 megabytes
>>                                     // You can also move these 3 bitmap
>>variables out of this procedure declaration into Heap memory
>>                                     // You will need to do this if your
>>images are 1600 by 1200 pixels or larger
>>
>>     Elapsed_Time_Message : String;
>>
>>     Start_Time,
>>     Finish_Time,
>>     Total_Elapsed_Time : Extended;  // Holds how many milliseconds
>>since midnight the system clock has been run - used for timing purposes
>>
>>   Begin
>>     Foreground_Image    := TBitmap.Create;
>>     Background_Image    := TBitmap.Create;
>>     Alpha_Channel_Image := TBitmap.Create;
>>
>>     // load bitmaps from files
>>     Foreground_Image.LoadFromFile    ( 'Foreground Image.bmp'    );
>>     Background_Image.LoadFromFile    ( 'Background Image.bmp'    );
>>     Alpha_Channel_Image.LoadFromFile ( 'Alpha Channel Image.bmp' ); //
>>if you change the name of this file to
>>                                                                     //
>>'Alpha Channel Image 50 Percent.bmp' then
>>                                                                     //
>>you'll see a 50% blend of both foreground and background
>>                                                                     //
>>also try the file 'Alpha Channel Image Left to Right Ramp.bmp'
>>
>>     // set bitmaps to 32 bit pixels per pixel with full 8-bits of Red,
>>Green Blue and Alpha channel
>>     Foreground_Image.PixelFormat    := pf32bit;
>>     Background_Image.PixelFormat    := pf32bit;
>>     Alpha_Channel_Image.PixelFormat := pf32bit;
>>
>>     Canvas_of_Form.Draw( 0, 0, Alpha_Channel_Image );
>>     Showmessage( 'Here is the Alpha Channel Image which will be used in
>>the Alpha Blend...' );
>>
>>     Canvas_of_Form.Draw( 0, 0, Foreground_Image );
>>     Showmessage( 'Here is the Foreground Image which will be used in
>>the Alpha Blend...' );
>>
>>     Canvas_of_Form.Draw( 0, 0, Background_Image );
>>     Showmessage( 'Here is the Background Image which will be used in
>>the Alpha Blend...' + #10 + #13 +
>>                  'Click on OK to begin Alpha Blend Test...' );
>>
>>
>>     // ******************* Clock 32 bit integer array access time
>>     Start_Time := Get_Clock_Count_in_Milliseconds;
>>
>>     Alpha_Blend_Bitmap_Over_Background( ZERO, ZERO, Foreground_Image,
>>Background_Image, Alpha_Channel_Image );
>>
>>     Finish_Time := Get_Clock_Count_in_Milliseconds;
>>
>>     Total_Elapsed_Time := Finish_Time - Start_Time;
>>
>>     Elapsed_Time_Message := 'Clocking Total Pixel by Pixel Alpha Blend:
>>' + FloatToStr( Total_Elapsed_Time ) + ' milliseconds.' + #10 + #13 +
>>                             'Note the soft edges on the images
>>boundaries.';
>>
>>     Canvas_of_Form.Draw( 0, 0, Background_Image );
>>
>>     Showmessage( Elapsed_Time_Message );
>>
>>     Foreground_Image.Free;
>>     Background_Image.Free;
>>     Alpha_Channel_Image.Free;
>>   End;
>>
>>
> 
> 
> 


From:  
Subject: Re: Here is a FAST Alpha Blend Routine - Borland said don't
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 5:0:17 PST
Unfortunately I tried doing the pointer arithmetic
but Borland said not to do that because the bitmaps may be accessed
by other routines at the same time as my blend routine. (even if LOCKED)
and because Bitmaps may have pixel coordinate 0,0 in lower left corner 
rather than upper left corner (i.e. Device Dependent Bitmap - depending 
upon graphics card) I was told to use scanline to get a pointer for each 
row.

There is a faster was to access the bitmaps pixels...by loading the 
bitmaps into a Integer array and then accessing each pixel as a
32 bit signed or unsigned integer.

I AGREE it is NOT the fastest blend routine, but it probably is
one of the most readable and understandable (once you get past the
line wrap problem.)  Any suggestions for improvement would be 
appreciated.  I alse wouldn't mind getting a copy of the MMX version
as all the ones i've found on Google do NOT have the ability to alpha 
blend using a 3 channel Alpha Bitmap.  I need a version that uses
not a single greyscale value but variable shades of greyscale in any 
shape or form.  Any help is definitely appreciated - Thank You.

Eric Grange wrote:
> You could probably get a boost by not calling ScanLine for each Y,
> but only twice, for Y=0 and Y=1 and then compute the next values
> from these two:
> 
> 	ScanLine[n]:=ScanLine[0]+(ScanLine[1]-ScanLine[0])*n
> 
> (of course, place the values in variables!)
> Once your raster loop gets efficient, ScanLine will become the limitting
> factor.
> 
> Eric


From: Paul Nicholls  
Subject: Re: Here is a FAST Alpha Blend Routine
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 14:6:50 PST
 I believe he did mention "Using scanline is one of the fastest ways in
software to access the
TBitmap pixels without using assembler or MMX instructions.", so he knows it
most likely won't be as fast a MMX version :)

"Nils Haeck"  wrote in message
news:3fcd5200@newsgroups.borland.com...
> Hello Henry,
>
> IMO this code is probably not as fast as the MMX-optimized versions in
e.g.
> Graphics32 (www.g32.org), Have you done a comparison?




From: Nils Haeck  
Subject: Re: Here is a FAST Alpha Blend Routine
NewsGroup: borland.public.delphi.graphics
Date Posted: 3-Dec-2003 at 4:10:56 PST
Hello Henry,

IMO this code is probably not as fast as the MMX-optimized versions in e.g.
Graphics32 (www.g32.org), Have you done a comparison?

Perhaps you can store the BMP images as JPG files in your code snippet
archive? Here's an algorithm to use to convert them back to BMP for input
into your algorithm:

uses Jpeg;
....
function Convert_Henrys_Now_Smaller_JPG_Back_To_Bitmap: TBitmap;
var
  AJpg: TJpgImage;
begin
  AJpg := TJpgImage.Create;
  try

AJpg.LoadFromFile('Henrys_initial_big_bmp_file_now_converted_to_jpg.jpg');
// This line will most probably wrap so please set your editor to 255 or
more before!
    Result := TBitmap.Create; // This line does barely not wrap
    Result.Assign(AJpg);
  finally
    AJpg.Free;
  end;
end;

Kind regards,

Nils ;)

"Henry A. Eckstein" wrote in message
news:3fcd327b$1@newsgroups.borland.com...
>
> Enjoy this FAST alpha blend routine which uses optimized
> pixel by pixel SCANLINE accesses...
>
> I do have a test images and code snippet archive
> with 1600 by 1200 pixel test images for you
> to test this system with. It's a 6.7 megabyte file.
> Email me at henry@comwave.com and i'll send it to you.
> It is too big to post in this forum.
>
> If the code looks a little messy in this post, cut & paste the code
> to your Delphi Editor and get rid of the 80 character line wrap.
> Set line wrap to 255 characters or more.