You can use region functions from WinApi.
Here are example for simple triangle:
function PtInTriangle(ptX,ptY,X1,Y1,X2,Y2,X3,Y3:integer):Boolean;
var rgn:THandle; pts:array [0..2] of TPoint;
begin
pts[0].X:=X1; pts[0].Y:=Y1;
pts[1].X:=X2; pts[1].Y:=Y2;
pts[2].X:=X3; pts[2].Y:=Y3;
rgn := CreatePolygonRgn( pts[0], 3, WINDING);
Result := PtInRegion(rgn, ptX, ptY);
DeleteObject(rgn);
end;
This function takes about ~30..40us on my machine, and PtInRegion() takes only ~10% of this time (so, you can optimize it by caching Region object). Here are code with simple bencmark:
function PtInTriangle(ptX,ptY,X1,Y1,X2,Y2,X3,Y3:integer):Boolean;
var rgn:THandle; pts:array [0..2] of TPoint;
t,t1,t2,t3:Int64;
begin
// Create region
QueryPerformanceCounter(t);
pts[0].X:=X1; pts[0].Y:=Y1;
pts[1].X:=X2; pts[1].Y:=Y2;
pts[2].X:=X3; pts[2].Y:=Y3;
rgn := CreatePolygonRgn( pts[0], 3, WINDING);
QueryPerformanceCounter(t1); Dec(t1,t);
// Check point
QueryPerformanceCounter(t);
Result := PtInRegion(rgn, ptX, ptY);
QueryPerformanceCounter(t2); Dec(t2,t);
// Delete region
QueryPerformanceCounter(t);
DeleteObject(rgn);
QueryPerformanceCounter(t3); Dec(t3,t);
// Debug output
QueryPerformanceFrequency(t);
OutputDebugString(PChar(Format('All:%d(%.1fus) Create:%d PtInRect:%d(%.1f%%) Delete:%d',
[t1+t2+t3,(t1+t2+t3)/t*1E6,t1,t2,t2*100/(t1+t2+t3),t3])));
end;
Also, you can create complex regions with CreatePolyPolygonRgn() or CombineRgn().