{************************************************************}
{ }
{ Pixelgenaue Kollision Abfrage mit Bitmaske }
{ }
{ Copyright (c) 2007 Henning Brackmann [url]www.blubplayer.de[/url] }
{ }
{************************************************************}
unit U_KollisionAbfrage;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls;
type
TBildBitMaske =
class
private
SegmentSize: integer;
//Segment der Maske in Bit
procedure initMaske;
procedure getMaske(aBild: TBitmap);
function isKollisionWidth(x1, y1, x2, y2: integer;
Bild2: TBildBitMaske): boolean;
public
Maske:
array of array of Cardinal;
width,height: integer;
widthSegmentCount: integer;
constructor create(aBild: TBitmap);
end;
implementation
function RectinRect(rect1,rect2: Trect): boolean;
begin
result := true;
if (rect1.Left >= rect2.BottomRight.x)
then result:=false;
if (rect1.top >= rect2.BottomRight.y)
then result:=false;
if (rect2.Left >= rect1.BottomRight.x)
then result:=false;
if (rect2.top >= rect1.BottomRight.y)
then result:=false;
end;
{ TBildBitMaske }
constructor TBildBitMaske.create(aBild: TBitmap);
begin
width := aBild.width;
height := aBild.height;
SegmentSize := sizeof(Cardinal)*8;
initMaske;
getMaske(aBild);
end;
procedure TBildBitMaske.initMaske;
var
tempWidth,tempHeight: integer;
x,y: integer;
begin
//Unterscheidung ob Rest oder nicht
//Bei Rest würde sonst Addition von 1 falsches Ergebnis ergeben
if (Width
mod SegmentSize) = 0
then
tempWidth := Width
div SegmentSize
else
tempWidth := trunc(Width/SegmentSize)+1;
widthSegmentCount := tempWidth;
tempHeight := Height;
setlength(Maske,tempWidth,tempHeight);
for x := 0
to tempWidth - 1
do
for y := 0
to tempHeight - 1
do
Maske[x,y]:=0;
end;
procedure TBildBitMaske.getMaske(aBild: TBitmap);
type
PixArray =
Array [1..3]
of byte;
var
p: ^PixArray;
x,y: integer;
Color: longint;
Bild: TBitmap;
Segment: Cardinal;
begin
Bild:= TBitmap.create;
Bild.Assign(aBild);
aBild.PixelFormat := pf24bit;
//Reihenfolge (Scanline) der Farbwerte pro Pixel: Blau - Grün - Rot.
Color:=ColortoRGB(aBild.TransparentColor);
for y:=0
to bild.Height-1
do
begin
p:= bild.ScanLine[y];
for x:=bild.Width-1
downto 0
do //downto wegen or 1 und nicht and 100000...
begin
Segment := Maske[widthSegmentCount-1 - (x
div SegmentSize),y];
if (GetBValue(Color)=p^[1])
and (GetGValue(Color)=p^[2])
and (GetRValue(Color)=p^[3])
then
begin
//transparentefarbe --> 0
Segment := Segment
shl 1;
end
else
begin
//nicht transparentefarbe --> 1
Segment := Segment
shl 1;
Segment := Segment
or 1;
end;
Maske[widthSegmentCount-1 - (x
div SegmentSize),y] := Segment;
Inc(p);
end;
end;
Bild.free;
end;
function TBildBitMaske.isKollisionWidth(x1,y1: integer; x2,y2: integer; Bild2: TBildBitMaske): boolean;
var
y1start,y1ende: integer;
y2start,y2ende: integer;
x1SegmentStart,x1SegmentEnde: integer;
x2SegmentStart,x2SegmentEnde: integer;
tempSegment: Cardinal;
x,y: integer;
shiftcountRight: integer;
shiftcountLeft: integer;
indexLeftBild1Segment: integer;
indexRightBild1Segment: integer;
Bild1CalcWidth,Bild2CalcWidth: integer;
SchnittRect: TRect;
Bild1SchnittRect: TRect;
Bild2SchnittRect: TRect;
begin
if RectinRect(Rect(x1,y1,x1+width,y1+height),Rect(x2,y2,x2+Bild2.width,y2+Bild2.height))
then
begin
//Koordinaten umrechnen durch die einteilung in elemnet
//ist Bildbreite immer vielfaches von 32 Also muss Von der normalen breite
//umgerechnet werden
x1:=x1-(widthSegmentCount*SegmentSize-Width);
x2:=x2-(Bild2.widthSegmentCount*SegmentSize-Bild2.Width);
//Breite umrechnen
Bild1CalcWidth := self.widthSegmentCount*SegmentSize;
Bild2CalcWidth := Bild2.widthSegmentCount*SegmentSize;
IntersectRect(SchnittRect,Rect(x1,y1,x1+Bild1CalcWidth,y1+height),Rect(x2,y2,x2+Bild2CalcWidth,y2+Bild2.height));
Bild1SchnittRect := Rect(SchnittRect.Left-x1,SchnittRect.Top-y1,SchnittRect.Right-x1-1,SchnittRect.Bottom-y1-1);
Bild2SchnittRect := Rect(SchnittRect.Left-x2,SchnittRect.Top-y2,SchnittRect.Right-x2-1,SchnittRect.Bottom-y2-1);
y1start := Bild1SchnittRect.top;
y1ende := Bild1SchnittRect.bottom;
y2start := Bild2SchnittRect.top;
y2ende := Bild2SchnittRect.bottom;
x1SegmentStart := Bild1SchnittRect.Left
div Segmentsize;
x1SegmentEnde := Bild1SchnittRect.Right
div Segmentsize;
x2SegmentStart := Bild2SchnittRect.Left
div Segmentsize;
x2SegmentEnde := Bild2SchnittRect.Right
div Segmentsize;
shiftcountRight := (Bild2CalcWidth+(x2-x1))
mod Segmentsize;
shiftcountLeft := Segmentsize-shiftcountRight;
result := false;
for x := x2SegmentStart
to x2SegmentEnde
do
begin
if (x2+x*Segmentsize)>=(x1+x1SegmentStart*Segmentsize)
then
begin //Es gibt links vom Element ein Bild1 element
indexLeftBild1Segment := (x2+(x)*Segmentsize-x1)
div Segmentsize;
for y := y2start
to y2ende
do
begin
tempSegment := (Bild2.maske[x,y]
shr shiftcountRight);
if (Maske[indexLeftBild1Segment,y1start+y-y2start]
and tempSegment)<>0
then
begin
result:=true;
exit;
end;
end;
end;
if (x2+x*Segmentsize)<=(x1+x1SegmentEnde*Segmentsize)
then
begin //Es gibt rechts vom Element ein Bild1 element
indexRightBild1Segment := ((x2-1-x1+SegmentSize+(x*Segmentsize))
div Segmentsize);
for y := y2start
to y2ende
do
begin
tempSegment := (Bild2.maske[x,y]
shl shiftcountLeft);
if (Maske[indexRightBild1Segment,y1start+y-y2start]
and tempSegment)<>0
then
begin
result:=true;
exit;
end;
end;
end;
end;
end;
end;
end.