首页  编辑  

淡入淡出和半透明

Tags: /超级猛料/Picture.图形图像编程/控件和绘图/   Date Created:

Hi Marin,

> If I have two bitmaps and want to put the second above the first with some

> transparency, what can I do?

> I want to achieve an effect like in Photoshop, when you use layer's

> transparency lower than 100%.

try this MMX code for blending which is taken from the next version of my

soon-to-be-available Virtual Treeview:

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

 );

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;

file://------------------------------------------------------------------------------

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

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;

file://------------------------------------------------------------------------------

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

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

file://------------------------------------------------------------------------------

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

procedure EMMS;

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

asm

       DB      $0F, $77               /// EMMS

end;

file://------------------------------------------------------------------------------

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

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

Pointer;

// Helper function used to retrieve the bitmap selected into the given 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

     Result := DIB.dsBm.bmBits;

     Width := DIB.dsBmih.biWidth;

     Height := DIB.dsBmih.biHeight;

   end;

 end;

end;

file://------------------------------------------------------------------------------

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

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;

file://------------------------------------------------------------------------------

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

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.

// Bias can have a value which gets added to every pixel (in the range

of -128..127).

// CAUTION: This procedure does not check whether MMX instructions are actually

available!

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;

   end;

 end;

end;

file://------------------------------------------------------------------------------

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

Source and Destination HDC are simply the handles of the bitmap canvases (e.g.

SourceBMP.Canvas.Handle) etc.

Ciao, Mike

--