fruit-popper/GDLIB/GDCLIP.PAS
2021-07-07 17:10:18 -04:00

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.