Bitmap - desenhando transparente

Top  Previous  Next

Rotina pronta para desenhar Bitmap transparente em RunTime:

 

 

procedure DrawTransparent(const X, Y   : Integer; const Bitmap    : TBitmap; 

                          const xCanvas: TCanvas; const CorDeFundo: TColor);

begin

  xCanvas.Brush.Color := CorDeFundo;

  xCanvas.BrushCopy(Rect(X, Y, Bitmap.Width + X, Bitmap.Height + Y), Bitmap,

                    Rect(00, Bitmap.Width    , Bitmap.Height)    , Bitmap.Canvas.Pixels[0,0]);

end;

 

//-------------------------- outro

 

MaskBitmap := TBitmap.Create

MaskBitmap.Assign(SrcBitmap ); 

MaskBitmap.Mask(FColor); {transparent color} 

BitBlt(DestBitmap.Canvas.Handle, x, y, SrcBitmap.Width, SrcBitmap.Height, MaskBitmap.Canvas.Handle, 00, SRCAND); 

BitBlt(DestBitmap.Canvas.Handle, x,y, SrcBitmap.Width, SrcBitmap.Height, SrcBitmap.Canvas.Handle, 00, SRCINVERT); 

MaskBitmap.Free; 

 

//-------------------------- Este funciona de verdade, eu (junior) testei

//-------------------------- desenha um TBitmap transparente sobre um TImage

 

procedure DrawTransparentBitmap(Ahdc: HDC; Image: TBitmap; XStart, YStart: Word);

var

  TransparentColor : TColor;

  CColor : TColorRef;

  BmAndBack, BmAndObject, BmAndMem, BmSave, BmBackOld, BmObjectOld, BmMemOld, BmSaveOld : HBitmap;

  HdcMem, HdcBack, HdcObject, HdcTemp, HdcSave : HDC;

  PtSize : TPoint;

begin

  // Set the transparent color to be the lower left pixel of the bitmap

  TransparentColor := Image.Canvas.Pixels [0, Image.Height - 1];

  TransparentColor := TransparentColor or $02000000;

 

  HdcTemp := CreateCompatibleDC (Ahdc);

  SelectObject (HdcTemp, Image.Handle); // Select the bitmap

 

  // Convert bitmap dimensions from device to logical points

  PtSize.X := Image.Width;

  PtSize.Y := Image.Height;

  DPtoLP(HdcTemp, PtSize, 1);  // Convert from device logical points

 

  // Create some DCs to hold temporary data

  HdcBack   := CreateCompatibleDC(Ahdc);

  HdcObject := CreateCompatibleDC(Ahdc);

  HdcMem    := CreateCompatibleDC(Ahdc);

  HdcSave   := CreateCompatibleDC(Ahdc);

 

  // Create a bitmap for each DC

  BmAndBack   := CreateBitmap(PtSize.X, PtSize.Y, 11, NIL);

  BmAndObject := CreateBitmap(PtSize.X, PtSize.Y, 11, NIL);

  BmAndMem    := CreateCompatibleBitmap(Ahdc, PtSize.X, PtSize.Y);

  BmSave      := CreateCompatibleBitmap(Ahdc, PtSize.X, PtSize.Y);

 

  // Each DC must select a bitmap object to store pixel data

  BmBackOld   := SelectObject(hdcBack, bmAndBack);

  BmObjectOld := SelectObject(hdcObject, bmAndObject);

  BmMemOld    := SelectObject(hdcMem, bmAndMem);

  BmSaveOld   := SelectObject(hdcSave, bmSave);

 

  // Set proper mapping mode

  SetMapMode (HdcTemp, GetMapMode (Ahdc));

 

  // Save the bitmap sent here, because it will be overwritten

  BitBlt (HdcSave, 00, PtSize.X, PtSize.Y, HdcTemp, 00, SrcCopy);

 

  // Set the background color of the source DC to the color contained in the

  // parts of the bitmap that should be transparent

  CColor := SetBkColor (HdcTemp, TransparentColor);

 

  // Create the object mask for the bitmap by performing a BitBlt()

  //  from the source bitmap to a monochrome bitmap

  BitBlt (HdcObject, 00, PtSize.X, PtSize.Y, HdcTemp, 00, SrcCopy);

 

  // Set the background color of the source DC back to the original color

  SetBkColor (HdcTemp, CColor);

 

  // Create the inverse of the object mask

  BitBlt (HdcBack, 00, PtSize.X, PtSize.Y, HdcObject, 00, NotSrcCopy);

 

  // Copy the background of the main DC to the destination

  BitBlt (HdcMem, 00, PtSize.X, PtSize.Y, Ahdc, XStart, YStart, SrcCopy);

 

  // Mask out the places where the bitmap will be placed

  BitBlt (HdcMem, 00, PtSize.X, PtSize.Y, HdcObject, 00, SrcAnd);

 

  // Mask out the transparent colored pixels on the bitmap

  BitBlt (HdcTemp, 00, PtSize.X, PtSize.Y, hdcBack, 00, SrcAnd);

 

  // Xor the bitmap with the background on the destination DC

  BitBlt (HdcMem, 00, PtSize.X, PtSize.Y, hdcTemp, 00, SrcPaint);

 

  // Copy the destination to the screen

  BitBlt (Ahdc, XStart, YStart, PtSize.X, PtSize.Y, HdcMem, 00, SrcCopy);

 

  // Place the original bitmap back into the bitmap sent here

  BitBlt (HdcTemp, 00, PtSize.X, PtSize.Y, HdcSave, 00, SrcCopy);

 

  // Delete the memory bitmaps

  DeleteObject (SelectObject (HdcBack, BmBackOld));

  DeleteObject (SelectObject (HdcObject, BmObjectOld));

  DeleteObject (SelectObject (HdcMem, BmMemOld));

  DeleteObject (SelectObject (HdcSave, BmSaveOld));

 

  // Delete the memory DCs

  DeleteDC (HdcMem);

  DeleteDC (HdcBack);

  DeleteDC (HdcObject);

  DeleteDC (HdcSave);

  DeleteDC (HdcTemp);

end;

 

Exemple:

 

  Bmp := TBitmap.Create;

  Bmp.Handle := LoadBitmap(HInstance,'SKINLOGO');

  // Draw the tranparent bitmap note how the DC of the foreground is used in the function below

  DrawTransparentBitmap(Image1.Picture.Bitmap.Canvas.Handle, Bmp, 6618);

 

// este eu nao testei, mas o comentario diz que é rápido!

{

Drawing transparent images/sprites for games/interfaces... 

Answer:

 

 

   You need to have to pictures: 

     1: the sprite you want to draw 

     2: the mask of the sprite 

 

   The sprite needs to have the transparent areas filled with black. The mask of the sprite is a monochrome bitmap painted with black in the areas you need to draw, and white in the transparent areas. 

 

    $$####$$$   WWBBBBWWW 

    $$$####$$   WWWBBBBWW 

    $$$$####$   WWWWBBBBW 

    $$$####$$   WWWBBBBWW 

    $$####$$$   WWBBBBWWW 

 

     SPRITE       MASK 

 

 

    $ - black 

    # - another color 

    W - white 

    B - black 

 

 

   HINT: If you design your own sprites, in programs such as 3D Studio MAX, Photoshop, or any other program that uses antialising, you have to assure that the background color is black. If you have a yellow antialised circle and a red background, when you create your mask you'll use a Magic Wand Tool or something like that to get the background. Some parts of the background whill be shaded with the sprite (antialising) and that parts will not be selected. Finally, drawing the circle transparent you'll obtain a yellow circle with a red border. Now, think that the sprite is a truck, or car and you draw that on a road. You get a nasty red border. If you use a black background, it wouldn't be so obvious. I don't know if you understood anything, but don't use colored backgrounds with programs that mixes colors (resizing images is done with color mix by "cool" programs) 

 

   If you work with many sprites, you may need to build a tool for creating the mask. It is a simple routine that checkes a image for a certain color. Check out the next procedure. This procedure could be optimized(using scanline) and then used realtime, when loading images (if you don't bother waiting). 

}

procedure TForm1.CreateMask; 

var i,j:integer; 

    TransparentColor:TColor; 

begin 

     TransparentColor:=Rgb(0,0,255); 

     // loop through the entire sprite 

     for i:=0 to Sprite.Width do 

     for j:=0 to Sprite.Height do 

     // if the pixel is the transparent color 

         If Sprite.Canvas.Pixels[i,j]=TransparentColor 

         then begin 

     // make black that pixel in the sprite 

                   Sprite.Canvas.Pixels[i,j]:=0

     // and white in the mask (white=transparent) 

                   Mask.Canvas.Pixels[i,j]:=Rgb(255,255,255); 

              end 

     // if the pixel aint transparent 

     // put black in the mask (black=solid) 

         else Mask.Canvas.Pixels[i,j]:=Rgb(0,0,0); 

end

 

  //  The transparent drawing is done with the BitBlt function: 

 

BitBlt( 

   DestDC: HDC,      // handle of the destination canvas (Canvas.Handle) 

   X: Integer,       // x-coord. of destination rectangle's upper-left corner 

   Y: Integer,       // x-coord. of destination rectangle's upper-left corner 

   Width: Integer,   // width of destination rectangle 

   Height: Integer,  // height of destination rectangle 

   SrcDC: HDC,       // handle of the source canvas 

   XSrc: Integer,    // x-coordinate of source rectangle's upper-left corner   

   YSrc: Integer,    // y-coordinate of source rectangle's upper-left corner 

   Rop:Cardinal );   // raster operation code (AND, NOT, INVERT, COPY...) 

 

//    Enough with the talking... Let's go into THE THING: 

 

  BitBlt(Form1.Canvas.Handle ,X ,Y ,Sprite.Width ,Sprite.Height, Mask.Canvas.Handle ,0 ,0 ,SrcAnd); 

  BitBlt(Form1.Canvas.Handle, X ,Y ,Sprite.Width ,Sprite.Height, Sprite.Canvas.Handle ,0 ,0 ,SrcInvert); 

{

   Yeah... with those two lines and a mask you can draw transparent image. That is FAAASSSTTTTT::::: 

 

   If you want a demo to that, write me at: cristim@elsaco.com , and i'll give it to you. Later i'll post a note at the address where you can get it. 

 

   You can use this technique to draw game sprites, interfaces, etc. I used this technique to make a RTS game :drawing units, animating them, drawing cursor, game interface, menus,... but the possibilities are unlimited. 

 

   I'll post another article about animations and states with BitBlt (you'll love this word) 

 

   If you have any questions, thoughts or demos you'll like to share please mail me: cristim@elsaco.com 

}