mirror of
				https://gitlab.com/freepascal.org/lazarus/lazarus.git
				synced 2025-10-31 02:01:46 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			1420 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
			
		
		
	
	
			1420 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			ObjectPascal
		
	
	
	
	
	
| unit TTProfile;
 | |
| 
 | |
| {$mode objfpc}{$H+}
 | |
| 
 | |
| interface
 | |
| 
 | |
| uses
 | |
| {$IFDEF VIRTUALPASCAL}
 | |
|      Use32,
 | |
| {$ENDIF}
 | |
|      LazFreeType,
 | |
|      TTTypes,
 | |
|      SysUtils;
 | |
| 
 | |
| const
 | |
|   PoolMaxCapacity = 512*1024 div 4; { = 512 Ko }
 | |
|   PoolIdleCapacity = 64*1024 div 4; { = 64 Ko }
 | |
| 
 | |
| type
 | |
|   TCurveDirection  = ( GoingUnknown, GoingUp, GoingDown, GoingHoriz );
 | |
|   TPoint = record x, y : long; end;
 | |
|   TBezierStack = packed array[0..64*2] of TPoint;
 | |
|   PBezierStack = ^TBezierStack;
 | |
| 
 | |
|   TProfile = class;
 | |
|   TProfileCollection = class;
 | |
| 
 | |
|   { TRenderPool }
 | |
| 
 | |
|   TRenderPool = class
 | |
|   protected
 | |
|     Precision, PrecisionHalf, BezierPrecision: integer;
 | |
|     BoundsMinY,BoundsMaxY: integer;
 | |
|     scaleShift: integer;
 | |
| 
 | |
|     function Bezier_Down(miny, maxy: Long): boolean;
 | |
|     function Bezier_State(y1, y2, y3: TT_F26Dot6): TCurveDirection;
 | |
|     function Bezier_To(x, y, Cx, Cy: LongInt): boolean;
 | |
|     function Bezier_Up(miny, maxy: Long): boolean;
 | |
|     function CEILING(x: Long): Long;
 | |
|     function DecomposeCurve(first, last: Int; flipped: Boolean;
 | |
|       points: TT_Points; flags: PByte): boolean;
 | |
|     function FLOOR(x: Long): Long;
 | |
|     function FRAC(x: Long): Int;
 | |
|     function GetCapacity: integer;
 | |
|     function Line_Down(x1, y1, x2, y2, miny, maxy: LongInt): boolean;
 | |
|     function Line_To(x, y: LongInt): boolean;
 | |
|     function Line_Up(x1, y1, x2, y2, miny, maxy: LongInt): boolean;
 | |
|     procedure Move_To(x, y: Longint);
 | |
|     procedure PushBezier(x1, y1, x2, y2, x3, y3: LongInt);
 | |
|     function SCALED(x: Long): Long;
 | |
|     procedure Split_Bezier(base: PBezierStack);
 | |
|     function TRUNC(x: Long): Long;
 | |
| 
 | |
|     function RequireCapacity(count: integer): boolean;
 | |
|     procedure PushValue(AValue: Long); inline;
 | |
|     property Capacity: integer read GetCapacity;
 | |
|   public
 | |
|     Joint    : Boolean;    (* Indicates that the last arc stopped sharp *)
 | |
|                            (* on a scan-line. Important to get rid of   *)
 | |
|                            (* doublets                                  *)
 | |
|     Fresh    : Boolean;    (* Indicates a new Profile which 'Start' field *)
 | |
|                            (* must be set                                 *)
 | |
|     cProfile  : TProfile;  (* current Profile                *)
 | |
|     ProfileColl: TProfileCollection;
 | |
|     LastX,                 (* Last and extrema coordinates during *)
 | |
|     LastY : longint;       (* rendering                           *)
 | |
|     CurveDir     : TCurveDirection;     (* State of current trace *)
 | |
| 
 | |
|     Arcs     : TBezierStack;
 | |
|     CurArc   : Int;             (* stack's top                 *)
 | |
| 
 | |
|     data: packed array of Long;
 | |
|     position: integer;
 | |
| 
 | |
|     constructor Create(APrecision, ABezierPrecision: integer);
 | |
|     destructor Destroy; override;
 | |
|     procedure SetPrecision(APrecision, ABezierPrecision: integer);
 | |
|     procedure SetBounds(MinY,MaxY: integer);
 | |
|     procedure SetScaleShift(value: integer);
 | |
|     procedure Clear;
 | |
|     procedure ReduceCapacity;
 | |
|     function Convert_Glyph(flipped: Boolean; points: TT_Points; flags: PByte;
 | |
|       Outs: TT_PConStarts; nContours : Int): boolean;
 | |
|   end;
 | |
| 
 | |
|   TProfile = class
 | |
|     Pool    : TRenderPool;
 | |
|     Flow    : Int;       (* ascending or descending Profile *)
 | |
|     Height  : Int;       (* Profile's height in scanlines   *)
 | |
|     Start   : Int;       (* Profile's starting scanline     *)
 | |
|     Offset  : integer;   (* offset of first coordinate in   *)
 | |
|                          (* render pool                     *)
 | |
| 
 | |
|     X       : Longint;   (* current coordinate during sweep *)
 | |
| 
 | |
|     nextInContour : TProfile;  (* next Profile of the same contour*)
 | |
|     nextInColl: TProfile;      (* next Profile in collection      *)
 | |
| 
 | |
|     nextInList, prevInList : TProfile;  (* in linked list *)
 | |
| 
 | |
|     constructor Create(APool: TRenderPool; ADirection: TCurveDirection);
 | |
|   end;
 | |
| 
 | |
|   { TProfileCollection }
 | |
| 
 | |
|   TProfileCollection = class
 | |
|   protected
 | |
|     procedure Remove_Profile;
 | |
|   public
 | |
|     Pool    : TRenderPool;
 | |
|     prevProfile : TProfile;
 | |
|     fProfile  : TProfile;  (* head of Profiles linked list   *)
 | |
|     gProfile  : TProfile;  (* last Profile in case of impact *)
 | |
| 
 | |
|     nProfs   : Int;        (* current number of Profiles *)
 | |
| 
 | |
|     constructor Create(APool: TRenderPool);
 | |
|     procedure Clear;
 | |
|     procedure New_Profile(ADirection: TCurveDirection);
 | |
|     procedure End_Profile;
 | |
|     destructor Destroy; override;
 | |
|   end;
 | |
| 
 | |
| procedure ProfileList_Init( out L : TProfile );
 | |
| procedure ProfileList_InsertFirstElement( var L: TProfile; Element: TProfile);
 | |
| procedure ProfileList_AppendToList( var ListOrElement: TProfile; ElementOrListToAppend: TProfile );
 | |
| procedure ProfileList_Remove( var List: TProfile; ElementToRemove: TProfile);
 | |
| procedure ProfileList_SortByX( var L: TProfile);
 | |
| procedure ProfileList_SortByStart( var L: TProfile);
 | |
| function ProfileList_Count( L: TProfile): integer;
 | |
| procedure ProfileList_Split( List : TProfile; out Part1, Part2: TProfile);
 | |
| 
 | |
| implementation
 | |
| 
 | |
| uses
 | |
|      TTCalc,       { used for MulDiv }
 | |
|      TTError ;
 | |
| 
 | |
| 
 | |
| (************************************************)
 | |
| (*                                              *)
 | |
| (*  ProfileList_Init                            *)
 | |
| (*                                              *)
 | |
| (*    Init an empty profile linked list.        *)
 | |
| (*                                              *)
 | |
| (************************************************)
 | |
| 
 | |
| procedure ProfileList_Init( out L : TProfile );
 | |
| begin
 | |
|   L := nil;
 | |
| end;
 | |
| 
 | |
| (************************************************)
 | |
| (*                                              *)
 | |
| (*  ProfileList_Append :                        *)
 | |
| (*                                              *)
 | |
| (*    Inserts a new Profile in a linked list.   *)
 | |
| (*                                              *)
 | |
| (************************************************)
 | |
| 
 | |
| procedure ProfileList_InsertFirstElement(var L: TProfile; Element: TProfile);
 | |
| begin
 | |
|   Element.nextInList := L;
 | |
|   if L <> nil then L.prevInList := Element;
 | |
|   L := Element;
 | |
| end;
 | |
| 
 | |
| procedure ProfileList_AppendToList( var ListOrElement: TProfile; ElementOrListToAppend: TProfile );
 | |
| var cur,next: TProfile;
 | |
| begin
 | |
|   if ElementOrListToAppend = nil then exit;
 | |
|   if ListOrElement = nil then
 | |
|     ListOrElement := ElementOrListToAppend else
 | |
|   begin
 | |
|     cur := ListOrElement;
 | |
|     next := cur.nextInList;
 | |
|     while next <> nil do
 | |
|     begin
 | |
|       cur := next;
 | |
|       next := cur.nextInList;
 | |
|     end;
 | |
|     ElementOrListToAppend.prevInList := cur;
 | |
|     cur.nextInList := ElementOrListToAppend;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| (************************************************)
 | |
| (*                                              *)
 | |
| (*  ProfileList_Remove :                        *)
 | |
| (*                                              *)
 | |
| (*    Removes an old Profile from a linked list *)
 | |
| (*                                              *)
 | |
| (************************************************)
 | |
| 
 | |
| procedure ProfileList_Remove( var List: TProfile; ElementToRemove: TProfile);
 | |
| var prev,next: TProfile;
 | |
| begin
 | |
|   if ElementToRemove = nil then exit;
 | |
|   prev := ElementToRemove.prevInList;
 | |
|   next := ElementToRemove.nextInList;
 | |
|   if prev <> nil then
 | |
|   begin
 | |
|     prev.nextInList := next;
 | |
|     ElementToRemove.prevInList := nil;
 | |
|   end;
 | |
|   if next <> nil then
 | |
|   begin
 | |
|     next.prevInList := prev;
 | |
|     ElementToRemove.nextInList := nil;
 | |
|   end;
 | |
|   if ElementToRemove = List then List := next;
 | |
| end;
 | |
| 
 | |
| procedure ProfileList_SortByX(var L: TProfile);
 | |
| 
 | |
|   function Merge(P1,P2: TProfile): TProfile;
 | |
|   var cur: TProfile;
 | |
|   begin
 | |
|     if P1 = nil then result := P2
 | |
|     else if P2 = nil then result := P1
 | |
|     else
 | |
|     begin
 | |
|       cur := nil;
 | |
|       while (P1 <> nil) and (P2 <> nil) do
 | |
|       begin
 | |
|         if P1.X <= P2.X then
 | |
|         begin
 | |
|           if cur = nil then
 | |
|           begin
 | |
|             result := P1;
 | |
|             result.prevInList := nil;
 | |
|             cur := result;
 | |
|           end else
 | |
|           begin
 | |
|             cur.nextInList := P1;
 | |
|             P1.prevInList := cur;
 | |
|             cur := P1;
 | |
|           end;
 | |
|           P1 := P1.nextInList;
 | |
|         end else
 | |
|         begin
 | |
|           if cur = nil then
 | |
|           begin
 | |
|             result := P2;
 | |
|             result.prevInList := nil;
 | |
|             cur := result;
 | |
|           end else
 | |
|           begin
 | |
|             cur.nextInList := P2;
 | |
|             P2.prevInList := cur;
 | |
|             cur := P2;
 | |
|           end;
 | |
|           P2 := P2.nextInList;
 | |
|         end;
 | |
|       end;
 | |
|       //append end of list
 | |
|       if P1 <> nil then
 | |
|       begin
 | |
|         cur.nextInList := P1;
 | |
|         P1.prevInList := cur;
 | |
|       end else
 | |
|       if P2 <> nil then
 | |
|       begin
 | |
|         cur.nextInList := P2;
 | |
|         P2.prevInList := cur;
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
| var L1,L2: TProfile;
 | |
| begin
 | |
|   if (L = nil) or (L.nextInList = nil) then exit;
 | |
|   ProfileList_Split(L, L1,L2);
 | |
|   ProfileList_SortByX(L1);
 | |
|   ProfileList_SortByX(L2);
 | |
|   L := Merge(L1, L2);
 | |
| end;
 | |
| 
 | |
| procedure ProfileList_SortByStart(var L: TProfile);
 | |
| 
 | |
|   function Merge(P1,P2: TProfile): TProfile;
 | |
|   var cur: TProfile;
 | |
|   begin
 | |
|     if P1 = nil then result := P2
 | |
|     else if P2 = nil then result := P1
 | |
|     else
 | |
|     begin
 | |
|       cur := nil;
 | |
|       while (P1 <> nil) and (P2 <> nil) do
 | |
|       begin
 | |
|         if P1.Start <= P2.Start then
 | |
|         begin
 | |
|           if cur = nil then
 | |
|           begin
 | |
|             result := P1;
 | |
|             result.prevInList := nil;
 | |
|             cur := result;
 | |
|           end else
 | |
|           begin
 | |
|             cur.nextInList := P1;
 | |
|             P1.prevInList := cur;
 | |
|             cur := P1;
 | |
|           end;
 | |
|           P1 := P1.nextInList;
 | |
|         end else
 | |
|         begin
 | |
|           if cur = nil then
 | |
|           begin
 | |
|             result := P2;
 | |
|             result.prevInList := nil;
 | |
|             cur := result;
 | |
|           end else
 | |
|           begin
 | |
|             cur.nextInList := P2;
 | |
|             P2.prevInList := cur;
 | |
|             cur := P2;
 | |
|           end;
 | |
|           P2 := P2.nextInList;
 | |
|         end;
 | |
|       end;
 | |
|       //append end of list
 | |
|       if P1 <> nil then
 | |
|       begin
 | |
|         cur.nextInList := P1;
 | |
|         P1.prevInList := cur;
 | |
|       end else
 | |
|       if P2 <> nil then
 | |
|       begin
 | |
|         cur.nextInList := P2;
 | |
|         P2.prevInList := cur;
 | |
|       end;
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
| var L1,L2: TProfile;
 | |
| begin
 | |
|   if (L = nil) or (L.nextInList = nil) then exit;
 | |
|   ProfileList_Split(L, L1,L2);
 | |
|   ProfileList_SortByStart(L1);
 | |
|   ProfileList_SortByStart(L2);
 | |
|   L := Merge(L1, L2);
 | |
| end;
 | |
| 
 | |
| function ProfileList_Count(L: TProfile): integer;
 | |
| begin
 | |
|   result := 0;
 | |
|   While L <> nil do
 | |
|   begin
 | |
|     inc(result);
 | |
|     L := L.nextInList;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure ProfileList_Split(List: TProfile; out Part1, Part2: TProfile);
 | |
| var n,m: integer;
 | |
|     p: TProfile;
 | |
| begin
 | |
|   if List = nil then
 | |
|   begin
 | |
|     Part1 := nil;
 | |
|     Part2 := nil;
 | |
|     exit;
 | |
|   end;
 | |
|   n := 0;
 | |
|   p := List;
 | |
|   while p <> nil do
 | |
|   begin
 | |
|     inc(n);
 | |
|     p := p.nextInList;
 | |
|   end;
 | |
|   m := (n+1) shr 1;
 | |
|   p := List;
 | |
|   while m > 0 do
 | |
|   begin
 | |
|     p := p.nextInList;
 | |
|     dec(m);
 | |
|   end;
 | |
| 
 | |
|   Part1 := List;
 | |
|   Part2 := p;
 | |
| 
 | |
|   if p <> nil then
 | |
|   begin
 | |
|     if p.prevInList = nil then
 | |
|       raise Exception.Create('Incoherent list');
 | |
|     p.prevInList.nextInList := nil;
 | |
|     p.prevInList := nil;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| {* integer computation *}
 | |
| 
 | |
| function TRenderPool.TRUNC( x : Long ) : Long; inline;
 | |
| begin
 | |
|   Trunc := (x and -Precision) div Precision;
 | |
| end;
 | |
| 
 | |
| function TRenderPool.FRAC( x : Long ) : Int; inline;
 | |
| begin
 | |
|   Frac := x and (Precision-1);
 | |
| end;
 | |
| 
 | |
| function TRenderPool.FLOOR( x : Long ) : Long; inline;
 | |
| begin
 | |
|   Floor := x and -Precision;
 | |
| end;
 | |
| 
 | |
| function TRenderPool.CEILING( x : Long ) : Long; inline;
 | |
| begin
 | |
|   Ceiling := (x + Precision-1) and -Precision;
 | |
| end;
 | |
| 
 | |
| function TRenderPool.SCALED( x : Long ) : Long; inline;
 | |
| begin
 | |
|   SCALED := (x shl scaleShift) - precisionHalf;
 | |
| end;
 | |
| 
 | |
| 
 | |
| { TProfileCollection }
 | |
| 
 | |
| constructor TProfileCollection.Create(APool: TRenderPool);
 | |
| begin
 | |
|   Pool := APool;
 | |
|   nProfs:= 0;
 | |
|   prevProfile := nil;
 | |
|   Pool.cProfile := nil;
 | |
|   fProfile := nil;
 | |
|   gProfile := nil;
 | |
|   Pool.ProfileColl := self;
 | |
| end;
 | |
| 
 | |
| procedure TProfileCollection.Clear;
 | |
| var p,p2: TProfile;
 | |
| begin
 | |
|   p := fProfile;
 | |
|   while p <> nil do
 | |
|   begin
 | |
|    p2 := p.nextInColl;
 | |
|    p.Free;
 | |
|    p := p2;
 | |
|   end;
 | |
| 
 | |
|   nProfs:= 0;
 | |
|   prevProfile := nil;
 | |
|   Pool.cProfile := nil;
 | |
|   fProfile := nil;
 | |
|   gProfile := nil;
 | |
| end;
 | |
| 
 | |
| procedure TProfileCollection.New_Profile(ADirection: TCurveDirection);
 | |
| var nProfile: TProfile;
 | |
| begin
 | |
|   nProfile := TProfile.Create(Pool, ADirection);
 | |
|   if fProfile = nil then fProfile := nProfile;
 | |
|   if gProfile = nil then gProfile := nProfile;
 | |
|   if Pool.cProfile <> nil then
 | |
|   begin
 | |
|     Pool.cProfile.nextInColl := nProfile;
 | |
|     Pool.cProfile.nextInContour := nProfile; //by default same contour
 | |
|   end;
 | |
|   prevProfile := Pool.cProfile;
 | |
|   Pool.cProfile := nProfile;
 | |
| 
 | |
|   Pool.CurveDir  := ADirection;
 | |
|   Pool.Fresh := True;
 | |
|   Pool.Joint := False;
 | |
|   inc(nProfs);
 | |
| end;
 | |
| 
 | |
| procedure TProfileCollection.Remove_Profile;
 | |
| begin
 | |
|   if prevProfile <> nil then
 | |
|   begin
 | |
|     prevProfile.nextInColl := nil;
 | |
|     if prevProfile.nextInContour = Pool.cProfile then
 | |
|       prevProfile.nextInContour := nil;
 | |
|     if gProfile = Pool.cProfile then gProfile := nil;
 | |
|     Pool.cProfile.Free;
 | |
|     Pool.cProfile := prevProfile;
 | |
|     prevProfile := nil;
 | |
|     dec(nProfs);
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TProfileCollection.End_Profile;
 | |
| var H: integer;
 | |
| begin
 | |
|   Pool.CurveDir  := GoingUnknown;
 | |
|   Pool.Fresh := False;
 | |
|   Pool.Joint := False;
 | |
| 
 | |
|   if (Pool.cProfile = nil) or (Pool.cProfile.Height <> 0) then exit;
 | |
|   H := Pool.position - Pool.cProfile.Offset;
 | |
|   if H = 0 then
 | |
|     Remove_Profile
 | |
|   else
 | |
|     Pool.cProfile.Height := H;
 | |
| end;
 | |
| 
 | |
| destructor TProfileCollection.Destroy;
 | |
| begin
 | |
|   Pool.ProfileColl := nil;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| { TRenderPool }
 | |
| 
 | |
| function TRenderPool.GetCapacity: integer;
 | |
| begin
 | |
|   result := length(data);
 | |
| end;
 | |
| 
 | |
| constructor TRenderPool.Create(APrecision, ABezierPrecision: integer);
 | |
| begin
 | |
|   position := 0;
 | |
|   SetPrecision(APrecision, ABezierPrecision);
 | |
|   setlength(data,64);
 | |
|   CurveDir := GoingUnknown;
 | |
|   Joint := false;
 | |
|   Fresh := False;
 | |
|   cProfile := nil;
 | |
|   BoundsMinY := 0;
 | |
|   BoundsMaxY := 65535;
 | |
|   ProfileColl := TProfileCollection.Create(self);
 | |
| end;
 | |
| 
 | |
| destructor TRenderPool.Destroy;
 | |
| begin
 | |
|   ProfileColl.Free;
 | |
|   inherited Destroy;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.SetPrecision(APrecision, ABezierPrecision: integer);
 | |
| begin
 | |
|   precision := APrecision;
 | |
|   precisionHalf := APrecision shr 1;
 | |
|   BezierPrecision := ABezierPrecision;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.SetBounds(MinY, MaxY: integer);
 | |
| begin
 | |
|   BoundsMinY := MinY;
 | |
|   BoundsMaxY := MaxY;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.SetScaleShift(value: integer);
 | |
| begin
 | |
|   scaleShift := value;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.Clear;
 | |
| begin
 | |
|   position := 0;
 | |
|   CurveDir := GoingUnknown;
 | |
|   Joint := false;
 | |
|   Fresh := False;
 | |
|   cProfile := nil;
 | |
|   ProfileColl.Clear;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.ReduceCapacity;
 | |
| begin
 | |
|   if Capacity > PoolIdleCapacity then
 | |
|      setLength(data, PoolIdleCapacity);
 | |
| end;
 | |
| 
 | |
| function TRenderPool.RequireCapacity(count: integer): boolean;
 | |
| begin
 | |
|   if count > PoolMaxCapacity then
 | |
|     result := false
 | |
|   else
 | |
|   begin
 | |
|     if length(data) < count then
 | |
|     begin
 | |
|       count := count*2;
 | |
|       if count > PoolMaxCapacity then
 | |
|         count := PoolMaxCapacity;
 | |
|       setlength(data, count);
 | |
|     end;
 | |
|     result := true;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.PushValue(AValue: Long);
 | |
| begin
 | |
|   if cProfile = nil then
 | |
|     raise Exception.Create('Out of profile');
 | |
|   data[position] := AValue;
 | |
|   inc( position );
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    TProfile.Create                                             *)
 | |
| (*                                                                          *)
 | |
| (* Description: Creates a new Profile in the render pool                    *)
 | |
| (*                                                                          *)
 | |
| (* Input:       ADirection    state/orientation of the new Profile          *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| constructor TProfile.Create(APool: TRenderPool; ADirection : TCurveDirection );
 | |
| begin
 | |
|   Case ADirection of
 | |
| 
 | |
|     GoingUp  : Flow := TT_Flow_Up;
 | |
|     GoingDown : Flow := TT_Flow_Down;
 | |
|   else
 | |
|     raise exception.Create('ERROR : Inconsistent Profile' );
 | |
|   end;
 | |
| 
 | |
|   Pool    := APool;
 | |
|   Start   := 0;
 | |
|   Height  := 0;
 | |
|   if APool <> nil then
 | |
|     Offset  := APool.position;
 | |
|   nextInContour   := nil;
 | |
|   nextInColl := nil;
 | |
| 
 | |
|   nextInList := nil;
 | |
|   prevInList := nil;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Split_Bezier                                                *)
 | |
| (*                                                                          *)
 | |
| (* Description: Subdivises one Bezier arc into two joint                    *)
 | |
| (*              sub-arcs in the Bezier stack.                               *)
 | |
| (*                                                                          *)
 | |
| (* Input:       None ( subdivised bezier is taken from the top of the       *)
 | |
| (*              stack )                                                     *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     Nada                                                        *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| procedure TRenderPool.Split_Bezier( base : PBezierStack );
 | |
| var
 | |
|   arc  : PBezierStack;
 | |
|   a, b : Long;
 | |
| begin
 | |
| {$IF defined(CPUI386) and defined(CPU32) and not defined(NO_ASM)} {$asmmode intel}
 | |
|   asm
 | |
|     push esi
 | |
|     push ebx
 | |
|     push ecx
 | |
| 
 | |
|     mov  esi, base
 | |
| 
 | |
|     mov  eax, [esi+2*8]       (* arc^[4].x := arc^[2].x *)
 | |
|     mov  ebx, [esi+1*8]       (* b := arc^[1].x *)
 | |
|     mov  ecx, [esi+0*8]       (* b := (arc^[0].x+b) div 2 *)
 | |
| 
 | |
|     mov  [esi+4*8], eax
 | |
| 
 | |
|     add  eax, ebx             (* a := (arc^[2].x+b) div 2 *)
 | |
|     add  ebx, ecx
 | |
|     mov  edx, eax
 | |
|     mov  ecx, ebx
 | |
|     sar  edx, 31
 | |
|     sar  ecx, 31
 | |
|     sub  eax, edx
 | |
|     sub  ebx, ecx
 | |
|     sar  eax, 1
 | |
|     sar  ebx, 1
 | |
| 
 | |
|     mov  [esi+3*8], eax       (* arc^[3].x := a *)
 | |
|     mov  [esi+1*8], ebx
 | |
| 
 | |
|     add  eax, ebx             (* arc[2].x := (a+b) div 2 *)
 | |
|     mov  edx, eax
 | |
|     sar  edx, 31
 | |
|     sub  eax, edx
 | |
|     sar  eax, 1
 | |
|     mov  [esi+2*8], eax
 | |
| 
 | |
|     add  esi, 4
 | |
| 
 | |
|     mov  eax, [esi+2*8]       (* arc^[4].x := arc^[2].x *)
 | |
|     mov  ebx, [esi+1*8]       (* b := arc^[1].x *)
 | |
|     mov  ecx, [esi+0*8]       (* b := (arc^[0].x+b) div 2 *)
 | |
| 
 | |
|     mov  [esi+4*8], eax
 | |
| 
 | |
|     add  eax, ebx             (* a := (arc^[2].x+b) div 2 *)
 | |
|     add  ebx, ecx
 | |
|     mov  edx, eax
 | |
|     mov  ecx, ebx
 | |
|     sar  edx, 31
 | |
|     sar  ecx, 31
 | |
|     sub  eax, edx
 | |
|     sub  ebx, ecx
 | |
|     sar  eax, 1
 | |
|     sar  ebx, 1
 | |
| 
 | |
|     mov  [esi+3*8], eax       (* arc^[3].x := a *)
 | |
|     mov  [esi+1*8], ebx
 | |
| 
 | |
|     add  eax, ebx             (* arc[2].x := (a+b) div 2 *)
 | |
|     mov  edx, eax
 | |
|     sar  edx, 31
 | |
|     sub  eax, edx
 | |
|     sar  eax, 1
 | |
|     mov  [esi+2*8], eax
 | |
| 
 | |
|     pop ecx
 | |
|     pop ebx
 | |
|     pop esi
 | |
|   end;
 | |
| {$ELSE}
 | |
|   arc := base;
 | |
| 
 | |
|   arc^[4].x := arc^[2].x;
 | |
|   b := arc^[1].x;
 | |
|   a := (arc^[2].x + b) div 2; arc^[3].x := a;
 | |
|   b := (arc^[0].x + b) div 2; arc^[1].x := b;
 | |
|   arc^[2].x := (a+b) div 2;
 | |
| 
 | |
|   arc^[4].y := arc^[2].y;
 | |
|   b := arc^[1].y;
 | |
|   a := (arc^[2].y + b) div 2; arc^[3].y := a;
 | |
|   b := (arc^[0].y + b) div 2; arc^[1].y := b;
 | |
|   arc^[2].y := (a+b) div 2;
 | |
| {$ENDIF}
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Push_Bezier                                                 *)
 | |
| (*                                                                          *)
 | |
| (* Description: Clears the Bezier stack and pushes a new Arc on top of it.  *)
 | |
| (*                                                                          *)
 | |
| (* Input:       x1,y1 x2,y2 x3,y3  new Bezier arc                           *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     nada                                                        *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| procedure TRenderPool.PushBezier( x1, y1, x2, y2, x3, y3 : LongInt );
 | |
| begin
 | |
|   curArc:=0;
 | |
| 
 | |
|   with Arcs[CurArc+2] do begin x:=x1; y:=y1; end;
 | |
|   with Arcs[CurArc+1] do begin x:=x2; y:=y2; end;
 | |
|   with Arcs[ CurArc ] do begin x:=x3; y:=y3; end;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Line_Up                                                     *)
 | |
| (*                                                                          *)
 | |
| (* Description: Compute the x-coordinates of an ascending line segment      *)
 | |
| (*              and stores them in the render pool.                         *)
 | |
| (*                                                                          *)
 | |
| (* Input:       x1,y1 x2,y2  Segment start (x1,y1) and end (x2,y2) points   *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if Render Pool overflow.                              *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| function TRenderPool.Line_Up( x1, y1, x2, y2, miny, maxy : LongInt ) : boolean;
 | |
| var
 | |
|   Dx, Dy               : LongInt;
 | |
|   e1, e2, f1, f2, size : Int;
 | |
|   Ix, Rx, Ax           : LongInt;
 | |
| begin
 | |
|   Line_Up := True;
 | |
| 
 | |
|   Dx := x2-x1; Dy := y2-y1;
 | |
| 
 | |
|   if (Dy <= 0) or (y2 < MinY) or (y1 > MaxY) then exit;
 | |
| 
 | |
|   if y1 < MinY then
 | |
|    begin
 | |
|     x1 := x1 + MulDiv( Dx, MinY-y1, Dy );
 | |
|     e1 := Trunc(MinY);
 | |
|     f1 := 0;
 | |
|    end
 | |
|   else
 | |
|    begin
 | |
|     e1 := Trunc(y1);
 | |
|     f1 := Frac(y1);
 | |
|    end;
 | |
| 
 | |
|   if y2 > MaxY then
 | |
|    begin
 | |
|     x2 := x2 + MulDiv( Dx, MaxY-y2, Dy );
 | |
|     e2 := Trunc(MaxY);
 | |
|     f2 := 0;
 | |
|    end
 | |
|   else
 | |
|    begin
 | |
|     e2 := Trunc(y2);
 | |
|     f2 := Frac(y2);
 | |
|    end;
 | |
| 
 | |
|   if f1 > 0 then
 | |
|     if e1 = e2 then exit
 | |
|     else
 | |
|       begin
 | |
|         inc( x1, MulDiv( Dx, precision-f1, Dy ) );
 | |
|         inc( e1 );
 | |
|       end
 | |
|   else
 | |
|     if Joint then
 | |
|       dec( self.Position );
 | |
| 
 | |
|   Joint := (f2 = 0);
 | |
| 
 | |
|   (* Indicates that the segment stopped sharp on a ScanLine *)
 | |
| 
 | |
|   if Fresh then
 | |
|    begin
 | |
|     cProfile.Start := e1;
 | |
|     Fresh           := False;
 | |
|    end;
 | |
| 
 | |
|   size := ( e2-e1 )+1;
 | |
|   if not RequireCapacity( self.Position+size ) then
 | |
|    begin
 | |
|      Line_Up := False;
 | |
|      Error   := Err_Ras_Overflow;
 | |
|      exit;
 | |
|    end;
 | |
| 
 | |
|   if Dx > 0 then
 | |
|     begin
 | |
|       Ix := (Precision*Dx) div Dy;
 | |
|       Rx := (Precision*Dx) mod Dy;
 | |
|       Dx := 1;
 | |
|     end
 | |
|   else
 | |
|     begin
 | |
|       Ix := -((Precision*-Dx) div Dy);
 | |
|       Rx :=   (Precision*-Dx) mod Dy;
 | |
|       Dx := -1;
 | |
|     end;
 | |
| 
 | |
|   Ax  := -Dy;
 | |
| 
 | |
|   while size > 0 do
 | |
|   begin
 | |
|     PushValue(x1);
 | |
| 
 | |
|     inc( x1, Ix );
 | |
|     inc( ax, rx );
 | |
| 
 | |
|     if ax >= 0 then
 | |
|     begin
 | |
|       dec( ax, dy );
 | |
|       inc( x1, dx );
 | |
|     end;
 | |
| 
 | |
|     dec( size );
 | |
|   end;
 | |
| 
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Line_Down                                                   *)
 | |
| (*                                                                          *)
 | |
| (* Description: Compute the x-coordinates of a descending line segment      *)
 | |
| (*              and stores them in the render pool.                         *)
 | |
| (*                                                                          *)
 | |
| (* Input:       x1,y1 x2,y2  Segment start (x1,y1) and end (x2,y2) points   *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if Render Pool overflow.                              *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| function TRenderPool.Line_Down( x1, y1, x2, y2, miny, maxy : LongInt ): boolean;
 | |
| var
 | |
|   _fresh : Boolean;
 | |
| begin
 | |
|   _fresh  := fresh;
 | |
| 
 | |
|   Line_Down := Line_Up( x1, -y1, x2, -y2, -maxy, -miny );
 | |
| 
 | |
|   if _fresh and not fresh then
 | |
|     cProfile.start := -cProfile.start;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Bezier_Up                                                   *)
 | |
| (*                                                                          *)
 | |
| (* Description: Compute the x-coordinates of an ascending bezier arc        *)
 | |
| (*              and stores them in the render pool.                         *)
 | |
| (*                                                                          *)
 | |
| (* Input:       None.The arc is taken from the top of the Bezier stack.     *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if Render Pool overflow.                              *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| function TRenderPool.Bezier_Up( miny, maxy : Long ) : boolean;
 | |
| var
 | |
|   y1, y2, e, e2, e0         : LongInt;
 | |
|   carc, debArc, f1          : Int;
 | |
|   base                      : PBezierStack;
 | |
|   maxArc                    : Int;
 | |
| label
 | |
|   Fin;
 | |
| begin
 | |
|   Bezier_Up := True;
 | |
| 
 | |
|   carc := curArc;
 | |
|   maxArc := length(Arcs)-cArc;
 | |
|   base := @Arcs[cArc];
 | |
|   y1   := base^[2].y;
 | |
|   y2   := base^[0].y;
 | |
| 
 | |
|   if ( y2 < MinY ) or ( y1 > MaxY ) then
 | |
|     goto Fin;
 | |
| 
 | |
|   e2 := FLOOR(y2);
 | |
| 
 | |
|   if e2 > MaxY then e2 := MaxY;
 | |
| 
 | |
|   e0 := MinY;
 | |
| 
 | |
|   if y1 < MinY then
 | |
|     e := MinY
 | |
|   else
 | |
|    begin
 | |
|     e  := CEILING(y1);
 | |
|     f1 := FRAC(y1);
 | |
|     e0 := e;
 | |
| 
 | |
|     if f1 = 0 then
 | |
|      begin
 | |
| 
 | |
|       //avoid duplicates
 | |
|       if Joint then begin dec(self.position); Joint:=False; end;
 | |
| 
 | |
|       PushValue(base^[2].x);
 | |
|       inc( e, Precision );
 | |
|      end
 | |
|    end;
 | |
| 
 | |
|   if Fresh then
 | |
|    begin
 | |
|     cProfile.Start := TRUNC(e0);
 | |
|     Fresh := False;
 | |
|    end;
 | |
| 
 | |
|   if e2 < e then
 | |
|     goto Fin;
 | |
| 
 | |
|   (* overflow ? *)
 | |
|   if not RequireCapacity( self.position + TRUNC(e2-e)+ 1) then
 | |
|     begin
 | |
|       Bezier_Up := False;
 | |
|       Error     := Err_Ras_Overflow;
 | |
|       exit;
 | |
|     end;
 | |
| 
 | |
|   debArc := cArc;
 | |
| 
 | |
|   while ( cArc >= debArc ) and ( e <= e2 ) do
 | |
|    begin
 | |
|     Joint := False;
 | |
|     y2    := base^[0].y;
 | |
| 
 | |
|     if y2 > e then
 | |
|       begin
 | |
|         y1 := base^[2].y;
 | |
|         if ( y2-y1 >= BezierPrecision ) and (cArc + 2 < maxArc) then
 | |
|           begin
 | |
|             Split_Bezier( base );
 | |
|             inc( cArc, 2 );
 | |
|             base := @base^[2];
 | |
|           end
 | |
|         else
 | |
|           begin
 | |
|             PushValue( base^[2].x +
 | |
|                        MulDiv( base^[0].x - base^[2].x,
 | |
|                                       e - y1,
 | |
|                                       y2 - y1 ) );
 | |
|             dec( cArc, 2 );
 | |
|             base := @Arcs[cArc];
 | |
|             inc( e, Precision );
 | |
|           end;
 | |
|       end
 | |
|     else
 | |
|       begin
 | |
|         if y2 = e then
 | |
|         begin
 | |
|           joint := True;
 | |
|           PushValue( Arcs[cArc].x);
 | |
|           inc( e, Precision );
 | |
|         end;
 | |
|         dec( cArc, 2 );
 | |
|         base := @Arcs[cArc];
 | |
|       end
 | |
|   end;
 | |
| 
 | |
| Fin:
 | |
|   dec( curArc, 2);
 | |
|   exit;
 | |
| end;
 | |
| 
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Bezier_Down                                                 *)
 | |
| (*                                                                          *)
 | |
| (* Description: Compute the x-coordinates of a descending bezier arc        *)
 | |
| (*              and stores them in the render pool.                         *)
 | |
| (*                                                                          *)
 | |
| (* Input:       None. Arc is taken from the top of the Bezier stack.        *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if Render Pool overflow.                              *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| function TRenderPool.Bezier_Down( miny, maxy : Long ) : boolean;
 | |
| var
 | |
|   base   : PBezierStack;
 | |
|   _fresh : Boolean;
 | |
| begin
 | |
|   _fresh := fresh;
 | |
|   base   := @Arcs[curArc];
 | |
| 
 | |
|   base^[0].y := -base^[0].y;
 | |
|   base^[1].y := -base^[1].y;
 | |
|   base^[2].y := -base^[2].y;
 | |
| 
 | |
|   Bezier_Down := Bezier_Up( -maxy, -miny );
 | |
| 
 | |
|   if _fresh and not fresh then
 | |
|     cProfile.start := -cProfile.start;
 | |
| 
 | |
|   base^[0].y := -base^[0].y;
 | |
| end;
 | |
| 
 | |
| procedure TRenderPool.Move_To( x,y: Longint);
 | |
| begin
 | |
|   LastX := x;
 | |
|   LastY := y;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Line_To                                                     *)
 | |
| (*                                                                          *)
 | |
| (* Description: Injects a new line segment and adjust Profiles list.        *)
 | |
| (*                                                                          *)
 | |
| (* Input:       x, y : segment endpoint ( start point with Move_To )        *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if Render Pool overflow or Incorrect Profile          *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| function TRenderPool.Line_To( x, y : LongInt ) : boolean;
 | |
| var LineDir: TCurveDirection;
 | |
| begin
 | |
|   Line_To := False;
 | |
| 
 | |
|   if y > LastY then LineDir := GoingUp else
 | |
|   if y < lastY then LineDir := GoingDown else
 | |
|     LineDir := GoingHoriz;
 | |
| 
 | |
|   if (CurveDir <> LineDir) and (LineDir <> GoingHoriz) then
 | |
|   begin
 | |
|     if CurveDir <> GoingUnknown then ProfileColl.End_Profile;
 | |
|     if LineDir <> GoingHoriz then
 | |
|       ProfileColl.New_Profile( LineDir);
 | |
|   end;
 | |
| 
 | |
|   Case CurveDir of
 | |
|     GoingUp  : if not Line_Up  ( LastX, LastY, X, Y, BoundsMiny, BoundsMaxy ) then exit;
 | |
|     GoingDown : if not Line_Down( LastX, LastY, X, Y, BoundsMiny, BoundsMaxy ) then exit;
 | |
|    end;
 | |
| 
 | |
|   LastX := x;
 | |
|   LastY := y;
 | |
| 
 | |
|   Line_To := True;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Bezier_State                                                *)
 | |
| (*                                                                          *)
 | |
| (* Description: Determines the state (ascending/descending/flat/undet)      *)
 | |
| (*              of a Bezier arc, along one given axis.                      *)
 | |
| (*                                                                          *)
 | |
| (* Input:       y1, y2, y3 : coordinates of the Bezier arc.                 *)
 | |
| (*                           along the concerned axis.                      *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     State, i.e. Ascending, Descending, Flat or Undetermined     *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| 
 | |
| function TRenderPool.Bezier_State( y1, y2, y3 : TT_F26Dot6 ) : TCurveDirection;
 | |
| begin
 | |
|   (* determine orientation of a Bezier arc *)
 | |
|   if y1 = y2 then
 | |
| 
 | |
|     if y2 = y3 then Bezier_State := GoingHoriz
 | |
|     else
 | |
|     if y2 > y3 then Bezier_State := GoingDown
 | |
|     else
 | |
|                     Bezier_State := GoingUp
 | |
|   else
 | |
|   if y1 > y2 then
 | |
| 
 | |
|     if y2 >= y3 then Bezier_State := GoingDown
 | |
|     else
 | |
|                      Bezier_State := GoingUnknown
 | |
|   else
 | |
| 
 | |
|     if y2 <= y3 then Bezier_State := GoingUp
 | |
|     else
 | |
|                      Bezier_State := GoingUnknown;
 | |
| end;
 | |
| 
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Bezier_To                                                   *)
 | |
| (*                                                                          *)
 | |
| (* Description: Injects a new bezier arc and adjust Profiles list.          *)
 | |
| (*                                                                          *)
 | |
| (* Input:       x,   y : arc endpoint ( start point with Move_To )          *)
 | |
| (*              Cx, Cy : control point                                      *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if Render Pool overflow or Incorrect Profile          *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| 
 | |
| function TRenderPool.Bezier_To( x, y, Cx, Cy : LongInt ) : boolean;
 | |
| var
 | |
|   y3, x3   : LongInt;
 | |
|   BezierDir : TCurveDirection;
 | |
| begin
 | |
|   Bezier_To := False;
 | |
| 
 | |
|   PushBezier( LastX, LastY, Cx, Cy, X, Y );
 | |
| 
 | |
|   while ( curArc >= 0 ) do
 | |
|    begin
 | |
|     y3 := Arcs[curArc].y;
 | |
|     x3 := Arcs[curArc].x;
 | |
| 
 | |
|     BezierDir := Bezier_State( Arcs[curArc+2].y, Arcs[curArc+1].y, y3 );
 | |
| 
 | |
|     case BezierDir of
 | |
| 
 | |
|       GoingHoriz   : dec( curArc, 2 );
 | |
| 
 | |
|       GoingUnknown : if curArc + 2 < length(arcs) then
 | |
|                     begin
 | |
|                       Split_Bezier( @Arcs[curArc] );
 | |
|                       inc( curArc, 2 );
 | |
|                     end else
 | |
|                       raise exception.Create('Bezier overflow');
 | |
|     else
 | |
|       if CurveDir <> BezierDir then
 | |
|       begin
 | |
|         if CurveDir <> GoingUnknown then
 | |
|             ProfileColl.End_Profile;
 | |
| 
 | |
|         ProfileColl.New_Profile( BezierDir );
 | |
|       end;
 | |
| 
 | |
|       case CurveDir of
 | |
|         GoingUp   : if not Bezier_Up( BoundsMiny, BoundsMaxy ) then exit;
 | |
|         GoingDown : if not Bezier_Down( BoundsMiny, BoundsMaxy ) then exit;
 | |
|       end;
 | |
| 
 | |
|     end;
 | |
|   end;
 | |
| 
 | |
|   LastX := x3;
 | |
|   LastY := y3;
 | |
| 
 | |
|   Bezier_To := True;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    DecomposeCurve                                              *)
 | |
| (*                                                                          *)
 | |
| (* Description: This functions scans the outline arrays in order to         *)
 | |
| (*              emit individual segments and beziers by calling the         *)
 | |
| (*              functions Line_To and Bezier_To. It handles all weird       *)
 | |
| (*              cases, like when the first point is off the curve, or       *)
 | |
| (*              when there are simply no "on" points in the contour !       *)
 | |
| (*                                                                          *)
 | |
| (* Input:       xCoord, yCoord : array coordinates to use.                  *)
 | |
| (*              first,  last   : indexes of first and last point in         *)
 | |
| (*                               contour.                                   *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if case of error.                                     *)
 | |
| (*                                                                          *)
 | |
| (* Notes:       The function assumes that 'first' < 'last'                  *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| procedure swap( var x, y : Long );  inline;
 | |
| var
 | |
|   s : Long;
 | |
| begin
 | |
|   s := x; x := y; y := s;
 | |
| end;
 | |
| 
 | |
| function TRenderPool.DecomposeCurve( first,  last   : Int;
 | |
|                          flipped        : Boolean;
 | |
|                          points: TT_Points; flags: PByte) : boolean;
 | |
| var
 | |
|   index : Int;
 | |
| 
 | |
|   x, y   : Long;    (* current point                *)
 | |
|   cx, cy : Long;    (* current Bezier control point *)
 | |
|   mx, my : Long;    (* middle point                 *)
 | |
| 
 | |
|   x_first, y_first : Long;    (* first point coordinates *)
 | |
|   x_last,  y_last  : Long;    (* last point coordinates  *)
 | |
| 
 | |
|   on_curve : Boolean;
 | |
| begin
 | |
| 
 | |
|   DecomposeCurve := False;
 | |
| 
 | |
|   with points^[first] do
 | |
|   begin
 | |
|     x_first := SCALED( x );
 | |
|     y_first := SCALED( y );
 | |
|   end;
 | |
| 
 | |
|   if flipped then  swap( x_first, y_first );
 | |
| 
 | |
|   with points^[last] do
 | |
|   begin
 | |
|     x_last  := SCALED( x );
 | |
|     y_last  := SCALED( y );
 | |
|   end;
 | |
| 
 | |
|   if flipped then  swap( x_last, y_last );
 | |
| 
 | |
|   Move_To(x_first, y_first);
 | |
|   cx := x_first;
 | |
|   cy := y_first;
 | |
| 
 | |
|   index    := first;
 | |
|   on_curve := Flags^[first] and 1 <> 0;
 | |
| 
 | |
|   (* check first point, and set origin *)
 | |
|   if not on_curve then
 | |
|   begin
 | |
|     (* first point is off the curve - yes, this happens !! *)
 | |
| 
 | |
|     if Flags^[last] and 1 <> 0 then
 | |
|       Move_To(x_last,y_last)  (* start at last point if it is *)
 | |
|     else                      (* on the curve                 *)
 | |
|       begin
 | |
|         (* if both first and last point    *)
 | |
|         (* are off the curve, start midway *)
 | |
|         Move_To( (LastX + x_last) div 2, (LastY + y_last) div 2);
 | |
| 
 | |
|         (* record midpoint in x_last,y_last *)
 | |
|         x_last := LastX;
 | |
|         y_last := LastY;
 | |
|       end;
 | |
|   end;
 | |
| 
 | |
| 
 | |
|   (* now process each contour point *)
 | |
|   while ( index < last ) do
 | |
|   begin
 | |
|     inc( index );
 | |
| 
 | |
|     x := SCALED( points^[index].x );
 | |
|     y := SCALED( points^[index].y );
 | |
| 
 | |
|     if flipped then swap( x, y );
 | |
| 
 | |
|     if on_curve then
 | |
|       begin
 | |
|         (* the previous point was on the curve *)
 | |
| 
 | |
|         on_curve := Flags^[index] and 1 <> 0;
 | |
|         if on_curve then
 | |
|           begin
 | |
|             (* two successive on points -> emit segment *)
 | |
|             if not Line_To( x, y ) then exit;
 | |
|           end
 | |
|         else
 | |
|           begin
 | |
|             (* else, keep current point as control for next bezier *)
 | |
|             cx := x;
 | |
|             cy := y;
 | |
|           end;
 | |
|       end
 | |
|     else
 | |
|       begin
 | |
|         (* the previous point was off the curve *)
 | |
| 
 | |
|         on_curve := Flags^[index] and 1 <> 0;
 | |
|         if on_curve then
 | |
|           begin
 | |
|             (* reaching on point -> emit Bezier *)
 | |
|             if not Bezier_To( x, y, cx, cy ) then exit;
 | |
|           end
 | |
|         else
 | |
|           begin
 | |
|             (* two successive off points -> create middle point *)
 | |
|             (* then emit Bezier                                 *)
 | |
|             mx := (cx + x) div 2;
 | |
|             my := (cy + y) div 2;
 | |
| 
 | |
|             if not Bezier_To( mx, my, cx, cy ) then exit;
 | |
| 
 | |
|             cx := x;
 | |
|             cy := y;
 | |
|           end;
 | |
|       end;
 | |
|   end;
 | |
| 
 | |
|   (* end of contour, close curve cleanly *)
 | |
|   if ( Flags^[first] and 1 <> 0 ) then
 | |
| 
 | |
|     if on_curve then
 | |
|       if not Line_To( x_first, y_first ) then exit else
 | |
|     else
 | |
|       if not Bezier_To( x_first, y_first, cx, cy ) then exit else
 | |
| 
 | |
|   else
 | |
|     if not on_curve then
 | |
|       if not Bezier_To( x_last, y_last, cx, cy ) then exit;
 | |
| 
 | |
|   DecomposeCurve := True;
 | |
| end;
 | |
| 
 | |
| (****************************************************************************)
 | |
| (*                                                                          *)
 | |
| (* Function:    Convert_Glyph                                               *)
 | |
| (*                                                                          *)
 | |
| (* Description: Converts a glyph into a series of segments and arcs         *)
 | |
| (*              and make a Profiles list with them.                         *)
 | |
| (*                                                                          *)
 | |
| (* Returns:     True on success                                             *)
 | |
| (*              False if any error was encountered during render.           *)
 | |
| (*                                                                          *)
 | |
| (****************************************************************************)
 | |
| 
 | |
| Function TRenderPool.Convert_Glyph( flipped : Boolean; points: TT_Points; flags: PByte; Outs : TT_PConStarts; nContours : Int ) : boolean;
 | |
| var
 | |
|   i, j : Int;
 | |
| 
 | |
| begin
 | |
|   result := False;
 | |
| 
 | |
|   try
 | |
| 
 | |
|     j        := 0;
 | |
|     for i := 0 to nContours-1 do
 | |
|     begin
 | |
|       // assign to nil to know first profile in contour
 | |
|       ProfileColl.gProfile := nil;
 | |
| 
 | |
|       (* decompose a single contour into individual segments and *)
 | |
|       (* beziers                                                 *)
 | |
| 
 | |
|       if not DecomposeCurve( j, outs^[i], flipped, points, flags) then exit;
 | |
|       j := outs^[i] + 1;
 | |
| 
 | |
|       ProfileColl.End_Profile;
 | |
| 
 | |
|       (* We _must_ take care of the case when the first and last arcs join  *)
 | |
|       (* while having the same orientation                                  *)
 | |
| 
 | |
|       if ( Frac(lastY) = 0 ) and
 | |
|          ( lastY >= BoundsMinY ) and
 | |
|          ( lastY <= BoundsMaxY ) then
 | |
| 
 | |
|         if ( ProfileColl.gProfile <> nil ) and                 (* gProfile can be nil   *)
 | |
|            ( cProfile <> nil) and
 | |
|            ( cProfile.Height > 0) and
 | |
|            ( ProfileColl.gProfile.Start = cProfile.Start + (cProfile.Height-1)*cProfile.Flow) and
 | |
|            ( ProfileColl.gProfile.Flow = cProfile.Flow ) then  (* if the contour was    *)
 | |
|                                                                (* too small to be drawn *)
 | |
|         begin
 | |
|           dec( self.position );
 | |
|           dec( cProfile.Height);
 | |
|         end;
 | |
| 
 | |
|       //if (cProfile <> nil) and (cProfile.Height > 1) then dec (cProfile.Height);
 | |
| 
 | |
|       // close contour
 | |
|       if ProfileColl.gProfile <> nil then cProfile.nextInContour := ProfileColl.gProfile;
 | |
| 
 | |
|     end;
 | |
|     result := true;
 | |
| 
 | |
|   except
 | |
|     on ex: Exception do
 | |
|     begin
 | |
| 
 | |
|     end;
 | |
|   end;
 | |
| end;
 | |
| 
 | |
| end.
 | |
| 
 | 
