123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704 |
- {
- $Id$
- This file is part of the Free Pascal run time library.
- Copyright (c) 2003 by the Free Pascal development team
- Drawing of ellipses and arcs, and filling ellipses and pies.
-
- 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}
- unit Ellipses;
- interface
- uses classes, FPImage, FPCanvas;
- procedure DrawSolidEllipse (Canv:TFPCustomCanvas; const Bounds:TRect; c:TFPColor);
- procedure DrawSolidEllipse (Canv:TFPCustomCanvas; const Bounds:TRect; Width:integer; c:TFPColor);
- procedure DrawPatternEllipse (Canv:TFPCustomCanvas; const Bounds:TRect; Pattern:TPenPattern; c:TFPColor);
- procedure FillEllipseColor (Canv:TFPCustomCanvas; const Bounds:TRect; c:TFPColor);
- procedure FillEllipsePattern (Canv:TFPCustomCanvas; const Bounds:TRect; Pattern:TBrushPattern; c:TFPColor);
- procedure FillEllipseHashHorizontal (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- procedure FillEllipseHashVertical (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- procedure FillEllipseHashDiagonal (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- procedure FillEllipseHashBackDiagonal (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- procedure FillEllipseHashDiagCross (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- procedure FillEllipseHashCross (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- procedure FillEllipseImage (Canv:TFPCustomCanvas; const Bounds:TRect; const Image:TFPCustomImage);
- procedure FillEllipseImageRel (Canv:TFPCustomCanvas; const Bounds:TRect; const Image:TFPCustomImage);
- type
- PEllipseInfoData = ^TEllipseInfoData;
- TEllipseInfoData = record
- x, ytopmax, ytopmin, ybotmax, ybotmin : integer;
- OnlyTop : boolean;
- end;
- TEllipseInfo = class
- private
- fcx, fcy, frx,fry,
- fa1, fa2, frot : real;
- fx1,fy1, fx2,fy2 : integer;
- InfoList : TList;
- procedure FreeList;
- procedure ClearList;
- function FindXIndex (x:integer) : integer;
- procedure PrepareCalculation (var np:integer; var delta:real);
- function NewInfoRec (anX:integer) : PEllipseInfoData;
- procedure CalculateCircular (const b:TRect; var x,y,rx,ry:real);
- public
- constructor create;
- destructor destroy; override;
- function GetInfoForX (x:integer; var ytopmax,ytopmin,ybotmax,ybotmin:integer):boolean;
- function GetInfoForX (x:integer; var Info:PEllipseInfoData):boolean;
- procedure GatherEllipseInfo (const bounds:TRect);
- procedure GatherArcInfo (const bounds:TRect; alpha1,alpha2:real);
- property cx : real read fcx; // center point
- property cy : real read fcy;
- property rhor : real read frx; // radius
- property rver : real read fry;
- { only usable when created with GatherArcInfo }
- property a1 : real read fa1; // angle 1 and point on ellipse
- property x1 : integer read fx1;
- property y1 : integer read fy1;
- property a2 : real read fa2; // angle 2 and point on ellipse
- property x2 : integer read fx2;
- property y2 : integer read fy2;
- end;
- implementation
- constructor TEllipseInfo.Create;
- begin
- inherited;
- InfoList := TList.Create;
- end;
- destructor TEllipseInfo.Destroy;
- begin
- FreeList;
- inherited;
- end;
- procedure TEllipseInfo.ClearList;
- var r : integer;
- d : PEllipseInfoData;
- begin
- if assigned (InfoList) then
- begin
- for r := 0 to infolist.count-1 do
- begin
- d := PEllipseInfoData(InfoList[r]);
- dispose (d);
- end;
- InfoList.clear;
- end;
- end;
- procedure TEllipseInfo.FreeList;
- begin
- if assigned (InfoList) then
- begin
- ClearList;
- InfoList.Free;
- InfoList := nil;
- end;
- end;
- function TEllipseInfo.GetInfoForX (x:integer; var ytopmax,ytopmin,ybotmax,ybotmin:integer):boolean;
- var r : PEllipseInfoData;
- begin
- result := GetInfoForX (x, r);
- if assigned(r) then
- begin
- ytopmax := ytopmax;
- ytopmin := ytopmin;
- ybotmax := ybotmax;
- ybotmin := ybotmin;
- end;
- end;
- function TEllipseInfo.FindXIndex (x : integer) : integer;
- begin
- result := InfoList.Count;
- repeat
- dec (result);
- until (result < 0) or (x = PEllipseInfoData(InfoList[result])^.x);
- end;
- function TEllipseInfo.GetInfoForX (x:integer; var Info:PEllipseInfoData):boolean;
- var r : integer;
- begin
- r := FindXIndex (x);
- result := (r >= 0);
- if result then
- Info := PEllipseInfoData(InfoList[r])
- end;
- procedure TEllipseInfo.PrepareCalculation (var np:integer; var delta:real);
- begin
- np := round(1.5708 * sqrt(sqr(frx)+sqr(fry)) );
- // number of pixel in quarter circel to calculate without gaps in drawing
- delta := pi / (2 * np);
- end;
- function TEllipseInfo.NewInfoRec (anX:integer) : PEllipseInfoData;
- begin
- new (result);
- result^.x := anX;
- infolist.Add (result);
- with result^ do
- begin
- ytopmax := -1;
- ytopmin := maxint;
- ybotmax := -1;
- ybotmin := maxint;
- end;
- end;
- procedure TEllipseInfo.CalculateCircular (const b:TRect; var x,y,rx,ry:real);
- begin
- with b do
- begin
- x := (right+left) / 2;
- y := (top+bottom) / 2;
- rx := abs(right-left) / 2;
- ry := abs(bottom-top) / 2;
- end;
- end;
- procedure TEllipseInfo.GatherEllipseInfo (const bounds:TRect);
- var infoP, infoM : PEllipseInfoData;
- halfnumber,
- r, NumberPixels, xtemp,yt,yb : integer;
- pPy, pMy, x,y, rx,ry, xd,yd,ra, rdelta : real;
- begin
- ClearList;
- CalculateCircular (bounds, x,y,rx,ry);
- with bounds do
- fcx := x;
- fcy := y;
- frx := rx;
- fry := ry;
- if (rx < 0.5) and (ry < 0.5) then
- with NewInfoRec (round(x))^ do
- begin
- ytopmax := round(y);
- ytopmin := ytopmax;
- ybotmax := ytopmax;
- ybotmin := ytopmax;
- end
- else
- begin
- PrepareCalculation (NumberPixels, rdelta);
- halfnumber := NumberPixels div 2;
- pPy := maxint;
- pMy := maxint;
- ra := 0;
- infoP := NewInfoRec (round(x + rx));
- infoM := NewInfoRec (round(x - rx));
- for r := 0 to NumberPixels do
- begin
- xd := rx * cos(ra);
- yd := ry * sin(ra);
- // take all 4 quarters
- yt := round(y - yd);
- yb := round(y + yd);
- xtemp := round (x + xd);
- // quarter 1 and 4 at the same x line
- if infoP^.x <> xtemp then // has correct record ?
- begin
- with infoP^ do // ensure single width
- begin
- if r < halfnumber then
- begin
- if ytopmin = yt then
- begin
- inc (ytopmin);
- dec (ybotmax);
- end;
- end
- else
- begin
- if (ytopmax = pPy) and (ytopmax <> ytopmin) then
- begin
- dec (ytopmax);
- inc (ybotmin);
- end;
- end;
- pPy := ytopmin;
- end;
- if not GetInfoForX (xtemp, infoP) then // record exists already ?
- infoP := NewInfoRec (xtemp); // create a new recod
- end;
- // lower y is top, min is lowest
- with InfoP^ do
- begin
- if yt < ytopmin then
- ytopmin := yt;
- if yb < ybotmin then
- ybotmin := yb;
- if yt > ytopmax then
- ytopmax := yt;
- if yb > ybotmax then
- ybotmax := yb;
- end;
- // quarter 2 and 3 on the same x line
- xtemp := round(x - xd);
- if infoM^.x <> xtemp then // has correct record ?
- begin
- with infoM^ do // ensure single width
- begin
- if r < halfnumber then
- begin
- if ytopmin = yt then
- begin
- inc (ytopmin);
- dec (ybotmax);
- end;
- end
- else
- begin
- if (ytopmax = pMy) and (ytopmax <> ytopmin) then
- begin
- dec (ytopmax);
- inc (ybotmin);
- end;
- end;
- pMy := ytopmin;
- end;
- if not GetInfoForX (xtemp, infoM) then // record exists already ?
- infoM := NewInfoRec (xtemp); // create a new recod
- end;
- // lower y is top, min is lowest
- with InfoM^ do
- begin
- if yt < ytopmin then
- ytopmin := yt;
- if yb < ybotmin then
- ybotmin := yb;
- if yt > ytopmax then
- ytopmax := yt;
- if yb > ybotmax then
- ybotmax := yb;
- end;
- ra := ra + rdelta;
- end;
- end;
- end;
- procedure TEllipseInfo.GatherArcInfo (const bounds:TRect; alpha1,alpha2:real);
- var stAngle,endAngle:real;
- procedure CheckAngles;
- begin
- if a1 < a2 then
- begin
- stAngle := a1;
- endAngle := a2;
- end
- else
- begin
- stAngle := a2;
- endAngle := a1;
- end;
- end;
- begin
- end;
- { The drawing routines }
- type
- TPutPixelProc = procedure (Canv:TFPCustomCanvas; x,y:integer; color:TFPColor);
- 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 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 DrawSolidEllipse (Canv:TFPCustomCanvas; const Bounds:TRect; c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- MyPutPix : TPutPixelProc;
- begin
- with canv.pen do
- case mode of
- pmAnd : MyPutPix := @PutPixelAnd;
- pmOr : MyPutPix := @PutPixelOr;
- pmXor : MyPutPix := @PutPixelXor;
- else MyPutPix := @PutPixelCopy;
- end;
- info := TEllipseInfo.Create;
- with Canv, info do
- try
- GatherEllipseInfo (bounds);
- for r := 0 to InfoList.count-1 do
- with PEllipseInfoData(InfoList[r])^ do
- begin
- for y := ytopmin to ytopmax do
- MyPutPix (Canv, x,y, c);
- for y := ybotmin to ybotmax do
- MyPutPix (Canv, x,y, c);
- end;
- finally
- info.Free;
- end;
- end;
- procedure DrawSolidEllipse (Canv:TFPCustomCanvas; const Bounds:TRect; Width:integer; c:TFPColor);
- var infoOut, infoIn : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- MyPutPix : TPutPixelProc;
- begin
- with canv.pen do
- case mode of
- pmAnd : MyPutPix := @PutPixelAnd;
- pmOr : MyPutPix := @PutPixelOr;
- pmXor : MyPutPix := @PutPixelXor;
- else MyPutPix := @PutPixelCopy;
- end;
- infoIn := TEllipseInfo.Create;
- infoOut := TEllipseInfo.Create;
- dec (width);
- try
- infoOut.GatherEllipseInfo(bounds);
- with bounds do
- infoIn.GatherEllipseInfo (Rect(left+width,top+width,right-width,bottom-width));
- with Canv do
- for r := 0 to infoOut.infolist.count-1 do
- with PEllipseInfoData (infoOut.infolist[r])^ do
- begin
- if infoIn.GetInfoForX (x, id) then
- begin
- for y := ytopmin to id^.ytopmax do
- MyPutPix (canv, x,y, c);
- for y := id^.ybotmin to ybotmax do
- MyPutPix (canv, x,y, c);
- end
- else
- begin // no inner circle found: draw all points between top and bottom
- for y := ytopmin to ybotmax do
- MyPutPix (canv, x,y, c);
- end;
- end;
- finally
- infoOut.Free;
- infoIn.Free;
- end;
- end;
- procedure DrawPatternEllipse (Canv:TFPCustomCanvas; const Bounds:TRect; Pattern:TPenPattern; c:TFPColor);
- var info : TEllipseInfo;
- xx, y : integer;
- LinePoints : TLinePoints;
- MyPutPix : TPutPixelProc;
- id : PEllipseInfoData;
- CountDown, CountUp, half : integer;
- begin
- with canv.pen do
- case mode of
- pmAnd : MyPutPix := @PutPixelAnd;
- pmOr : MyPutPix := @PutPixelOr;
- pmXor : MyPutPix := @PutPixelXor;
- else MyPutPix := @PutPixelCopy;
- end;
- PatternToPoints (pattern, @LinePoints);
- info := TEllipseInfo.Create;
- with Canv, info do
- try
- GatherEllipseInfo (bounds);
- CountUp := 0;
- CountDown := PatternBitCount - 1;
- half := round (cx);
- for xx := bounds.left to half do
- if GetInfoForX (xx, id) then
- begin
- with id^ do
- begin
- for y := ytopmax downto ytopmin do
- begin
- if LinePoints[CountUp mod PatternBitCount] then
- MyPutPix (Canv, xx,y, c);
- inc (CountUp);
- end;
- for y := ybotmin to ybotmax do
- begin
- if LinePoints[PatternBitCount - (CountDown mod PatternBitCount) - 1] then
- MyPutPix (Canv, xx,y, c);
- inc (CountDown);
- end;
- end;
- end;
- for xx := half+1 to bounds.right do
- if GetInfoForX (xx, id) then
- begin
- with id^ do
- begin
- for y := ytopmin to ytopmax do
- begin
- if LinePoints[CountUp mod PatternBitCount] then
- MyPutPix (Canv, xx,y, c);
- inc (CountUp);
- end;
- for y := ybotmax downto ybotmin do
- begin
- if LinePoints[Patternbitcount - (CountDown mod PatternBitCount) - 1] then
- MyPutPix (Canv, xx,y, c);
- inc (CountDown);
- end;
- end;
- end;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseColor (Canv:TFPCustomCanvas; const Bounds:TRect; c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- with Canv do
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- for y := ytopmin to ybotmax do
- colors[x,y] := c;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipsePattern (Canv:TFPCustomCanvas; const Bounds:TRect; Pattern:TBrushPattern; c:TFPColor);
- begin
- end;
- procedure FillEllipseHashHorizontal (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- for y := ytopmin to ybotmax do
- if (y mod width) = 0 then
- canv.colors[x,y] := c;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseHashVertical (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- if (x mod width) = 0 then
- for y := ytopmin to ybotmax do
- canv.colors[x,y] := c;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseHashDiagonal (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- w : integer;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- begin
- w := width - 1 - (x mod width);
- for y := ytopmin to ybotmax do
- if (y mod width) = w then
- canv.colors[x,y] := c;
- end;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseHashBackDiagonal (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- w : integer;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- begin
- w := (x mod width);
- for y := ytopmin to ybotmax do
- if (y mod width) = w then
- canv.colors[x,y] := c;
- end;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseHashDiagCross (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- wy,w1,w2 : integer;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- begin
- w1 := (x mod width);
- w2 := width - 1 - (x mod width);
- for y := ytopmin to ybotmax do
- begin
- wy := y mod width;
- if (wy = w1) or (wy = w2) then
- canv.colors[x,y] := c;
- end;
- end;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseHashCross (Canv:TFPCustomCanvas; const Bounds:TRect; width:integer; const c:TFPColor);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- if (x mod width) = 0 then
- for y := ytopmin to ybotmax do
- canv.colors[x,y] := c
- else
- for y := ytopmin to ybotmax do
- if (y mod width) = 0 then
- canv.colors[x,y] := c;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseImage (Canv:TFPCustomCanvas; const Bounds:TRect; const Image:TFPCustomImage);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- w : integer;
- begin
- info := TEllipseInfo.Create;
- try
- info.GatherEllipseInfo(bounds);
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- begin
- w := (x mod image.width);
- for y := ytopmin to ybotmax do
- canv.colors[x,y] := Image.colors[w, (y mod image.height)];
- end;
- finally
- info.Free;
- end;
- end;
- procedure FillEllipseImageRel (Canv:TFPCustomCanvas; const Bounds:TRect; const Image:TFPCustomImage);
- var info : TEllipseInfo;
- r, y : integer;
- id : PEllipseInfoData;
- xo,yo, xi,yi : integer;
- begin
- info := TEllipseInfo.Create;
- try
- with info do
- begin
- GatherEllipseInfo(bounds);
- xo := round(cx) - (image.width div 2);
- yo := round(cy) - (image.height div 2);
- end;
- for r := 0 to info.infolist.count-1 do
- with PEllipseInfoData (info.infolist[r])^ do
- begin
- xi := (x - xo) mod image.width;
- if xi < 0 then
- inc (xi, image.width);
- for y := ytopmin to ybotmax do
- begin
- yi := (y - yo) mod image.height;
- if yi < 0 then
- inc (yi, image.height);
- canv.colors[x,y] := Image.colors[xi, yi];
- end;
- end;
- finally
- info.Free;
- end;
- end;
- end.
|