2441 lines
84 KiB
ObjectPascal

// SPDX-License-Identifier: LGPL-3.0-linking-exception
{ @abstract(This unit provides vectorizers for black'n'white images and text.)
The TBGRAVectorizedFontRenderer class can be provided as a renderer to TBGRABitmap.
**Font rendering units** : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
}
unit BGRAVectorize;
{$mode objfpc}{$H+}
interface
uses
BGRAClasses, SysUtils, BGRAGraphics, BGRABitmapTypes, BGRATypewriter,
BGRATransform, BGRACanvas2D, BGRAText;
{ Vectorize a monochrome bitmap (actually checking the green channel) }
function VectorizeMonochrome(ASource: TBGRACustomBitmap; AZoom: single; APixelCenteredCoordinates: boolean;
AWhiteBackground: boolean = true; ADiagonalFillPercent: single = 66; AIntermediateDiagonals: boolean = true): ArrayOfTPointF;
{ Vectorize a rectangular area in a monochrome bitmap (actually checking the green channel) }
function VectorizeMonochrome(ASource: TBGRACustomBitmap; ARect: TRect; AZoom: single; APixelCenteredCoordinates: boolean;
AWhiteBackground: boolean = true; ADiagonalFillPercent: single = 66; AIntermediateDiagonals: boolean = true): ArrayOfTPointF;
type
TBGRAVectorizedFont = class;
{ @abstract(Font renderer using vectorized fonts and with effects.)
TBGRAVectorizedFontRenderer class works like other font renderers, i.e., it can
be assigned to the FontRenderer property. You can use it in two different modes :
- if you supply a directory, it will look for *.glyphs files in it to load fonts
- if you don't supply a directory, fonts will be vectorized from the system
If provides effect like TBGRATextEffectFontRenderer (outline and shadow) but that difference
is that the font is always vectorized whereas TBGRATextEffectFontRenderer will use bitmap
rendering when possible.
}
TBGRAVectorizedFontRenderer = class(TBGRACustomFontRenderer)
protected
FVectorizedFontArray: array of record
FontName: string;
FontStyle: TFontStyles;
VectorizedFont: TBGRAVectorizedFont;
end;
FVectorizedFont: TBGRAVectorizedFont;
FCanvas2D: TBGRACanvas2D;
FDirectoryUTF8: string;
function OutlineActuallyVisible: boolean;
procedure UpdateFont;
function GetCanvas2D(ASurface: TBGRACustomBitmap): TBGRACanvas2D;
procedure InternalTextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string; style: TTextStyle; c: TBGRAPixel; texture: IBGRAScanner);
procedure InternalCopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment; ABidiMode: TFontBidiMode);
procedure InternalTextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; c: TBGRAPixel; texture: IBGRAScanner; align: TAlignment; ABidiMode: TFontBidiMode);
procedure Init;
public
MinFontResolution, MaxFontResolution: integer;
QuadraticCurves: boolean;
OutlineVisible: boolean;
OutlineWidth: single;
OutlineColor: TBGRAPixel;
OutlineTexture: IBGRAScanner;
OuterOutlineOnly: boolean;
OutlineJoin: TPenJoinStyle;
ShadowVisible: boolean;
ShadowColor: TBGRAPixel;
ShadowRadius: integer;
ShadowOffset: TPoint;
constructor Create; overload;
constructor Create(ADirectoryUTF8: string); overload;
function GetFontPixelMetric: TFontPixelMetric; override;
function GetFontPixelMetricF: TFontPixelMetricF; override;
function FontExists(AName: string): boolean; override;
function TextVisible(const AColor: TBGRAPixel): boolean; override;
procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment); overload; override;
procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment; ARightToLeft: boolean); overload; override;
procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; texture: IBGRAScanner; align: TAlignment); overload; override;
procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; texture: IBGRAScanner; align: TAlignment; ARightToLeft: boolean); overload; override;
procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); overload; override;
procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; texture: IBGRAScanner; align: TAlignment; ARightToLeft: boolean); overload; override;
procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; align: TAlignment); overload; override;
procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; align: TAlignment; ARightToLeft: boolean); overload; override;
procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel); overload; override;
procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; texture: IBGRAScanner); overload; override;
procedure CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment); override;
procedure CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment; ARightToLeft: boolean); override;
function HandlesTextPath: boolean; override;
function TextSize(sUTF8: string): TSize; overload; override;
function TextSizeF(sUTF8: string): TPointF; overload; override;
function TextSize(sUTF8: string; AMaxWidth: integer; {%H-}ARightToLeft: boolean): TSize; overload; override;
function TextSizeF(sUTF8: string; AMaxWidthF: single; {%H-}ARightToLeft: boolean): TPointF; overload; override;
function TextFitInfo(sUTF8: string; AMaxWidth: integer): integer; override;
function TextFitInfoF(sUTF8: string; AMaxWidthF: single): integer; override;
destructor Destroy; override;
end;
{ Size of glyphs in text }
TGlyphSizes = array of record
Text, Glyph: String;
Width,Height: single;
end;
{ Header of a serialized vectorized font }
TBGRAVectorizedFontHeader = record
Name: string;
Style: TFontStyles;
EmHeightRatio: single;
Resolution: integer;
PixelMetric: TFontPixelMetric;
end;
{ General information on glyph stream }
TBGRAGlyphsInfo = record
Name: string;
Style: TFontStyles;
NbGlyphs: integer;
end;
{ Allows to vectorize and to load vectorized font and draw them }
TBGRAVectorizedFont = class(TBGRACustomTypeWriter)
private
FName : string;
FStyle: TFontStyles;
FResolution: integer;
FFont: TFont;
FBuffer: TBGRACustomBitmap;
FFullHeight: single;
FFontMatrix: TAffineMatrix;
FOrientation: single;
FQuadraticCurves: boolean;
FItalicSlope: single;
FWordBreakHandler: TWordBreakHandler;
FDirectory: string;
FDirectoryContent: array of record
Filename: string;
FontName: string;
FontStyle: TFontStyles;
end;
FFontEmHeightRatioComputed: boolean;
FFontEmHeightRatio: single;
FFontPixelMetric: TFontPixelMetric;
FFontPixelMetricComputed: boolean;
FFontFound: boolean;
function GetEmHeight: single;
function GetFontPixelMetric: TFontPixelMetric;
function GetLCLHeight: single;
function GetVectorizeLCL: boolean;
procedure SetEmHeight(AValue: single);
procedure SetItalicSlope(AValue: single);
procedure SetLCLHeight(AValue: single);
procedure SetOrientation(AValue: single);
procedure SetQuadraticCurves(AValue: boolean);
procedure SetResolution(AValue: integer);
procedure SetFontMatrix(AValue: TAffineMatrix);
procedure SetFullHeight(AValue: single);
procedure SetName(AValue: string);
procedure SetStyle(AValue: TFontStyles);
function GetFontEmHeightRatio: single;
procedure SetVectorizeLCL(AValue: boolean);
procedure GlyphCallbackForGlyphSizes(ATextUTF8: string; AGlyph: TBGRAGlyph;
{%H-}AFlags: TBrowseGlyphCallbackFlags; AData: Pointer; out AContinue: boolean);
procedure UpdateQuadraticCallback({%H-}ATextUTF8: string; AGlyph: TBGRAGlyph;
{%H-}AFlags: TBrowseGlyphCallbackFlags; {%H-}AData: Pointer; out AContinue: boolean);
protected
procedure UpdateFont;
procedure UpdateMatrix;
function GetGlyph(AIdentifier: string): TBGRAGlyph; override;
procedure DefaultWordBreakHandler(var ABefore, AAfter: string);
procedure Init(AVectorize: boolean);
function CustomHeaderSize: integer; override;
procedure WriteCustomHeader(AStream: TStream); override;
procedure ReadAdditionalHeader(AStream: TStream); override;
function ReadVectorizedFontHeader(AStream: TStream): TBGRAVectorizedFontHeader;
function HeaderName: string; override;
procedure SetDirectory(const AValue: string);
function ComputeKerning(AIdLeft, AIdRight: string): single; override;
public
UnderlineDecoration,StrikeOutDecoration: boolean;
constructor Create; overload;
constructor Create(AVectorizeLCL: boolean); overload;
destructor Destroy; override;
function GetGlyphSize(AIdentifier:string): TPointF;
function GetTextGlyphSizes(ATextUTF8:string): TGlyphSizes;
function GetTextSize(ATextUTF8:string): TPointF;
function TextFitInfo(ATextUTF8: string; AMaxWidth: single): integer;
procedure SplitText(var ATextUTF8: string; AMaxWidth: single; out ARemainsUTF8: string);
procedure DrawText(ADest: TBGRACanvas2D; ATextUTF8: string; X, Y: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft); override;
procedure CopyTextPathTo(ADest: IBGRAPath; ATextUTF8: string; X, Y: Single;
AAlign: TBGRATypeWriterAlignment=twaTopLeft); override;
procedure DrawTextWordBreak(ADest: TBGRACanvas2D; ATextUTF8: string; X, Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft);
procedure DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string; X1,Y1,X2,Y2: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft); overload;
procedure DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string; ATopLeft,ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment=twaTopLeft); overload;
function GetTextWordBreakGlyphBoxes(ATextUTF8: string; X,Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment = twaTopLeft): TGlyphBoxes;
function GetTextRectGlyphBoxes(ATextUTF8: string; X1,Y1,X2,Y2: Single; AAlign: TBGRATypeWriterAlignment=twaTopLeft): TGlyphBoxes; overload;
function GetTextRectGlyphBoxes(ATextUTF8: string; ATopLeft,ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment=twaTopLeft): TGlyphBoxes; overload;
procedure UpdateDirectory;
function LoadGlyphsInfo(AFilenameUTF8: string): TBGRAGlyphsInfo;
property Resolution: integer read FResolution write SetResolution;
property Style: TFontStyles read FStyle write SetStyle;
property Name: string read FName write SetName;
property LCLHeight: single read GetLCLHeight write SetLCLHeight;
property EmHeight: single read GetEmHeight write SetEmHeight;
property FullHeight: single read FFullHeight write SetFullHeight;
property FontMatrix: TAffineMatrix read FFontMatrix write SetFontMatrix;
property Orientation: single read FOrientation write SetOrientation;
property QuadraticCurves: boolean read FQuadraticCurves write SetQuadraticCurves;
property ItalicSlope: single read FItalicSlope write SetItalicSlope;
property OnWordBreak: TWordBreakHandler read FWordBreakHandler write FWordBreakHandler;
property Directory: string read FDirectory write SetDirectory;
property FontEmHeightRatio: single read GetFontEmHeightRatio;
property FontPixelMetric: TFontPixelMetric read GetFontPixelMetric;
property FontFound: boolean read FFontFound;
property VectorizeLCL: boolean read GetVectorizeLCL write SetVectorizeLCL;
end;
implementation
uses BGRAUTF8{$IFDEF LCL}, Forms{$ENDIF};
type
TGlyphSizesCallbackData = record
Sizes: TGlyphSizes;
Count: integer;
end;
function VectorizeMonochrome(ASource: TBGRACustomBitmap; ARect: TRect; AZoom: single; APixelCenteredCoordinates: boolean;
AWhiteBackground: boolean; ADiagonalFillPercent: single; AIntermediateDiagonals: boolean): ArrayOfTPointF;
const unitShift = 6;
iHalf = 1 shl (unitShift-1);
iUnit = 1 shl unitShift;
var
iDiag,iOut: integer;
n: integer;
factor: single;
offset: single;
p,pprev,pnext : PBGRAPixel;
x,y,ix,iy: integer;
points: array of record
coord: tpoint;
prev,next: integer;
drawn,{shouldRemove,}removed,done: boolean;
end;
nbPoints:integer;
PointsPreviousLineStart,PointsCurrentLineStart: integer;
cur: packed array[1..9] of boolean;
ortho: array of array of boolean;
polygonF: array of TPointF;
function CheckPixel(const APixel: TBGRAPixel): boolean; inline;
begin
result := (APixel.green <= 128) xor not AWhiteBackground;
end;
function AddPoint(x,y,APrev,ANext: integer): integer;
begin
if nbpoints = length(points) then
setlength(points, nbpoints*2+1);
result := nbpoints;
with points[result] do
begin
coord := point(x,y);
prev := APrev;
next := ANext;
drawn := false;
removed := false;
// shouldRemove := false;
end;
inc(nbpoints);
end;
function InsertPoint(x,y,APrev,ANext: integer): integer;
begin
if nbpoints = length(points) then
setlength(points, nbpoints*2+1);
result := nbpoints;
with points[result] do
begin
coord := point(x,y);
prev := APrev;
next := ANext;
drawn := false;
removed := false;
// shouldRemove := false;
end;
if APrev<>-1 then points[APrev].next := result;
if ANext<>-1 then points[ANext].prev := result;
inc(nbpoints);
end;
procedure RemovePoint(idx: integer);
begin
points[idx].removed:= true;
if points[idx].prev <> -1 then points[points[idx].prev].next := points[idx].next;
if points[idx].next <> -1 then points[points[idx].next].prev := points[idx].prev;
end;
procedure AddLine(x1,y1,x2,y2: integer); overload;
var i,j,k: integer;
begin
for i := PointsPreviousLineStart to nbpoints-1 do
if (points[i].coord.x = x2) and (points[i].coord.y = y2) and (points[i].prev = -1) then
begin
for j := i+1 to nbpoints-1 do
if (points[j].coord.x = x1) and (points[j].coord.y = y1) and (points[j].next = -1) then
begin
points[j].next := i;
points[i].prev := j;
exit;
end;
k := addpoint(x1,y1,-1,i);
points[i].prev := k;
exit;
end else
if (points[i].coord.x = x1) and (points[i].coord.y = y1) and (points[i].next = -1) then
begin
for j := i+1 to nbpoints-1 do
if (points[j].coord.x = x2) and (points[j].coord.y = y2) and (points[j].prev = -1) then
begin
points[j].prev := i;
points[i].next := j;
exit;
end;
k := addpoint(x2,y2,i,-1);
points[i].next := k;
exit;
end;
k := addpoint(x1,y1,-1,-1);
points[k].next := addpoint(x2,y2,k,-1);
end;
procedure AddLine(x1,y1,x2,y2,x3,y3: integer); overload;
begin
AddLine(x1,y1,x2,y2);
AddLine(x2,y2,x3,y3);
end;
procedure AddLine(x1,y1,x2,y2,x3,y3,x4,y4: integer); overload;
begin
AddLine(x1,y1,x2,y2);
AddLine(x2,y2,x3,y3);
AddLine(x3,y3,x4,y4);
end;
procedure AddLine(x1,y1,x2,y2,x3,y3,x4,y4,x5,y5: integer); overload;
begin
AddLine(x1,y1,x2,y2);
AddLine(x2,y2,x3,y3);
AddLine(x3,y3,x4,y4);
AddLine(x4,y4,x5,y5);
end;
procedure AddPolygon(nStart: integer);
procedure Rewind(out cycle: boolean);
var cur: integer;
begin
cur := nStart;
cycle := false;
while (points[cur].prev <> -1) do
begin
cur := points[cur].prev;
if cur = nStart then
begin
cycle := true; //identify cycle
break;
end;
end;
nStart := cur;
end;
function aligned(start1,end1,start2,end2: integer): boolean;
var
u,v: TPointF;
lu,lv: single;
begin
if (start1=-1) or (end1=-1) or (start2=-1) or (end2=-1) then
begin
result :=false;
exit;
end;
u := pointF(points[end1].coord.x - points[start1].coord.x, points[end1].coord.y - points[start1].coord.y);
lu := VectLen(u);
if lu <> 0 then u.Scale(1/lu);
v := pointF(points[end2].coord.x - points[start2].coord.x, points[end2].coord.y - points[start2].coord.y);
lv := VectLen(v);
if lv <> 0 then v.Scale(1/lv);
result := u**v > 0.999;
end;
function angle45(prev,cur,next: integer): boolean;
var
u,v: TPointF;
lu,lv,dp: single;
begin
if (prev=-1) or (cur=-1) or (next=-1) then
begin
result :=false;
exit;
end;
u := pointF(points[next].coord.x - points[cur].coord.x, points[next].coord.y - points[cur].coord.y);
lu := VectLen(u);
if lu <> 0 then u.Scale(1/lu);
v := pointF(points[cur].coord.x - points[prev].coord.x, points[cur].coord.y - points[prev].coord.y);
lv := VectLen(v);
if lv <> 0 then v.Scale(1/lv);
dp := u**v;
result := (dp > 0.70) and (dp < 0.72);
end;
procedure RemoveAligned;
var cur,prev,next: integer;
begin
cur := nStart;
prev := -1;
while not points[cur].removed do
begin
next := points[cur].next;
//remove aligned points
if (prev <> -1) and aligned(prev,cur,cur,next) then
RemovePoint(cur)
else
prev := cur;
cur := next;
if next = nStart then
begin
next := points[cur].next;
if (prev <> -1) and (next <> prev) then
if aligned(prev,cur,cur,next) then
begin
RemovePoint(cur);
nStart := next;
end;
break; //cycle
end;
end;
end;
procedure MakePolygon(cycle: boolean);
var ptsF: array of TPointF;
nbPtsF: integer;
nb,nb2,cur,i: integer;
begin
cur := nStart;
nb := 0;
nb2 := 0;
repeat
if not points[cur].removed then inc(nb);
inc(nb2);
cur := points[cur].next;
until (cur = -1) or (cur = nStart) or (nb2 > nbPoints);
if (nb2 > nbPoints) or (nb <= 2) then exit;
setlength(ptsF,nb);
cur := nStart;
nbPtsF := 0;
repeat
with points[cur] do
if not removed then
begin
ptsF[nbPtsF] := pointf(coord.x*factor+offset,coord.y*factor+offset);
points[cur].drawn := true;
inc(nbPtsF);
end;
cur := points[cur].next;
until (cur = -1) or (cur = nStart);
if cycle then
begin
if polygonF = nil then
polygonF := ptsF else
begin
cur := length(polygonF);
setlength(polygonF, length(polygonF)+length(ptsF)+1);
polygonF[cur] := EmptyPointF;
for i := 0 to high(ptsF) do
begin
inc(cur);
polygonF[cur] := ptsF[i];
end;
end;
end;
ptsF := nil;
//Bitmap.DrawPolyLineAntialias(ptsF,BGRABlack,1);
end;
function segabslength(cur,next: integer): integer;
var
tx,ty: integer;
begin
if (cur = -1) or (next = -1) then result := 0
else
begin
tx := abs(points[next].coord.x - points[cur].coord.x);
ty := abs(points[next].coord.y - points[cur].coord.y);
if tx > ty then result := tx else result := ty;
end;
end;
function getnext(cur: integer): integer;
begin
result := cur;
if result <> -1 then
begin
repeat
result := points[result].next;
if result = cur then result := -1;
until (result = -1) or not points[result].removed;
end;
end;
function getprev(cur: integer): integer;
begin
result := cur;
if result <> -1 then
begin
repeat
result := points[result].prev;
if result = cur then result := -1;
until (result = -1) or not points[result].removed;
end;
end;
procedure NiceLines;
var cur2, next,next2, startIdx, endIdx: integer;
nb: integer;
u: TPoint;
function SameDirection(p1,p2: integer): boolean;
var
v: TPoint;
begin
v := Point(points[p2].coord.x-points[p1].coord.x,
points[p2].coord.y-points[p1].coord.y);
result := (v.x*u.y - v.y*u.x = 0) and (v.x*u.x + v.y*u.y > 0);
end;
function GetSide(p1,p2: integer): integer;
var
v: TPoint;
begin
v := Point(points[p2].coord.x-points[p1].coord.x,
points[p2].coord.y-points[p1].coord.y);
result := v.x*u.y - v.y*u.x;
if result < 0 then result := -1 else if result > 0 then result := 1;
end;
procedure DoNiceLines(DoDiag: boolean);
var cur, nbSegs, i: integer;
isDiag: Boolean;
segs: array of record
p1,p2: TPoint;
abslen: integer;
end;
begin
for cur := 0 to nb-1 do
if not points[cur].removed and not points[cur].done then
begin
next := getnext(cur);
isDiag := (points[next].coord.x <> points[cur].coord.x) and
(points[next].coord.y <> points[cur].coord.y);
if (segabslength(cur,next) > iUnit) and (DoDiag xor (not isDiag)) then
begin
startIdx := cur;
endIdx := next;
u := Point(points[next].coord.x-points[cur].coord.x,
points[next].coord.y-points[cur].coord.y);
nbsegs := 1;
if (u.x <> 0) or (u.y <> 0) then
begin
repeat
next := getnext(endIdx);
next2 := getnext(next);
if (next<>startIdx) and (next2<>startIdx) and angle45(getprev(endIdx),endIdx,next) and
(segabslength(endIdx,next) < 2*iUnit) and SameDirection(next,next2) then
begin
endIdx := next2;
inc(nbsegs);
end
else
break;
until false;
repeat
next := getprev(startIdx);
next2 := getprev(next);
if (next<>endIdx) and (next2<>endIdx) and angle45(getnext(startIdx),startIdx,next) and
(segabslength(startIdx,next) < 2*iUnit) and SameDirection(next2,next) then
begin
startIdx := next2;
inc(nbsegs);
end
else
break;
until false;
setlength(segs, nbSegs);
cur2 := startIdx;
for i := 0 to nbSegs-1 do
begin
next := getnext(cur2);
segs[i].p1 := points[cur2].coord;
segs[i].p2 := points[next].coord;
segs[i].abslen := segabslength(cur2,next);
points[cur2].done := true;
points[next].done := true;
if cur2 <> startIdx then RemovePoint(cur2);
if next = endIdx then break
else
begin
cur2 := getnext(next);
RemovePoint(next);
end;
end;
cur2 := startIdx;
for i := 0 to nbSegs-2 do
begin
if i <> 0 then
cur2 := InsertPoint( (segs[i].p1.x+segs[i].p2.x) div 2,
(segs[i].p1.y+segs[i].p2.y) div 2,
cur2, endIdx);
if abs(segs[i].abslen-segs[i+1].abslen) > iHalf then
cur2 := InsertPoint( (segs[i].p2.x+segs[i+1].p1.x) div 2,
(segs[i].p2.y+segs[i+1].p1.y) div 2,
cur2, endIdx);
end;
end;
end;
end;
end;
procedure Init;
var cur: integer;
begin
u := Point(0,0);
nb := nbPoints;
for cur := 0 to nb-1 do
points[cur].done := false;
end;
begin
Init;
DoNiceLines(false);
DoNiceLines(true);
end;
var cycle: boolean;
begin
//rewind
Rewind(cycle);
RemoveAligned;
if AIntermediateDiagonals then NiceLines;
MakePolygon(cycle);
end;
function GetBoundsWithin: TRect;
var p: PBGRAPixel;
yb, xb, xb2, maxx, maxy, minx, miny: LongInt;
begin
maxx := ARect.Left-1;
maxy := ARect.Top-1;
minx := ARect.Right;
miny := ARect.Bottom;
for yb := ARect.Top to ARect.Bottom-1 do
begin
p := ASource.ScanLine[yb] + ARect.Left;
for xb := ARect.Left to ARect.Right - 1 do
begin
if CheckPixel(p^) then
begin
if xb < minx then minx := xb;
if yb < miny then miny := yb;
if xb > maxx then maxx := xb;
if yb > maxy then maxy := yb;
inc(p, ARect.Right-1-xb);
for xb2 := ARect.Right-1 downto xb+1 do
begin
if CheckPixel(p^) then
begin
if xb2 > maxx then maxx := xb2;
break;
end;
dec(p);
end;
break;
end;
Inc(p);
end;
end;
if minx > maxx then Result := EmptyRect
else
begin
Result.left := minx;
Result.top := miny;
Result.right := maxx + 1;
Result.bottom := maxy + 1;
end;
end;
function IsRectFull: boolean;
var
yb, xb: LongInt;
p: PBGRAPixel;
begin
for yb := ARect.Top to ARect.Bottom-1 do
begin
p := ASource.ScanLine[yb] + ARect.Left;
for xb := ARect.Left to ARect.Right - 1 do
begin
if not CheckPixel(p^) then exit(false);
inc(p);
end;
end;
result := true;
end;
begin
ARect.Intersect( rect(0,0,ASource.Width,ASource.Height) );
if ARect.IsEmpty then exit(nil);
ARect := GetBoundsWithin;
if ARect.IsEmpty then exit(nil);
factor := AZoom;
offset := AZoom*0.5;
if APixelCenteredCoordinates then DecF(Offset, 0.5);
if IsRectFull then
begin
result := PointsF([PointF((ARect.Left-0.5)*factor+offset, (ARect.Top-0.5)*factor+offset),
PointF((ARect.Left-0.5)*factor+offset, (ARect.Bottom-0.5)*factor+offset),
PointF((ARect.Right-0.5)*factor+offset, (ARect.Bottom-0.5)*factor+offset),
PointF((ARect.Right-0.5)*factor+offset, (ARect.Top-0.5)*factor+offset)]);
exit;
end;
iDiag := round((ADiagonalFillPercent-50)/100 * iHalf)*2; //even rounding to keep alignment with iOut
iOut := (iHalf-iDiag) div 2;
nbpoints := 0;
points := nil;
polygonF := nil;
setlength(ortho,ARect.Height,ARect.Width);
for y := 0 to ARect.Height-1 do
begin
if y = 0 then
pprev := nil
else
pprev := ASource.ScanLine[ARect.Top+y-1]+ARect.Left;
p := ASource.ScanLine[ARect.Top+y]+ARect.Left;
if y = ARect.Height-1 then
pnext := nil
else
pnext := ASource.ScanLine[ARect.Top+y+1]+ARect.Left;
{$hints off}
fillchar(cur,sizeof(cur),0);
{$hints on}
cur[6] := CheckPixel(p^); inc(p);
if pprev <> nil then begin cur[9] := CheckPixel(pprev^); inc(pprev); end;
if pnext <> nil then begin cur[3] := CheckPixel(pnext^); inc(pnext); end;
for x := 0 to ARect.Width-1 do
begin
cur[1] := cur[2];
cur[2] := cur[3];
cur[4] := cur[5];
cur[5] := cur[6];
cur[7] := cur[8];
cur[8] := cur[9];
if x = ARect.Width-1 then
begin
cur[6]:= false;
cur[9]:= false;
cur[3]:= false;
end else
begin
cur[6] := CheckPixel(p^); inc(p);
if pprev <> nil then begin cur[9] := CheckPixel(pprev^); inc(pprev); end;
if pnext <> nil then begin cur[3] := CheckPixel(pnext^); inc(pnext); end;
end;
ortho[y,x] := (cur[5] and not cur[7] and not cur[9] and not cur[3] and not cur[1]);
if (not cur[5] and (cur[4] xor cur[6]) and (cur[8] xor cur[2]) and
(ord(cur[1])+ord(cur[3])+ord(cur[7])+ord(cur[9]) = 3)) then
begin
if (not cur[6] and not cur[9] and not cur[8] and (CheckPixel(ASource.getPixel(x-1,y-2)) or CheckPixel(ASource.getPixel(x+2,y+1).green)) ) or
(not cur[8] and not cur[7] and not cur[4] and (CheckPixel(ASource.getPixel(x-2,y+1)) or CheckPixel(ASource.getPixel(x+1,y-2).green)) ) or
(not cur[4] and not cur[1] and not cur[2] and (CheckPixel(ASource.getPixel(x+1,y+2)) or CheckPixel(ASource.getPixel(x-2,y-1).green)) ) or
(not cur[2] and not cur[3] and not cur[6] and (CheckPixel(ASource.getPixel(x-1,y+2)) or CheckPixel(ASource.getPixel(x+2,y-1).green)) ) then
ortho[y,x] := true;
end;
{ or
(cur[5] and cur[4] and cur[6] and cur[2] and cur[8] and (Ord(cur[1])+ord(cur[3])+ord(cur[7])+ord(cur[9]) = 3))};
//if ortho[y,x] then AddPoint(x shl unitShift,y shl unitShift,-1,-1);
end;
end;
PointsCurrentLineStart := nbPoints;
for y := 0 to ARect.Height-1 do
begin
iy := (y+ARect.Top) shl unitShift;
PointsPreviousLineStart := PointsCurrentLineStart;
PointsCurrentLineStart := nbPoints;
if y = 0 then
pprev := nil
else
pprev := ASource.ScanLine[ARect.Top+y-1]+ARect.Left;
p := ASource.ScanLine[ARect.Top+y]+ARect.Left;
if y = ARect.Height-1 then
pnext := nil
else
pnext := ASource.ScanLine[ARect.Top+y+1]+ARect.Left;
{$hints off}
fillchar(cur,sizeof(cur),0);
{$hints on}
cur[6] := CheckPixel(p^); inc(p);
if pprev <> nil then begin cur[9] := CheckPixel(pprev^); inc(pprev); end;
if pnext <> nil then begin cur[3] := CheckPixel(pnext^); inc(pnext); end;
ix := ARect.Left shl unitShift;
for x := 0 to ARect.Width-1 do
begin
cur[1] := cur[2];
cur[2] := cur[3];
cur[4] := cur[5];
cur[5] := cur[6];
cur[7] := cur[8];
cur[8] := cur[9];
if x = ARect.Width-1 then
begin
cur[6]:= false;
cur[9]:= false;
cur[3]:= false;
end else
begin
cur[6] := CheckPixel(p^); inc(p);
if pprev <> nil then begin cur[9] := CheckPixel(pprev^); inc(pprev); end;
if pnext <> nil then begin cur[3] := CheckPixel(pnext^); inc(pnext); end;
end;
if cur[5] then
begin
if not cur[1] and not cur[2] and not cur[3] and not cur[4] and not cur[6] and not cur[7] and not cur[8] and not cur[9] then
begin
if iDiag > 0 then
begin
AddLine(ix-iHalf,iy-iDiag,ix-iDiag,iy-iHalf,ix+iDiag,iy-iHalf,ix+iHalf,iy-iDiag,ix+iHalf,iy+iDiag);
AddLine(ix+iHalf,iy+iDiag,ix+iDiag,iy+iHalf,ix-iDiag,iy+iHalf,ix-iHalf,iy+iDiag,ix-iHalf,iy-iDiag);
end else
begin
AddLine(ix-iHalf,iy,ix,iy-iHalf,ix+iHalf,iy);
AddLine(ix+iHalf,iy,ix,iy+iHalf,ix-iHalf,iy);
end;
end else
if cur[6] and not cur[9] and not cur[8] then
begin
if cur[7] then
begin
if not ortho[y-1,x] then
begin
if ortho[y,x-1] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
AddLine(ix+iHalf,iy-iHalf,ix+iDiag,iy-iHalf,ix-iOut,iy-iUnit+iOut);
end;
end else
if cur[4] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
if cur[1] then AddLine(ix+iHalf,iy-iHalf,ix-iDiag,iy-iHalf,ix-iUnit+iOut,iy+iOut) else
if cur[2] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
if cur[3] then
begin
if ortho[y,x+1] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iDiag,ix+iOut,iy+iUnit-iOut)
end else
AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf);
end;
if cur[8] and not cur[7] and not cur[4] then
begin
if cur[1] then
begin
if not ortho[y,x-1] then
begin
if ortho[y+1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy-iDiag,ix-iUnit+iOut,iy+iOut);
end;
end else
if cur[2] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
if cur[3] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iDiag,ix+iOut,iy+iUnit-iOut) else
if cur[6] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
if cur[9] then
begin
if ortho[y-1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iDiag,iy+iHalf,ix+iUnit-iOut,iy-iOut)
end else
AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf);
end;
if cur[4] and not cur[1] and not cur[2] then
begin
if cur[3] then
begin
if not ortho[y+1,x] then
begin
if ortho[y,x+1] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
AddLine(ix-iHalf,iy+iHalf,ix-iDiag,iy+iHalf,ix+iOut,iy+iUnit-iOut);
end;
end else
if cur[6] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
if cur[9] then AddLine(ix-iHalf,iy+iHalf,ix+iDiag,iy+iHalf,ix+iUnit-iOut,iy-iOut) else
if cur[8] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
if cur[7] then
begin
if ortho[y,x-1] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iDiag,ix-iOut,iy-iUnit+iOut)
end else
AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf);
end;
if cur[2] and not cur[3] and not cur[6] then
begin
if cur[9] then
begin
if not ortho[y,x+1] then
begin
if ortho[y-1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy+iDiag,ix+iUnit-iOut,iy-iOut);
end;
end else
if cur[8] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
if cur[7] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iDiag,ix-iOut,iy-iUnit+iOut) else
if cur[4] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
if cur[1] then
begin
if ortho[y+1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix-iHalf,iy+iHalf) else
AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iDiag,iy-iHalf,ix-iUnit+iOut,iy+iOut)
end else
AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf);
end;
if cur[3] and not cur[6] then
begin
if cur[9] then
begin
if ortho[y+1,x] and ortho[y-1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
if ortho[y+1,x] and not ortho[y-1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy+iDiag,ix+iUnit-iOut,iy-iOut) else
if not ortho[y+1,x] and ortho[y-1,x] then AddLine(ix+iUnit-iOut,iy+iOut,ix+iHalf,iy-iDiag,ix+iHalf,iy-iHalf) else
AddLine(ix+iUnit-iOut,iy+iOut,ix+iUnit-iOut*2,iy,ix+iUnit-iOut,iy-iOut);
end else
if cur[8] then
begin
if not ortho[y,x+1] then
if ortho[y+1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
AddLine(ix+iUnit-iOut,iy+iOut,ix+iHalf,iy-iDiag,ix+iHalf,iy-iHalf)
end else
if cur[7] then
begin
if ortho[y+1,x] and ortho[y,x-1] then
AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
if ortho[y+1,x] and not ortho[y,x-1] then
AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iDiag,ix-iOut,iy-iUnit+iOut) else
if not ortho[y+1,x] and ortho[y,x-1] then
AddLine(ix+iUnit-iOut,iy+iOut, ix+iDiag,iy-iHalf, ix-iHalf,iy-iHalf) else
AddLine(ix+iUnit-iOut,iy+iOut,ix-iOut,iy-iUnit+iOut)
end else
if cur[4] then AddLine(ix+iUnit-iOut,iy+iOut,ix+iDiag,iy-iHalf,ix-iHalf,iy-iHalf) else
if cur[1] then
begin
if ortho[y+1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
AddLine(ix+iUnit-iOut,iy+iOut,ix+iDiag,iy-iHalf,ix-iDiag,iy-iHalf,ix-iUnit+iOut,iy+iOut);
end else
if cur[2] then
begin
if ortho[y+1,x] then AddLine(ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
AddLine(ix+iUnit-iOut,iy+iOut,ix+iDiag,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf);
end else
AddLine(ix+iUnit-iOut,iy+iOut,ix+iDiag,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iDiag,ix+iOut,iy+iUnit-iOut);
end;
if cur[9] and not cur[8] then
begin
if cur[7] then
begin
if ortho[y,x+1] and ortho[y,x-1] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
if ortho[y,x+1] and not ortho[y,x-1] then AddLine(ix+iHalf,iy-iHalf,ix+iDiag,iy-iHalf,ix-iOut,iy-iUnit+iOut) else
if not ortho[y,x+1] and ortho[y,x-1] then AddLine(ix+iOut,iy-iUnit+iOut,ix-iDiag,iy-iHalf,ix-iHalf,iy-iHalf) else
AddLine(ix+iOut,iy-iUnit+iOut,ix,iy-iUnit+iOut*2,ix-iOut,iy-iUnit+iOut);
end else
if cur[4] then
begin
if not ortho[y-1,x] then
if ortho[y,x+1] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
AddLine(ix+iOut,iy-iUnit+iOut,ix-iDiag,iy-iHalf,ix-iHalf,iy-iHalf)
end else
if cur[1] then
begin
if ortho[y,x+1] and ortho[y+1,x] then
AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
if ortho[y,x+1] and not ortho[y+1,x] then
AddLine(ix+iHalf,iy-iHalf,ix-iDiag,iy-iHalf,ix-iUnit+iOut,iy+iOut) else
if not ortho[y,x+1] and ortho[y+1,x] then
AddLine(ix+iOut,iy-iUnit+iOut, ix-iHalf,iy-iDiag, ix-iHalf,iy+iHalf) else
AddLine(ix+iOut,iy-iUnit+iOut,ix-iUnit+iOut,iy+iOut)
end else
if cur[2] then AddLine(ix+iOut,iy-iUnit+iOut,ix-iHalf,iy-iDiag,ix-iHalf,iy+iHalf) else
if cur[3] then
begin
if ortho[y,x+1] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
AddLine(ix+iOut,iy-iUnit+iOut,ix-iHalf,iy-iDiag,ix-iHalf,iy+iDiag,ix+iOut,iy+iUnit-iOut);
end else
if cur[6] then
begin
if ortho[y,x+1] then AddLine(ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
AddLine(ix+iOut,iy-iUnit+iOut,ix-iHalf,iy-iDiag,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf);
end else
AddLine(ix+iOut,iy-iUnit+iOut,ix-iHalf,iy-iDiag,ix-iHalf,iy+iHalf,ix+iDiag,iy+iHalf,ix+iUnit-iOut,iy-iOut);
end;
if cur[7] and not cur[4] then
begin
if cur[1] then
begin
if ortho[y-1,x] and ortho[y+1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
if ortho[y-1,x] and not ortho[y+1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy-iDiag,ix-iUnit+iOut,iy+iOut) else
if not ortho[y-1,x] and ortho[y+1,x] then AddLine(ix-iUnit+iOut,iy-iOut,ix-iHalf,iy+iDiag,ix-iHalf,iy+iHalf) else
AddLine(ix-iUnit+iOut,iy-iOut,ix-iUnit+iOut*2,iy,ix-iUnit+iOut,iy+iOut);
end else
if cur[2] then
begin
if not ortho[y,x-1] then
if ortho[y-1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf) else
AddLine(ix-iUnit+iOut,iy-iOut,ix-iHalf,iy+iDiag,ix-iHalf,iy+iHalf)
end else
if cur[3] then
begin
if ortho[y-1,x] and ortho[y,x+1] then
AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
if ortho[y-1,x] and not ortho[y,x+1] then
AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iDiag,ix+iOut,iy+iUnit-iOut) else
if not ortho[y-1,x] and ortho[y,x+1] then
AddLine(ix-iUnit+iOut,iy-iOut, ix-iDiag,iy+iHalf, ix+iHalf,iy+iHalf) else
AddLine(ix-iUnit+iOut,iy-iOut,ix+iOut,iy+iUnit-iOut)
end else
if cur[6] then AddLine(ix-iUnit+iOut,iy-iOut,ix-iDiag,iy+iHalf,ix+iHalf,iy+iHalf) else
if cur[9] then
begin
if ortho[y-1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
AddLine(ix-iUnit+iOut,iy-iOut,ix-iDiag,iy+iHalf,ix+iDiag,iy+iHalf,ix+iUnit-iOut,iy-iOut);
end else
if cur[8] then
begin
if ortho[y-1,x] then AddLine(ix-iHalf,iy-iHalf,ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
AddLine(ix-iUnit+iOut,iy-iOut,ix-iDiag,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf);
end else
AddLine(ix-iUnit+iOut,iy-iOut,ix-iDiag,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iDiag,ix-iOut,iy-iUnit+iOut);
end;
if cur[1] and not cur[2] then
begin
if cur[3] then
begin
if ortho[y,x-1] and ortho[y,x+1] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
if ortho[y,x-1] and not ortho[y,x+1] then AddLine(ix-iHalf,iy+iHalf,ix-iDiag,iy+iHalf,ix+iOut,iy+iUnit-iOut) else
if not ortho[y,x-1] and ortho[y,x+1] then AddLine(ix-iOut,iy+iUnit-iOut,ix+iDiag,iy+iHalf,ix+iHalf,iy+iHalf) else
AddLine(ix-iOut,iy+iUnit-iOut,ix,iy+iUnit-iOut*2,ix+iOut,iy+iUnit-iOut);
end else
if cur[6] then
begin
if not ortho[y+1,x] then
if ortho[y,x-1] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf) else
AddLine(ix-iOut,iy+iUnit-iOut,ix+iDiag,iy+iHalf,ix+iHalf,iy+iHalf)
end else
if cur[9] then
begin
if ortho[y,x-1] and ortho[y-1,x] then
AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf) else
if ortho[y,x-1] and not ortho[y-1,x] then
AddLine(ix-iHalf,iy+iHalf,ix+iDiag,iy+iHalf,ix+iUnit-iOut,iy-iOut) else
if not ortho[y,x-1] and ortho[y-1,x] then
AddLine(ix-iOut,iy+iUnit-iOut, ix+iHalf,iy+iDiag, ix+iHalf,iy-iHalf) else
AddLine(ix-iOut,iy+iUnit-iOut,ix+iUnit-iOut,iy-iOut)
end else
if cur[8] then AddLine(ix-iOut,iy+iUnit-iOut,ix+iHalf,iy+iDiag,ix+iHalf,iy-iHalf) else
if cur[7] then
begin
if ortho[y,x-1] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
AddLine(ix-iOut,iy+iUnit-iOut,ix+iHalf,iy+iDiag,ix+iHalf,iy-iDiag,ix-iOut,iy-iUnit+iOut);
end else
if cur[4] then
begin
if ortho[y,x-1] then AddLine(ix-iHalf,iy+iHalf,ix+iHalf,iy+iHalf,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf) else
AddLine(ix-iOut,iy+iUnit-iOut,ix+iHalf,iy+iDiag,ix+iHalf,iy-iHalf,ix-iHalf,iy-iHalf);
end else
AddLine(ix-iOut,iy+iUnit-iOut,ix+iHalf,iy+iDiag,ix+iHalf,iy-iHalf,ix-iDiag,iy-iHalf,ix-iUnit+iOut,iy+iOut);
end;
end else
if ortho[y,x] then
begin
if not cur[9] then AddLine(ix+iHalf,iy+iHalf,ix-iHalf,iy+iHalf,ix-iHalf,iy-iHalf) else
if not cur[7] then AddLine(ix+iHalf,iy-iHalf,ix+iHalf,iy+iHalf,ix-iHalf,iy+iHalf) else
if not cur[1] then AddLine(ix-iHalf,iy-iHalf,ix+iHalf,iy-iHalf,ix+iHalf,iy+iHalf) else
if not cur[3] then AddLine(ix-iHalf,iy+iHalf,ix-iHalf,iy-iHalf,ix+iHalf,iy-iHalf);
end;
inc(ix,iUnit);
end;
end;
factor := factor / iUnit;
for n := 0 to nbPoints-1 do
with points[n] do
if not drawn and not removed then
AddPolygon(n);
result := polygonF;
end;
function VectorizeMonochrome(ASource: TBGRACustomBitmap;
AZoom: single; APixelCenteredCoordinates: boolean; AWhiteBackground: boolean;
ADiagonalFillPercent: single; AIntermediateDiagonals: boolean): ArrayOfTPointF;
begin
result := VectorizeMonochrome(ASource, rect(0,0,ASource.Width,ASource.Height), AZoom, APixelCenteredCoordinates,
AWhiteBackground, ADiagonalFillPercent, AIntermediateDiagonals);
end;
{ TBGRAVectorizedFontRenderer }
function TBGRAVectorizedFontRenderer.OutlineActuallyVisible: boolean;
begin
result := OutlineVisible and (abs(OutlineWidth) > 0) and (OutlineColor.Alpha <> 0) or (OutlineTexture <> nil);
end;
procedure TBGRAVectorizedFontRenderer.UpdateFont;
var i,neededResolution: integer;
begin
FVectorizedFont := nil;
FontName := Trim(FontName);
for i := 0 to high(FVectorizedFontArray) do
if (CompareText(FVectorizedFontArray[i].FontName,FontName)=0) and
(FVectorizedFontArray[i].FontStyle = FontStyle) then
begin
FVectorizedFont := FVectorizedFontArray[i].VectorizedFont;
break;
end;
if FVectorizedFont = nil then
begin
FVectorizedFont:= TBGRAVectorizedFont.Create(False);
FVectorizedFont.Name := FontName;
FVectorizedFont.Style := FontStyle - [fsUnderline];
FVectorizedFont.UnderlineDecoration := fsUnderline in FontStyle;
FVectorizedFont.Directory := FDirectoryUTF8;
if not FVectorizedFont.FontFound and SystemFontAvailable then
begin
FVectorizedFont.VectorizeLCL := True;
FVectorizedFont.Name := TBGRASystemFontRenderer.PatchSystemFontName(FontName);
end;
Setlength(FVectorizedFontArray,length(FVectorizedFontArray)+1);
FVectorizedFontArray[high(FVectorizedFontArray)].FontName := FontName;
FVectorizedFontArray[high(FVectorizedFontArray)].FontStyle := FontStyle;
FVectorizedFontArray[high(FVectorizedFontArray)].VectorizedFont := FVectorizedFont;
end;
if FontEmHeight > 0 then
FVectorizedFont.EmHeight := FontEmHeightF
else
FVectorizedFont.FullHeight:= -FontEmHeightF;
if OutlineActuallyVisible then
begin
if OuterOutlineOnly then
FVectorizedFont.OutlineMode := twoFillOverStroke
else
FVectorizedFont.OutlineMode := twoStrokeOverFill;
end
else FVectorizedFont.OutlineMode := twoFill;
FVectorizedFont.QuadraticCurves := (FVectorizedFont.FullHeight > FVectorizedFont.Resolution*1.2) and QuadraticCurves;
if FVectorizedFont.VectorizeLCL then
begin
neededResolution := trunc((FVectorizedFont.FullHeight+80)/50)*50;
if neededResolution < MinFontResolution then neededResolution := MinFontResolution;
if neededResolution > MaxFontResolution then neededResolution := MaxFontResolution;
if FVectorizedFont.Resolution < neededResolution then FVectorizedFont.Resolution:= neededResolution;
end;
end;
function TBGRAVectorizedFontRenderer.GetCanvas2D(ASurface: TBGRACustomBitmap
): TBGRACanvas2D;
begin
if (FCanvas2D = nil) or (FCanvas2D.surface <> ASurface) then
begin
FCanvas2D.Free;
FCanvas2D := TBGRACanvas2D.Create(ASurface);
end;
result := FCanvas2D;
FCanvas2D.antialiasing:= FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB];
FCanvas2D.lineJoinLCL := OutlineJoin;
if OutlineTexture <> nil then
FCanvas2D.strokeStyle(OutlineTexture)
else
FCanvas2D.strokeStyle(OutlineColor);
if abs(OutlineWidth) < 3 then
FCanvas2D.lineWidth := abs(OutlineWidth)*2/3
else
FCanvas2D.lineWidth := abs(OutlineWidth)-1;
if not ShadowVisible then
FCanvas2D.shadowColor(BGRAPixelTransparent)
else
begin
FCanvas2D.shadowColor(ShadowColor);
FCanvas2D.shadowBlur:= ShadowRadius;
FCanvas2D.shadowOffset := PointF(ShadowOffset.X,ShadowOffset.Y);
end;
end;
procedure TBGRAVectorizedFontRenderer.InternalTextRect(
ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; sUTF8: string;
style: TTextStyle; c: TBGRAPixel; texture: IBGRAScanner);
var
twAlign : TBGRATypeWriterAlignment;
c2D: TBGRACanvas2D;
intersectedClip,previousClip: TRect;
begin
previousClip := ADest.ClipRect;
if style.Clipping then
begin
intersectedClip := TRect.Intersect(previousClip, ARect);
if intersectedClip.IsEmpty then exit;
ADest.ClipRect := intersectedClip;
end;
UpdateFont;
if style.RightToLeft then
FVectorizedFont.BidiMode := fbmRightToLeft
else
FVectorizedFont.BidiMode := fbmLeftToRight;
FVectorizedFont.Orientation := 0;
case style.Alignment of
taCenter: case style.Layout of
tlCenter: twAlign := twaMiddle;
tlBottom: twAlign := twaBottom;
else twAlign:= twaTop;
end;
taRightJustify:
case style.Layout of
tlCenter: twAlign := twaRight;
tlBottom: twAlign := twaBottomRight;
else twAlign := twaTopRight;
end;
else
case style.Layout of
tlCenter: twAlign := twaLeft;
tlBottom: twAlign := twaBottomLeft;
else twAlign:= twaTopLeft;
end;
end;
c2D := GetCanvas2D(ADest);
if texture = nil then
c2D.fillStyle(c)
else
c2D.fillStyle(texture);
if style.Wordbreak then
FVectorizedFont.DrawTextRect(c2D, sUTF8, x-0.5,y-0.5,ARect.Right-0.5,ARect.Bottom-0.5, twAlign)
else
begin
case style.Layout of
tlCenter: y := (ARect.Top+ARect.Bottom) div 2;
tlBottom: y := ARect.Bottom;
end;
case style.Alignment of
taCenter: FVectorizedFont.DrawText(c2D, sUTF8, (ARect.Left+ARect.Right-1)/2,y-0.5, twAlign);
taRightJustify: FVectorizedFont.DrawText(c2D, sUTF8, ARect.Right-0.5,y-0.5, twAlign);
else
FVectorizedFont.DrawText(c2D, sUTF8, x-0.5,y-0.5, twAlign);
end;
end;
if style.Clipping then
ADest.ClipRect := previousClip;
end;
procedure TBGRAVectorizedFontRenderer.InternalCopyTextPathTo(ADest: IBGRAPath;
x, y: single; s: string; align: TAlignment; ABidiMode: TFontBidiMode);
var
twAlign : TBGRATypeWriterAlignment;
ofs: TPointF;
begin
UpdateFont;
FVectorizedFont.BidiMode := ABidiMode;
FVectorizedFont.Orientation := 0;
case align of
taCenter: twAlign:= twaMiddle;
taRightJustify: twAlign := twaRight;
else twAlign:= twaLeft;
end;
ofs := PointF(x,y);
ofs.Offset(0, FVectorizedFont.FullHeight*0.5);
FVectorizedFont.CopyTextPathTo(ADest, s, ofs.x,ofs.y, twAlign);
end;
procedure TBGRAVectorizedFontRenderer.InternalTextOutAngle(
ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string;
c: TBGRAPixel; texture: IBGRAScanner; align: TAlignment;
ABidiMode: TFontBidiMode);
var
twAlign : TBGRATypeWriterAlignment;
c2D: TBGRACanvas2D;
ofs: TPointF;
begin
UpdateFont;
FVectorizedFont.Orientation := orientation;
FVectorizedFont.BidiMode := ABidiMode;
case align of
taCenter: twAlign:= twaMiddle;
taRightJustify: twAlign := twaRight;
else twAlign:= twaLeft;
end;
c2D := GetCanvas2D(ADest);
if Assigned(texture) then
c2D.fillStyle(texture)
else
c2D.fillStyle(c);
ofs := PointF(x,y);
ofs.Offset( AffineMatrixRotationDeg(-orientation*0.1)*PointF(0,FVectorizedFont.FullHeight*0.5) );
FVectorizedFont.DrawText(c2D, s, ofs.x,ofs.y, twAlign);
end;
procedure TBGRAVectorizedFontRenderer.Init;
begin
FVectorizedFontArray := nil;
FDirectoryUTF8 := '';
OutlineVisible:= True;
OutlineColor := BGRAPixelTransparent;
OuterOutlineOnly := false;
OutlineJoin := pjsMiter;
ShadowColor := BGRABlack;
ShadowVisible := false;
ShadowOffset := Point(5,5);
ShadowRadius := 5;
MaxFontResolution := 300;
end;
constructor TBGRAVectorizedFontRenderer.Create;
begin
Init;
end;
constructor TBGRAVectorizedFontRenderer.Create(ADirectoryUTF8: string);
begin
Init;
FDirectoryUTF8 := ADirectoryUTF8;
end;
function TBGRAVectorizedFontRenderer.GetFontPixelMetric: TFontPixelMetric;
var factor: single;
begin
UpdateFont;
result := FVectorizedFont.FontPixelMetric;
if FVectorizedFont.Resolution > 0 then
begin
factor := FVectorizedFont.FullHeight/FVectorizedFont.Resolution;
result.Baseline := round(result.Baseline*factor);
result.CapLine := round(result.CapLine*factor);
result.Lineheight := round(result.Lineheight*factor);
result.DescentLine := round(result.DescentLine*factor);
result.xLine := round(result.xLine*factor);
end;
end;
function TBGRAVectorizedFontRenderer.GetFontPixelMetricF: TFontPixelMetricF;
var factor: single;
fpm: TFontPixelMetric;
begin
UpdateFont;
fpm := FVectorizedFont.FontPixelMetric;
result.Defined := fpm.Defined;
if FVectorizedFont.Resolution > 0 then
factor := FVectorizedFont.FullHeight/FVectorizedFont.Resolution
else factor := 1;
result.Baseline := fpm.Baseline*factor;
result.CapLine := fpm.CapLine*factor;
result.Lineheight := fpm.Lineheight*factor;
result.DescentLine := fpm.DescentLine*factor;
result.xLine := fpm.xLine*factor;
end;
function TBGRAVectorizedFontRenderer.FontExists(AName: string): boolean;
var
i: Integer;
begin
{$IFDEF LCL}
for i := 0 to Screen.Fonts.Count-1 do
if CompareText(Screen.Fonts[i], AName) = 0 then exit(true);
result := false;
{$ELSE}
result := true;
{$ENDIF}
end;
function TBGRAVectorizedFontRenderer.TextVisible(const AColor: TBGRAPixel): boolean;
begin
Result:=inherited TextVisible(AColor) or OutlineActuallyVisible;
end;
procedure TBGRAVectorizedFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
begin
InternalTextOutAngle(ADest,x,y,orientation,s,c,nil,align,fbmAuto);
end;
procedure TBGRAVectorizedFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment;
ARightToLeft: boolean);
begin
if ARightToLeft then
InternalTextOutAngle(ADest,x,y,orientation,s,c,nil,align,fbmRightToLeft)
else
InternalTextOutAngle(ADest,x,y,orientation,s,c,nil,align,fbmLeftToRight);
end;
procedure TBGRAVectorizedFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
y: single; orientation: integer; s: string; texture: IBGRAScanner;
align: TAlignment);
begin
InternalTextOutAngle(ADest,x,y,orientation,s,BGRAPixelTransparent,texture,align,fbmAuto);
end;
procedure TBGRAVectorizedFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
y: single; orientation: integer; s: string; texture: IBGRAScanner;
align: TAlignment; ARightToLeft: boolean);
begin
if ARightToLeft then
InternalTextOutAngle(ADest,x,y,orientation,s,BGRAPixelTransparent,texture,align,fbmRightToLeft)
else
InternalTextOutAngle(ADest,x,y,orientation,s,BGRAPixelTransparent,texture,align,fbmLeftToRight);
end;
procedure TBGRAVectorizedFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
y: single; s: string; texture: IBGRAScanner; align: TAlignment);
begin
TextOutAngle(ADest,x,y,FontOrientation,s,texture,align);
end;
procedure TBGRAVectorizedFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
y: single; s: string; texture: IBGRAScanner; align: TAlignment;
ARightToLeft: boolean);
begin
TextOutAngle(ADest,x,y,FontOrientation,s,texture,align,ARightToLeft);
end;
procedure TBGRAVectorizedFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
y: single; s: string; c: TBGRAPixel; align: TAlignment);
begin
TextOutAngle(ADest,x,y,FontOrientation,s,c,align);
end;
procedure TBGRAVectorizedFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
y: single; s: string; c: TBGRAPixel; align: TAlignment; ARightToLeft: boolean);
begin
TextOutAngle(ADest,x,y,FontOrientation,s,c,align,ARightToLeft);
end;
procedure TBGRAVectorizedFontRenderer.TextRect(ADest: TBGRACustomBitmap;
ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel);
begin
InternalTextRect(ADest,ARect,x,y,s,style,c,nil);
end;
procedure TBGRAVectorizedFontRenderer.TextRect(ADest: TBGRACustomBitmap;
ARect: TRect; x, y: integer; s: string; style: TTextStyle;
texture: IBGRAScanner);
begin
InternalTextRect(ADest,ARect,x,y,s,style,BGRAPixelTransparent,texture);
end;
procedure TBGRAVectorizedFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x, y: single; s: string; align: TAlignment);
begin
InternalCopyTextPathTo(ADest, x,y, s, align, fbmAuto);
end;
procedure TBGRAVectorizedFontRenderer.CopyTextPathTo(ADest: IBGRAPath; x,
y: single; s: string; align: TAlignment; ARightToLeft: boolean);
begin
if ARightToLeft then
InternalCopyTextPathTo(ADest, x,y, s, align, fbmRightToLeft)
else
InternalCopyTextPathTo(ADest, x,y, s, align, fbmLeftToRight);
end;
function TBGRAVectorizedFontRenderer.HandlesTextPath: boolean;
begin
Result:= true;
end;
function TBGRAVectorizedFontRenderer.TextSize(sUTF8: string): TSize;
var sizeF: TPointF;
begin
sizeF := TextSizeF(sUTF8);
result.cx := round(sizeF.x);
result.cy := round(sizeF.y);
end;
function TBGRAVectorizedFontRenderer.TextSizeF(sUTF8: string): TPointF;
begin
UpdateFont;
FVectorizedFont.BidiMode := fbmAuto;
result := FVectorizedFont.GetTextSize(sUTF8);
end;
function TBGRAVectorizedFontRenderer.TextSize(sUTF8: string;
AMaxWidth: integer; ARightToLeft: boolean): TSize;
begin
with TextSizeF(sUTF8, AMaxWidth, ARightToLeft) do
result := Size(system.Round(x),system.Round(y));
end;
function TBGRAVectorizedFontRenderer.TextSizeF(sUTF8: string;
AMaxWidthF: single; ARightToLeft: boolean): TPointF;
var
remains: string;
w,h: single;
begin
UpdateFont;
FVectorizedFont.BidiMode := fbmAuto;
result.x := 0;
result.y := 0;
h := FVectorizedFont.FullHeight;
repeat
FVectorizedFont.SplitText(sUTF8, AMaxWidthF, remains);
w := FVectorizedFont.GetTextSize(sUTF8).x;
if w > result.x then result.x := w;
IncF(result.y, h);
sUTF8 := remains;
until remains = '';
end;
function TBGRAVectorizedFontRenderer.TextFitInfo(sUTF8: string;
AMaxWidth: integer): integer;
begin
UpdateFont;
FVectorizedFont.BidiMode := fbmAuto;
result := FVectorizedFont.TextFitInfo(sUTF8, AMaxWidth);
end;
function TBGRAVectorizedFontRenderer.TextFitInfoF(sUTF8: string;
AMaxWidthF: single): integer;
begin
UpdateFont;
FVectorizedFont.BidiMode := fbmAuto;
result := FVectorizedFont.TextFitInfo(sUTF8, AMaxWidthF);
end;
destructor TBGRAVectorizedFontRenderer.Destroy;
var i: integer;
begin
FCanvas2D.Free;
for i := 0 to high(FVectorizedFontArray) do
FVectorizedFontArray[i].VectorizedFont.Free;
FVectorizedFontArray := nil;
inherited Destroy;
end;
{ TBGRAVectorizedFont }
procedure TBGRAVectorizedFont.SetResolution(AValue: integer);
begin
if FResolution=AValue then Exit;
FResolution:=AValue;
UpdateFont;
end;
procedure TBGRAVectorizedFont.SetOrientation(AValue: single);
begin
if FOrientation=AValue then Exit;
FOrientation:=AValue;
UpdateMatrix;
end;
procedure TBGRAVectorizedFont.SetItalicSlope(AValue: single);
begin
if FItalicSlope=AValue then Exit;
FItalicSlope:=AValue;
UpdateMatrix;
end;
procedure TBGRAVectorizedFont.SetLCLHeight(AValue: single);
begin
if (AValue > 0) xor (FontEmHeightSign < 0) then
EmHeight := abs(AValue)
else
FullHeight := abs(AValue);
end;
function TBGRAVectorizedFont.GetEmHeight: single;
begin
result := FullHeight * FontEmHeightRatio;
end;
function TBGRAVectorizedFont.GetFontPixelMetric: TFontPixelMetric;
begin
if not FFontPixelMetricComputed and (FFont <> nil) then
begin
FFontPixelMetric := BGRAText.GetLCLFontPixelMetric(FFont);
FFontPixelMetricComputed := true;
end;
result := FFontPixelMetric;
end;
function TBGRAVectorizedFont.GetLCLHeight: single;
begin
result := FullHeight * FontFullHeightSign;
end;
function TBGRAVectorizedFont.GetVectorizeLCL: boolean;
begin
result := FFont <> nil;
end;
procedure TBGRAVectorizedFont.GlyphCallbackForGlyphSizes(ATextUTF8: string; AGlyph: TBGRAGlyph;
AFlags: TBrowseGlyphCallbackFlags; AData: Pointer; out AContinue: boolean);
begin
with TGlyphSizesCallbackData(AData^) do
begin
if Count = length(Sizes) then
setlength(Sizes, 2*Count+1);
Sizes[Count].Text := ATextUTF8;
if AGlyph<>nil then
begin
Sizes[Count].Glyph:= AGlyph.Identifier;
Sizes[Count].Width:= AGlyph.Width*FullHeight;
Sizes[Count].Height:= AGlyph.Height*FullHeight;
end else
begin
Sizes[Count].Glyph:= '';
Sizes[Count].Width:= 0;
Sizes[Count].Height:= 0;
end;
inc(Count);
end;
AContinue:= true;
end;
procedure TBGRAVectorizedFont.UpdateQuadraticCallback(ATextUTF8: string;
AGlyph: TBGRAGlyph; AFlags: TBrowseGlyphCallbackFlags; AData: Pointer; out AContinue: boolean);
begin
if AGlyph is TBGRAPolygonalGlyph then
TBGRAPolygonalGlyph(AGlyph).QuadraticCurves:= FQuadraticCurves;
AContinue := true;
end;
procedure TBGRAVectorizedFont.SetEmHeight(AValue: single);
begin
if FontEmHeightRatio > 0 then
FullHeight := AValue / FontEmHeightRatio;
end;
procedure TBGRAVectorizedFont.SetQuadraticCurves(AValue: boolean);
begin
if FQuadraticCurves=AValue then Exit;
FQuadraticCurves:=AValue;
BrowseAllGlyphs(@UpdateQuadraticCallback, nil);
end;
procedure TBGRAVectorizedFont.SetFontMatrix(AValue: TAffineMatrix);
begin
FFontMatrix:=AValue;
UpdateMatrix;
end;
procedure TBGRAVectorizedFont.SetFullHeight(AValue: single);
begin
if FFullHeight=AValue then Exit;
FFullHeight:=AValue;
UpdateMatrix;
end;
procedure TBGRAVectorizedFont.SetName(AValue: string);
begin
if FName=AValue then Exit;
FName:=AValue;
UpdateFont;
end;
procedure TBGRAVectorizedFont.SetStyle(AValue: TFontStyles);
begin
if FStyle=AValue then Exit;
FStyle:=AValue;
UpdateFont;
SubstituteBidiBracket:= fsItalic in AValue;
end;
function TBGRAVectorizedFont.GetFontEmHeightRatio: single;
var
lEmHeight, lFullHeight: single;
OldHeight: integer;
begin
if not FFontEmHeightRatioComputed then
begin
if FFont <> nil then
begin
OldHeight := FFont.Height;
FFont.Height := FontEmHeightSign * 100;
lEmHeight := BGRATextSize(FFont, fqSystem, 'Hg', 1).cy;
FFont.Height := FixSystemFontFullHeight(FFont.Name, FontFullHeightSign * 100);
lFullHeight := BGRATextSize(FFont, fqSystem, 'Hg', 1).cy;
if lEmHeight = 0 then
FFontEmHeightRatio := 1
else
FFontEmHeightRatio := lFullHeight/lEmHeight;
FFontEmHeightRatioComputed := true;
FFont.Height := OldHeight;
end else
begin
result := 1;
exit;
end;
end;
result := FFontEmHeightRatio;
end;
procedure TBGRAVectorizedFont.SetVectorizeLCL(AValue: boolean);
begin
if AValue then
begin
if FFont = nil then
FFont := TFont.Create;
end else
begin
if FFont <> nil then
FreeAndNil(FFont);
end;
UpdateFont;
end;
procedure TBGRAVectorizedFont.UpdateFont;
var i: integer;
bestIndex, bestDistance: integer;
distance: integer;
begin
if FFont <> nil then
begin
ClearGlyphs;
FFont.Name := TBGRASystemFontRenderer.PatchSystemFontName(FName);
FFont.Style := FStyle;
FFont.Height := FixSystemFontFullHeight(FFont.Name, FontFullHeightSign * FResolution);
FFont.Quality := fqNonAntialiased;
FFontEmHeightRatio := 1;
FFontEmHeightRatioComputed := false;
fillchar(FFontPixelMetric,sizeof(FFontPixelMetric),0);
FFontPixelMetricComputed := false;
FFontFound := True;
end else
begin
bestIndex := -1;
bestDistance := 1000;
for i := 0 to high(FDirectoryContent) do
begin
if CompareText(FDirectoryContent[i].FontName,FName) = 0 then
begin
distance := 0;
if (fsBold in FDirectoryContent[i].FontStyle) xor (fsBold in FStyle) then inc(distance, 10);
if (fsItalic in FDirectoryContent[i].FontStyle) xor (fsItalic in FStyle) then inc(distance, 5);
if (fsStrikeOut in FDirectoryContent[i].FontStyle) xor (fsStrikeOut in FStyle) then inc(distance, 1);
if (fsUnderline in FDirectoryContent[i].FontStyle) xor (fsUnderline in FStyle) then inc(distance, 1);
if (bestIndex = -1) or (distance < bestDistance) then
begin
bestIndex := i;
bestDistance := distance;
if FDirectoryContent[i].FontStyle = FStyle then break;
end;
end;
end;
if bestIndex <> -1 then
begin
if not (fsItalic in FDirectoryContent[bestIndex].FontStyle) and (fsItalic in FStyle) then
ItalicSlope := 0.25
else if (fsItalic in FDirectoryContent[bestIndex].FontStyle) and not (fsItalic in FStyle) then
ItalicSlope := -0.25
else
ItalicSlope := 0;
UnderlineDecoration := not (fsUnderline in FDirectoryContent[bestIndex].FontStyle) and (fsUnderline in FStyle);
StrikeOutDecoration := not (fsStrikeOut in FDirectoryContent[bestIndex].FontStyle) and (fsStrikeOut in FStyle);
ClearGlyphs;
LoadGlyphsFromFile(FDirectoryContent[bestIndex].Filename);
FFontFound := True;
end else
FFontFound := false;
end;
end;
procedure TBGRAVectorizedFont.UpdateMatrix;
begin
TypeWriterMatrix := FFontMatrix*AffineMatrixRotationDeg(-Orientation*0.1)*AffineMatrixScale(FFullHeight,FFullHeight)*AffineMatrixLinear(PointF(1,0),PointF(-FItalicSlope,1));
end;
constructor TBGRAVectorizedFont.Create;
begin
inherited Create;
Init(True);
end;
constructor TBGRAVectorizedFont.Create(AVectorizeLCL: boolean);
begin
inherited Create;
Init(AVectorizeLCL);
end;
destructor TBGRAVectorizedFont.Destroy;
begin
FFont.Free;
FBuffer.Free;
inherited Destroy;
end;
function TBGRAVectorizedFont.GetGlyphSize(AIdentifier: string): TPointF;
var g: TBGRAGlyph;
begin
g := GetGlyph(AIdentifier);
if g = nil then result := EmptyPointF else
result := PointF(g.Width*FullHeight,g.Height*FullHeight);
end;
function TBGRAVectorizedFont.GetTextGlyphSizes(ATextUTF8: string): TGlyphSizes;
var
data: TGlyphSizesCallbackData;
begin
data.Count:= 0;
setlength(data.Sizes, UTF8Length(ATextUTF8));
BrowseGlyphs(ATextUTF8, @GlyphCallbackForGlyphSizes, @data, false);
setlength(data.Sizes, data.Count);
result := data.Sizes;
end;
function TBGRAVectorizedFont.GetTextSize(ATextUTF8: string): TPointF;
begin
result := GetTextSizeBeforeTransform(ATextUTF8)*FullHeight;
end;
function TBGRAVectorizedFont.TextFitInfo(ATextUTF8: string; AMaxWidth: single): integer;
var
charCount, byteCount: integer;
usedWidth: single;
begin
if FullHeight=0 then result := UTF8Length(ATextUTF8) else
begin
TextFitInfoBeforeTransform(ATextUTF8, AMaxWidth/FullHeight, charCount, byteCount, usedWidth);
result := charCount;
end;
end;
procedure TBGRAVectorizedFont.SplitText(var ATextUTF8: string; AMaxWidth: single;
out ARemainsUTF8: string);
var
pstr: pchar;
p,left,charlen: integer;
totalWidth: single;
firstChar: boolean;
nextchar: string;
g: TBGRAGlyph;
begin
totalWidth := 0;
if ATextUTF8 = '' then
begin
ARemainsUTF8 := '';
exit;
end else
begin
p := 1;
pstr := @ATextUTF8[1];
left := length(ATextUTF8);
firstChar := true;
while left > 0 do
begin
if RemoveLineEnding(ATextUTF8,p) then
begin
ARemainsUTF8 := copy(ATextUTF8,p,length(ATextUTF8)-p+1);
ATextUTF8 := copy(ATextUTF8,1,p-1);
exit;
end;
charlen := UTF8CharacterLength(pstr);
setlength(nextchar, charlen);
move(pstr^, nextchar[1], charlen);
inc(pstr,charlen);
g := GetGlyph(nextchar);
if g <> nil then
begin
IncF(totalWidth, g.Width*FullHeight);
if not firstChar and (totalWidth > AMaxWidth) then
begin
ARemainsUTF8:= copy(ATextUTF8,p,length(ATextUTF8)-p+1);
ATextUTF8 := copy(ATextUTF8,1,p-1);
if Assigned(FWordBreakHandler) then
FWordBreakHandler(ATextUTF8,ARemainsUTF8) else
DefaultWordBreakHandler(ATextUTF8,ARemainsUTF8);
exit;
end;
end;
dec(left,charlen);
inc(p,charlen);
firstChar := false;
end;
end;
ARemainsUTF8 := ''; //no split
end;
procedure TBGRAVectorizedFont.DrawText(ADest: TBGRACanvas2D; ATextUTF8: string; X,
Y: Single; AAlign: TBGRATypeWriterAlignment);
var underlinePoly: ArrayOfTPointF;
m: TAffineMatrix;
i: integer;
deltaY: single;
begin
inherited DrawText(ADest, ATextUTF8, X, Y, AAlign);
if AAlign in [twaBottom,twaBottomLeft,twaBottomRight] then deltaY := -1 else
if AAlign in [twaLeft,twaMiddle,twaRight] then deltaY := -0.5 else
deltaY := 0;
if UnderlineDecoration and (Resolution > 0) then
begin
underlinePoly := BGRATextUnderline(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
(FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution);
if underlinePoly <> nil then
begin
m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
for i := 0 to high(underlinePoly) do
underlinePoly[i] := m*underlinePoly[i];
if OutlineMode <> twoPath then ADest.beginPath;
ADest.moveTo(m*underlinePoly[high(underlinePoly)]);
for i := high(underlinePoly)-1 downto 0 do
ADest.lineTo(m*underlinePoly[i]);
DrawLastPath(ADest);
end;
end;
if StrikeOutDecoration and (Resolution > 0) then
begin
underlinePoly := BGRATextStrikeOut(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
(FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution, (FontPixelMetric.Baseline-FontPixelMetric.xLine)/Resolution);
if underlinePoly <> nil then
begin
m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
for i := 0 to high(underlinePoly) do
underlinePoly[i] := m*underlinePoly[i];
if OutlineMode <> twoPath then ADest.beginPath;
ADest.moveTo(m*underlinePoly[high(underlinePoly)]);
for i := high(underlinePoly)-1 downto 0 do
ADest.lineTo(m*underlinePoly[i]);
DrawLastPath(ADest);
end;
end;
end;
procedure TBGRAVectorizedFont.CopyTextPathTo(ADest: IBGRAPath;
ATextUTF8: string; X, Y: Single; AAlign: TBGRATypeWriterAlignment);
var underlinePoly: ArrayOfTPointF;
m: TAffineMatrix;
i: integer;
deltaY: single;
begin
inherited CopyTextPathTo(ADest,ATextUTF8, X, Y, AAlign);
if AAlign in [twaBottom,twaBottomLeft,twaBottomRight] then deltaY := -1 else
if AAlign in [twaLeft,twaMiddle,twaRight] then deltaY := -0.5 else
deltaY := 0;
if UnderlineDecoration and (Resolution > 0) then
begin
underlinePoly := BGRATextUnderline(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
(FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution);
if underlinePoly <> nil then
begin
m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
ADest.moveTo(m*underlinePoly[high(underlinePoly)]);
for i := high(underlinePoly)-1 downto 0 do
ADest.lineTo(m*underlinePoly[i]);
ADest.closePath;
end;
end;
if StrikeOutDecoration and (Resolution > 0) then
begin
underlinePoly := BGRATextStrikeOut(PointF(0,deltaY), GetTextSize(ATextUTF8).x/FullHeight, FontPixelMetric.Baseline/Resolution,
(FontPixelMetric.Baseline-FontPixelMetric.CapLine)/Resolution, (FontPixelMetric.Baseline-FontPixelMetric.xLine)/Resolution);
if underlinePoly <> nil then
begin
m := GetTextMatrix(ATextUTF8, X,Y,AAlign);
ADest.moveTo(m*underlinePoly[high(underlinePoly)]);
for i := high(underlinePoly)-1 downto 0 do
ADest.lineTo(m*underlinePoly[i]);
ADest.closePath;
end;
end;
end;
procedure TBGRAVectorizedFont.DrawTextWordBreak(ADest: TBGRACanvas2D;
ATextUTF8: string; X, Y, MaxWidth: Single; AAlign: TBGRATypeWriterAlignment);
var ARemains: string;
pos,step: TPointF;
lines: TStringList;
i: integer;
lineShift: single;
oldItalicSlope: single;
lineAlignment: TBGRATypeWriterAlignment;
begin
if (ATextUTF8 = '') or (MaxWidth <= 0) then exit;
oldItalicSlope:= ItalicSlope;
ItalicSlope := 0;
pos := PointF(X,Y);
step := TypeWriterMatrix*PointF(0,1);
ItalicSlope := oldItalicSlope;
if AAlign in[twaTop,twaMiddle,twaBottom] then
lineAlignment := twaMiddle
else if AAlign in[twaTopLeft,twaLeft,twaBottomLeft] then
begin
if ItalicSlope < 0 then
lineAlignment:= twaTopLeft
else
lineAlignment := twaBottomLeft;
end else
begin
if ItalicSlope < 0 then
lineAlignment := twaBottomRight
else
lineAlignment := twaTopRight;
end;
if AAlign in[twaTopLeft,twaTop,twaTopRight] then
begin
case lineAlignment of
twaMiddle: lineShift := 0.5;
twaBottomLeft,twaBottomRight: lineShift := 1;
else {twaTopRight,twaTopLeft} lineShift := 0;
end;
pos.Offset(step*lineShift);
repeat
SplitText(ATextUTF8, MaxWidth, ARemains);
DrawText(ADest,ATextUTF8,pos.X,pos.Y,lineAlignment);
ATextUTF8 := ARemains;
pos.Offset(step);
until ARemains = '';
end else
begin
lines := TStringList.Create;
repeat
SplitText(ATextUTF8, MaxWidth, ARemains);
lines.Add(ATextUTF8);
ATextUTF8 := ARemains;
until ARemains = '';
if AAlign in[twaLeft,twaMiddle,twaRight] then lineShift := lines.Count/2-0.5
else if AAlign in[twaBottomLeft,twaBottom,twaBottomRight] then lineShift := lines.Count-0.5
else lineShift := -0.5;
case lineAlignment of
twaMiddle: ;
twaBottomLeft,twaBottomRight: DecF(lineShift, 0.5);
twaTopRight,twaTopLeft : IncF(lineShift, 0.5);
end;
pos.Offset(step*(-lineShift));
for i := 0 to lines.Count-1 do
begin
DrawText(ADest,lines[i],pos.X,pos.Y,lineAlignment);
pos.Offset(step);
end;
lines.Free;
end;
end;
procedure TBGRAVectorizedFont.DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string;
X1, Y1, X2, Y2: Single; AAlign: TBGRATypeWriterAlignment);
var X,Y: single;
oldOrientation: single;
begin
if X2 <= X1 then exit;
if AAlign in[twaTopLeft,twaTop,twaTopRight] then Y := Y1 else
if AAlign in[twaLeft,twaMiddle,twaRight] then Y := (Y1+Y2)/2 else
{twaBottomLeft,twaBottom,twaBottomRight} Y := Y2;
if AAlign in[twaLeft,twaTopLeft,twaBottomLeft] then X := X1 else
if AAlign in[twaTop,twaMiddle,twaBottom] then X := (X1+X2)/2 else
{twaRight,twaTopRight,twaBottomRight} X := X2;
oldOrientation:= Orientation;
Orientation:= 0;
DrawTextWordBreak(ADest,ATextUTF8,X,Y,X2-X1,AAlign);
Orientation:= oldOrientation;
end;
procedure TBGRAVectorizedFont.DrawTextRect(ADest: TBGRACanvas2D; ATextUTF8: string;
ATopLeft, ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment);
begin
DrawTextRect(ADest,ATextUTF8,ATopLeft.X,ATopLeft.Y,ABottomRight.X,ABottomRight.Y,AAlign);
end;
function TBGRAVectorizedFont.GetTextWordBreakGlyphBoxes(ATextUTF8: string; X, Y,
MaxWidth: Single; AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
var ARemains: string;
pos,step: TPointF;
lines: TStringList;
i: integer;
lineShift: single;
oldItalicSlope: single;
tempArray: array of TGlyphBoxes;
tempPos,j: integer;
lineAlignment: TBGRATypeWriterAlignment;
begin
result := nil;
if ATextUTF8 = '' then exit;
oldItalicSlope:= ItalicSlope;
ItalicSlope := 0;
pos := PointF(X,Y);
step := TypeWriterMatrix*PointF(0,1);
ItalicSlope := oldItalicSlope;
if AAlign in[twaTop,twaMiddle,twaBottom] then
lineAlignment := twaMiddle
else if AAlign in[twaTopLeft,twaLeft,twaBottomLeft] then
begin
if ItalicSlope < 0 then
lineAlignment:= twaTopLeft
else
lineAlignment := twaBottomLeft;
end else
begin
if ItalicSlope < 0 then
lineAlignment := twaBottomRight
else
lineAlignment := twaTopRight;
end;
lines := TStringList.Create;
repeat
SplitText(ATextUTF8, MaxWidth, ARemains);
lines.Add(ATextUTF8);
ATextUTF8 := ARemains;
until ARemains = '';
if AAlign in[twaLeft,twaMiddle,twaRight] then lineShift := lines.Count/2-0.5
else if AAlign in[twaBottomLeft,twaBottom,twaBottomRight] then lineShift := lines.Count-0.5
else lineShift := -0.5;
case lineAlignment of
twaMiddle: ;
twaBottomLeft, twaBottomRight: DecF(lineShift, 0.5);
twaTopRight,twaTopLeft : IncF(lineShift, 0.5);
end;
pos.Offset(step*(-lineShift));
setlength(tempArray, lines.Count);
tempPos := 0;
for i := 0 to lines.Count-1 do
begin
tempArray[i] := GetTextGlyphBoxes(lines[i],pos.X,pos.Y,lineAlignment);
inc(tempPos, length(tempArray[i]));
pos.Offset(step);
end;
lines.Free;
setlength(result, tempPos);
tempPos := 0;
for i := 0 to high(tempArray) do
for j := 0 to high(tempArray[i]) do
begin
result[tempPos] := tempArray[i][j];
inc(tempPos);
end;
end;
function TBGRAVectorizedFont.GetTextRectGlyphBoxes(ATextUTF8: string; X1, Y1, X2,
Y2: Single; AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
var X,Y,oldOrientation: single;
begin
if X2 <= X1 then
begin
result := nil;
exit;
end;
if AAlign in[twaTopLeft,twaTop,twaTopRight] then Y := Y1 else
if AAlign in[twaLeft,twaMiddle,twaRight] then Y := (Y1+Y2)/2 else
{twaBottomLeft,twaBottom,twaBottomRight} Y := Y2;
if AAlign in[twaLeft,twaTopLeft,twaBottomLeft] then X := X1 else
if AAlign in[twaTop,twaMiddle,twaBottom] then X := (X1+X2)/2 else
{twaRight,twaTopRight,twaBottomRight} X := X2;
oldOrientation:= Orientation;
Orientation:= 0;
result := GetTextWordBreakGlyphBoxes(ATextUTF8,X,Y,X2-X1,AAlign);
Orientation:= oldOrientation;
end;
function TBGRAVectorizedFont.GetTextRectGlyphBoxes(ATextUTF8: string; ATopLeft,
ABottomRight: TPointF; AAlign: TBGRATypeWriterAlignment): TGlyphBoxes;
begin
result := GetTextRectGlyphBoxes(ATextUTF8,ATopLeft.X,ATopLeft.Y,ABottomRight.X,ABottomRight.Y,AAlign);
end;
procedure TBGRAVectorizedFont.UpdateDirectory;
var
NbFiles: integer;
SearchRec: TSearchRec;
Info: TBGRAGlyphsInfo;
Fullname: string;
begin
NbFiles := 0;
FDirectoryContent := nil;
if FDirectory = '' then exit;
if (length(FDirectory) > 0) and not (FDirectory[length(FDirectory)] in AllowDirectorySeparators) then
AppendStr(FDirectory, DirectorySeparator);
if FindFirstUTF8(FDirectory +'*.glyphs', faAnyFile, SearchRec) = 0 then
repeat
{$PUSH}{$WARNINGS OFF}
if (faDirectory or faVolumeId or faSysFile) and SearchRec.Attr = 0 then
{$POP}
begin
Fullname := FDirectory+SearchRec.Name;
Info := LoadGlyphsInfo(Fullname);
if (info.Name <> '') and (info.NbGlyphs > 0) then
begin
if NbFiles = length(FDirectoryContent) then
setlength(FDirectoryContent,2*NbFiles+1);
FDirectoryContent[NbFiles].Filename:= Fullname;
FDirectoryContent[NbFiles].FontName:= info.Name;
FDirectoryContent[NbFiles].FontStyle:= info.Style;
inc(NbFiles);
end;
end;
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
SetLength(FDirectoryContent,NbFiles);
end;
function TBGRAVectorizedFont.LoadGlyphsInfo(AFilenameUTF8: string): TBGRAGlyphsInfo;
var Stream: TFileStreamUTF8;
twHeader: TBGRACustomTypeWriterHeader;
vfHeader: TBGRAVectorizedFontHeader;
begin
result.Name := '';
result.NbGlyphs := 0;
result.Style := [];
Stream := nil;
try
Stream := TFileStreamUTF8.Create(AFilenameUTF8,fmOpenRead);
Stream.Position := 4;
twHeader := ReadCustomTypeWriterHeader(Stream);
result.NbGlyphs := twHeader.NbGlyphs;
if twHeader.HeaderName = HeaderName then
begin
vfHeader := ReadVectorizedFontHeader(Stream);
result.Name := vfHeader.Name;
result.Style:= vfHeader.Style;
end;
except
on ex:exception do
begin
end;
end;
Stream.Free;
end;
function TBGRAVectorizedFont.GetGlyph(AIdentifier: string): TBGRAGlyph;
var size: TSize;
g: TBGRAPolygonalGlyph;
pts: array of TPointF;
dx,dy: Integer;
begin
Result:=inherited GetGlyph(AIdentifier);
if (result = nil) and (FResolution > 0) and (FFont <> nil) then
begin
g := TBGRAPolygonalGlyph.Create(AIdentifier);
size := BGRATextSize(FFont, fqSystem, AIdentifier, 1);
dx := FResolution div 2;
dy := FResolution div 2;
FBuffer.SetSize(size.cx+2*dx,FResolution+2*dy);
FBuffer.Fill(BGRAWhite);
BGRATextOut(FBuffer, FFont, fqSystem, dx,dy, AIdentifier, BGRABlack, nil, taLeftJustify);
pts := VectorizeMonochrome(FBuffer,1/FResolution,False,true,50);
g.SetPoints(pts);
g.QuadraticCurves := FQuadraticCurves;
g.Width := size.cx/FResolution;
g.Height := 1;
g.Offset := PointF(-dx/FResolution,-dy/FResolution);
SetGlyph(AIdentifier,g);
result := g;
end else
if (result <> nil) and (result is TBGRAPolygonalGlyph) then
TBGRAPolygonalGlyph(result).QuadraticCurves := FQuadraticCurves;
end;
procedure TBGRAVectorizedFont.DefaultWordBreakHandler(var ABefore,AAfter: string);
begin
BGRADefaultWordBreakHandler(ABefore,AAfter);
end;
procedure TBGRAVectorizedFont.Init(AVectorize: boolean);
begin
FName := 'Arial';
FStyle := [];
FFontMatrix := AffineMatrixIdentity;
FOrientation := 0;
FResolution := 100;
FFontEmHeightRatio := 1;
FFontEmHeightRatioComputed := false;
if AVectorize then
FFont := TFont.Create
else
FFont := nil;
if BGRABitmapFactory = nil then
raise Exception.Create('No bitmap factory available');
FBuffer := BGRABitmapFactory.Create;
FFullHeight := 20;
FItalicSlope := 0;
LigatureWithF := true;
UpdateFont;
UpdateMatrix;
FWordBreakHandler:= nil;
end;
function TBGRAVectorizedFont.CustomHeaderSize: integer;
begin
Result:= (inherited CustomHeaderSize) + 4+length(FName)+4 + sizeof(single) + 4 + 5*4;
end;
const
FS_BOLD = 1;
FS_ITALIC = 2;
FS_UNDERLINE = 4;
FS_STRIKE_OUT = 8;
function IntToFontStyles(AFlags: integer): TFontStyles;
begin
Result:= [];
if (AFlags and FS_BOLD)<>0 then Include(Result, fsBold);
if (AFlags and FS_ITALIC)<>0 then Include(Result, fsItalic);
if (AFlags and FS_UNDERLINE)<>0 then Include(Result, fsUnderline);
if (AFlags and FS_STRIKE_OUT)<>0 then Include(Result, fsStrikeOut);
end;
function FontStylesToInt(AStyles: TFontStyles): Integer;
begin
Result := 0;
If fsBold in AStyles then Inc(Result, FS_BOLD);
If fsItalic in AStyles then Inc(Result, FS_ITALIC);
If fsUnderline in AStyles then Inc(Result, FS_UNDERLINE);
If fsStrikeOut in AStyles then Inc(Result, FS_STRIKE_OUT);
end;
procedure TBGRAVectorizedFont.WriteCustomHeader(AStream: TStream);
var metric: TFontPixelMetric;
begin
inherited WriteCustomHeader(AStream);
LEWriteLongint(AStream, length(FName));
AStream.Write(FName[1],length(FName));
LEWriteLongint(AStream, FontStylesToInt(FStyle));
LEWriteSingle(AStream, FontEmHeightRatio);
LEWriteLongint(AStream, Resolution);
metric := FontPixelMetric;
LEWriteLongint(AStream, metric.Baseline);
LEWriteLongint(AStream, metric.xLine);
LEWriteLongint(AStream, metric.CapLine);
LEWriteLongint(AStream, metric.DescentLine);
LEWriteLongint(AStream, metric.Lineheight);
end;
procedure TBGRAVectorizedFont.ReadAdditionalHeader(AStream: TStream);
var Header: TBGRAVectorizedFontHeader;
begin
inherited ReadAdditionalHeader(AStream);
Header := ReadVectorizedFontHeader(AStream);
FName := Header.Name;
FStyle := Header.Style;
if header.EmHeightRatio <> 0 then
begin
FFontEmHeightRatio := Header.EmHeightRatio;
FFontEmHeightRatioComputed := true;
end else
begin
FFontEmHeightRatio := 1;
FFontEmHeightRatioComputed := false;
end;
FFontPixelMetric := Header.PixelMetric;
FFontPixelMetricComputed := True;
if FFont = nil then
FResolution := Header.Resolution;
end;
function TBGRAVectorizedFont.ReadVectorizedFontHeader(AStream: TStream): TBGRAVectorizedFontHeader;
var lNameLength: integer;
begin
lNameLength := LEReadLongint(AStream);
setlength(result.Name, lNameLength);
AStream.Read(result.Name[1],length(result.Name));
result.Style := IntToFontStyles(LEReadLongint(AStream));
result.EmHeightRatio:= LEReadSingle(AStream);
result.Resolution := LEReadLongint(AStream);
result.PixelMetric.Baseline := LEReadLongint(AStream);
result.PixelMetric.xLine := LEReadLongint(AStream);
result.PixelMetric.CapLine := LEReadLongint(AStream);
result.PixelMetric.DescentLine := LEReadLongint(AStream);
result.PixelMetric.Lineheight := LEReadLongint(AStream);
result.PixelMetric.Defined := result.PixelMetric.Lineheight > 0;
end;
function TBGRAVectorizedFont.HeaderName: string;
begin
Result:= 'TBGRAVectorizedFont';
end;
procedure TBGRAVectorizedFont.SetDirectory(const AValue: string);
begin
if Trim(AValue) = Trim(FDirectory) then exit;
FDirectory := Trim(AValue);
UpdateDirectory;
UpdateFont;
end;
function TBGRAVectorizedFont.ComputeKerning(AIdLeft, AIdRight: string): single;
var
together: String;
begin
if (Resolution = 0) or not VectorizeLCL then exit(0);
if IsRightToLeftUTF8(AIdLeft) then
begin
if IsRightToLeftUTF8(AIdRight) then
together := AIdRight + AIdLeft
else
together := UTF8OverrideDirection(AIdRight + AIdLeft, true);
end else
together := AIdLeft + AIdRight;
result := BGRATextSize(FFont, fqSystem, together, 1).cx/Resolution
- Glyph[AIdLeft].Width - Glyph[AIdRight].Width;
end;
end.