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.
|
|
|