This repository has been archived on 2024-02-26. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
MyPresenter1.0/bgrabitmap/bgratextfx.pas
2015-02-08 16:52:18 -08:00

1271 lines
43 KiB
ObjectPascal

unit BGRATextFX;
{$mode objfpc}{$H+}
{
Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
This unit provide text effects. The simplest way to render effects is to use TBGRATextEffectFontRenderer class.
To do this, create an instance of this class and assign it to a TBGRABitmap.FontRenderer property. Now functions
to draw text like TBGRABitmap.TextOut will use the chosen renderer. To set the effects, keep a variable containing
the TBGRATextEffectFontRenderer class and modify ShadowVisible and other effects parameters.
The TBGRATextEffectFontRenderer class makes use of other classes depending on the situation. For example,
TBGRATextEffect, which is also in this unit, provides effects on a text mask. But the renderer also uses
BGRAVectorize unit in order to have big texts or to rotate them at will.
Note that you may need TBGRATextEffect if you want to have more control over text effects, especially
if you always draw the same text. Keeping the same TBGRATextEffect object will avoid creating the text
mask over and over again.
TextShadow function is a simple function to compute an image containing a text with shadow.
}
interface
uses
Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText, BGRAVectorize;
type
TBGRATextEffect = class;
{ TBGRATextEffectFontRenderer }
TBGRATextEffectFontRenderer = class(TCustomLCLFontRenderer)
private
function GetShaderLightPosition: TPoint;
function GetVectorizedRenderer: TBGRAVectorizedFontRenderer;
procedure SetShaderLightPosition(AValue: TPoint);
protected
FShaderOwner: boolean;
FShader: TCustomPhongShading;
FVectorizedRenderer: TBGRAVectorizedFontRenderer;
function ShadowActuallyVisible :boolean;
function ShaderActuallyActive: boolean;
function OutlineActuallyVisible: boolean;
procedure Init;
function VectorizedFontNeeded: boolean;
procedure InternalTextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; texture: IBGRAScanner; align: TAlignment);
public
ShaderActive: boolean;
ShadowVisible: boolean;
ShadowColor: TBGRAPixel;
ShadowRadius: integer;
ShadowOffset: TPoint;
OutlineColor: TBGRAPixel;
OutlineWidth: single;
OutlineVisible,OuterOutlineOnly: boolean;
OutlineTexture: IBGRAScanner;
constructor Create;
constructor Create(AShader: TCustomPhongShading; AShaderOwner: boolean);
destructor Destroy; override;
procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer;
s: string; texture: IBGRAScanner; align: TAlignment); override;
procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer;
s: string; c: TBGRAPixel; align: TAlignment); override;
procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string;
texture: IBGRAScanner; align: TAlignment); override;
procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel;
align: TAlignment); override;
function TextSize(sUTF8: string): TSize; override;
property Shader: TCustomPhongShading read FShader;
property ShaderLightPosition: TPoint read GetShaderLightPosition write SetShaderLightPosition;
property VectorizedFontRenderer: TBGRAVectorizedFontRenderer read GetVectorizedRenderer;
end;
{ TBGRATextEffect }
TBGRATextEffect = class
private
function GetBounds: TRect;
function GetMaskHeight: integer;
class function GetOutlineWidth: integer; static;
function GetShadowBounds(ARadius: integer): TRect;
function GetMaskWidth: integer;
function GetTextHeight: integer;
function GetTextWidth: integer;
protected
FTextMask: TBGRACustomBitmap;
FShadowRadius: integer;
FOutlineMask, FShadowMask, FShadingMask : TBGRACustomBitmap;
FShadingAltitude: integer;
FShadingRounded: boolean;
FTextSize: TSize;
FOffset: TPoint;
function DrawMaskMulticolored(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; const AColors: array of TBGRAPixel): TRect;
function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; AColor: TBGRAPixel): TRect;
function DrawMask(ADest: TBGRACustomBitmap; AMask: TBGRACustomBitmap; X,Y: Integer; ATexture: IBGRAScanner): TRect;
function InternalDrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;
procedure InitImproveReadability(AText: string; Font: TFont; SubOffsetX,SubOffsetY: single);
procedure Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
procedure InitWithFontName(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
public
constructor Create(AText: string; Font: TFont; Antialiasing: boolean);
constructor Create(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
constructor Create(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
constructor Create(AText: string; AFontName: string; AFullHeight: integer; Antialiasing: boolean);
constructor Create(AText: string; AFontName: string; AFullHeight: integer; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean);
constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
constructor Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,AHeight: integer; AOffset: TPoint);
procedure ApplySphere;
procedure ApplyVerticalCylinder;
procedure ApplyHorizontalCylinder;
function Draw(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;
function Draw(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;
function Draw(ADest: TBGRACustomBitmap; X, Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
function Draw(ADest: TBGRACustomBitmap; X, Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; ARounded: Boolean = true): TRect;
function DrawShaded(ADest: TBGRACustomBitmap; X,Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; ARounded: Boolean = true): TRect;
function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel; AAlign: TAlignment; ARounded: Boolean = true): TRect;
function DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer; Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner; AAlign: TAlignment; ARounded: Boolean = true): TRect;
function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel): TRect;
function DrawMulticolored(ADest: TBGRACustomBitmap; X,Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;
function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel): TRect;
function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner): TRect;
function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
function DrawOutline(ADest: TBGRACustomBitmap; X,Y: integer; ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel): TRect;
function DrawShadow(ADest: TBGRACustomBitmap; X,Y,Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
destructor Destroy; override;
property TextMask: TBGRACustomBitmap read FTextMask;
property TextMaskOffset: TPoint read FOffset;
property Width: integer read GetTextWidth; deprecated;
property Height: integer read GetTextHeight; deprecated;
property MaskWidth: integer read GetMaskWidth;
property MaskHeight: integer read GetMaskHeight;
property TextSize: TSize read FTextSize;
property TextWidth: integer read GetTextWidth;
property TextHeight: integer read GetTextHeight;
property Bounds: TRect read GetBounds;
property ShadowBounds[ARadius: integer]: TRect read GetShadowBounds;
class property OutlineWidth: integer read GetOutlineWidth;
end;
function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True; AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
implementation
uses BGRAGradientScanner, GraphType, Math, BGRAGrayscaleMask;
const DefaultOutlineWidth = 3;
procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
var
useClearType,clearTypeRGBOrder: boolean;
metric: TFontPixelMetric;
deltaX: single;
x,y,yb,cury,fromy: integer;
toAdd: integer;
lines: array[0..3] of integer;
parts: array[0..3] of TGrayscaleMask;
n,nbLines: integer;
alphaMax: NativeUint;
ptrPart: TBGRACustomBitmap;
pmask: PByte;
fx: TBGRATextEffect;
FxFont: TFont;
prevCenter, newCenter, diffCenter: single;
xThird: integer;
begin
useClearType:= mode in[irClearTypeRGB,irClearTypeBGR];
clearTypeRGBOrder := mode <> irClearTypeBGR;
deltaX := xf-floor(xf);
x := round(floor(xf));
FxFont := TFont.Create;
FxFont.Assign(AFont);
FxFont.Height := fxFont.Height*FontAntialiasingLevel;
metric := GetFontPixelMetric(FxFont);
if not metric.Defined or (metric.Lineheight < 8*FontAntialiasingLevel) or (metric.Lineheight >= 24*FontAntialiasingLevel) then
begin
fxFont.Free;
if useClearType then
begin
if ClearTypeRGBOrder then
BGRATextOut(bmp, AFont, fqFineClearTypeRGB, xf,yf, text, color, tex, align)
else
BGRATextOut(bmp, AFont, fqFineClearTypeBGR, xf,yf, text, color, tex, align)
end else
BGRATextOut(bmp, AFont, fqFineAntialiasing, xf,yf, text, color, tex, align);
exit;
end;
if (metric.Baseline-metric.xLine) mod FontAntialiasingLevel >= FontAntialiasingLevel div 3 then
begin
toAdd := FontAntialiasingLevel- ((metric.Baseline-metric.xLine) mod FontAntialiasingLevel);
for yb := 1 to toAdd div 2 do
begin
if metric.xLine > 0 then dec(metric.xLine);
if metric.Baseline < metric.Lineheight then inc(metric.Baseline);
end;
end;
if metric.CapLine >= metric.xLine then metric.CapLine := -1 else
begin
if (metric.xLine-metric.CapLine) mod FontAntialiasingLevel >= FontAntialiasingLevel div 2 then
begin
toAdd := FontAntialiasingLevel - (metric.xLine-metric.CapLine) mod FontAntialiasingLevel;
metric.CapLine -= toAdd;
if metric.CapLine <= 0 then metric.CapLine := -1;
end;
end;
nbLines := 0;
lines[nbLines] := metric.CapLine+1;
inc(nbLines);
lines[nbLines] := metric.xLine+1;
inc(nbLines);
lines[nbLines] := metric.Baseline+1;
inc(nbLines);
lines[nbLines] := metric.Lineheight+1;
inc(nbLines);
if not useClearType then
fx := TBGRATextEffect.Create(text,FxFont,False,deltaX*FontAntialiasingLevel,0,FontAntialiasingLevel,FontAntialiasingLevel) else
fx := TBGRATextEffect.Create(text,FxFont,False,0,0,3,0);
if fx.TextMask = nil then
begin
fx.Free;
FxFont.Free;
exit;
end;
alphaMax := 0;
prevCenter := 0;
newCenter := 0;
for yb := 0 to nbLines-1 do
begin
if yb= 0 then fromy := 0
else fromy := lines[yb-1];
if lines[yb] > fromy then
begin
ptrPart := fx.TextMask.GetPtrBitmap(fromy,lines[yb]);
if useClearType then
parts[yb] := TGrayscaleMask.CreateDownSample(ptrPart,round(ptrPart.Width/FontAntialiasingLevel*3),round(ptrPart.Height/FontAntialiasingLevel))
else
parts[yb] := TGrayscaleMask.CreateDownSample(ptrPart,round(ptrPart.Width/FontAntialiasingLevel),round(ptrPart.Height/FontAntialiasingLevel));
ptrPart.Free;
if alphaMax < 255 then
begin
pmask := parts[yb].Data;
for n := parts[yb].NbPixels-1 downto 0 do
begin
if pmask^ > alphaMax then alphaMax := pmask^;
inc(pmask);
end;
end;
if yb < 2 then
begin
newCenter += parts[yb].Height;
prevCenter += lines[yb]-fromy;
end else
if yb = 2 then
begin
newCenter += parts[yb].Height/2;
prevCenter += (lines[yb]-fromy)/2;
end;
end else
parts[yb] := nil;
end;
prevCenter /= FontAntialiasingLevel;
diffCenter := prevCenter-newCenter;
y := round( yf + diffCenter );
xThird := 0;
if useClearType then
begin
case align of
taCenter: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.TextWidth/2)/FontAntialiasingLevel+deltaX)*3);
taRightJustify: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.TextWidth)/FontAntialiasingLevel+deltaX)*3);
else xThird:= xThird+round((fx.TextMaskOffset.x/FontAntialiasingLevel+deltaX)*3);
end;
end else
begin
case align of
taCenter: x:= x+round((fx.TextMaskOffset.x-fx.TextWidth/2)/FontAntialiasingLevel);
taRightJustify: x:= x+round((fx.TextMaskOffset.x-fx.TextWidth)/FontAntialiasingLevel);
else x:= x+round(fx.TextMaskOffset.x/FontAntialiasingLevel);
end;
end;
cury := y+round(fx.TextMaskOffset.y/FontAntialiasingLevel);
for yb := 0 to nbLines-1 do
if parts[yb] <> nil then
begin
if (alphaMax > 0) and (alphaMax < 255) then
begin
pmask := parts[yb].data;
for n := parts[yb].NbPixels-1 downto 0 do
begin
pmask^ := pmask^*255 div alphaMax;
inc(pmask);
end;
end;
if useClearType then
BGRAFillClearTypeGrayscaleMask(bmp,x,cury,xThird,parts[yb],color,tex,ClearTypeRGBOrder)
else if mode = irMask then
parts[yb].Draw(bmp,x,cury)
else
begin
if tex <> nil then
parts[yb].DrawAsAlpha(bmp,x,cury,tex) else
parts[yb].DrawAsAlpha(bmp,x,cury,color);
end;
inc(cury,parts[yb].Height);
parts[yb].Free;
end;
fx.Free;
FxFont.Free;
end;
procedure BGRAReplace(var Destination: TBGRACustomBitmap; Temp: TObject);
begin
Destination.Free;
Destination := Temp as TBGRACustomBitmap;
end;
function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True; AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
var
bmpOut,bmpSdw: TBGRACustomBitmap; OutTxtSize: TSize; OutX,OutY: Integer;
begin
bmpOut:= BGRABitmapFactory.Create(AWidth,AHeight);
bmpOut.FontAntialias:= True;
bmpOut.FontHeight:= AFontHeight;
bmpOut.FontStyle:= AFontStyle;
bmpOut.FontName:= AFontName;
bmpOut.FontQuality:= AFontQuality;
OutTxtSize:= bmpOut.TextSize(AText);
OutX:= Round(AWidth/2) - Round(OutTxtSize.cx/2);
OutY:= Round(AHeight/2) - Round(OutTxtSize.cy/2);
bmpSdw:= BGRABitmapFactory.Create(OutTxtSize.cx+2*ARadius,OutTxtSize.cy+2*ARadius);
bmpSdw.FontAntialias:= True;
bmpSdw.FontHeight:= AFontHeight;
bmpSdw.FontStyle:= AFontStyle;
bmpSdw.FontName:= AFontName;
bmpSdw.FontQuality:= AFontQuality;
bmpSdw.TextOut(ARadius,ARadius,AText,AShadowColor);
BGRAReplace(bmpSdw,bmpSdw.FilterBlurRadial(ARadius,rbFast));
bmpOut.PutImage(OutX+AOffSetX-ARadius,OutY+AOffSetY-ARadius,bmpSdw,dmDrawWithTransparency);
bmpSdw.Free;
if AShowText = True then bmpOut.TextOut(OutX,OutY,AText,ATextColor);
Result:= bmpOut;
end;
{ TBGRATextEffectFontRenderer }
function TBGRATextEffectFontRenderer.GetShaderLightPosition: TPoint;
begin
if FShader = nil then
result := point(0,0)
else
result := FShader.LightPosition;
end;
function TBGRATextEffectFontRenderer.GetVectorizedRenderer: TBGRAVectorizedFontRenderer;
begin
FVectorizedRenderer.FontEmHeight := FontEmHeight;
FVectorizedRenderer.FontName := FontName;
FVectorizedRenderer.FontOrientation:= FontOrientation;
FVectorizedRenderer.FontQuality := FontQuality;
FVectorizedRenderer.FontStyle:= FontStyle;
FVectorizedRenderer.ShadowColor := ShadowColor;
FVectorizedRenderer.ShadowVisible := ShadowVisible;
FVectorizedRenderer.ShadowOffset := ShadowOffset;
FVectorizedRenderer.ShadowRadius := ShadowRadius;
FVectorizedRenderer.OutlineColor := OutlineColor;
FVectorizedRenderer.OutlineVisible := OutlineVisible;
FVectorizedRenderer.OutlineWidth := OutlineWidth;
FVectorizedRenderer.OutlineTexture := OutlineTexture;
FVectorizedRenderer.OuterOutlineOnly := OuterOutlineOnly;
result := FVectorizedRenderer;
end;
procedure TBGRATextEffectFontRenderer.SetShaderLightPosition(AValue: TPoint);
begin
if FShader <> nil then
FShader.LightPosition := AValue;
end;
function TBGRATextEffectFontRenderer.ShadowActuallyVisible: boolean;
begin
result := ShadowVisible and (ShadowColor.alpha <> 0);
end;
function TBGRATextEffectFontRenderer.ShaderActuallyActive: boolean;
begin
result := (FShader <> nil) and ShaderActive;
end;
function TBGRATextEffectFontRenderer.OutlineActuallyVisible: boolean;
begin
result := (OutlineWidth <> 0) and ((OutlineTexture <> nil) or (OutlineColor.alpha <> 0)) and OutlineVisible;
end;
procedure TBGRATextEffectFontRenderer.Init;
begin
ShaderActive := true;
ShadowColor := BGRABlack;
ShadowVisible := false;
ShadowOffset := Point(5,5);
ShadowRadius := 5;
OutlineColor := BGRAPixelTransparent;
OutlineVisible := True;
OutlineWidth:= DefaultOutlineWidth;
OuterOutlineOnly:= false;
FVectorizedRenderer := TBGRAVectorizedFontRenderer.Create;
end;
function TBGRATextEffectFontRenderer.VectorizedFontNeeded: boolean;
var bAntialiasing, bBigFont, bSpecialOutline, bOriented, bEffectVectorizedSupported: boolean;
textsz: TSize;
begin
bAntialiasing := FontQuality in [fqFineAntialiasing,fqFineClearTypeRGB,fqFineClearTypeBGR];
textsz := inherited TextSize('Hg');
bBigFont := (not OutlineActuallyVisible and (textsz.cy >= 24)) or
(OutlineActuallyVisible and (textsz.cy > 42));
bSpecialOutline:= OutlineActuallyVisible and (abs(OutlineWidth) <> DefaultOutlineWidth);
bOriented := FontOrientation <> 0;
bEffectVectorizedSupported := OutlineActuallyVisible or ShadowActuallyVisible;
if ShaderActuallyActive and (FontOrientation = 0) then
result := false //shader not supported by vectorized font
else
result := bSpecialOutline or
(bAntialiasing and bBigFont) or
(bOriented and bEffectVectorizedSupported);
end;
procedure TBGRATextEffectFontRenderer.InternalTextOut(ADest: TBGRACustomBitmap;
x, y: single; s: string; c: TBGRAPixel; texture: IBGRAScanner;
align: TAlignment);
var fx: TBGRATextEffect;
procedure DoOutline;
begin
if OutlineActuallyVisible then
begin
if OutlineTexture <> nil then
fx.DrawOutline(ADest,round(x),round(y), OutlineTexture, align)
else
fx.DrawOutline(ADest,round(x),round(y), OutlineColor, align);
end;
end;
begin
UpdateFont;
if (FFont.Orientation <> 0) or (not ShaderActuallyActive and not ShadowActuallyVisible and not OutlineActuallyVisible) then
begin
if texture <> nil then
inherited TextOut(ADest,x,y,s,texture,align)
else
inherited TextOut(ADest,x,y,s,c,align);
exit;
end;
fx := TBGRATextEffect.Create(s, FFont, FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB], x-floor(x),y-floor(y));
if ShadowActuallyVisible then
fx.DrawShadow(ADest,round(x)+ShadowOffset.X,round(y)+ShadowOffset.Y,ShadowRadius,ShadowColor, align);
if OuterOutlineOnly then DoOutline;
if texture <> nil then
begin
if ShaderActuallyActive then
fx.DrawShaded(ADest,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), texture, align)
else
fx.Draw(ADest,round(x),round(y), texture, align);
end else
begin
if ShaderActuallyActive then
fx.DrawShaded(ADest,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), c, align)
else
fx.Draw(ADest,round(x),round(y), c, align);
end;
if not OuterOutlineOnly then DoOutline;
fx.Free;
end;
constructor TBGRATextEffectFontRenderer.Create;
begin
inherited Create;
FShader := nil;
FShaderOwner:= false;
Init;
end;
constructor TBGRATextEffectFontRenderer.Create(AShader: TCustomPhongShading;
AShaderOwner: boolean);
begin
inherited Create;
Init;
FShader := AShader;
FShaderOwner := AShaderOwner;
end;
destructor TBGRATextEffectFontRenderer.Destroy;
begin
if FShaderOwner then FShader.Free;
FVectorizedRenderer.Free;
inherited Destroy;
end;
procedure TBGRATextEffectFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
y: single; orientation: integer; s: string; texture: IBGRAScanner;
align: TAlignment);
begin
VectorizedFontRenderer.TextOutAngle(ADest, x, y, orientation, s, texture, align);
end;
procedure TBGRATextEffectFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
begin
VectorizedFontRenderer.TextOutAngle(ADest, x, y, orientation, s, c, align);
end;
procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
y: single; s: string; texture: IBGRAScanner; align: TAlignment);
begin
if VectorizedFontNeeded then
VectorizedFontRenderer.TextOut(ADest,x,y,s,texture,align)
else
InternalTextOut(ADest,x,y,s,BGRAPixelTransparent,texture,align);
end;
procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
y: single; s: string; c: TBGRAPixel; align: TAlignment);
begin
if VectorizedFontNeeded then
VectorizedFontRenderer.TextOut(ADest,x,y,s,c,align)
else
InternalTextOut(ADest,x,y,s,c,nil,align);
end;
function TBGRATextEffectFontRenderer.TextSize(sUTF8: string): TSize;
begin
if VectorizedFontNeeded then
result := VectorizedFontRenderer.TextSize(sUTF8)
else
begin
result := inherited TextSize(sUTF8);
end;
end;
{ TBGRATextEffect }
function TBGRATextEffect.GetBounds: TRect;
begin
if TextMask = nil then
result := EmptyRect else
with TextMaskOffset do
result := rect(X,Y,X+TextMask.Width,Y+TextMask.Height);
end;
function TBGRATextEffect.GetMaskHeight: integer;
begin
if FTextMask = nil then
result := 0
else
result := FTextMask.Height;
end;
class function TBGRATextEffect.GetOutlineWidth: integer; static;
begin
result := DefaultOutlineWidth;
end;
function TBGRATextEffect.GetShadowBounds(ARadius: integer): TRect;
begin
result := Bounds;
if (ARadius > 0) and not IsRectEmpty(result) then
begin
result.left -= ARadius;
result.top -= ARadius;
result.right += ARadius;
result.bottom += ARadius;
end;
end;
function TBGRATextEffect.GetMaskWidth: integer;
begin
if FTextMask = nil then
result := 0
else
result := FTextMask.Width;
end;
function TBGRATextEffect.GetTextHeight: integer;
begin
result := FTextSize.cy;
end;
function TBGRATextEffect.GetTextWidth: integer;
begin
result := FTextSize.cx;
end;
function TBGRATextEffect.DrawMaskMulticolored(ADest: TBGRACustomBitmap;
AMask: TBGRACustomBitmap; X, Y: Integer; const AColors: array of TBGRAPixel
): TRect;
var
scan: TBGRASolidColorMaskScanner;
xb,yb,startX,numColor: integer;
p0,p: PBGRAPixel;
emptyCol, nextCol: boolean;
begin
if (AMask = nil) or (length(AColors)=0) then
begin
result := EmptyRect;
exit;
end;
if (length(AColors)=0) then
begin
result := DrawMask(ADest,AMask,X,Y,AColors[0]);
exit;
end;
scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColors[0]);
numColor := 0;
startX := -1;
p0 := AMask.data;
for xb := 0 to AMask.Width-1 do
begin
p := p0;
if startX=-1 then
begin
emptyCol := true;
for yb := AMask.Height-1 downto 0 do
begin
if (p^<>BGRABlack) then
begin
emptyCol := false;
break;
end;
inc(p, AMask.Width);
end;
if not emptyCol then
begin
if startX=-1 then
startX := xb;
end else
begin
if startX<>-1 then
begin
ADest.FillRect(X+startX,Y,X+xb,Y+AMask.Height,scan,dmDrawWithTransparency);
inc(numColor);
if numColor = length(AColors) then
numColor := 0;
scan.Color := AColors[numColor];
startX := -1;
end;
end;
end else
begin
emptyCol := true;
nextCol := true;
for yb := AMask.Height-1 downto 0 do
begin
if (p^<>BGRABlack) then
begin
emptyCol := false;
if ((p-1)^<>BGRABlack) then
begin
nextCol := false;
break;
end;
end;
inc(p, AMask.Width);
end;
if nextCol or emptyCol then
begin
ADest.FillRect(X+startX,Y,X+xb,Y+AMask.Height,scan,dmDrawWithTransparency);
inc(numColor);
if numColor = length(AColors) then
numColor := 0;
scan.Color := AColors[numColor];
if emptyCol then startX := -1
else startX := xb;
end;
end;
inc(p0);
end;
if startX<>-1 then
ADest.FillRect(X+startX,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
scan.Free;
result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
end;
function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;
AMask: TBGRACustomBitmap; X, Y: Integer; AColor: TBGRAPixel): TRect;
var
scan: TBGRACustomScanner;
begin
if AMask = nil then
begin
result := EmptyRect;
exit;
end;
scan := TBGRASolidColorMaskScanner.Create(AMask,Point(-X,-Y),AColor);
ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
scan.Free;
result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
end;
function TBGRATextEffect.DrawMask(ADest: TBGRACustomBitmap;
AMask: TBGRACustomBitmap; X, Y: Integer; ATexture: IBGRAScanner): TRect;
var
scan: TBGRACustomScanner;
begin
if AMask = nil then
begin
result := EmptyRect;
exit;
end;
scan := TBGRATextureMaskScanner.Create(AMask,Point(-X,-Y),ATexture);
ADest.FillRect(X,Y,X+AMask.Width,Y+AMask.Height,scan,dmDrawWithTransparency);
scan.Free;
result := rect(X,Y,X+AMask.Width,Y+AMask.Height);
end;
function TBGRATextEffect.InternalDrawShaded(ADest: TBGRACustomBitmap; X,
Y: integer; Shader: TCustomPhongShading; Altitude: integer;
AColor: TBGRAPixel; ATexture: IBGRAScanner; ARounded: Boolean): TRect;
var
WithMargin,Map: TBGRACustomBitmap;
p: PBGRAPixel;
n,maxv: integer;
v,blurRadius: single;
iBlurRadius: integer;
begin
if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
begin
result := EmptyRect;
exit;
end;
if (FShadingMask <> nil) and ((FShadingAltitude <> Altitude) or (FShadingRounded <> ARounded)) then
FreeAndNil(FShadingMask);
if FShadingMask = nil then
begin
FShadingRounded := ARounded;
FShadingAltitude := Altitude;
if ARounded then blurRadius := Altitude
else blurRadius := Altitude*0.5;
iBlurRadius := ceil(blurRadius);
WithMargin := BGRABitmapFactory.Create(FTextMask.Width+iBlurRadius*2, FTextMask.Height+iBlurRadius*2,BGRABlack);
WithMargin.PutImage(iBlurRadius,iBlurRadius,FTextMask,dmSet);
if (iBlurRadius <> blurRadius) and (blurRadius < 3) then
Map := WithMargin.FilterBlurRadial(round(blurRadius*10),rbPrecise)
else
Map := WithMargin.FilterBlurRadial(iBlurRadius,rbFast);
p := Map.Data;
maxv := 0;
for n := Map.NbPixels-1 downto 0 do
begin
if p^.green > maxv then
maxv := p^.green;
inc(p);
end;
if maxv > 0 then
begin
p := Map.Data;
for n := Map.NbPixels-1 downto 0 do
begin
v := p^.green/maxv;
if ARounded then
begin
if v <= 0.5 then
v := v*v*2 else
v := 1-(1-v)*(1-v)*2;
end;
p^ := MapHeightToBGRA( v, p^.alpha);
inc(p);
end;
end;
Map.ApplyMask(WithMargin);
WithMargin.Free;
BGRAReplace(Map, Map.GetPart(rect(iBlurRadius,iBlurRadius,Map.Width-iBlurRadius,Map.Height-iBlurRadius)));
FShadingMask := Map;
end;
inc(X, FOffset.X);
Inc(Y, FOffset.Y);
if ATexture <> nil then
Shader.DrawScan(ADest,FShadingMask,Altitude,X,Y, ATexture)
else
Shader.Draw(ADest,FShadingMask,Altitude,X,Y, AColor);
result := rect(X,Y, X+FShadingMask.Width,Y+FShadingMask.Height);
end;
procedure TBGRATextEffect.InitImproveReadability(AText: string; Font: TFont;
SubOffsetX, SubOffsetY: single);
var size: TSize;
overhang: integer;
begin
if SubOffsetX < 0 then SubOffsetX := 0;
if SubOffsetY < 0 then SubOffsetY := 0;
size := BGRATextSize(Font, fqFineAntialiasing, AText, FontAntialiasingLevel);
FTextSize := size;
if size.cy = 0 then FTextSize.cy := BGRATextSize(Font, fqFineAntialiasing, 'Hg', FontAntialiasingLevel).cy;
overhang := size.cy div 2;
size.cx += 2*overhang + ceil(SubOffsetX);
size.cy += 2 + ceil(SubOffsetY);
FOffset := Point(-overhang,-1); //include overhang
FTextMask := BGRABitmapFactory.Create(size.cx,size.cy,BGRABlack);
BGRATextOutImproveReadability(FTextMask, Font, overhang+SubOffsetX,1+SubOffsetY, AText, BGRAWhite, nil, taLeftJustify, irMask);
end;
function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
AColor: TBGRAPixel; AAlign: TAlignment): TRect;
begin
Case AAlign of
taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,AColor);
taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,AColor);
else result := Draw(ADest,X,Y,AColor);
end;
end;
function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
begin
Case AAlign of
taRightJustify: result := Draw(ADest,X-TextSize.cx,Y,ATexture);
taCenter: result := Draw(ADest,X-TextSize.cx div 2,Y,ATexture);
else result := Draw(ADest,X,Y,ATexture);
end;
end;
function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel;
ARounded: Boolean): TRect;
begin
result := InternalDrawShaded(ADest,X,Y,Shader,Altitude,AColor,nil,ARounded);
end;
function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner;
ARounded: Boolean): TRect;
begin
result := InternalDrawShaded(ADest,X,Y,Shader,Altitude,BGRAPixelTransparent,ATexture,ARounded);
end;
function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
Shader: TCustomPhongShading; Altitude: integer; AColor: TBGRAPixel;
AAlign: TAlignment; ARounded: Boolean): TRect;
begin
Case AAlign of
taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,AColor,ARounded);
taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,AColor,ARounded);
taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,AColor,ARounded);
else
result := EmptyRect;
end;
end;
function TBGRATextEffect.DrawShaded(ADest: TBGRACustomBitmap; X, Y: integer;
Shader: TCustomPhongShading; Altitude: integer; ATexture: IBGRAScanner;
AAlign: TAlignment; ARounded: Boolean): TRect;
begin
Case AAlign of
taLeftJustify: result := DrawShaded(ADest,X,Y,Shader,Altitude,ATexture,ARounded);
taRightJustify: result := DrawShaded(ADest,X-TextSize.cx,Y,Shader,Altitude,ATexture,ARounded);
taCenter: result := DrawShaded(ADest,X-TextSize.cx div 2,Y,Shader,Altitude,ATexture,ARounded);
else
result := EmptyRect;
end;
end;
constructor TBGRATextEffect.Create(AText: string; Font: TFont;
Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
begin
Init(AText, Font, Antialiasing, SubOffsetX, SubOffsetY, 0,0);
end;
constructor TBGRATextEffect.Create(AText: string; Font: TFont;
Antialiasing: boolean; SubOffsetX, SubOffsetY: single; GrainX, GrainY: Integer
);
begin
Init(AText, Font, Antialiasing, SubOffsetX, SubOffsetY, GrainX, GrainY);
end;
constructor TBGRATextEffect.Create(AText: string; AFontName: string;
AFullHeight: integer; Antialiasing: boolean);
begin
InitWithFontName(AText, AFontName, AFullHeight, [], Antialiasing, 0, 0);
end;
constructor TBGRATextEffect.Create(AText: string; AFontName: string;
AFullHeight: integer; Antialiasing: boolean; SubOffsetX, SubOffsetY: single);
begin
InitWithFontName(AText, AFontName, AFullHeight, [], Antialiasing, SubOffsetX, SubOffsetY);
end;
constructor TBGRATextEffect.Create(AText: string; AFontName: string;
AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean);
begin
InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, 0, 0);
end;
constructor TBGRATextEffect.Create(AText: string; AFontName: string;
AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,
SubOffsetY: single);
begin
InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, SubOffsetX, SubOffsetY);
end;
constructor TBGRATextEffect.Create(AMask: TBGRACustomBitmap; AMaskOwner: boolean; AWidth,
AHeight: integer; AOffset: TPoint);
begin
FTextSize := Size(AWidth,AHeight);
FOffset := AOffset;
if not AMaskOwner then
FTextMask := AMask.Duplicate()
else
FTextMask := AMask;
end;
procedure TBGRATextEffect.Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
const FXAntialiasingLevel = FontAntialiasingLevel;
var temp: TBGRACustomBitmap;
size: TSize;
p: PBGRAPixel;
n,v,maxAlpha: integer;
alpha: byte;
sizeX,sizeY: integer;
onePixel: integer;
quality: TBGRAFontQuality;
iSubX,iSubY: integer;
begin
if Antialiasing and Assigned(BGRATextOutImproveReadabilityProc) then
begin
InitImproveReadability(AText, Font, SubOffsetX,SubOffsetY);
exit;
end;
if Antialiasing then
quality := fqFineAntialiasing
else
quality := fqSystem;
size := BGRAOriginalTextSize(Font,quality,AText,FXAntialiasingLevel);
if (size.cx = 0) or (size.cy = 0) then
begin
size := BGRATextSize(Font,quality,'Hg',FXAntialiasingLevel);
FTextSize.cx := 0;
FTextSize.cy := size.cy;
FOffset := Point(0,0);
exit;
end;
FTextSize := size;
sizeX := size.cx+size.cy;
sizeY := size.cy;
iSubX := 0;
iSubY := 0;
if SubOffsetX < 0 then SubOffsetX := 0;
if SubOffsetY < 0 then SubOffsetY := 0;
if Antialiasing then
begin
sizeX := (sizeX + FXAntialiasingLevel-1);
sizeX -= sizeX mod FXAntialiasingLevel;
sizeY := (sizeY + FXAntialiasingLevel-1);
sizeY -= sizeY mod FXAntialiasingLevel;
if SubOffsetX <> 0 then
begin
sizeX += ceil(SubOffsetX*FXAntialiasingLevel);
iSubX := round(SubOffsetX*FXAntialiasingLevel);
end;
if SubOffsetY <> 0 then
begin
sizeY += ceil(SubOffsetY*FXAntialiasingLevel);
iSubY := round(SubOffsetY*FXAntialiasingLevel);
end;
OnePixel := FXAntialiasingLevel;
end else
begin
OnePixel := 1;
if SubOffsetX <> 0 then
begin
iSubX := round(SubOffsetX);
sizeX += iSubX;
end;
if SubOffsetY <> 0 then
begin
iSubY := round(SubOffsetY);
sizeY += iSubY;
end;
end;
FOffset := Point(-size.cy div 2,-OnePixel); //include overhang
if GrainX > 0 then
begin
SizeX := SizeX+ (GrainX-1);
SizeX -= SizeX mod GrainX;
end;
if GrainY > 0 then
begin
SizeY := SizeY+ (GrainY-1);
SizeY -= SizeY mod GrainY;
end;
temp := BGRABitmapFactory.Create(sizeX, sizeY+2*OnePixel,clBlack);
temp.Canvas.Font := Font;
temp.Canvas.Font.Height := Font.Height*OnePixel;
temp.Canvas.Font.Color := clWhite;
temp.Canvas.Font.Quality := FontDefaultQuality;
temp.Canvas.Brush.Style := bsClear;
temp.Canvas.TextOut(-FOffset.X+iSubX, -FOffset.Y+iSubY, AText);
if Antialiasing then
begin
FTextSize.cx := round(FTextSize.cx/FXAntialiasingLevel);
FTextSize.cy := round(FTextSize.cy/FXAntialiasingLevel);
FOffset := Point(round(FOffset.X/FXAntialiasingLevel),round(FOffset.Y/FXAntialiasingLevel));
FTextMask := temp.Resample(round(temp.width/FXAntialiasingLevel),round(temp.Height/FXAntialiasingLevel),rmSimpleStretch);
maxAlpha := 0;
p := FTextMask.Data;
for n := FTextMask.NbPixels - 1 downto 0 do
begin
alpha := P^.green;
if alpha > maxAlpha then maxAlpha := alpha;
Inc(p);
end;
if maxAlpha <> 0 then
begin
p := FTextMask.Data;
for n := FTextMask.NbPixels - 1 downto 0 do
begin
v:= integer(p^.green * 255) div maxAlpha;
p^.red := v;
p^.green := v;
p^.blue := v;
Inc(p);
end;
end;
temp.Free;
end
else
begin
FTextMask := temp;
p := FTextMask.data;
for n := FTextMask.NbPixels-1 downto 0 do
begin
alpha := GammaExpansionTab[P^.green] shr 8;
p^.green := alpha;
p^.red := alpha;
p^.blue := alpha;
end;
end;
end;
procedure TBGRATextEffect.InitWithFontName(AText: string; AFontName: string;
AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX, SubOffsetY: single);
var lFont: TFont;
begin
lFont := TFont.Create;
lFont.Name := AFontName;
lFont.Height := AFullHeight * FontFullHeightSign;
lFont.Style := AStyle;
Init(AText, lFont, Antialiasing, SubOffsetX, SubOffsetY, 0,0);
lFont.Free;
end;
constructor TBGRATextEffect.Create(AText: string; Font: TFont;
Antialiasing: boolean);
begin
Init(AText, Font, Antialiasing, 0,0,0,0);
end;
procedure TBGRATextEffect.ApplySphere;
var sphere: TBGRACustomBitmap;
begin
if FTextMask = nil then exit;
FreeAndNil(FOutlineMask);
FreeAndNil(FShadowMask);
FShadowRadius := 0;
sphere := FTextMask.FilterSphere;
FTextMask.Fill(BGRABlack);
FTextMask.PutImage(0,0,sphere,dmDrawWithTransparency);
sphere.Free;
end;
procedure TBGRATextEffect.ApplyVerticalCylinder;
begin
if FTextMask = nil then exit;
FreeAndNil(FOutlineMask);
FreeAndNil(FShadowMask);
FShadowRadius := 0;
BGRAReplace(FTextMask,FTextMask.FilterCylinder);
end;
procedure TBGRATextEffect.ApplyHorizontalCylinder;
begin
if FTextMask = nil then exit;
FreeAndNil(FOutlineMask);
FreeAndNil(FShadowMask);
FShadowRadius := 0;
BGRAReplace(FTextMask,FTextMask.RotateCW);
BGRAReplace(FTextMask,FTextMask.FilterCylinder);
BGRAReplace(FTextMask,FTextMask.RotateCCW);
end;
function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
AColor: TBGRAPixel): TRect;
begin
result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColor);
end;
function TBGRATextEffect.Draw(ADest: TBGRACustomBitmap; X, Y: integer;
ATexture: IBGRAScanner): TRect;
begin
result := DrawMask(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,ATexture);
end;
function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
Y: integer; const AColors: array of TBGRAPixel): TRect;
begin
result := DrawMaskMulticolored(ADest,FTextMask,X+FOffset.X,Y+FOffset.Y,AColors);
end;
function TBGRATextEffect.DrawMulticolored(ADest: TBGRACustomBitmap; X,
Y: integer; const AColors: array of TBGRAPixel; AAlign: TAlignment): TRect;
begin
Case AAlign of
taRightJustify: result := DrawMulticolored(ADest,X-TextSize.cx,Y,AColors);
taCenter: result := DrawMulticolored(ADest,X-TextSize.cx div 2,Y,AColors);
else result := DrawMulticolored(ADest,X,Y,AColors);
end;
end;
function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
AColor: TBGRAPixel): TRect;
begin
if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
begin
result := EmptyRect;
exit;
end;
if FOutlineMask = nil then
begin
FOutlineMask := FTextMask.FilterContour;
FOutlineMask.LinearNegative;
end;
result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,AColor);
end;
function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
ATexture: IBGRAScanner): TRect;
begin
if (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
begin
result := EmptyRect;
exit;
end;
if FOutlineMask = nil then
begin
FOutlineMask := FTextMask.FilterContour;
FOutlineMask.LinearNegative;
end;
result := DrawMask(ADest,FOutlineMask,X+FOffset.X,Y+FOffset.Y,ATexture);
end;
function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
AColor: TBGRAPixel; AAlign: TAlignment): TRect;
begin
Case AAlign of
taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,AColor);
taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,AColor);
else result := DrawOutline(ADest,X,Y,AColor);
end;
end;
function TBGRATextEffect.DrawOutline(ADest: TBGRACustomBitmap; X, Y: integer;
ATexture: IBGRAScanner; AAlign: TAlignment): TRect;
begin
Case AAlign of
taRightJustify: result := DrawOutline(ADest,X-TextSize.cx,Y,ATexture);
taCenter: result := DrawOutline(ADest,X-TextSize.cx div 2,Y,ATexture);
else result := DrawOutline(ADest,X,Y,ATexture);
end;
end;
function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
Radius: integer; AColor: TBGRAPixel): TRect;
begin
if (Radius <= 0) or (FTextMask = nil) or (FTextMask.Width = 0) or (FTextMask.Height = 0) then
begin
result := Draw(ADest,X,Y,AColor);
exit;
end;
if FShadowRadius <> Radius then
begin
FShadowRadius := Radius;
FreeAndNil(FShadowMask);
FShadowMask := BGRABitmapFactory.Create(FTextMask.Width+Radius*2,FTextMask.Height+Radius*2,BGRABlack);
FShadowMask.PutImage(Radius,Radius,FTextMask,dmSet);
BGRAReplace(FShadowMask, FShadowMask.FilterBlurRadial(Radius,rbFast));
end;
Inc(X,FOffset.X-Radius);
Inc(Y,FOffset.Y-Radius);
DrawMask(ADest,FShadowMask,X,Y,AColor);
result := rect(X,Y,X+FShadowMask.Width,Y+FShadowMask.Height);
end;
function TBGRATextEffect.DrawShadow(ADest: TBGRACustomBitmap; X, Y,
Radius: integer; AColor: TBGRAPixel; AAlign: TAlignment): TRect;
begin
Case AAlign of
taRightJustify: result := DrawShadow(ADest,X-TextSize.cx,Y,Radius,AColor);
taCenter: result := DrawShadow(ADest,X-TextSize.cx div 2,Y,Radius,AColor);
else result := DrawShadow(ADest,X,Y,Radius,AColor);
end;
end;
destructor TBGRATextEffect.Destroy;
begin
FShadowMask.free;
textMask.Free;
FOutlineMask.Free;
FShadingMask.Free;
inherited Destroy;
end;
initialization
BGRATextOutImproveReadabilityProc := @BGRATextOutImproveReadability;
end.