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(0, 0, 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, 0, 0, SRCAND); BitBlt(DestBitmap.Canvas.Handle, x,y, SrcBitmap.Width, SrcBitmap.Height, SrcBitmap.Canvas.Handle, 0, 0, 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, 1, 1, NIL); BmAndObject := CreateBitmap(PtSize.X, PtSize.Y, 1, 1, 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, 0, 0, PtSize.X, PtSize.Y, HdcTemp, 0, 0, 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, 0, 0, PtSize.X, PtSize.Y, HdcTemp, 0, 0, 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, 0, 0, PtSize.X, PtSize.Y, HdcObject, 0, 0, NotSrcCopy);
// Copy the background of the main DC to the destination BitBlt (HdcMem, 0, 0, PtSize.X, PtSize.Y, Ahdc, XStart, YStart, SrcCopy);
// Mask out the places where the bitmap will be placed BitBlt (HdcMem, 0, 0, PtSize.X, PtSize.Y, HdcObject, 0, 0, SrcAnd);
// Mask out the transparent colored pixels on the bitmap BitBlt (HdcTemp, 0, 0, PtSize.X, PtSize.Y, hdcBack, 0, 0, SrcAnd);
// Xor the bitmap with the background on the destination DC BitBlt (HdcMem, 0, 0, PtSize.X, PtSize.Y, hdcTemp, 0, 0, SrcPaint);
// Copy the destination to the screen BitBlt (Ahdc, XStart, YStart, PtSize.X, PtSize.Y, HdcMem, 0, 0, SrcCopy);
// Place the original bitmap back into the bitmap sent here BitBlt (HdcTemp, 0, 0, PtSize.X, PtSize.Y, HdcSave, 0, 0, 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, 66, 18);
// 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 } |