1 | unit BGRATextFX;
|
---|
2 |
|
---|
3 | {$mode objfpc}{$H+}
|
---|
4 |
|
---|
5 | {
|
---|
6 | Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
|
---|
7 |
|
---|
8 | This unit provide text effects. The simplest way to render effects is to use TBGRATextEffectFontRenderer class.
|
---|
9 | To do this, create an instance of this class and assign it to a TBGRABitmap.FontRenderer property. Now functions
|
---|
10 | to draw text like TBGRABitmap.TextOut will use the chosen renderer. To set the effects, keep a variable containing
|
---|
11 | the TBGRATextEffectFontRenderer class and modify ShadowVisible and other effects parameters.
|
---|
12 |
|
---|
13 | The TBGRATextEffectFontRenderer class makes use of other classes depending on the situation. For example,
|
---|
14 | TBGRATextEffect, which is also in this unit, provides effects on a text mask. But the renderer also uses
|
---|
15 | BGRAVectorize unit in order to have big texts or to rotate them at will.
|
---|
16 |
|
---|
17 | Note that you may need TBGRATextEffect if you want to have more control over text effects, especially
|
---|
18 | if you always draw the same text. Keeping the same TBGRATextEffect object will avoid creating the text
|
---|
19 | mask over and over again.
|
---|
20 |
|
---|
21 | TextShadow function is a simple function to compute an image containing a text with shadow.
|
---|
22 |
|
---|
23 | }
|
---|
24 |
|
---|
25 | interface
|
---|
26 |
|
---|
27 | uses
|
---|
28 | Classes, SysUtils, Graphics, Types, BGRABitmapTypes, BGRAPhongTypes, BGRAText,
|
---|
29 | BGRACustomTextFX, BGRAVectorize;
|
---|
30 |
|
---|
31 | type
|
---|
32 | TBGRATextEffect = class;
|
---|
33 |
|
---|
34 | { TBGRATextEffectFontRenderer }
|
---|
35 |
|
---|
36 | TBGRATextEffectFontRenderer = class(TCustomLCLFontRenderer)
|
---|
37 | private
|
---|
38 | function GetShaderLightPosition: TPoint;
|
---|
39 | function GetVectorizedRenderer: TBGRAVectorizedFontRenderer;
|
---|
40 | procedure SetShaderLightPosition(AValue: TPoint);
|
---|
41 | protected
|
---|
42 | FShaderOwner: boolean;
|
---|
43 | FShader: TCustomPhongShading;
|
---|
44 | FVectorizedRenderer: TBGRAVectorizedFontRenderer;
|
---|
45 | function ShadowActuallyVisible :boolean;
|
---|
46 | function ShaderActuallyActive: boolean;
|
---|
47 | function OutlineActuallyVisible: boolean;
|
---|
48 | procedure Init;
|
---|
49 | function VectorizedFontNeeded: boolean;
|
---|
50 | procedure InternalTextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; texture: IBGRAScanner; align: TAlignment);
|
---|
51 | public
|
---|
52 | ShaderActive: boolean;
|
---|
53 |
|
---|
54 | ShadowVisible: boolean;
|
---|
55 | ShadowColor: TBGRAPixel;
|
---|
56 | ShadowRadius: integer;
|
---|
57 | ShadowOffset: TPoint;
|
---|
58 | ShadowQuality: TRadialBlurType;
|
---|
59 |
|
---|
60 | OutlineColor: TBGRAPixel;
|
---|
61 | OutlineWidth: single;
|
---|
62 | OutlineVisible,OuterOutlineOnly: boolean;
|
---|
63 | OutlineTexture: IBGRAScanner;
|
---|
64 | constructor Create; overload;
|
---|
65 | constructor Create(AShader: TCustomPhongShading; AShaderOwner: boolean); overload;
|
---|
66 | destructor Destroy; override;
|
---|
67 | procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer;
|
---|
68 | s: string; texture: IBGRAScanner; align: TAlignment); overload; override;
|
---|
69 | procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer;
|
---|
70 | s: string; c: TBGRAPixel; align: TAlignment); overload; override;
|
---|
71 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); overload; override;
|
---|
72 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; align: TAlignment); overload; override;
|
---|
73 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment; {%H-}ARightToLeft: boolean); overload; override;
|
---|
74 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment; {%H-}ARightToLeft: boolean); overload; override;
|
---|
75 | function TextSize(sUTF8: string): TSize; overload; override;
|
---|
76 | function TextSize(sUTF8: string; AMaxWidth: integer; {%H-}ARightToLeft: boolean): TSize; overload; override;
|
---|
77 | function TextFitInfo(sUTF8: string; AMaxWidth: integer): integer; override;
|
---|
78 | property Shader: TCustomPhongShading read FShader;
|
---|
79 | property ShaderLightPosition: TPoint read GetShaderLightPosition write SetShaderLightPosition;
|
---|
80 | property VectorizedFontRenderer: TBGRAVectorizedFontRenderer read GetVectorizedRenderer;
|
---|
81 | end;
|
---|
82 |
|
---|
83 | { TBGRATextEffect }
|
---|
84 |
|
---|
85 | TBGRATextEffect = class(TBGRACustomTextEffect)
|
---|
86 | protected
|
---|
87 | procedure InitImproveReadability(AText: string; Font: TFont; SubOffsetX,SubOffsetY: single);
|
---|
88 | procedure Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
|
---|
89 | procedure InitWithFontName(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
|
---|
90 | public
|
---|
91 | constructor Create(AText: string; Font: TFont; Antialiasing: boolean); overload;
|
---|
92 | constructor Create(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single); overload;
|
---|
93 | constructor Create(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer); overload;
|
---|
94 | constructor Create(AText: string; AFontName: string; AFullHeight: integer; Antialiasing: boolean); overload;
|
---|
95 | constructor Create(AText: string; AFontName: string; AFullHeight: integer; Antialiasing: boolean; SubOffsetX,SubOffsetY: single); overload;
|
---|
96 | constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean); overload;
|
---|
97 | constructor Create(AText: string; AFontName: string; AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,SubOffsetY: single); overload;
|
---|
98 | end;
|
---|
99 |
|
---|
100 | function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
|
---|
101 | AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True; AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
|
---|
102 |
|
---|
103 | procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
|
---|
104 |
|
---|
105 | implementation
|
---|
106 |
|
---|
107 | uses BGRAGradientScanner, GraphType, Math, BGRAGrayscaleMask;
|
---|
108 |
|
---|
109 | procedure BGRATextOutImproveReadability(bmp: TBGRACustomBitmap; AFont: TFont; xf,yf: single; text: string; color: TBGRAPixel; tex: IBGRAScanner; align: TAlignment; mode : TBGRATextOutImproveReadabilityMode);
|
---|
110 | var
|
---|
111 | useClearType,clearTypeRGBOrder: boolean;
|
---|
112 | metric: TFontPixelMetric;
|
---|
113 | deltaX: single;
|
---|
114 | x,y,yb,cury,fromy: integer;
|
---|
115 | toAdd: integer;
|
---|
116 | lines: array[0..3] of integer;
|
---|
117 | parts: array[0..3] of TGrayscaleMask;
|
---|
118 | n,nbLines: integer;
|
---|
119 | alphaMax: NativeUint;
|
---|
120 | ptrPart: TBGRACustomBitmap;
|
---|
121 | pmask: PByte;
|
---|
122 | fx: TBGRATextEffect;
|
---|
123 | FxFont: TFont;
|
---|
124 | prevCenter, newCenter, diffCenter: single;
|
---|
125 | xThird: integer;
|
---|
126 |
|
---|
127 | begin
|
---|
128 | useClearType:= mode in[irClearTypeRGB,irClearTypeBGR];
|
---|
129 | clearTypeRGBOrder := mode <> irClearTypeBGR;
|
---|
130 | deltaX := xf-floor(xf);
|
---|
131 | x := round(floor(xf));
|
---|
132 |
|
---|
133 | FxFont := TFont.Create;
|
---|
134 | FxFont.Assign(AFont);
|
---|
135 | FxFont.Height := fxFont.Height*FontAntialiasingLevel;
|
---|
136 | metric := GetLCLFontPixelMetric(FxFont);
|
---|
137 | if not metric.Defined or (metric.Lineheight < 8*FontAntialiasingLevel) or (metric.Lineheight >= 24*FontAntialiasingLevel) then
|
---|
138 | begin
|
---|
139 | fxFont.Free;
|
---|
140 | if useClearType then
|
---|
141 | begin
|
---|
142 | if ClearTypeRGBOrder then
|
---|
143 | BGRATextOut(bmp, AFont, fqFineClearTypeRGB, xf,yf, text, color, tex, align)
|
---|
144 | else
|
---|
145 | BGRATextOut(bmp, AFont, fqFineClearTypeBGR, xf,yf, text, color, tex, align)
|
---|
146 | end else
|
---|
147 | BGRATextOut(bmp, AFont, fqFineAntialiasing, xf,yf, text, color, tex, align);
|
---|
148 | exit;
|
---|
149 | end;
|
---|
150 |
|
---|
151 | if (metric.Baseline-metric.xLine) mod FontAntialiasingLevel >= FontAntialiasingLevel div 3 then
|
---|
152 | begin
|
---|
153 | toAdd := FontAntialiasingLevel- ((metric.Baseline-metric.xLine) mod FontAntialiasingLevel);
|
---|
154 | for yb := 1 to toAdd div 2 do
|
---|
155 | begin
|
---|
156 | if metric.xLine > 0 then dec(metric.xLine);
|
---|
157 | if metric.Baseline < metric.Lineheight then inc(metric.Baseline);
|
---|
158 | end;
|
---|
159 | end;
|
---|
160 | if metric.CapLine >= metric.xLine then metric.CapLine := -1 else
|
---|
161 | begin
|
---|
162 | if (metric.xLine-metric.CapLine) mod FontAntialiasingLevel >= FontAntialiasingLevel div 2 then
|
---|
163 | begin
|
---|
164 | toAdd := FontAntialiasingLevel - (metric.xLine-metric.CapLine) mod FontAntialiasingLevel;
|
---|
165 | metric.CapLine -= toAdd;
|
---|
166 | if metric.CapLine <= 0 then metric.CapLine := -1;
|
---|
167 | end;
|
---|
168 | end;
|
---|
169 |
|
---|
170 | nbLines := 0;
|
---|
171 | lines[nbLines] := metric.CapLine+1;
|
---|
172 | inc(nbLines);
|
---|
173 | lines[nbLines] := metric.xLine+1;
|
---|
174 | inc(nbLines);
|
---|
175 | lines[nbLines] := metric.Baseline+1;
|
---|
176 | inc(nbLines);
|
---|
177 | lines[nbLines] := metric.Lineheight+1;
|
---|
178 | inc(nbLines);
|
---|
179 |
|
---|
180 | if not useClearType then
|
---|
181 | fx := TBGRATextEffect.Create(text,FxFont,False,deltaX*FontAntialiasingLevel,0,FontAntialiasingLevel,FontAntialiasingLevel) else
|
---|
182 | fx := TBGRATextEffect.Create(text,FxFont,False,0,0,3,0);
|
---|
183 |
|
---|
184 | if fx.TextMask = nil then
|
---|
185 | begin
|
---|
186 | fx.Free;
|
---|
187 | FxFont.Free;
|
---|
188 | exit;
|
---|
189 | end;
|
---|
190 | alphaMax := 0;
|
---|
191 | prevCenter := 0;
|
---|
192 | newCenter := 0;
|
---|
193 | for yb := 0 to nbLines-1 do
|
---|
194 | begin
|
---|
195 | if yb= 0 then fromy := 0
|
---|
196 | else fromy := lines[yb-1];
|
---|
197 |
|
---|
198 | if lines[yb] > fromy then
|
---|
199 | begin
|
---|
200 | ptrPart := fx.TextMask.GetPtrBitmap(fromy,lines[yb]);
|
---|
201 | if useClearType then
|
---|
202 | parts[yb] := TGrayscaleMask.CreateDownSample(ptrPart,round(ptrPart.Width/FontAntialiasingLevel*3),round(ptrPart.Height/FontAntialiasingLevel))
|
---|
203 | else
|
---|
204 | parts[yb] := TGrayscaleMask.CreateDownSample(ptrPart,round(ptrPart.Width/FontAntialiasingLevel),round(ptrPart.Height/FontAntialiasingLevel));
|
---|
205 | ptrPart.Free;
|
---|
206 |
|
---|
207 | if alphaMax < 255 then
|
---|
208 | begin
|
---|
209 | pmask := parts[yb].Data;
|
---|
210 | for n := parts[yb].NbPixels-1 downto 0 do
|
---|
211 | begin
|
---|
212 | if pmask^ > alphaMax then alphaMax := pmask^;
|
---|
213 | inc(pmask);
|
---|
214 | end;
|
---|
215 | end;
|
---|
216 |
|
---|
217 | if yb < 2 then
|
---|
218 | begin
|
---|
219 | newCenter += parts[yb].Height;
|
---|
220 | prevCenter += lines[yb]-fromy;
|
---|
221 | end else
|
---|
222 | if yb = 2 then
|
---|
223 | begin
|
---|
224 | newCenter += parts[yb].Height/2;
|
---|
225 | prevCenter += (lines[yb]-fromy)/2;
|
---|
226 | end;
|
---|
227 | end else
|
---|
228 | parts[yb] := nil;
|
---|
229 | end;
|
---|
230 |
|
---|
231 | prevCenter /= FontAntialiasingLevel;
|
---|
232 | diffCenter := prevCenter-newCenter;
|
---|
233 | y := round( yf + diffCenter );
|
---|
234 |
|
---|
235 | xThird := 0;
|
---|
236 | if useClearType then
|
---|
237 | begin
|
---|
238 | case align of
|
---|
239 | taCenter: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.TextWidth/2)/FontAntialiasingLevel+deltaX)*3);
|
---|
240 | taRightJustify: xThird:= xThird+round(((fx.TextMaskOffset.x-fx.TextWidth)/FontAntialiasingLevel+deltaX)*3);
|
---|
241 | else xThird:= xThird+round((fx.TextMaskOffset.x/FontAntialiasingLevel+deltaX)*3);
|
---|
242 | end;
|
---|
243 | end else
|
---|
244 | begin
|
---|
245 | case align of
|
---|
246 | taCenter: x:= x+round((fx.TextMaskOffset.x-fx.TextWidth/2)/FontAntialiasingLevel);
|
---|
247 | taRightJustify: x:= x+round((fx.TextMaskOffset.x-fx.TextWidth)/FontAntialiasingLevel);
|
---|
248 | else x:= x+round(fx.TextMaskOffset.x/FontAntialiasingLevel);
|
---|
249 | end;
|
---|
250 | end;
|
---|
251 | cury := y+round(fx.TextMaskOffset.y/FontAntialiasingLevel);
|
---|
252 | for yb := 0 to nbLines-1 do
|
---|
253 | if parts[yb] <> nil then
|
---|
254 | begin
|
---|
255 | if (alphaMax > 0) and (alphaMax < 255) then
|
---|
256 | begin
|
---|
257 | pmask := parts[yb].data;
|
---|
258 | for n := parts[yb].NbPixels-1 downto 0 do
|
---|
259 | begin
|
---|
260 | pmask^ := pmask^*255 div alphaMax;
|
---|
261 | inc(pmask);
|
---|
262 | end;
|
---|
263 | end;
|
---|
264 | if useClearType then
|
---|
265 | BGRAFillClearTypeGrayscaleMask(bmp,x,cury,xThird,parts[yb],color,tex,ClearTypeRGBOrder)
|
---|
266 | else if mode = irMask then
|
---|
267 | parts[yb].Draw(bmp,x,cury)
|
---|
268 | else
|
---|
269 | begin
|
---|
270 | if tex <> nil then
|
---|
271 | parts[yb].DrawAsAlpha(bmp,x,cury,tex) else
|
---|
272 | parts[yb].DrawAsAlpha(bmp,x,cury,color);
|
---|
273 | end;
|
---|
274 | inc(cury,parts[yb].Height);
|
---|
275 | parts[yb].Free;
|
---|
276 | end;
|
---|
277 |
|
---|
278 | fx.Free;
|
---|
279 | FxFont.Free;
|
---|
280 | end;
|
---|
281 |
|
---|
282 | procedure BGRAReplace(var Destination: TBGRACustomBitmap; Temp: TObject);
|
---|
283 | begin
|
---|
284 | Destination.Free;
|
---|
285 | Destination := Temp as TBGRACustomBitmap;
|
---|
286 | end;
|
---|
287 |
|
---|
288 | function TextShadow(AWidth,AHeight: Integer; AText: String; AFontHeight: Integer; ATextColor,AShadowColor: TBGRAPixel;
|
---|
289 | AOffSetX,AOffSetY: Integer; ARadius: Integer = 0; AFontStyle: TFontStyles = []; AFontName: String = 'Default'; AShowText: Boolean = True;
|
---|
290 | AFontQuality: TBGRAFontQuality = fqFineAntialiasing): TBGRACustomBitmap;
|
---|
291 | var
|
---|
292 | bmpOut,bmpSdw: TBGRACustomBitmap; OutTxtSize: TSize; OutX,OutY: Integer;
|
---|
293 | begin
|
---|
294 | bmpOut:= BGRABitmapFactory.Create(AWidth,AHeight);
|
---|
295 | bmpOut.FontAntialias:= True;
|
---|
296 | bmpOut.FontHeight:= AFontHeight;
|
---|
297 | bmpOut.FontStyle:= AFontStyle;
|
---|
298 | bmpOut.FontName:= AFontName;
|
---|
299 | bmpOut.FontQuality:= AFontQuality;
|
---|
300 |
|
---|
301 | OutTxtSize:= bmpOut.TextSize(AText);
|
---|
302 | OutX:= Round(AWidth/2) - Round(OutTxtSize.cx/2);
|
---|
303 | OutY:= Round(AHeight/2) - Round(OutTxtSize.cy/2);
|
---|
304 |
|
---|
305 | bmpSdw:= BGRABitmapFactory.Create(OutTxtSize.cx+2*ARadius,OutTxtSize.cy+2*ARadius);
|
---|
306 | bmpSdw.FontAntialias:= True;
|
---|
307 | bmpSdw.FontHeight:= AFontHeight;
|
---|
308 | bmpSdw.FontStyle:= AFontStyle;
|
---|
309 | bmpSdw.FontName:= AFontName;
|
---|
310 | bmpSdw.FontQuality:= AFontQuality;
|
---|
311 |
|
---|
312 | bmpSdw.TextOut(ARadius,ARadius,AText,AShadowColor);
|
---|
313 | BGRAReplace(bmpSdw,bmpSdw.FilterBlurRadial(ARadius,rbFast));
|
---|
314 | bmpOut.PutImage(OutX+AOffSetX-ARadius,OutY+AOffSetY-ARadius,bmpSdw,dmDrawWithTransparency);
|
---|
315 | bmpSdw.Free;
|
---|
316 |
|
---|
317 | if AShowText = True then bmpOut.TextOut(OutX,OutY,AText,ATextColor);
|
---|
318 |
|
---|
319 | Result:= bmpOut;
|
---|
320 | end;
|
---|
321 |
|
---|
322 | { TBGRATextEffectFontRenderer }
|
---|
323 |
|
---|
324 | function TBGRATextEffectFontRenderer.GetShaderLightPosition: TPoint;
|
---|
325 | begin
|
---|
326 | if FShader = nil then
|
---|
327 | result := point(0,0)
|
---|
328 | else
|
---|
329 | result := FShader.LightPosition;
|
---|
330 | end;
|
---|
331 |
|
---|
332 | function TBGRATextEffectFontRenderer.GetVectorizedRenderer: TBGRAVectorizedFontRenderer;
|
---|
333 | begin
|
---|
334 | FVectorizedRenderer.FontEmHeight := FontEmHeight;
|
---|
335 | FVectorizedRenderer.FontName := FontName;
|
---|
336 | FVectorizedRenderer.FontOrientation:= FontOrientation;
|
---|
337 | FVectorizedRenderer.FontQuality := FontQuality;
|
---|
338 | FVectorizedRenderer.FontStyle:= FontStyle;
|
---|
339 |
|
---|
340 | FVectorizedRenderer.ShadowColor := ShadowColor;
|
---|
341 | FVectorizedRenderer.ShadowVisible := ShadowVisible;
|
---|
342 | FVectorizedRenderer.ShadowOffset := ShadowOffset;
|
---|
343 | FVectorizedRenderer.ShadowRadius := ShadowRadius;
|
---|
344 |
|
---|
345 | FVectorizedRenderer.OutlineColor := OutlineColor;
|
---|
346 | FVectorizedRenderer.OutlineVisible := OutlineVisible;
|
---|
347 | FVectorizedRenderer.OutlineWidth := OutlineWidth;
|
---|
348 | FVectorizedRenderer.OutlineTexture := OutlineTexture;
|
---|
349 | FVectorizedRenderer.OuterOutlineOnly := OuterOutlineOnly;
|
---|
350 | result := FVectorizedRenderer;
|
---|
351 | end;
|
---|
352 |
|
---|
353 | procedure TBGRATextEffectFontRenderer.SetShaderLightPosition(AValue: TPoint);
|
---|
354 | begin
|
---|
355 | if FShader <> nil then
|
---|
356 | FShader.LightPosition := AValue;
|
---|
357 | end;
|
---|
358 |
|
---|
359 | function TBGRATextEffectFontRenderer.ShadowActuallyVisible: boolean;
|
---|
360 | begin
|
---|
361 | result := ShadowVisible and (ShadowColor.alpha <> 0);
|
---|
362 | end;
|
---|
363 |
|
---|
364 | function TBGRATextEffectFontRenderer.ShaderActuallyActive: boolean;
|
---|
365 | begin
|
---|
366 | result := (FShader <> nil) and ShaderActive;
|
---|
367 | end;
|
---|
368 |
|
---|
369 | function TBGRATextEffectFontRenderer.OutlineActuallyVisible: boolean;
|
---|
370 | begin
|
---|
371 | result := (OutlineWidth <> 0) and ((OutlineTexture <> nil) or (OutlineColor.alpha <> 0)) and OutlineVisible;
|
---|
372 | end;
|
---|
373 |
|
---|
374 | procedure TBGRATextEffectFontRenderer.Init;
|
---|
375 | begin
|
---|
376 | ShaderActive := true;
|
---|
377 |
|
---|
378 | ShadowColor := BGRABlack;
|
---|
379 | ShadowVisible := false;
|
---|
380 | ShadowOffset := Point(5,5);
|
---|
381 | ShadowRadius := 5;
|
---|
382 | ShadowQuality:= rbFast;
|
---|
383 |
|
---|
384 | OutlineColor := BGRAPixelTransparent;
|
---|
385 | OutlineVisible := True;
|
---|
386 | OutlineWidth:= DefaultOutlineWidth;
|
---|
387 | OuterOutlineOnly:= false;
|
---|
388 | FVectorizedRenderer := TBGRAVectorizedFontRenderer.Create;
|
---|
389 | end;
|
---|
390 |
|
---|
391 | function TBGRATextEffectFontRenderer.VectorizedFontNeeded: boolean;
|
---|
392 | var bAntialiasing, bBigFont, bSpecialOutline, bOriented, bEffectVectorizedSupported: boolean;
|
---|
393 | textsz: TSize;
|
---|
394 | begin
|
---|
395 | bAntialiasing := FontQuality in [fqFineAntialiasing,fqFineClearTypeRGB,fqFineClearTypeBGR];
|
---|
396 | textsz := inherited TextSize('Hg');
|
---|
397 | bBigFont := (not OutlineActuallyVisible and (textsz.cy >= 24)) or
|
---|
398 | (OutlineActuallyVisible and (textsz.cy > 42));
|
---|
399 | bSpecialOutline:= OutlineActuallyVisible and (abs(OutlineWidth) <> DefaultOutlineWidth);
|
---|
400 | bOriented := FontOrientation <> 0;
|
---|
401 | bEffectVectorizedSupported := OutlineActuallyVisible or ShadowActuallyVisible;
|
---|
402 | if ShaderActuallyActive and (FontOrientation = 0) then
|
---|
403 | result := false //shader not supported by vectorized font
|
---|
404 | else
|
---|
405 | result := bSpecialOutline or
|
---|
406 | (bAntialiasing and bBigFont) or
|
---|
407 | (bOriented and bEffectVectorizedSupported);
|
---|
408 | end;
|
---|
409 |
|
---|
410 | procedure TBGRATextEffectFontRenderer.InternalTextOut(ADest: TBGRACustomBitmap;
|
---|
411 | x, y: single; s: string; c: TBGRAPixel; texture: IBGRAScanner;
|
---|
412 | align: TAlignment);
|
---|
413 | var fx: TBGRATextEffect;
|
---|
414 | procedure DoOutline;
|
---|
415 | begin
|
---|
416 | if OutlineActuallyVisible then
|
---|
417 | begin
|
---|
418 | if OutlineTexture <> nil then
|
---|
419 | fx.DrawOutline(ADest,round(x),round(y), OutlineTexture, align)
|
---|
420 | else
|
---|
421 | fx.DrawOutline(ADest,round(x),round(y), OutlineColor, align);
|
---|
422 | end;
|
---|
423 | end;
|
---|
424 | begin
|
---|
425 | UpdateFont;
|
---|
426 | if (FFont.Orientation <> 0) or (not ShaderActuallyActive and not ShadowActuallyVisible and not OutlineActuallyVisible) then
|
---|
427 | begin
|
---|
428 | if texture <> nil then
|
---|
429 | inherited TextOut(ADest,x,y,s,texture,align)
|
---|
430 | else
|
---|
431 | inherited TextOut(ADest,x,y,s,c,align);
|
---|
432 | exit;
|
---|
433 | end;
|
---|
434 | fx := TBGRATextEffect.Create(s, FFont, FontQuality in[fqFineAntialiasing,fqFineClearTypeBGR,fqFineClearTypeRGB], x-floor(x),y-floor(y));
|
---|
435 | if ShadowActuallyVisible then
|
---|
436 | begin
|
---|
437 | fx.ShadowQuality := ShadowQuality;
|
---|
438 | fx.DrawShadow(ADest,round(x)+ShadowOffset.X,round(y)+ShadowOffset.Y,ShadowRadius,ShadowColor, align);
|
---|
439 | end;
|
---|
440 | if OuterOutlineOnly then DoOutline;
|
---|
441 | if texture <> nil then
|
---|
442 | begin
|
---|
443 | if ShaderActuallyActive then
|
---|
444 | fx.DrawShaded(ADest,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), texture, align)
|
---|
445 | else
|
---|
446 | fx.Draw(ADest,round(x),round(y), texture, align);
|
---|
447 | end else
|
---|
448 | begin
|
---|
449 | if ShaderActuallyActive then
|
---|
450 | fx.DrawShaded(ADest,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), c, align)
|
---|
451 | else
|
---|
452 | fx.Draw(ADest,round(x),round(y), c, align);
|
---|
453 | end;
|
---|
454 | if not OuterOutlineOnly then DoOutline;
|
---|
455 | fx.Free;
|
---|
456 | end;
|
---|
457 |
|
---|
458 | constructor TBGRATextEffectFontRenderer.Create;
|
---|
459 | begin
|
---|
460 | inherited Create;
|
---|
461 | FShader := nil;
|
---|
462 | FShaderOwner:= false;
|
---|
463 | Init;
|
---|
464 | end;
|
---|
465 |
|
---|
466 | constructor TBGRATextEffectFontRenderer.Create(AShader: TCustomPhongShading;
|
---|
467 | AShaderOwner: boolean);
|
---|
468 | begin
|
---|
469 | inherited Create;
|
---|
470 | Init;
|
---|
471 | FShader := AShader;
|
---|
472 | FShaderOwner := AShaderOwner;
|
---|
473 | end;
|
---|
474 |
|
---|
475 | destructor TBGRATextEffectFontRenderer.Destroy;
|
---|
476 | begin
|
---|
477 | if FShaderOwner then FShader.Free;
|
---|
478 | FVectorizedRenderer.Free;
|
---|
479 | inherited Destroy;
|
---|
480 | end;
|
---|
481 |
|
---|
482 | procedure TBGRATextEffectFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
|
---|
483 | y: single; orientation: integer; s: string; texture: IBGRAScanner;
|
---|
484 | align: TAlignment);
|
---|
485 | begin
|
---|
486 | VectorizedFontRenderer.TextOutAngle(ADest, x, y, orientation, s, texture, align);
|
---|
487 | end;
|
---|
488 |
|
---|
489 | procedure TBGRATextEffectFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
|
---|
490 | y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
|
---|
491 | begin
|
---|
492 | VectorizedFontRenderer.TextOutAngle(ADest, x, y, orientation, s, c, align);
|
---|
493 | end;
|
---|
494 |
|
---|
495 | procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
---|
496 | y: single; s: string; texture: IBGRAScanner; align: TAlignment);
|
---|
497 | begin
|
---|
498 | if VectorizedFontNeeded then
|
---|
499 | VectorizedFontRenderer.TextOut(ADest,x,y,s,texture,align)
|
---|
500 | else
|
---|
501 | InternalTextOut(ADest,x,y,s,BGRAPixelTransparent,texture,align);
|
---|
502 | end;
|
---|
503 |
|
---|
504 | procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
---|
505 | y: single; s: string; c: TBGRAPixel; align: TAlignment);
|
---|
506 | begin
|
---|
507 | if VectorizedFontNeeded then
|
---|
508 | VectorizedFontRenderer.TextOut(ADest,x,y,s,c,align)
|
---|
509 | else
|
---|
510 | InternalTextOut(ADest,x,y,s,c,nil,align);
|
---|
511 | end;
|
---|
512 |
|
---|
513 | procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
---|
514 | y: single; sUTF8: string; texture: IBGRAScanner; align: TAlignment;
|
---|
515 | ARightToLeft: boolean);
|
---|
516 | begin
|
---|
517 | if VectorizedFontNeeded then
|
---|
518 | VectorizedFontRenderer.TextOut(ADest,x,y,sUTF8,texture,align,ARightToLeft)
|
---|
519 | else
|
---|
520 | InternalTextOut(ADest,x,y,sUTF8,BGRAPixelTransparent,texture,align);
|
---|
521 | end;
|
---|
522 |
|
---|
523 | procedure TBGRATextEffectFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
---|
524 | y: single; sUTF8: string; c: TBGRAPixel; align: TAlignment;
|
---|
525 | ARightToLeft: boolean);
|
---|
526 | begin
|
---|
527 | if VectorizedFontNeeded then
|
---|
528 | VectorizedFontRenderer.TextOut(ADest,x,y,sUTF8,c,align,ARightToLeft)
|
---|
529 | else
|
---|
530 | InternalTextOut(ADest,x,y,sUTF8,c,nil,align);
|
---|
531 | end;
|
---|
532 |
|
---|
533 | function TBGRATextEffectFontRenderer.TextSize(sUTF8: string): TSize;
|
---|
534 | begin
|
---|
535 | if VectorizedFontNeeded then
|
---|
536 | result := VectorizedFontRenderer.TextSize(sUTF8)
|
---|
537 | else
|
---|
538 | result := inherited TextSize(sUTF8);
|
---|
539 | end;
|
---|
540 |
|
---|
541 | function TBGRATextEffectFontRenderer.TextSize(sUTF8: string;
|
---|
542 | AMaxWidth: integer; ARightToLeft: boolean): TSize;
|
---|
543 | begin
|
---|
544 | if VectorizedFontNeeded then
|
---|
545 | result := VectorizedFontRenderer.TextSize(sUTF8, AMaxWidth, ARightToLeft)
|
---|
546 | else
|
---|
547 | result := inherited TextSize(sUTF8, AMaxWidth, ARightToLeft);
|
---|
548 | end;
|
---|
549 |
|
---|
550 | function TBGRATextEffectFontRenderer.TextFitInfo(sUTF8: string;
|
---|
551 | AMaxWidth: integer): integer;
|
---|
552 | begin
|
---|
553 | if VectorizedFontNeeded then
|
---|
554 | result := VectorizedFontRenderer.TextFitInfo(sUTF8, AMaxWidth)
|
---|
555 | else
|
---|
556 | result := inherited TextFitInfo(sUTF8, AMaxWidth)
|
---|
557 | end;
|
---|
558 |
|
---|
559 | { TBGRATextEffect }
|
---|
560 |
|
---|
561 | procedure TBGRATextEffect.InitImproveReadability(AText: string; Font: TFont;
|
---|
562 | SubOffsetX, SubOffsetY: single);
|
---|
563 | var size: TSize;
|
---|
564 | overhang: integer;
|
---|
565 | begin
|
---|
566 | FShadowQuality:= rbFast;
|
---|
567 | if SubOffsetX < 0 then SubOffsetX := 0;
|
---|
568 | if SubOffsetY < 0 then SubOffsetY := 0;
|
---|
569 | size := BGRATextSize(Font, fqFineAntialiasing, AText, FontAntialiasingLevel);
|
---|
570 | FTextSize := size;
|
---|
571 | if size.cy = 0 then FTextSize.cy := BGRATextSize(Font, fqFineAntialiasing, 'Hg', FontAntialiasingLevel).cy;
|
---|
572 | overhang := size.cy div 2;
|
---|
573 | size.cx += 2*overhang + ceil(SubOffsetX);
|
---|
574 | size.cy += 2 + ceil(SubOffsetY);
|
---|
575 |
|
---|
576 | FOffset := Point(-overhang,-1); //include overhang
|
---|
577 | FTextMask := BGRABitmapFactory.Create(size.cx,size.cy,BGRABlack);
|
---|
578 | BGRATextOutImproveReadability(FTextMask, Font, overhang+SubOffsetX,1+SubOffsetY, AText, BGRAWhite, nil, taLeftJustify, irMask);
|
---|
579 | end;
|
---|
580 |
|
---|
581 | constructor TBGRATextEffect.Create(AText: string; Font: TFont;
|
---|
582 | Antialiasing: boolean; SubOffsetX,SubOffsetY: single);
|
---|
583 | begin
|
---|
584 | Init(AText, Font, Antialiasing, SubOffsetX, SubOffsetY, 0,0);
|
---|
585 | end;
|
---|
586 |
|
---|
587 | constructor TBGRATextEffect.Create(AText: string; Font: TFont;
|
---|
588 | Antialiasing: boolean; SubOffsetX, SubOffsetY: single; GrainX, GrainY: Integer
|
---|
589 | );
|
---|
590 | begin
|
---|
591 | Init(AText, Font, Antialiasing, SubOffsetX, SubOffsetY, GrainX, GrainY);
|
---|
592 | end;
|
---|
593 |
|
---|
594 | constructor TBGRATextEffect.Create(AText: string; AFontName: string;
|
---|
595 | AFullHeight: integer; Antialiasing: boolean);
|
---|
596 | begin
|
---|
597 | InitWithFontName(AText, AFontName, AFullHeight, [], Antialiasing, 0, 0);
|
---|
598 | end;
|
---|
599 |
|
---|
600 | constructor TBGRATextEffect.Create(AText: string; AFontName: string;
|
---|
601 | AFullHeight: integer; Antialiasing: boolean; SubOffsetX, SubOffsetY: single);
|
---|
602 | begin
|
---|
603 | InitWithFontName(AText, AFontName, AFullHeight, [], Antialiasing, SubOffsetX, SubOffsetY);
|
---|
604 | end;
|
---|
605 |
|
---|
606 | constructor TBGRATextEffect.Create(AText: string; AFontName: string;
|
---|
607 | AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean);
|
---|
608 | begin
|
---|
609 | InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, 0, 0);
|
---|
610 | end;
|
---|
611 |
|
---|
612 | constructor TBGRATextEffect.Create(AText: string; AFontName: string;
|
---|
613 | AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX,
|
---|
614 | SubOffsetY: single);
|
---|
615 | begin
|
---|
616 | InitWithFontName(AText, AFontName, AFullHeight, AStyle, Antialiasing, SubOffsetX, SubOffsetY);
|
---|
617 | end;
|
---|
618 |
|
---|
619 | procedure TBGRATextEffect.Init(AText: string; Font: TFont; Antialiasing: boolean; SubOffsetX,SubOffsetY: single; GrainX, GrainY: Integer);
|
---|
620 | const FXAntialiasingLevel = FontAntialiasingLevel;
|
---|
621 | var temp: TBGRACustomBitmap;
|
---|
622 | size: TSize;
|
---|
623 | p: PBGRAPixel;
|
---|
624 | n,v,maxAlpha: integer;
|
---|
625 | alpha: byte;
|
---|
626 | sizeX,sizeY: integer;
|
---|
627 | onePixel: integer;
|
---|
628 | quality: TBGRAFontQuality;
|
---|
629 | iSubX,iSubY: integer;
|
---|
630 | begin
|
---|
631 | FShadowQuality := rbFast;
|
---|
632 | if Antialiasing and Assigned(BGRATextOutImproveReadabilityProc) then
|
---|
633 | begin
|
---|
634 | InitImproveReadability(AText, Font, SubOffsetX,SubOffsetY);
|
---|
635 | exit;
|
---|
636 | end;
|
---|
637 | if Antialiasing then
|
---|
638 | quality := fqFineAntialiasing
|
---|
639 | else
|
---|
640 | quality := fqSystem;
|
---|
641 | size := BGRAOriginalTextSize(Font,quality,AText,FXAntialiasingLevel);
|
---|
642 | if (size.cx = 0) or (size.cy = 0) then
|
---|
643 | begin
|
---|
644 | size := BGRATextSize(Font,quality,'Hg',FXAntialiasingLevel);
|
---|
645 | FTextSize.cx := 0;
|
---|
646 | FTextSize.cy := size.cy;
|
---|
647 | FOffset := Point(0,0);
|
---|
648 | exit;
|
---|
649 | end;
|
---|
650 | FTextSize := size;
|
---|
651 |
|
---|
652 | sizeX := size.cx+size.cy;
|
---|
653 | sizeY := size.cy;
|
---|
654 |
|
---|
655 | iSubX := 0;
|
---|
656 | iSubY := 0;
|
---|
657 | if SubOffsetX < 0 then SubOffsetX := 0;
|
---|
658 | if SubOffsetY < 0 then SubOffsetY := 0;
|
---|
659 |
|
---|
660 | if Antialiasing then
|
---|
661 | begin
|
---|
662 | sizeX := (sizeX + FXAntialiasingLevel-1);
|
---|
663 | sizeX -= sizeX mod FXAntialiasingLevel;
|
---|
664 |
|
---|
665 | sizeY := (sizeY + FXAntialiasingLevel-1);
|
---|
666 | sizeY -= sizeY mod FXAntialiasingLevel;
|
---|
667 |
|
---|
668 | if SubOffsetX <> 0 then
|
---|
669 | begin
|
---|
670 | sizeX += ceil(SubOffsetX*FXAntialiasingLevel);
|
---|
671 | iSubX := round(SubOffsetX*FXAntialiasingLevel);
|
---|
672 | end;
|
---|
673 | if SubOffsetY <> 0 then
|
---|
674 | begin
|
---|
675 | sizeY += ceil(SubOffsetY*FXAntialiasingLevel);
|
---|
676 | iSubY := round(SubOffsetY*FXAntialiasingLevel);
|
---|
677 | end;
|
---|
678 |
|
---|
679 | OnePixel := FXAntialiasingLevel;
|
---|
680 | end else
|
---|
681 | begin
|
---|
682 | OnePixel := 1;
|
---|
683 |
|
---|
684 | if SubOffsetX <> 0 then
|
---|
685 | begin
|
---|
686 | iSubX := round(SubOffsetX);
|
---|
687 | sizeX += iSubX;
|
---|
688 | end;
|
---|
689 | if SubOffsetY <> 0 then
|
---|
690 | begin
|
---|
691 | iSubY := round(SubOffsetY);
|
---|
692 | sizeY += iSubY;
|
---|
693 | end;
|
---|
694 | end;
|
---|
695 | FOffset := Point(-size.cy div 2,-OnePixel); //include overhang
|
---|
696 |
|
---|
697 | if GrainX > 0 then
|
---|
698 | begin
|
---|
699 | SizeX := SizeX+ (GrainX-1);
|
---|
700 | SizeX -= SizeX mod GrainX;
|
---|
701 | end;
|
---|
702 | if GrainY > 0 then
|
---|
703 | begin
|
---|
704 | SizeY := SizeY+ (GrainY-1);
|
---|
705 | SizeY -= SizeY mod GrainY;
|
---|
706 | end;
|
---|
707 | temp := BGRABitmapFactory.Create(sizeX, sizeY+2*OnePixel,clBlack);
|
---|
708 | temp.Canvas.Font := Font;
|
---|
709 | temp.Canvas.Font.Height := Font.Height*OnePixel;
|
---|
710 | temp.Canvas.Font.Color := clWhite;
|
---|
711 | temp.Canvas.Font.Quality := FontDefaultQuality;
|
---|
712 | temp.Canvas.Brush.Style := bsClear;
|
---|
713 | temp.Canvas.TextOut(-FOffset.X+iSubX, -FOffset.Y+iSubY, AText);
|
---|
714 |
|
---|
715 | if Antialiasing then
|
---|
716 | begin
|
---|
717 | FTextSize.cx := round(FTextSize.cx/FXAntialiasingLevel);
|
---|
718 | FTextSize.cy := round(FTextSize.cy/FXAntialiasingLevel);
|
---|
719 | FOffset := Point(round(FOffset.X/FXAntialiasingLevel),round(FOffset.Y/FXAntialiasingLevel));
|
---|
720 |
|
---|
721 | FTextMask := temp.Resample(round(temp.width/FXAntialiasingLevel),round(temp.Height/FXAntialiasingLevel),rmSimpleStretch);
|
---|
722 |
|
---|
723 | maxAlpha := 0;
|
---|
724 | p := FTextMask.Data;
|
---|
725 | for n := FTextMask.NbPixels - 1 downto 0 do
|
---|
726 | begin
|
---|
727 | alpha := P^.green;
|
---|
728 | if alpha > maxAlpha then maxAlpha := alpha;
|
---|
729 | Inc(p);
|
---|
730 | end;
|
---|
731 | if maxAlpha <> 0 then
|
---|
732 | begin
|
---|
733 | p := FTextMask.Data;
|
---|
734 | for n := FTextMask.NbPixels - 1 downto 0 do
|
---|
735 | begin
|
---|
736 | v:= integer(p^.green * 255) div maxAlpha;
|
---|
737 | p^.red := v;
|
---|
738 | p^.green := v;
|
---|
739 | p^.blue := v;
|
---|
740 | Inc(p);
|
---|
741 | end;
|
---|
742 | end;
|
---|
743 | temp.Free;
|
---|
744 | end
|
---|
745 | else
|
---|
746 | begin
|
---|
747 | FTextMask := temp;
|
---|
748 | p := FTextMask.data;
|
---|
749 | for n := FTextMask.NbPixels-1 downto 0 do
|
---|
750 | begin
|
---|
751 | alpha := GammaExpansionTab[P^.green] shr 8;
|
---|
752 | p^.green := alpha;
|
---|
753 | p^.red := alpha;
|
---|
754 | p^.blue := alpha;
|
---|
755 | end;
|
---|
756 | end;
|
---|
757 | end;
|
---|
758 |
|
---|
759 | procedure TBGRATextEffect.InitWithFontName(AText: string; AFontName: string;
|
---|
760 | AFullHeight: integer; AStyle: TFontStyles; Antialiasing: boolean; SubOffsetX, SubOffsetY: single);
|
---|
761 | var lFont: TFont;
|
---|
762 | begin
|
---|
763 | lFont := TFont.Create;
|
---|
764 | lFont.Name := AFontName;
|
---|
765 | lFont.Height := AFullHeight * FontFullHeightSign;
|
---|
766 | lFont.Style := AStyle;
|
---|
767 | Init(AText, lFont, Antialiasing, SubOffsetX, SubOffsetY, 0,0);
|
---|
768 | lFont.Free;
|
---|
769 | end;
|
---|
770 |
|
---|
771 | constructor TBGRATextEffect.Create(AText: string; Font: TFont;
|
---|
772 | Antialiasing: boolean);
|
---|
773 | begin
|
---|
774 | Init(AText, Font, Antialiasing, 0,0,0,0);
|
---|
775 | end;
|
---|
776 |
|
---|
777 | initialization
|
---|
778 |
|
---|
779 | BGRATextOutImproveReadabilityProc := @BGRATextOutImproveReadability;
|
---|
780 |
|
---|
781 | end.
|
---|
782 |
|
---|