393 lines
11 KiB
Plaintext
393 lines
11 KiB
Plaintext
{ GDlib Coordinate/Region/Screen Clipping Utilities
|
|
Gered King, 2018 }
|
|
|
|
{$A+,B-,F-,G+,I-,N+,P-,Q-,R-,S-,T-,V-,X+}
|
|
|
|
unit GDClip;
|
|
|
|
interface
|
|
|
|
function IsPointInScreen(x, y: integer) : boolean;
|
|
function IsPointInClipRegion(x, y: integer) : boolean;
|
|
function IsRegionInScreen(x1, y1, x2, y2: integer) : boolean;
|
|
function IsRegionInClipRegion(x1, y1, x2, y2: integer) : boolean;
|
|
function ClampToScreen(var x1, y1, x2, y2: integer) : boolean;
|
|
function ClampToClipRegion(var x1, y1, x2, y2: integer) : boolean;
|
|
function ClipToScreen(var x, y, width, height: integer) : boolean;
|
|
function ClipToClipRegion(var x, y, width, height: integer) : boolean;
|
|
function ClipBlitToScreen(var srcX, srcY, srcWidth, srcHeight, destX, destY: integer) : boolean;
|
|
function ClipBlitToClipRegion(var srcX, srcY, srcWidth, srcHeight, destX, destY: integer) : boolean;
|
|
|
|
implementation
|
|
|
|
uses GDGfx;
|
|
|
|
function IsPointInScreen(x, y: integer) : boolean;
|
|
{ returns true if the given point is within the screen boundaries }
|
|
begin
|
|
IsPointInScreen := ((x < SCREEN_LEFT)
|
|
or (y < SCREEN_TOP)
|
|
or (x > SCREEN_RIGHT)
|
|
or (y > SCREEN_BOTTOM));
|
|
end;
|
|
|
|
function IsPointInClipRegion(x, y: integer) : boolean;
|
|
{ returns true if the given point is within the current clipping region }
|
|
begin
|
|
IsPointInClipRegion := ((x < ClipRegionLeft)
|
|
or (y < ClipRegionTop)
|
|
or (x > ClipRegionRight)
|
|
or (y > ClipRegionBottom));
|
|
end;
|
|
|
|
function IsRegionInScreen(x1, y1, x2, y2: integer) : boolean;
|
|
{ returns true if the given region is partially or completely within the
|
|
screen boundaries }
|
|
begin
|
|
IsRegionInScreen := false;
|
|
|
|
if (y1 < SCREEN_TOP) and (y2 < SCREEN_TOP) then
|
|
exit;
|
|
if (y1 > SCREEN_BOTTOM) and (y2 > SCREEN_BOTTOM) then
|
|
exit;
|
|
if (x1 < SCREEN_LEFT) and (x2 < SCREEN_LEFT) then
|
|
exit;
|
|
if (x1 > SCREEN_RIGHT) and (x2 > SCREEN_RIGHT) then
|
|
exit;
|
|
|
|
IsRegionInScreen := true;
|
|
end;
|
|
|
|
function IsRegionInClipRegion(x1, y1, x2, y2: integer) : boolean;
|
|
{ returns true if the given region is partially or completely within the
|
|
current clipping region }
|
|
begin
|
|
IsRegionInClipRegion := false;
|
|
|
|
if (y1 < ClipRegionTop) and (y2 < ClipRegionTop) then
|
|
exit;
|
|
if (y1 > ClipRegionBottom) and (y2 > ClipRegionBottom) then
|
|
exit;
|
|
if (x1 < ClipRegionLeft) and (x2 < ClipRegionLeft) then
|
|
exit;
|
|
if (x1 > ClipRegionRight) and (x2 > ClipRegionRight) then
|
|
exit;
|
|
|
|
IsRegionInClipRegion := true;
|
|
end;
|
|
|
|
function ClampToScreen(var x1, y1, x2, y2: integer) : boolean;
|
|
{ if the given region is visible on screen (either partially or completely)
|
|
the coordinates are checked individually against each edge of the screen
|
|
and adjusted if need-be to keep them within bounds. if the region is not
|
|
visible at all, then nothing is changed and false is returned. }
|
|
begin
|
|
ClampToScreen := false;
|
|
|
|
if (not IsRegionInScreen(x1, y1, x2, y2)) then
|
|
exit;
|
|
|
|
{ we now know the given region is at least partially visible }
|
|
ClampToScreen := true;
|
|
|
|
if (y1 < SCREEN_TOP) then
|
|
y1 := SCREEN_TOP;
|
|
if (y1 > SCREEN_BOTTOM) then
|
|
y1 := SCREEN_BOTTOM;
|
|
if (y2 < SCREEN_TOP) then
|
|
y2 := SCREEN_TOP;
|
|
if (y2 > SCREEN_BOTTOM) then
|
|
y2 := SCREEN_BOTTOM;
|
|
if (x1 < SCREEN_LEFT) then
|
|
x1 := SCREEN_LEFT;
|
|
if (x1 > SCREEN_RIGHT) then
|
|
x1 := SCREEN_RIGHT;
|
|
if (x2 < SCREEN_LEFT) then
|
|
x2 := SCREEN_LEFT;
|
|
if (x2 > SCREEN_RIGHT) then
|
|
x2 := SCREEN_RIGHT;
|
|
end;
|
|
|
|
function ClampToClipRegion(var x1, y1, x2, y2: integer) : boolean;
|
|
{ if the given region is within the current clipping region (either
|
|
partially or completely) the coordinates are checked individually
|
|
against each edge of the clipping region and adjusted if need-be to keep
|
|
them within bounds. if the region is not visible at all, then nothing is
|
|
changed and false is returned. }
|
|
begin
|
|
ClampToClipRegion := false;
|
|
|
|
if (not IsRegionInClipRegion(x1, y1, x2, y2)) then
|
|
exit;
|
|
|
|
{ we now know the given region is at least partially visible }
|
|
ClampToClipRegion := true;
|
|
|
|
if (y1 < ClipRegionTop) then
|
|
y1 := ClipRegionTop;
|
|
if (y1 > ClipRegionBottom) then
|
|
y1 := ClipRegionBottom;
|
|
if (y2 < ClipRegionTop) then
|
|
y2 := ClipRegionTop;
|
|
if (y2 > ClipRegionBottom) then
|
|
y2 := ClipRegionBottom;
|
|
if (x1 < ClipRegionLeft) then
|
|
x1 := ClipRegionLeft;
|
|
if (x1 > ClipRegionRight) then
|
|
x1 := ClipRegionRight;
|
|
if (x2 < ClipRegionLeft) then
|
|
x2 := ClipRegionLeft;
|
|
if (x2 > ClipRegionRight) then
|
|
x2 := ClipRegionRight;
|
|
end;
|
|
|
|
function ClipToScreen(var x, y, width, height: integer) : boolean;
|
|
{ clips a region to the screen by adjusting the top-left x,y coordinate
|
|
and/or the width/height of the region as appropriate. returns false if a
|
|
"completely out of bounds" scenario was encountered. returns true if the
|
|
region was completely in bounds (no clipping/clamping needed) or if the
|
|
region was clipped to the screen and is still partially in bounds. }
|
|
var
|
|
right, bottom, offset : integer;
|
|
begin
|
|
ClipToScreen := false;
|
|
|
|
right := x + width - 1;
|
|
bottom := y + height - 1;
|
|
|
|
{ off the left edge? }
|
|
if x < SCREEN_LEFT then begin
|
|
{ completely off the left edge? }
|
|
if right < SCREEN_LEFT then
|
|
exit;
|
|
|
|
offset := SCREEN_LEFT - x;
|
|
inc(x, offset);
|
|
dec(width, offset);
|
|
end;
|
|
|
|
{ off the right edge? }
|
|
if x > (SCREEN_WIDTH - width) then begin
|
|
{ completely off the right edge? }
|
|
if x > SCREEN_RIGHT then
|
|
exit;
|
|
|
|
offset := x + width - SCREEN_WIDTH;
|
|
dec(width, offset);
|
|
end;
|
|
|
|
{ off the top edge? }
|
|
if y < SCREEN_TOP then begin
|
|
{ completely off the top edge? }
|
|
if bottom < SCREEN_TOP then
|
|
exit;
|
|
|
|
offset := SCREEN_TOP - y;
|
|
inc(y, offset);
|
|
dec(height, offset);
|
|
end;
|
|
|
|
{ off the bottom edge? }
|
|
if y > (SCREEN_HEIGHT - height) then begin
|
|
{ completely off the bottom edge? }
|
|
if y > SCREEN_BOTTOM then
|
|
exit;
|
|
|
|
offset := y + height - SCREEN_HEIGHT;
|
|
dec(height, offset);
|
|
end;
|
|
|
|
ClipToScreen := true;
|
|
end;
|
|
|
|
function ClipToClipRegion(var x, y, width, height: integer) : boolean;
|
|
{ clips a region to the current clipping region by adjusting the top-left
|
|
x,y coordinate and/or the width/height of the region as appropriate.
|
|
returns false if a "completely out of bounds" scenario was encountered.
|
|
returns true if the region was completely in bounds (no clipping/clamping
|
|
needed) or if the region was clipped to the clipping region and is still
|
|
partially in bounds. }
|
|
var
|
|
right, bottom, offset : integer;
|
|
begin
|
|
ClipToClipRegion := false;
|
|
|
|
right := x + width - 1;
|
|
bottom := y + height - 1;
|
|
|
|
{ off the left edge? }
|
|
if x < ClipRegionLeft then begin
|
|
{ completely off the left edge? }
|
|
if right < ClipRegionLeft then
|
|
exit;
|
|
|
|
offset := ClipRegionLeft - x;
|
|
inc(x, offset);
|
|
dec(width, offset);
|
|
end;
|
|
|
|
{ off the right edge? }
|
|
if (x - ClipRegionLeft) > (ClipRegionWidth - width) then begin
|
|
{ completely off the right edge? }
|
|
if x > ClipRegionRight then
|
|
exit;
|
|
|
|
offset := (x - ClipRegionLeft) + (width - ClipRegionWidth);
|
|
dec(width, offset);
|
|
end;
|
|
|
|
{ off the top edge? }
|
|
if y < ClipRegionTop then begin
|
|
{ completely off the top edge? }
|
|
if bottom < ClipRegionTop then
|
|
exit;
|
|
|
|
offset := ClipRegionTop - y;
|
|
inc(y, offset);
|
|
dec(height, offset);
|
|
end;
|
|
|
|
{ off the bottom edge? }
|
|
if (y - ClipRegionTop) > (ClipRegionHeight - height) then begin
|
|
{ completely off the bottom edge? }
|
|
if y > ClipRegionBottom then
|
|
exit;
|
|
|
|
offset := (y - ClipRegionTop) + (height - ClipRegionHeight);
|
|
dec(height, offset);
|
|
end;
|
|
|
|
ClipToClipRegion := true;
|
|
end;
|
|
|
|
function ClipBlitToScreen(var srcX, srcY: integer;
|
|
var srcWidth, srcHeight: integer;
|
|
var destX, destY: integer) : boolean;
|
|
{ clips a source bitmap blit region to the screen by adjusting the source
|
|
blit region and the destination x,y coordinate as appropriate.
|
|
returns false if a "completely out of bounds" scenario was encountered.
|
|
returns true if the region was either completely in bounds (no
|
|
clipping/clamping needed), or if the region was clipped to the screen
|
|
and is still partially in bounds. }
|
|
var
|
|
right, bottom, offset : integer;
|
|
begin
|
|
ClipBlitToScreen := false;
|
|
|
|
right := srcX + srcWidth - 1;
|
|
bottom := srcY + srcHeight - 1;
|
|
|
|
{ off the left edge? }
|
|
if destX < SCREEN_LEFT then begin
|
|
{ completely off the left edge? }
|
|
if (destX + srcWidth - 1) < SCREEN_LEFT then
|
|
exit;
|
|
|
|
offset := srcX - destX;
|
|
destX := SCREEN_LEFT;
|
|
inc(srcX, offset);
|
|
dec(srcWidth, offset);
|
|
end;
|
|
|
|
{ off the right edge? }
|
|
if destX > (SCREEN_WIDTH - srcWidth) then begin
|
|
{ completely off the right edge? }
|
|
if destX > SCREEN_RIGHT then
|
|
exit;
|
|
|
|
offset := destX + srcWidth - SCREEN_WIDTH;
|
|
dec(srcWidth, offset);
|
|
end;
|
|
|
|
{ off the top edge? }
|
|
if destY < SCREEN_TOP then begin
|
|
{ completely off the top edge? }
|
|
if (destY + srcHeight - 1) < SCREEN_TOP then
|
|
exit;
|
|
|
|
offset := srcY - destY;
|
|
destY := SCREEN_TOP;
|
|
inc(srcY, offset);
|
|
dec(srcHeight, offset);
|
|
end;
|
|
|
|
{ off the bottom edge? }
|
|
if destY > (SCREEN_HEIGHT - srcHeight) then begin
|
|
{ completely off the bottom edge? }
|
|
if destY > SCREEN_BOTTOM then
|
|
exit;
|
|
|
|
offset := destY + srcHeight - SCREEN_HEIGHT;
|
|
dec(srcHeight, offset);
|
|
end;
|
|
|
|
ClipBlitToScreen := true;
|
|
end;
|
|
|
|
function ClipBlitToClipRegion(var srcX, srcY: integer;
|
|
var srcWidth, srcHeight: integer;
|
|
var destX, destY: integer) : boolean;
|
|
{ clips a source bitmap blit region to the current clipping region by
|
|
adjusting the source blit region and the destination x,y coordinate as
|
|
appropriate.
|
|
returns false if a "completely out of bounds" scenario was encountered.
|
|
returns true if the region was either completely in bounds (no
|
|
clipping/clamping needed), or if the region was clipped to the clipping
|
|
region and is still partially in bounds. }
|
|
var
|
|
right, bottom, offset : integer;
|
|
begin
|
|
ClipBlitToClipRegion := false;
|
|
|
|
right := srcX + srcWidth - 1;
|
|
bottom := srcY + srcHeight - 1;
|
|
|
|
{ off the left edge? }
|
|
if destX < ClipRegionLeft then begin
|
|
{ completely off the left edge? }
|
|
if (destX + srcWidth - 1) < ClipRegionLeft then
|
|
exit;
|
|
|
|
offset := srcX - destX;
|
|
destX := ClipRegionLeft;
|
|
inc(srcX, offset);
|
|
dec(srcWidth, offset);
|
|
end;
|
|
|
|
{ off the right edge? }
|
|
if destX > (ClipRegionWidth - srcWidth) then begin
|
|
{ completely off the right edge? }
|
|
if destX > ClipRegionRight then
|
|
exit;
|
|
|
|
offset := destX + srcWidth - ClipRegionWidth;
|
|
dec(srcWidth, offset);
|
|
end;
|
|
|
|
{ off the top edge? }
|
|
if destY < ClipRegionTop then begin
|
|
{ completely off the top edge? }
|
|
if (destY + srcHeight - 1) < ClipRegionTop then
|
|
exit;
|
|
|
|
offset := srcY - destY;
|
|
destY := ClipRegionTop;
|
|
inc(srcY, offset);
|
|
dec(srcHeight, offset);
|
|
end;
|
|
|
|
{ off the bottom edge? }
|
|
if destY > (ClipRegionHeight - srcHeight) then begin
|
|
{ completely off the bottom edge? }
|
|
if destY > ClipRegionBottom then
|
|
exit;
|
|
|
|
offset := destY + srcHeight - ClipRegionHeight;
|
|
dec(srcHeight, offset);
|
|
end;
|
|
|
|
ClipBlitToClipRegion := true;
|
|
end;
|
|
|
|
end.
|