mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-04-20 06:59:42 +02:00
1170 lines
31 KiB
ObjectPascal
1170 lines
31 KiB
ObjectPascal
{
|
|
$Id$
|
|
This file is part of the Free Pascal run time library.
|
|
Copyright (c) 2003 by the Free Pascal development team
|
|
|
|
Pixel drawing routines.
|
|
|
|
See the file COPYING.FPC, included in this distribution,
|
|
for details about the copyright.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
|
|
**********************************************************************}
|
|
{$mode objfpc}{$h+}
|
|
unit PixTools;
|
|
|
|
interface
|
|
|
|
uses classes, FPCanvas, FPimage;
|
|
|
|
procedure DrawSolidLine (Canv : TFPCustomCanvas; x1,y1, x2,y2:integer; const color:TFPColor);
|
|
procedure DrawPatternLine (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; Pattern:TPenPattern; const color:TFPColor);
|
|
procedure FillRectangleColor (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const color:TFPColor);
|
|
procedure FillRectanglePattern (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const pattern:TBrushPattern; const color:TFPColor);
|
|
procedure FillRectangleHashHorizontal (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
procedure FillRectangleHashVertical (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
procedure FillRectangleHashDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
procedure FillRectangleHashBackDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
procedure FillFloodColor (Canv:TFPCustomCanvas; x,y:integer; const color:TFPColor);
|
|
procedure FillFloodPattern (Canv:TFPCustomCanvas; x,y:integer; const pattern:TBrushPattern; const color:TFPColor);
|
|
procedure FillFloodHashHorizontal (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
procedure FillFloodHashVertical (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
procedure FillFloodHashDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
procedure FillFloodHashBackDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
procedure FillFloodHashDiagCross (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
procedure FillFloodHashCross (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
|
|
procedure DrawSolidLine (Canv : TFPCustomCanvas; x1,y1, x2,y2:integer);
|
|
procedure DrawPatternLine (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; Pattern:TPenPattern);
|
|
procedure FillRectanglePattern (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const pattern:TBrushPattern);
|
|
procedure FillRectangleColor (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer);
|
|
procedure FillRectangleHashHorizontal (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
procedure FillRectangleHashVertical (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
procedure FillRectangleHashDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
procedure FillRectangleHashBackDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
procedure FillFloodColor (Canv:TFPCustomCanvas; x,y:integer);
|
|
procedure FillFloodPattern (Canv:TFPCustomCanvas; x,y:integer; const pattern:TBrushPattern);
|
|
procedure FillFloodHashHorizontal (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
procedure FillFloodHashVertical (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
procedure FillFloodHashDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
procedure FillFloodHashBackDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
procedure FillFloodHashDiagCross (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
procedure FillFloodHashCross (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
|
|
procedure FillRectangleImage (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const Image:TFPCustomImage);
|
|
procedure FillRectangleImageRel (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const Image:TFPCustomImage);
|
|
procedure FillFloodImage (Canv:TFPCustomCanvas; x,y :integer; const Image:TFPCustomImage);
|
|
procedure FillFloodImageRel (Canv:TFPCustomCanvas; x,y :integer; const Image:TFPCustomImage);
|
|
|
|
implementation
|
|
|
|
uses clipping, ellipses;
|
|
|
|
procedure FillRectangleColor (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer);
|
|
begin
|
|
FillRectangleColor (Canv, x1,y1, x2,y2, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillRectangleColor (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const color:TFPColor);
|
|
var x,y : integer;
|
|
begin
|
|
SortRect (x1,y1, x2,y2);
|
|
with Canv do
|
|
begin
|
|
for x := x1 to x2 do
|
|
for y := y1 to y2 do
|
|
colors[x,y] := color;
|
|
end;
|
|
end;
|
|
|
|
{procedure DrawSolidPolyLine (Canv : TFPCustomCanvas; points:array of TPoint; close:boolean);
|
|
var i,a, r : integer;
|
|
p : TPoint;
|
|
begin
|
|
i := low(points);
|
|
a := high(points);
|
|
p := points[i];
|
|
with Canv do
|
|
begin
|
|
for r := i+1 to a do
|
|
begin
|
|
Line (p.x, p.y, points[r].x, points[r].y);
|
|
p := points[r];
|
|
end;
|
|
if close then
|
|
Line (p.x,p.y, points[i].x,points[i].y);
|
|
end;
|
|
end;
|
|
}
|
|
type
|
|
TPutPixelProc = procedure (Canv:TFPCustomCanvas; x,y:integer; color:TFPColor);
|
|
|
|
procedure PutPixelCopy(Canv:TFPCustomCanvas; x,y:integer; color:TFPColor);
|
|
begin
|
|
with Canv do
|
|
Colors[x,y] := color;
|
|
end;
|
|
|
|
procedure PutPixelXor(Canv:TFPCustomCanvas; x,y:integer; color:TFPColor);
|
|
begin
|
|
with Canv do
|
|
Colors[x,y] := Colors[x,y] xor color;
|
|
end;
|
|
|
|
procedure PutPixelOr(Canv:TFPCustomCanvas; x,y:integer; color:TFPColor);
|
|
begin
|
|
with Canv do
|
|
Colors[x,y] := Colors[x,y] or color;
|
|
end;
|
|
|
|
procedure PutPixelAnd(Canv:TFPCustomCanvas; x,y:integer; color:TFPColor);
|
|
begin
|
|
with Canv do
|
|
Colors[x,y] := Colors[x,y] and color;
|
|
end;
|
|
|
|
procedure DrawSolidLine (Canv : TFPCustomCanvas; x1,y1, x2,y2:integer);
|
|
begin
|
|
DrawSolidLine (Canv, x1,y1, x2,y2, Canv.Pen.FPColor);
|
|
end;
|
|
|
|
procedure DrawSolidLine (Canv : TFPCustomCanvas; x1,y1, x2,y2:integer; const color:TFPColor);
|
|
var PutPixelProc : TPutPixelProc;
|
|
procedure HorizontalLine (x1,x2,y:integer);
|
|
var x : integer;
|
|
begin
|
|
for x := x1 to x2 do
|
|
PutPixelProc (Canv, x,y, color);
|
|
end;
|
|
procedure VerticalLine (x,y1,y2:integer);
|
|
var y : integer;
|
|
begin
|
|
for y := y1 to y2 do
|
|
PutPixelProc (Canv, x,y, color);
|
|
end;
|
|
procedure SlopedLine;
|
|
var npixels,xinc1,yinc1,xinc2,yinc2,dx,dy,d,dinc1,dinc2 : integer;
|
|
procedure initialize;
|
|
begin // precalculations
|
|
dx := abs(x2-x1);
|
|
dy := abs(y2-y1);
|
|
if dx > dy then // determining independent variable
|
|
begin // x is independent
|
|
npixels := dx + 1;
|
|
d := (2 * dy) - dx;
|
|
dinc1 := dy * 2;
|
|
dinc2:= (dy - dx) * 2;
|
|
xinc1 := 1;
|
|
xinc2 := 1;
|
|
yinc1 := 0;
|
|
yinc2 := 1;
|
|
end
|
|
else
|
|
begin // y is independent
|
|
npixels := dy + 1;
|
|
d := (2 * dx) - dy;
|
|
dinc1 := dx * 2;
|
|
dinc2:= (dx - dy) * 2;
|
|
xinc1 := 0;
|
|
xinc2 := 1;
|
|
yinc1 := 1;
|
|
yinc2 := 1;
|
|
end;
|
|
// going into the correct direction
|
|
if x1 > x2 then
|
|
begin
|
|
xinc1 := - xinc1;
|
|
xinc2 := - xinc2;
|
|
end;
|
|
if y1 > y2 then
|
|
begin
|
|
yinc1 := - yinc1;
|
|
yinc2 := - yinc2;
|
|
end;
|
|
end;
|
|
var r,x,y : integer;
|
|
begin
|
|
initialize;
|
|
x := x1;
|
|
y := y1;
|
|
for r := 1 to nPixels do
|
|
begin
|
|
PutPixelProc (Canv, x,y, color);
|
|
if d < 0 then
|
|
begin
|
|
d := d + dinc1;
|
|
x := x + xinc1;
|
|
y := y + yinc1;
|
|
end
|
|
else
|
|
begin
|
|
d := d + dinc2;
|
|
x := x + xinc2;
|
|
y := y + yinc2;
|
|
end;
|
|
end;
|
|
end;
|
|
begin
|
|
with canv.pen do
|
|
case mode of
|
|
pmMerge : PutPixelProc := @PutPixelAnd;
|
|
pmMask : PutPixelProc := @PutPixelOr;
|
|
pmXor : PutPixelProc := @PutPixelXor;
|
|
else PutPixelProc := @PutPixelCopy;
|
|
end;
|
|
if x1 = x2 then // vertical line
|
|
if y1 < y2 then
|
|
VerticalLine (x1, y1, y2)
|
|
else
|
|
VerticalLine (x1, y2, y1)
|
|
else if y1 = y2 then
|
|
if x1 < x2 then
|
|
HorizontalLine (x1, x2, y1)
|
|
else
|
|
HorizontalLine (x2, x1, y1)
|
|
else // sloped line
|
|
SlopedLine;
|
|
end;
|
|
|
|
type
|
|
TLinePoints = array[0..PatternBitCount-1] of boolean;
|
|
PLinePoints = ^TLinePoints;
|
|
|
|
procedure PatternToPoints (const APattern:TPenPattern; LinePoints:PLinePoints);
|
|
var r : integer;
|
|
i : longword;
|
|
begin
|
|
i := 1;
|
|
for r := PatternBitCount-1 downto 1 do
|
|
begin
|
|
LinePoints^[r] := (APattern and i) <> 0;
|
|
i := i shl 1;
|
|
end;
|
|
LinePoints^[0] := (APattern and i) <> 0;
|
|
end;
|
|
|
|
procedure DrawPatternLine (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; Pattern:TPenPattern);
|
|
begin
|
|
DrawPatternLine (Canv, x1,y1, x2,y2, pattern, Canv.Pen.FPColor);
|
|
end;
|
|
|
|
procedure DrawPatternLine (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; Pattern:TPenPattern; const color:TFPColor);
|
|
// Is copy of DrawSolidLine with paterns added. Not the same procedure for faster solid lines
|
|
var LinePoints : TLinePoints;
|
|
PutPixelProc : TPutPixelProc;
|
|
procedure HorizontalLine (x1,x2,y:integer);
|
|
var x : integer;
|
|
begin
|
|
for x := x1 to x2 do
|
|
if LinePoints[x mod PatternBitCount] then
|
|
PutPixelProc (Canv, x,y, color);
|
|
end;
|
|
procedure VerticalLine (x,y1,y2:integer);
|
|
var y : integer;
|
|
begin
|
|
for y := y1 to y2 do
|
|
if LinePoints[y mod PatternBitCount] then
|
|
PutPixelProc (Canv, x,y, color);
|
|
end;
|
|
procedure SlopedLine;
|
|
var npixels,xinc1,yinc1,xinc2,yinc2,dx,dy,d,dinc1,dinc2 : integer;
|
|
procedure initialize;
|
|
begin // precalculations
|
|
dx := abs(x2-x1);
|
|
dy := abs(y2-y1);
|
|
if dx > dy then // determining independent variable
|
|
begin // x is independent
|
|
npixels := dx + 1;
|
|
d := (2 * dy) - dx;
|
|
dinc1 := dy * 2;
|
|
dinc2:= (dy - dx) * 2;
|
|
xinc1 := 1;
|
|
xinc2 := 1;
|
|
yinc1 := 0;
|
|
yinc2 := 1;
|
|
end
|
|
else
|
|
begin // y is independent
|
|
npixels := dy + 1;
|
|
d := (2 * dx) - dy;
|
|
dinc1 := dx * 2;
|
|
dinc2:= (dx - dy) * 2;
|
|
xinc1 := 0;
|
|
xinc2 := 1;
|
|
yinc1 := 1;
|
|
yinc2 := 1;
|
|
end;
|
|
// going into the correct direction
|
|
if x1 > x2 then
|
|
begin
|
|
xinc1 := - xinc1;
|
|
xinc2 := - xinc2;
|
|
end;
|
|
if y1 > y2 then
|
|
begin
|
|
yinc1 := - yinc1;
|
|
yinc2 := - yinc2;
|
|
end;
|
|
end;
|
|
var r,x,y : integer;
|
|
begin
|
|
initialize;
|
|
x := x1;
|
|
y := y1;
|
|
for r := 1 to nPixels do
|
|
begin
|
|
if LinePoints[r mod PatternBitCount] then
|
|
PutPixelProc (Canv, x,y, color);
|
|
if d < 0 then
|
|
begin
|
|
d := d + dinc1;
|
|
x := x + xinc1;
|
|
y := y + yinc1;
|
|
end
|
|
else
|
|
begin
|
|
d := d + dinc2;
|
|
x := x + xinc2;
|
|
y := y + yinc2;
|
|
end;
|
|
end;
|
|
end;
|
|
begin
|
|
PatternToPoints (pattern, @LinePoints);
|
|
with canv.pen do
|
|
case mode of
|
|
pmMask : PutPixelProc := @PutPixelAnd;
|
|
pmMerge : PutPixelProc := @PutPixelOr;
|
|
pmXor : PutPixelProc := @PutPixelXor;
|
|
else PutPixelProc := @PutPixelCopy;
|
|
end;
|
|
if x1 = x2 then // vertical line
|
|
if y1 < y2 then
|
|
VerticalLine (x1, y1, y2)
|
|
else
|
|
VerticalLine (x1, y2, y1)
|
|
else if y1 = y2 then
|
|
if x1 < x2 then
|
|
HorizontalLine (x1, x2, y1)
|
|
else
|
|
HorizontalLine (x2, x1, y1)
|
|
else // sloped line
|
|
SlopedLine;
|
|
end;
|
|
|
|
procedure FillRectangleHashHorizontal (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
begin
|
|
FillRectangleHashHorizontal (Canv, rect, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillRectangleHashHorizontal (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
var y : integer;
|
|
begin
|
|
with rect do
|
|
begin
|
|
y := Width + top;
|
|
while y <= bottom do
|
|
begin
|
|
DrawSolidLine (Canv, left,y, right,y, c);
|
|
inc (y,Width);
|
|
end
|
|
end;
|
|
end;
|
|
|
|
procedure FillRectangleHashVertical (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
begin
|
|
FillRectangleHashVertical (Canv, rect, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillRectangleHashVertical (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
var x : integer;
|
|
begin
|
|
with rect do
|
|
begin
|
|
x := Width + left;
|
|
while x <= right do
|
|
begin
|
|
DrawSolidLine (Canv, x,top, x,bottom, c);
|
|
inc (x, Width);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure FillRectangleHashDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
begin
|
|
FillRectangleHashDiagonal (Canv, rect, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillRectangleHashDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
function CheckCorner (Current, max, start : integer) : integer;
|
|
begin
|
|
if Current > max then
|
|
result := Start + current - max
|
|
else
|
|
result := Start;
|
|
end;
|
|
var r, rx, ry : integer;
|
|
begin
|
|
with rect do
|
|
begin
|
|
// draw from bottom-left corner away
|
|
ry := top + Width;
|
|
rx := left + Width;
|
|
while (rx < right) and (ry < bottom) do
|
|
begin
|
|
DrawSolidLine (Canv, left,ry, rx,top, c);
|
|
inc (rx, Width);
|
|
inc (ry, Width);
|
|
end;
|
|
// check which turn need to be taken: left-bottom, right-top, or both
|
|
if (rx >= right) then
|
|
begin
|
|
if (ry >= bottom) then
|
|
begin // Both corners reached
|
|
r := CheckCorner (rx, right, top);
|
|
rx := CheckCorner (ry, bottom, left);
|
|
ry := r;
|
|
end
|
|
else
|
|
begin // fill vertical
|
|
r := CheckCorner (rx, right, top);
|
|
while (ry < bottom) do
|
|
begin
|
|
DrawSolidLine (Canv, left,ry, right,r, c);
|
|
inc (r, Width);
|
|
inc (ry, Width);
|
|
end;
|
|
rx := CheckCorner (ry, bottom, left);
|
|
ry := r;
|
|
end
|
|
end
|
|
else
|
|
if (ry >= bottom) then
|
|
begin // fill horizontal
|
|
r := checkCorner (ry, bottom, left);
|
|
while (rx <= right) do
|
|
begin
|
|
DrawSolidLine (Canv, r,bottom, rx,top, c);
|
|
inc (r, Width);
|
|
inc (rx, Width);
|
|
end;
|
|
ry := CheckCorner (rx, right, top);
|
|
rx := r;
|
|
end;
|
|
while (rx < right) do // fill lower right corner
|
|
begin
|
|
DrawSolidLine (Canv, rx,bottom, right,ry, c);
|
|
inc (rx, Width);
|
|
inc (ry, Width);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure FillRectangleHashBackDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer);
|
|
begin
|
|
FillRectangleHashBackDiagonal (Canv, rect, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillRectangleHashBackDiagonal (Canv:TFPCustomCanvas; const rect:TRect; width:integer; const c:TFPColor);
|
|
function CheckInversCorner (Current, min, start : integer) : integer;
|
|
begin
|
|
if Current < min then
|
|
result := Start - current + min
|
|
else
|
|
result := Start;
|
|
end;
|
|
function CheckCorner (Current, max, start : integer) : integer;
|
|
begin
|
|
if Current > max then
|
|
result := Start - current + max
|
|
else
|
|
result := Start;
|
|
end;
|
|
var r, rx, ry : integer;
|
|
begin
|
|
with rect do
|
|
begin
|
|
// draw from bottom-left corner away
|
|
ry := bottom - Width;
|
|
rx := left + Width;
|
|
while (rx < right) and (ry > top) do
|
|
begin
|
|
DrawSolidLine (Canv, left,ry, rx,bottom, c);
|
|
inc (rx, Width);
|
|
dec (ry, Width);
|
|
end;
|
|
// check which turn need to be taken: left-top, right-bottom, or both
|
|
if (rx >= right) then
|
|
begin
|
|
if (ry <= top) then
|
|
begin // Both corners reached
|
|
r := CheckCorner (rx, right, bottom);
|
|
rx := CheckInversCorner (ry, top, left);
|
|
ry := r;
|
|
end
|
|
else
|
|
begin // fill vertical
|
|
r := CheckCorner (rx, right, bottom);
|
|
while (ry > top) do
|
|
begin
|
|
DrawSolidLine (Canv, left,ry, right,r, c);
|
|
dec (r, Width);
|
|
dec (ry, Width);
|
|
end;
|
|
rx := CheckInversCorner (ry, top, left);
|
|
ry := r;
|
|
end
|
|
end
|
|
else
|
|
if (ry <= top) then
|
|
begin // fill horizontal
|
|
r := checkInversCorner (ry, top, left);
|
|
while (rx < right) do
|
|
begin
|
|
DrawSolidLine (Canv, r,top, rx,bottom, c);
|
|
inc (r, Width);
|
|
inc (rx, Width);
|
|
end;
|
|
ry := CheckCorner (rx, right, bottom);
|
|
rx := r;
|
|
end;
|
|
while (rx < right) do // fill upper right corner
|
|
begin
|
|
DrawSolidLine (Canv, rx,top, right,ry, c);
|
|
inc (rx, Width);
|
|
dec (ry, Width);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure FillRectanglePattern (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const pattern:TBrushPattern);
|
|
begin
|
|
FillRectanglePattern (Canv, x1,y1, x2,y2, pattern, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillRectanglePattern (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const pattern:TBrushPattern; const color:TFPColor);
|
|
var r : integer;
|
|
begin
|
|
for r := y1 to y2 do
|
|
DrawPatternLine (Canv, x1,r, x2,r, pattern[r mod PatternBitCount], color);
|
|
end;
|
|
|
|
procedure FillRectangleImage (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const Image:TFPCustomImage);
|
|
var x,y : integer;
|
|
begin
|
|
with image do
|
|
for x := x1 to x2 do
|
|
for y := y1 to y2 do
|
|
Canv.colors[x,y] := colors[x mod width, y mod height];
|
|
end;
|
|
|
|
procedure FillRectangleImageRel (Canv:TFPCustomCanvas; x1,y1, x2,y2:integer; const Image:TFPCustomImage);
|
|
var x,y : integer;
|
|
begin
|
|
with image do
|
|
for x := x1 to x2 do
|
|
for y := y1 to y2 do
|
|
Canv.colors[x,y] := colors[(x-x1) mod width, (y-y1) mod height];
|
|
end;
|
|
|
|
type
|
|
TFuncSetColor = procedure (Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
|
|
PDoneRec = ^TDoneRec;
|
|
TDoneRec = record
|
|
x, min, max : integer;
|
|
next : PDoneRec;
|
|
end;
|
|
|
|
PFloodFillData = ^TFloodFillData;
|
|
TFloodFillData = record
|
|
Canv : TFPCustomCanvas;
|
|
ReplColor : TFPColor;
|
|
SetColor : TFuncSetColor;
|
|
ExtraData : pointer;
|
|
DoneList : TList;
|
|
end;
|
|
|
|
function FindDoneIndex (const data:PFloodFillData; x:integer; var index:integer):boolean;
|
|
begin
|
|
with data^.DoneList do
|
|
begin
|
|
index := 0;
|
|
while (index < count) and (PDoneRec(items[index])^.x <> x) do
|
|
inc (index);
|
|
result := (index < count) and (PDoneRec(items[index])^.x = x);
|
|
end;
|
|
end;
|
|
|
|
procedure FreeDoneList (const data:TFloodFillData);
|
|
procedure FreeList (p:PDoneRec);
|
|
var n : PDoneRec;
|
|
begin
|
|
while assigned(p) do
|
|
begin
|
|
n := p^.Next;
|
|
dispose (p);
|
|
p := n;
|
|
end;
|
|
end;
|
|
var r : integer;
|
|
begin
|
|
with data do
|
|
for r := 0 to DoneList.Count-1 do
|
|
FreeList (PDoneRec(DoneList[r]));
|
|
end;
|
|
|
|
procedure CheckFloodFillColor (x,top,bottom,Direction:integer; data:PFloodFillData);
|
|
|
|
procedure CheckRange;
|
|
var r,t,b : integer;
|
|
begin
|
|
t := top;
|
|
b := top -1;
|
|
for r := top to bottom do
|
|
with data^ do
|
|
begin
|
|
if canv.colors[x,r] = ReplColor then
|
|
begin
|
|
b := r;
|
|
SetColor(Canv,x,r,ExtraData);
|
|
end
|
|
else
|
|
begin
|
|
if t < r then
|
|
CheckFloodFillColor (x+Direction, t, r-1, Direction, data);
|
|
t := r + 1;
|
|
end;
|
|
end;
|
|
if t <= b then
|
|
CheckFloodFillColor (x+Direction, t, b, Direction, data);
|
|
end;
|
|
|
|
procedure CheckAboveRange;
|
|
var t,b : integer;
|
|
begin
|
|
with data^ do
|
|
begin
|
|
t := top - 1;
|
|
while (t >= 0) and (Canv.colors[x,t]=ReplColor) do
|
|
begin
|
|
SetColor(Canv, x,t, ExtraData);
|
|
dec (t);
|
|
end;
|
|
t := t + 1;
|
|
b := top - 1;
|
|
if t <= b then
|
|
begin
|
|
CheckFloodFillColor (x-1, t, b, -1, data);
|
|
CheckFloodFillColor (x+1, t, b, 1, data);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckBelowRange;
|
|
var r,t,b : integer;
|
|
begin
|
|
with data^ do
|
|
begin
|
|
r := Canv.Height;
|
|
b := bottom + 1;
|
|
t := b;
|
|
while (b < r) and (Canv.colors[x,b]=ReplColor) do
|
|
begin
|
|
SetColor (Canv,x,b,ExtraData);
|
|
inc (b);
|
|
end;
|
|
b := b - 1;
|
|
if t <= b then
|
|
begin
|
|
CheckFloodFillColor (x-1, t, b, -1, data);
|
|
CheckFloodFillColor (x+1, t, b, 1, data);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var DoAbove, DoBelow : boolean;
|
|
|
|
begin
|
|
with data^ do
|
|
begin
|
|
if (x >= Canv.width) or (x < 0) then
|
|
Exit;
|
|
if top < 0 then
|
|
top := 0;
|
|
if bottom >= Canv.Height then
|
|
bottom := Canv.Height-1;
|
|
DoAbove := (Canv.colors[x,top] = ReplColor);
|
|
DoBelow := (Canv.colors[x,bottom] = ReplColor);
|
|
end;
|
|
CheckRange;
|
|
if DoAbove then
|
|
CheckAboveRange;
|
|
if DoBelow then
|
|
CheckBelowRange;
|
|
end;
|
|
|
|
procedure CheckFloodFill (x,top,bottom,Direction:integer; data:PFloodFillData);
|
|
var beforetop, ontop, chain, myrec : PDoneRec;
|
|
doneindex : integer;
|
|
|
|
procedure CheckRange;
|
|
var r,t,b : integer;
|
|
n : PDoneRec;
|
|
begin
|
|
ontop := nil;
|
|
beforetop := nil;
|
|
n := chain;
|
|
while (n <> nil) and (n^.min <= top) do
|
|
begin
|
|
beforetop := ontop;
|
|
ontop := n;
|
|
n := n^.next;
|
|
end;
|
|
if assigned(ontop) and (ontop^.max < top) then
|
|
begin
|
|
beforetop := ontop;
|
|
ontop := nil;
|
|
end;
|
|
// ontop is: nil OR rec before top OR rec containing top
|
|
if assigned(ontop) then
|
|
begin
|
|
t := ontop^.max + 1;
|
|
myrec := ontop;
|
|
end
|
|
else
|
|
begin
|
|
t := top;
|
|
new(myrec);
|
|
myrec^.x := x;
|
|
myrec^.min := top;
|
|
myrec^.max := top;
|
|
myrec^.Next := n;
|
|
if assigned(beforetop) then
|
|
beforetop^.next := myrec
|
|
else
|
|
begin
|
|
with data^.DoneList do
|
|
if DoneIndex < Count then
|
|
Items[DoneIndex] := myrec
|
|
else
|
|
Add (myrec);
|
|
chain := myrec;
|
|
end;
|
|
end;
|
|
ontop := myrec;
|
|
// ontop is rec containing the top
|
|
b := t-1;
|
|
r := t;
|
|
while (r <= bottom) do
|
|
begin
|
|
with data^ do
|
|
begin
|
|
if canv.colors[x,r] = ReplColor then
|
|
begin
|
|
b := r;
|
|
SetColor(Canv,x,r,ExtraData);
|
|
end
|
|
else
|
|
begin
|
|
if t < r then
|
|
begin
|
|
myrec^.max := r;
|
|
CheckFloodFill (x+Direction, t, r-1, Direction, data);
|
|
end;
|
|
t := r + 1;
|
|
end;
|
|
inc (r);
|
|
end;
|
|
if assigned(n) and (r >= n^.min) then
|
|
begin
|
|
if t < r then
|
|
begin
|
|
myrec^.max := n^.min-1;
|
|
CheckFloodFill (x+Direction, t, r-1, Direction, data);
|
|
end;
|
|
while assigned(n) and (r >= n^.min) do
|
|
begin
|
|
myrec := n;
|
|
r := myrec^.max + 1;
|
|
n := n^.next;
|
|
end;
|
|
t := r;
|
|
end;
|
|
end;
|
|
myrec^.max := r - 1;
|
|
if t <= b then
|
|
CheckFloodFill (x+Direction, t, b, Direction, data);
|
|
end;
|
|
|
|
procedure CheckAboveRange (highest:integer);
|
|
var t,b : integer;
|
|
begin
|
|
with data^ do
|
|
begin
|
|
t := top - 1;
|
|
while (t >= highest) and (Canv.colors[x,t]=ReplColor) do
|
|
begin
|
|
SetColor(Canv, x,t, ExtraData);
|
|
dec (t);
|
|
end;
|
|
t := t + 1;
|
|
b := top - 1;
|
|
if t <= b then
|
|
begin
|
|
ontop^.min := t - 1;
|
|
CheckFloodFill (x-1, t, b, -1, data);
|
|
CheckFloodFill (x+1, t, b, 1, data);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure CheckBelowRange (lowest : integer);
|
|
var t,b : integer;
|
|
begin
|
|
with data^ do
|
|
begin
|
|
b := bottom + 1;
|
|
t := b;
|
|
while (b <= lowest) and (Canv.colors[x,b]=ReplColor) do
|
|
begin
|
|
SetColor (Canv,x,b,ExtraData);
|
|
inc (b);
|
|
end;
|
|
b := b - 1;
|
|
if t <= b then
|
|
begin
|
|
myrec^.max := b+1;
|
|
CheckFloodFill (x-1, t, b, -1, data);
|
|
CheckFloodFill (x+1, t, b, 1, data);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
var DoAbove, DoBelow : boolean;
|
|
m : integer;
|
|
begin
|
|
with data^ do
|
|
begin
|
|
if (x >= Canv.width) or (x < 0) then
|
|
Exit;
|
|
if top < 0 then
|
|
top := 0;
|
|
if bottom >= Canv.Height then
|
|
bottom := Canv.Height-1;
|
|
DoAbove := (Canv.colors[x,top] = ReplColor);
|
|
DoBelow := (Canv.colors[x,bottom] = ReplColor);
|
|
end;
|
|
if FindDoneIndex (data, x, DoneIndex) then
|
|
begin
|
|
chain := PDoneRec(data^.DoneList[DoneIndex]);
|
|
myrec := chain;
|
|
while assigned(myrec) do
|
|
with myrec^ do
|
|
myrec := next;
|
|
end
|
|
else
|
|
chain := nil;
|
|
CheckRange;
|
|
// ontop: rec containing top
|
|
// myrec: rec containing bottom
|
|
if DoAbove and (ontop^.min = top) then
|
|
begin
|
|
if assigned (beforetop) then
|
|
m := beforetop^.max + 1
|
|
else
|
|
m := 0;
|
|
CheckAboveRange (m);
|
|
end;
|
|
if DoBelow and (myrec^.max = bottom) then
|
|
begin
|
|
if assigned (myrec^.next) then
|
|
m := myrec^.next^.min - 1
|
|
else
|
|
m := data^.Canv.Height - 1;
|
|
CheckBelowRange (m);
|
|
end;
|
|
end;
|
|
|
|
procedure SetFloodColor (Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
begin
|
|
Canv.colors[x,y] := PFPColor(data)^;
|
|
end;
|
|
|
|
procedure FillFloodColor (Canv:TFPCustomCanvas; x,y:integer; const color:TFPColor);
|
|
var d : TFloodFillData;
|
|
begin
|
|
d.Canv := canv;
|
|
d.ReplColor := Canv.colors[x,y];
|
|
d.SetColor := @SetFloodColor;
|
|
d.ExtraData := @color;
|
|
CheckFloodFillColor (x, y, y, 1, @d);
|
|
end;
|
|
|
|
procedure FillFloodColor (Canv:TFPCustomCanvas; x,y:integer);
|
|
begin
|
|
FillFloodColor (Canv, x, y, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
type
|
|
TBoolPlane = array[0..PatternBitCount-1] of TLinePoints;
|
|
TFloodPatternRec = record
|
|
plane : TBoolPlane;
|
|
color : TFPColor;
|
|
end;
|
|
PFloodPatternRec = ^TFloodPatternRec;
|
|
|
|
procedure SetFloodPattern (Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var p : PFloodPatternRec;
|
|
begin
|
|
p := PFloodPatternRec(data);
|
|
if p^.plane[x mod PatternBitCount, y mod PatternBitCount] then
|
|
Canv.colors[x,y] := p^.color;
|
|
end;
|
|
|
|
procedure FillFloodPattern (Canv:TFPCustomCanvas; x,y:integer; const pattern:TBrushPattern; const color:TFPColor);
|
|
var rec : TFloodPatternRec;
|
|
d : TFloodFillData;
|
|
|
|
procedure FillPattern;
|
|
var r : integer;
|
|
begin
|
|
for r := 0 to PatternBitCount-1 do
|
|
PatternToPoints (pattern[r], @rec.plane[r]);
|
|
end;
|
|
|
|
begin
|
|
d.Canv := canv;
|
|
d.ReplColor := Canv.colors[x,y];
|
|
d.SetColor := @SetFloodPattern;
|
|
d.ExtraData := @rec;
|
|
d.DoneList := TList.Create;
|
|
try
|
|
FillPattern;
|
|
rec.color := Color;
|
|
CheckFloodFill (x, y, y, 1, @d);
|
|
finally
|
|
FreeDoneList (d);
|
|
end;
|
|
end;
|
|
|
|
procedure FillFloodPattern (Canv:TFPCustomCanvas; x,y:integer; const pattern:TBrushPattern);
|
|
begin
|
|
FillFloodPattern (Canv, x, y, pattern, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
type
|
|
TFloodHashRec = record
|
|
color : TFPColor;
|
|
width : integer;
|
|
end;
|
|
PFloodHashRec = ^TFloodHashRec;
|
|
|
|
procedure SetFloodHashHor(Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodHashRec;
|
|
begin
|
|
r := PFloodHashRec(data);
|
|
if (y mod r^.width) = 0 then
|
|
Canv.colors[x,y] := r^.color;
|
|
end;
|
|
|
|
procedure SetFloodHashVer(Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodHashRec;
|
|
begin
|
|
r := PFloodHashRec(data);
|
|
if (x mod r^.width) = 0 then
|
|
Canv.colors[x,y] := r^.color;
|
|
end;
|
|
|
|
procedure SetFloodHashDiag(Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodHashRec;
|
|
w : integer;
|
|
begin
|
|
r := PFloodHashRec(data);
|
|
w := r^.width;
|
|
if ((x mod w) + (y mod w)) = (w - 1) then
|
|
Canv.colors[x,y] := r^.color;
|
|
end;
|
|
|
|
procedure SetFloodHashBDiag(Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodHashRec;
|
|
w : 0..PatternBitCount-1;
|
|
begin
|
|
r := PFloodHashRec(data);
|
|
w := r^.width;
|
|
if (x mod w) = (y mod w) then
|
|
Canv.colors[x,y] := r^.color;
|
|
end;
|
|
|
|
procedure SetFloodHashCross(Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodHashRec;
|
|
w : 0..PatternBitCount-1;
|
|
begin
|
|
r := PFloodHashRec(data);
|
|
w := r^.width;
|
|
if ((x mod w) = 0) or ((y mod w) = 0) then
|
|
Canv.colors[x,y] := r^.color;
|
|
end;
|
|
|
|
procedure SetFloodHashDiagCross(Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodHashRec;
|
|
w : 0..PatternBitCount-1;
|
|
begin
|
|
r := PFloodHashRec(data);
|
|
w := r^.width;
|
|
if ( (x mod w) = (y mod w) ) or
|
|
( ((x mod w) + (y mod w)) = (w - 1) ) then
|
|
Canv.colors[x,y] := r^.color;
|
|
end;
|
|
|
|
procedure FillFloodHash (Canv:TFPCustomCanvas; x,y:integer; width:integer; SetHashColor:TFuncSetColor; const c:TFPColor);
|
|
var rec : TFloodHashRec;
|
|
d : TFloodFillData;
|
|
begin
|
|
d.Canv := canv;
|
|
d.ReplColor := Canv.colors[x,y];
|
|
d.SetColor := SetHashColor;
|
|
d.ExtraData := @rec;
|
|
d.DoneList := TList.Create;
|
|
rec.color := c;
|
|
rec.width := Width;
|
|
try
|
|
CheckFloodFill (x, y, y, 1, @d);
|
|
finally
|
|
FreeDoneList (d);
|
|
end;
|
|
end;
|
|
|
|
procedure FillFloodHashHorizontal (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
begin
|
|
FillFloodHash (canv, x, y, width, @SetFloodHashHor, c);
|
|
end;
|
|
|
|
procedure FillFloodHashHorizontal (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
begin
|
|
FillFloodHashHorizontal (Canv, x, y, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillFloodHashVertical (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
begin
|
|
FillFloodHash (canv, x, y, width, @SetFloodHashVer, c);
|
|
end;
|
|
|
|
procedure FillFloodHashVertical (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
begin
|
|
FillFloodHashVertical (Canv, x, y, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillFloodHashDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
begin
|
|
FillFloodHash (canv, x, y, width, @SetFloodHashDiag, c);
|
|
end;
|
|
|
|
procedure FillFloodHashDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
begin
|
|
FillFloodHashDiagonal (Canv, x, y, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillFloodHashBackDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
begin
|
|
FillFloodHash (canv, x, y, width, @SetFloodHashBDiag, c);
|
|
end;
|
|
|
|
procedure FillFloodHashBackDiagonal (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
begin
|
|
FillFloodHashBackDiagonal (Canv, x, y, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillFloodHashDiagCross (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
begin
|
|
FillFloodHash (canv, x, y, width, @SetFloodHashDiagCross, c);
|
|
end;
|
|
|
|
procedure FillFloodHashDiagCross (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
begin
|
|
FillFloodHashDiagCross (Canv, x, y, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
procedure FillFloodHashCross (Canv:TFPCustomCanvas; x,y:integer; width:integer; const c:TFPColor);
|
|
begin
|
|
FillFloodHash (canv, x, y, width, @SetFloodHashCross, c);
|
|
end;
|
|
|
|
procedure FillFloodHashCross (Canv:TFPCustomCanvas; x,y:integer; width:integer);
|
|
begin
|
|
FillFloodHashCross (Canv, x, y, width, Canv.Brush.FPColor);
|
|
end;
|
|
|
|
type
|
|
TFloodImageRec = record
|
|
xo,yo : integer;
|
|
image : TFPCustomImage;
|
|
end;
|
|
PFloodImageRec = ^TFloodImageRec;
|
|
|
|
procedure SetFloodImage (Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodImageRec;
|
|
begin
|
|
r := PFloodImageRec(data);
|
|
with r^.image do
|
|
Canv.colors[x,y] := colors[x mod width, y mod height];
|
|
end;
|
|
|
|
procedure FillFloodImage (Canv:TFPCustomCanvas; x,y :integer; const Image:TFPCustomImage);
|
|
var rec : TFloodImageRec;
|
|
d : TFloodFillData;
|
|
begin
|
|
d.Canv := canv;
|
|
d.ReplColor := Canv.colors[x,y];
|
|
d.SetColor := @SetFloodImage;
|
|
d.ExtraData := @rec;
|
|
d.DoneList := Tlist.Create;
|
|
rec.image := image;
|
|
try
|
|
CheckFloodFill (x, y, y, 1, @d);
|
|
finally
|
|
FreeDoneList (d);
|
|
end;
|
|
end;
|
|
|
|
procedure SetFloodImageRel (Canv:TFPCustomCanvas; x,y:integer; data:pointer);
|
|
var r : PFloodImageRec;
|
|
xi, yi : integer;
|
|
begin
|
|
r := PFloodImageRec(data);
|
|
with r^, image do
|
|
begin
|
|
xi := (x - xo) mod width;
|
|
if xi < 0 then
|
|
xi := width - xi;
|
|
yi := (y - yo) mod height;
|
|
if yi < 0 then
|
|
yi := height - yi;
|
|
Canv.colors[x,y] := colors[xi,yi];
|
|
end;
|
|
end;
|
|
|
|
procedure FillFloodImageRel (Canv:TFPCustomCanvas; x,y :integer; const Image:TFPCustomImage);
|
|
var rec : TFloodImageRec;
|
|
d : TFloodFillData;
|
|
begin
|
|
d.Canv := canv;
|
|
d.ReplColor := Canv.colors[x,y];
|
|
d.SetColor := @SetFloodImageRel;
|
|
d.ExtraData := @rec;
|
|
d.DoneList := TList.Create;
|
|
rec.image := image;
|
|
rec.xo := x;
|
|
rec.yo := y;
|
|
try
|
|
CheckFloodFill (x, y, y, 1, @d);
|
|
finally
|
|
FreeDoneList (d);
|
|
end;
|
|
end;
|
|
|
|
end.
|