| 1 | unit BGRAFreeType;
|
|---|
| 2 |
|
|---|
| 3 | {$mode objfpc}{$H+}
|
|---|
| 4 |
|
|---|
| 5 | {
|
|---|
| 6 | Font rendering units : BGRAText, BGRATextFX, BGRAVectorize, BGRAFreeType
|
|---|
| 7 |
|
|---|
| 8 | This units provide a font renderer with FreeType fonts, using the integrated FreeType font engine in Lazarus.
|
|---|
| 9 | The simplest way to render effects is to use TBGRAFreeTypeFontRenderer class.
|
|---|
| 10 | To do this, create an instance of this class and assign it to a TBGRABitmap.FontRenderer property. Now functions
|
|---|
| 11 | to draw text like TBGRABitmap.TextOut will use the chosen renderer.
|
|---|
| 12 |
|
|---|
| 13 | >> Note that you need to define the default FreeType font collection
|
|---|
| 14 | >> using EasyLazFreeType unit.
|
|---|
| 15 |
|
|---|
| 16 | To set the effects, keep a variable containing
|
|---|
| 17 | the TBGRAFreeTypeFontRenderer class and modify ShadowVisible and other effects parameters. The FontHinted property
|
|---|
| 18 | allows you to choose if the font is snapped to pixels to make it more readable.
|
|---|
| 19 |
|
|---|
| 20 | TBGRAFreeTypeDrawer class is the class that provides basic FreeType drawing
|
|---|
| 21 | by deriving the TFreeTypeDrawer type. You can use it directly, but it is not
|
|---|
| 22 | recommended, because there are less text layout parameters. However, it is
|
|---|
| 23 | necessary if you want to create TBGRATextEffect objects using FreeType fonts.
|
|---|
| 24 | }
|
|---|
| 25 |
|
|---|
| 26 | interface
|
|---|
| 27 |
|
|---|
| 28 | {$i bgrabitmap.inc}
|
|---|
| 29 |
|
|---|
| 30 | uses
|
|---|
| 31 | Types, Classes, SysUtils, BGRAGraphics, BGRABitmapTypes, EasyLazFreeType, FPimage,
|
|---|
| 32 | BGRACustomTextFX, BGRAPhongTypes;
|
|---|
| 33 |
|
|---|
| 34 | type
|
|---|
| 35 | TBGRAFreeTypeDrawer = class;
|
|---|
| 36 |
|
|---|
| 37 | //this is the class to assign to FontRenderer property of TBGRABitmap
|
|---|
| 38 | { TBGRAFreeTypeFontRenderer }
|
|---|
| 39 |
|
|---|
| 40 | TBGRAFreeTypeFontRenderer = class(TBGRACustomFontRenderer)
|
|---|
| 41 | private
|
|---|
| 42 | FDrawer: TBGRAFreeTypeDrawer;
|
|---|
| 43 | FFont: TFreeTypeFont;
|
|---|
| 44 | function GetCollection: TCustomFreeTypeFontCollection;
|
|---|
| 45 | function GetDrawer(ASurface: TBGRACustomBitmap): TBGRAFreeTypeDrawer;
|
|---|
| 46 | function GetShaderLightPosition: TPoint;
|
|---|
| 47 | procedure SetShaderLightPosition(AValue: TPoint);
|
|---|
| 48 | protected
|
|---|
| 49 | FShaderOwner: boolean;
|
|---|
| 50 | FShader: TCustomPhongShading;
|
|---|
| 51 | procedure UpdateFont;
|
|---|
| 52 | procedure Init;
|
|---|
| 53 | procedure TextOutAnglePatch(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string;
|
|---|
| 54 | c: TBGRAPixel; tex: IBGRAScanner; align: TAlignment);
|
|---|
| 55 | public
|
|---|
| 56 | FontHinted: boolean;
|
|---|
| 57 |
|
|---|
| 58 | ShaderActive: boolean;
|
|---|
| 59 |
|
|---|
| 60 | ShadowVisible: boolean;
|
|---|
| 61 | ShadowColor: TBGRAPixel;
|
|---|
| 62 | ShadowRadius: integer;
|
|---|
| 63 | ShadowOffset: TPoint;
|
|---|
| 64 | ShadowQuality: TRadialBlurType;
|
|---|
| 65 |
|
|---|
| 66 | OutlineColor: TBGRAPixel;
|
|---|
| 67 | OutlineVisible,OuterOutlineOnly: boolean;
|
|---|
| 68 | OutlineTexture: IBGRAScanner;
|
|---|
| 69 |
|
|---|
| 70 | constructor Create; overload;
|
|---|
| 71 | constructor Create(AShader: TCustomPhongShading; AShaderOwner: boolean); overload;
|
|---|
| 72 | function GetFontPixelMetric: TFontPixelMetric; override;
|
|---|
| 73 | procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment); overload; override;
|
|---|
| 74 | procedure TextOutAngle(ADest: TBGRACustomBitmap; x, y: single; orientation: integer; s: string; texture: IBGRAScanner; align: TAlignment); overload; override;
|
|---|
| 75 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; texture: IBGRAScanner; align: TAlignment); overload; override;
|
|---|
| 76 | procedure TextOut(ADest: TBGRACustomBitmap; x, y: single; s: string; c: TBGRAPixel; align: TAlignment); overload; override;
|
|---|
| 77 | procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel); overload; override;
|
|---|
| 78 | procedure TextRect(ADest: TBGRACustomBitmap; ARect: TRect; x, y: integer; s: string; style: TTextStyle; texture: IBGRAScanner); overload; override;
|
|---|
| 79 | function TextSize(s: string): TSize; overload; override;
|
|---|
| 80 | function TextSize(sUTF8: string; AMaxWidth: integer; {%H-}ARightToLeft: boolean): TSize; overload; override;
|
|---|
| 81 | function TextFitInfo(sUTF8: string; AMaxWidth: integer): integer; override;
|
|---|
| 82 | destructor Destroy; override;
|
|---|
| 83 | property Collection: TCustomFreeTypeFontCollection read GetCollection;
|
|---|
| 84 | property ShaderLightPosition: TPoint read GetShaderLightPosition write SetShaderLightPosition;
|
|---|
| 85 | end;
|
|---|
| 86 |
|
|---|
| 87 | { TBGRAFreeTypeDrawer }
|
|---|
| 88 |
|
|---|
| 89 | TBGRAFreeTypeDrawer = class(TFreeTypeDrawer)
|
|---|
| 90 | private
|
|---|
| 91 | FMask: TBGRACustomBitmap;
|
|---|
| 92 | FColor: TBGRAPixel;
|
|---|
| 93 | FInCreateTextEffect: boolean;
|
|---|
| 94 | procedure RenderDirectly(x, y, tx: integer; data: pointer);
|
|---|
| 95 | procedure RenderDirectlyClearType(x, y, tx: integer; data: pointer);
|
|---|
| 96 | function ShadowActuallyVisible :boolean;
|
|---|
| 97 | function OutlineActuallyVisible: boolean;
|
|---|
| 98 | function ShaderActuallyActive : boolean;
|
|---|
| 99 | public
|
|---|
| 100 | Destination: TBGRACustomBitmap;
|
|---|
| 101 | ClearTypeRGBOrder: boolean;
|
|---|
| 102 | Texture: IBGRAScanner;
|
|---|
| 103 |
|
|---|
| 104 | Shader: TCustomPhongShading;
|
|---|
| 105 | ShaderActive: boolean;
|
|---|
| 106 |
|
|---|
| 107 | ShadowVisible: boolean;
|
|---|
| 108 | ShadowColor: TBGRAPixel;
|
|---|
| 109 | ShadowRadius: integer;
|
|---|
| 110 | ShadowOffset: TPoint;
|
|---|
| 111 | ShadowQuality: TRadialBlurType;
|
|---|
| 112 |
|
|---|
| 113 | OutlineColor: TBGRAPixel;
|
|---|
| 114 | OutlineVisible,OuterOutlineOnly: boolean;
|
|---|
| 115 | OutlineTexture: IBGRAScanner;
|
|---|
| 116 |
|
|---|
| 117 | constructor Create(ADestination: TBGRACustomBitmap);
|
|---|
| 118 | procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TFPColor); overload; override;
|
|---|
| 119 | procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload;
|
|---|
| 120 | procedure DrawText(AText: string; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
|
|---|
| 121 | { If this code does not compile, you probably have an older version of Lazarus. To fix the problem,
|
|---|
| 122 | go into "bgrabitmap.inc" and comment the compiler directives }
|
|---|
| 123 | {$IFDEF BGRABITMAP_USE_LCL12}
|
|---|
| 124 | procedure DrawTextWordBreak(AText: string; AFont: TFreeTypeRenderableFont; x, y, AMaxWidth: Single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
|
|---|
| 125 | procedure DrawTextRect(AText: string; AFont: TFreeTypeRenderableFont; X1,Y1,X2,Y2: Single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
|
|---|
| 126 | {$ENDIF}
|
|---|
| 127 | {$IFDEF BGRABITMAP_USE_LCL15}
|
|---|
| 128 | procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TFPColor); overload; override;
|
|---|
| 129 | procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel); overload;
|
|---|
| 130 | procedure DrawGlyph(AGlyph: integer; AFont: TFreeTypeRenderableFont; x,y: single; AColor: TBGRAPixel; AAlign: TFreeTypeAlignments); overload;
|
|---|
| 131 | {$ENDIF}
|
|---|
| 132 | function CreateTextEffect(AText: string; AFont: TFreeTypeRenderableFont): TBGRACustomTextEffect;
|
|---|
| 133 | destructor Destroy; override;
|
|---|
| 134 | end;
|
|---|
| 135 |
|
|---|
| 136 |
|
|---|
| 137 | implementation
|
|---|
| 138 |
|
|---|
| 139 | uses BGRABlend, Math, BGRATransform;
|
|---|
| 140 |
|
|---|
| 141 | { TBGRAFreeTypeFontRenderer }
|
|---|
| 142 |
|
|---|
| 143 | function TBGRAFreeTypeFontRenderer.GetCollection: TCustomFreeTypeFontCollection;
|
|---|
| 144 | begin
|
|---|
| 145 | result := EasyLazFreeType.FontCollection;
|
|---|
| 146 | end;
|
|---|
| 147 |
|
|---|
| 148 | function TBGRAFreeTypeFontRenderer.GetDrawer(ASurface: TBGRACustomBitmap): TBGRAFreeTypeDrawer;
|
|---|
| 149 | begin
|
|---|
| 150 | result := FDrawer;
|
|---|
| 151 | result.ShadowColor := ShadowColor;
|
|---|
| 152 | result.ShadowOffset := ShadowOffset;
|
|---|
| 153 | result.ShadowRadius := ShadowRadius;
|
|---|
| 154 | result.ShadowVisible := ShadowVisible;
|
|---|
| 155 | result.ShadowQuality := ShadowQuality;
|
|---|
| 156 | result.ClearTypeRGBOrder := FontQuality <> fqFineClearTypeBGR;
|
|---|
| 157 | result.Destination := ASurface;
|
|---|
| 158 | result.OutlineColor := OutlineColor;
|
|---|
| 159 | result.OutlineVisible := OutlineVisible;
|
|---|
| 160 | result.OuterOutlineOnly := OuterOutlineOnly;
|
|---|
| 161 | result.OutlineTexture := OutlineTexture;
|
|---|
| 162 | if ShaderActive then result.Shader := FShader
|
|---|
| 163 | else result.Shader := nil;
|
|---|
| 164 | end;
|
|---|
| 165 |
|
|---|
| 166 | function TBGRAFreeTypeFontRenderer.GetShaderLightPosition: TPoint;
|
|---|
| 167 | begin
|
|---|
| 168 | if FShader = nil then
|
|---|
| 169 | result := point(0,0)
|
|---|
| 170 | else
|
|---|
| 171 | result := FShader.LightPosition;
|
|---|
| 172 | end;
|
|---|
| 173 |
|
|---|
| 174 | procedure TBGRAFreeTypeFontRenderer.SetShaderLightPosition(AValue: TPoint);
|
|---|
| 175 | begin
|
|---|
| 176 | if FShader <> nil then
|
|---|
| 177 | FShader.LightPosition := AValue;
|
|---|
| 178 | end;
|
|---|
| 179 |
|
|---|
| 180 | procedure TBGRAFreeTypeFontRenderer.UpdateFont;
|
|---|
| 181 | var fts: TFreeTypeStyles;
|
|---|
| 182 | filename: string;
|
|---|
| 183 | begin
|
|---|
| 184 | fts := [];
|
|---|
| 185 | if fsBold in FontStyle then fts += [ftsBold];
|
|---|
| 186 | if fsItalic in FontStyle then fts += [ftsItalic];
|
|---|
| 187 | try
|
|---|
| 188 | filename := FontName;
|
|---|
| 189 | {$IFDEF BGRABITMAP_USE_LCL12}
|
|---|
| 190 | FFont.SetNameAndStyle(filename,fts);
|
|---|
| 191 | {$ELSE}
|
|---|
| 192 | FFont.Name := filename;
|
|---|
| 193 | FFont.Style := fts;
|
|---|
| 194 | {$ENDIF}
|
|---|
| 195 | except
|
|---|
| 196 | on ex: exception do
|
|---|
| 197 | begin
|
|---|
| 198 | end;
|
|---|
| 199 | end;
|
|---|
| 200 | if FontEmHeight >= 0 then
|
|---|
| 201 | FFont.SizeInPixels := FontEmHeight
|
|---|
| 202 | else
|
|---|
| 203 | FFont.LineFullHeight := -FontEmHeight;
|
|---|
| 204 | case FontQuality of
|
|---|
| 205 | fqSystem:
|
|---|
| 206 | begin
|
|---|
| 207 | FFont.Quality := grqMonochrome;
|
|---|
| 208 | FFont.ClearType := false;
|
|---|
| 209 | end;
|
|---|
| 210 | fqSystemClearType:
|
|---|
| 211 | begin
|
|---|
| 212 | FFont.Quality:= grqLowQuality;
|
|---|
| 213 | FFont.ClearType:= true;
|
|---|
| 214 | end;
|
|---|
| 215 | fqFineAntialiasing:
|
|---|
| 216 | begin
|
|---|
| 217 | FFont.Quality:= grqHighQuality;
|
|---|
| 218 | FFont.ClearType:= false;
|
|---|
| 219 | end;
|
|---|
| 220 | fqFineClearTypeRGB,fqFineClearTypeBGR:
|
|---|
| 221 | begin
|
|---|
| 222 | FFont.Quality:= grqHighQuality;
|
|---|
| 223 | FFont.ClearType:= true;
|
|---|
| 224 | end;
|
|---|
| 225 | end;
|
|---|
| 226 | FFont.Hinted := FontHinted;
|
|---|
| 227 | {$IFDEF BGRABITMAP_USE_LCL12}
|
|---|
| 228 | FFont.StrikeOutDecoration := fsStrikeOut in FontStyle;
|
|---|
| 229 | FFont.UnderlineDecoration := fsUnderline in FontStyle;
|
|---|
| 230 | {$ENDIF}
|
|---|
| 231 | end;
|
|---|
| 232 |
|
|---|
| 233 | procedure TBGRAFreeTypeFontRenderer.Init;
|
|---|
| 234 | begin
|
|---|
| 235 | ShaderActive := true;
|
|---|
| 236 |
|
|---|
| 237 | FDrawer := TBGRAFreeTypeDrawer.Create(nil);
|
|---|
| 238 | FFont := TFreeTypeFont.Create;
|
|---|
| 239 | FontHinted:= True;
|
|---|
| 240 |
|
|---|
| 241 | ShadowColor := BGRABlack;
|
|---|
| 242 | ShadowVisible := false;
|
|---|
| 243 | ShadowOffset := Point(5,5);
|
|---|
| 244 | ShadowRadius := 5;
|
|---|
| 245 | ShadowQuality:= rbFast;
|
|---|
| 246 | end;
|
|---|
| 247 |
|
|---|
| 248 | procedure TBGRAFreeTypeFontRenderer.TextOutAnglePatch(ADest: TBGRACustomBitmap;
|
|---|
| 249 | x, y: single; orientation: integer; s: string; c: TBGRAPixel;
|
|---|
| 250 | tex: IBGRAScanner; align: TAlignment);
|
|---|
| 251 | const orientationToDeg = -0.1;
|
|---|
| 252 | var
|
|---|
| 253 | temp: TBGRACustomBitmap;
|
|---|
| 254 | coord: TPointF;
|
|---|
| 255 | angleDeg: single;
|
|---|
| 256 | OldOrientation: integer;
|
|---|
| 257 | filter: TResampleFilter;
|
|---|
| 258 | OldFontQuality: TBGRAFontQuality;
|
|---|
| 259 | begin
|
|---|
| 260 | OldOrientation := FontOrientation;
|
|---|
| 261 | FontOrientation:= 0;
|
|---|
| 262 | OldFontQuality := FontQuality;
|
|---|
| 263 |
|
|---|
| 264 | if FontQuality in[fqFineClearTypeRGB,fqFineClearTypeBGR] then FontQuality:= fqFineAntialiasing
|
|---|
| 265 | else if FontQuality = fqSystemClearType then FontQuality:= fqSystem;
|
|---|
| 266 |
|
|---|
| 267 | temp := BGRABitmapFactory.Create;
|
|---|
| 268 | with TextSize(s) do
|
|---|
| 269 | temp.SetSize(cx,cy);
|
|---|
| 270 | temp.FillTransparent;
|
|---|
| 271 | if tex<>nil then
|
|---|
| 272 | TextOut(temp,0,0, s, tex, taLeftJustify)
|
|---|
| 273 | else
|
|---|
| 274 | TextOut(temp,0,0, s, c, taLeftJustify);
|
|---|
| 275 |
|
|---|
| 276 | orientation:= orientation mod 3600;
|
|---|
| 277 | if orientation < 0 then orientation += 3600;
|
|---|
| 278 |
|
|---|
| 279 | angleDeg := orientation * orientationToDeg;
|
|---|
| 280 | coord := PointF(x,y);
|
|---|
| 281 | case align of
|
|---|
| 282 | taRightJustify: coord -= AffineMatrixRotationDeg(angleDeg)*PointF(temp.Width,0);
|
|---|
| 283 | taCenter: coord -= AffineMatrixRotationDeg(angleDeg)*PointF(temp.Width,0)*0.5;
|
|---|
| 284 | end;
|
|---|
| 285 | case orientation of
|
|---|
| 286 | 0,900,1800,2700: filter := rfBox;
|
|---|
| 287 | else filter := rfCosine;
|
|---|
| 288 | end;
|
|---|
| 289 | ADest.PutImageAngle(coord.x,coord.y, temp, angleDeg, filter);
|
|---|
| 290 | temp.Free;
|
|---|
| 291 |
|
|---|
| 292 | FontOrientation:= OldOrientation;
|
|---|
| 293 | FontQuality:= OldFontQuality;
|
|---|
| 294 | end;
|
|---|
| 295 |
|
|---|
| 296 | constructor TBGRAFreeTypeFontRenderer.Create;
|
|---|
| 297 | begin
|
|---|
| 298 | Init;
|
|---|
| 299 | end;
|
|---|
| 300 |
|
|---|
| 301 | constructor TBGRAFreeTypeFontRenderer.Create(AShader: TCustomPhongShading;
|
|---|
| 302 | AShaderOwner: boolean);
|
|---|
| 303 | begin
|
|---|
| 304 | Init;
|
|---|
| 305 | FShader := AShader;
|
|---|
| 306 | FShaderOwner := AShaderOwner;
|
|---|
| 307 | end;
|
|---|
| 308 |
|
|---|
| 309 | function TBGRAFreeTypeFontRenderer.GetFontPixelMetric: TFontPixelMetric;
|
|---|
| 310 | begin
|
|---|
| 311 | UpdateFont;
|
|---|
| 312 | result.Baseline := round(FFont.Ascent);
|
|---|
| 313 | result.CapLine:= round(FFont.Ascent*0.2);
|
|---|
| 314 | result.DescentLine:= round(FFont.Ascent+FFont.Descent);
|
|---|
| 315 | result.Lineheight := round(FFont.LineFullHeight);
|
|---|
| 316 | result.xLine := round(FFont.Ascent*0.45);
|
|---|
| 317 | result.Defined := True;
|
|---|
| 318 | end;
|
|---|
| 319 |
|
|---|
| 320 | procedure TBGRAFreeTypeFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
|
|---|
| 321 | y: single; orientation: integer; s: string; c: TBGRAPixel; align: TAlignment);
|
|---|
| 322 | begin
|
|---|
| 323 | TextOutAnglePatch(ADest, x,y, orientation, s, c, nil, align);
|
|---|
| 324 | {procedure TForm1.TextOutAnglePatch(ADest: TBGRABitmap;
|
|---|
| 325 | x, y: single; orientationTenthDegCCW: integer;
|
|---|
| 326 | s: string; c: TBGRAPixel; AAlign: TAlignment; AResampleFilter: TResampleFilter);
|
|---|
| 327 | const orientationToDeg = -0.1;
|
|---|
| 328 | var
|
|---|
| 329 | temp: TBGRABitmap;
|
|---|
| 330 | coord: TPointF;
|
|---|
| 331 | angleDeg: single;
|
|---|
| 332 | begin
|
|---|
| 333 | temp := TBGRABitmap.Create;
|
|---|
| 334 | ADest.CopyPropertiesTo(temp);
|
|---|
| 335 | temp.FontOrientation := 0;
|
|---|
| 336 | with temp.TextSize(s) do
|
|---|
| 337 | temp.SetSize(cx,cy);
|
|---|
| 338 | temp.FillTransparent;
|
|---|
| 339 | +
|
|---|
| 340 | temp.TextOut(0,0, s, c);
|
|---|
| 341 |
|
|---|
| 342 | angleDeg := orientationTenthDegCCW * orientationToDeg;
|
|---|
| 343 | coord := PointF(x,y);
|
|---|
| 344 | case AAlign of
|
|---|
| 345 | taRightJustify: coord -= AffineMatrixRotationDeg(angleDeg)*PointF(temp.Width,0);
|
|---|
| 346 | taCenter: coord -= AffineMatrixRotationDeg(angleDeg)*PointF(temp.Width,0)*0.5;
|
|---|
| 347 | end;
|
|---|
| 348 |
|
|---|
| 349 | ADest.PutImageAngle(coord.x,coord.y, temp, angleDeg, rfBox);
|
|---|
| 350 | temp.Free;
|
|---|
| 351 | end; }
|
|---|
| 352 |
|
|---|
| 353 | end;
|
|---|
| 354 |
|
|---|
| 355 | procedure TBGRAFreeTypeFontRenderer.TextOutAngle(ADest: TBGRACustomBitmap; x,
|
|---|
| 356 | y: single; orientation: integer; s: string; texture: IBGRAScanner;
|
|---|
| 357 | align: TAlignment);
|
|---|
| 358 | begin
|
|---|
| 359 | TextOutAnglePatch(ADest, x,y, orientation, s, BGRAPixelTransparent, texture, align);
|
|---|
| 360 | end;
|
|---|
| 361 |
|
|---|
| 362 | procedure TBGRAFreeTypeFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
|---|
| 363 | y: single; s: string; texture: IBGRAScanner; align: TAlignment);
|
|---|
| 364 | begin
|
|---|
| 365 | FDrawer.Texture := texture;
|
|---|
| 366 | TextOut(ADest,x,y,s,BGRAWhite,align);
|
|---|
| 367 | FDrawer.Texture := nil;
|
|---|
| 368 | end;
|
|---|
| 369 |
|
|---|
| 370 | procedure TBGRAFreeTypeFontRenderer.TextOut(ADest: TBGRACustomBitmap; x,
|
|---|
| 371 | y: single; s: string; c: TBGRAPixel; align: TAlignment);
|
|---|
| 372 | var
|
|---|
| 373 | ftaAlign: TFreeTypeAlignments;
|
|---|
| 374 | begin
|
|---|
| 375 | UpdateFont;
|
|---|
| 376 | ftaAlign:= [ftaTop];
|
|---|
| 377 | case align of
|
|---|
| 378 | taLeftJustify: ftaAlign += [ftaLeft];
|
|---|
| 379 | taCenter: ftaAlign += [ftaCenter];
|
|---|
| 380 | taRightJustify: ftaAlign += [ftaRight];
|
|---|
| 381 | end;
|
|---|
| 382 | GetDrawer(ADest).DrawText(s,FFont,x,y,BGRAToFPColor(c),ftaAlign);
|
|---|
| 383 | end;
|
|---|
| 384 |
|
|---|
| 385 | procedure TBGRAFreeTypeFontRenderer.TextRect(ADest: TBGRACustomBitmap;
|
|---|
| 386 | ARect: TRect; x, y: integer; s: string; style: TTextStyle; c: TBGRAPixel);
|
|---|
| 387 | var align: TFreeTypeAlignments;
|
|---|
| 388 | intersectedClip,previousClip: TRect;
|
|---|
| 389 | begin
|
|---|
| 390 | previousClip := ADest.ClipRect;
|
|---|
| 391 | if style.Clipping then
|
|---|
| 392 | begin
|
|---|
| 393 | intersectedClip := rect(0,0,0,0);
|
|---|
| 394 | if not IntersectRect(intersectedClip, previousClip, ARect) then exit;
|
|---|
| 395 | ADest.ClipRect := intersectedClip;
|
|---|
| 396 | end;
|
|---|
| 397 | UpdateFont;
|
|---|
| 398 | align := [];
|
|---|
| 399 | case style.Alignment of
|
|---|
| 400 | taCenter: begin ARect.Left := x; align += [ftaCenter]; end;
|
|---|
| 401 | taRightJustify: begin ARect.Left := x; align += [ftaRight]; end;
|
|---|
| 402 | else
|
|---|
| 403 | align += [ftaLeft];
|
|---|
| 404 | end;
|
|---|
| 405 | case style.Layout of
|
|---|
| 406 | {$IFDEF BGRABITMAP_USE_LCL12}
|
|---|
| 407 | tlCenter: begin ARect.Top := y; align += [ftaVerticalCenter]; end;
|
|---|
| 408 | {$ENDIF}
|
|---|
| 409 | tlBottom: begin ARect.top := y; align += [ftaBottom]; end;
|
|---|
| 410 | else align += [ftaTop];
|
|---|
| 411 | end;
|
|---|
| 412 | try
|
|---|
| 413 | {$IFDEF BGRABITMAP_USE_LCL12}
|
|---|
| 414 | if style.Wordbreak then
|
|---|
| 415 | GetDrawer(ADest).DrawTextRect(s, FFont, ARect.Left,ARect.Top,ARect.Right,ARect.Bottom,BGRAToFPColor(c),align)
|
|---|
| 416 | else
|
|---|
| 417 | {$ENDIF}
|
|---|
| 418 | begin
|
|---|
| 419 | case style.Layout of
|
|---|
| 420 | tlCenter: y := (ARect.Top+ARect.Bottom) div 2;
|
|---|
| 421 | tlBottom: y := ARect.Bottom;
|
|---|
| 422 | else
|
|---|
| 423 | y := ARect.Top;
|
|---|
| 424 | end;
|
|---|
| 425 | case style.Alignment of
|
|---|
| 426 | taLeftJustify: GetDrawer(ADest).DrawText(s,FFont,ARect.Left,y,BGRAToFPColor(c),align);
|
|---|
| 427 | taCenter: GetDrawer(ADest).DrawText(s,FFont,(ARect.Left+ARect.Right-1) div 2,y,BGRAToFPColor(c),align);
|
|---|
| 428 | taRightJustify: GetDrawer(ADest).DrawText(s,FFont,ARect.Right,y,BGRAToFPColor(c),align);
|
|---|
| 429 | end;
|
|---|
| 430 | end;
|
|---|
| 431 | finally
|
|---|
| 432 | if style.Clipping then
|
|---|
| 433 | ADest.ClipRect := previousClip;
|
|---|
| 434 | end;
|
|---|
| 435 | end;
|
|---|
| 436 |
|
|---|
| 437 | procedure TBGRAFreeTypeFontRenderer.TextRect(ADest: TBGRACustomBitmap;
|
|---|
| 438 | ARect: TRect; x, y: integer; s: string; style: TTextStyle;
|
|---|
| 439 | texture: IBGRAScanner);
|
|---|
| 440 | begin
|
|---|
| 441 | FDrawer.Texture := texture;
|
|---|
| 442 | TextRect(ADest,ARect,x,y,s,style,BGRAWhite);
|
|---|
| 443 | FDrawer.Texture := nil;
|
|---|
| 444 | end;
|
|---|
| 445 |
|
|---|
| 446 | function TBGRAFreeTypeFontRenderer.TextSize(s: string): TSize;
|
|---|
| 447 | begin
|
|---|
| 448 | UpdateFont;
|
|---|
| 449 | result.cx := round(FFont.TextWidth(s));
|
|---|
| 450 | result.cy := round(FFont.LineFullHeight);
|
|---|
| 451 | end;
|
|---|
| 452 |
|
|---|
| 453 | function TBGRAFreeTypeFontRenderer.TextSize(sUTF8: string; AMaxWidth: integer;
|
|---|
| 454 | ARightToLeft: boolean): TSize;
|
|---|
| 455 | var
|
|---|
| 456 | remains: string;
|
|---|
| 457 | w,h,totalH: single;
|
|---|
| 458 | begin
|
|---|
| 459 | UpdateFont;
|
|---|
| 460 |
|
|---|
| 461 | result.cx := 0;
|
|---|
| 462 | totalH := 0;
|
|---|
| 463 | h := FFont.LineFullHeight;
|
|---|
| 464 | repeat
|
|---|
| 465 | FFont.SplitText(sUTF8, AMaxWidth, remains);
|
|---|
| 466 | w := FFont.TextWidth(sUTF8);
|
|---|
| 467 | if round(w)>result.cx then result.cx := round(w);
|
|---|
| 468 | totalH += h;
|
|---|
| 469 | sUTF8 := remains;
|
|---|
| 470 | until remains = '';
|
|---|
| 471 | result.cy := ceil(totalH);
|
|---|
| 472 | end;
|
|---|
| 473 |
|
|---|
| 474 | function TBGRAFreeTypeFontRenderer.TextFitInfo(sUTF8: string; AMaxWidth: integer): integer;
|
|---|
| 475 | var
|
|---|
| 476 | remains: string;
|
|---|
| 477 | begin
|
|---|
| 478 | UpdateFont;
|
|---|
| 479 | FFont.SplitText(sUTF8, AMaxWidth, remains);
|
|---|
| 480 | result := length(sUTF8);
|
|---|
| 481 | end;
|
|---|
| 482 |
|
|---|
| 483 | destructor TBGRAFreeTypeFontRenderer.Destroy;
|
|---|
| 484 | begin
|
|---|
| 485 | FDrawer.Free;
|
|---|
| 486 | FFont.Free;
|
|---|
| 487 | if FShaderOwner then FShader.Free;
|
|---|
| 488 | inherited Destroy;
|
|---|
| 489 | end;
|
|---|
| 490 |
|
|---|
| 491 | { TBGRAFreeTypeDrawer }
|
|---|
| 492 |
|
|---|
| 493 | procedure TBGRAFreeTypeDrawer.RenderDirectly( x,y,tx: integer;
|
|---|
| 494 | data: pointer );
|
|---|
| 495 | var psrc: pbyte;
|
|---|
| 496 | pdest: PBGRAPixel;
|
|---|
| 497 | c: TBGRAPixel;
|
|---|
| 498 | begin
|
|---|
| 499 | if Destination <> nil then
|
|---|
| 500 | begin
|
|---|
| 501 | //ensure rendering in bounds
|
|---|
| 502 | if (y < 0) or (y >= Destination.height) or (x < 0) or (x > Destination.width-tx) then exit;
|
|---|
| 503 |
|
|---|
| 504 | psrc := pbyte(data);
|
|---|
| 505 | pdest := Destination.ScanLine[y]+x;
|
|---|
| 506 | if Texture = nil then
|
|---|
| 507 | begin
|
|---|
| 508 | c := FColor;
|
|---|
| 509 | while tx > 0 do
|
|---|
| 510 | begin
|
|---|
| 511 | DrawPixelInlineWithAlphaCheck(pdest,c,psrc^);
|
|---|
| 512 | inc(psrc);
|
|---|
| 513 | inc(pdest);
|
|---|
| 514 | dec(tx);
|
|---|
| 515 | end;
|
|---|
| 516 | end else
|
|---|
| 517 | begin
|
|---|
| 518 | Texture.ScanMoveTo(x,y);
|
|---|
| 519 | while tx > 0 do
|
|---|
| 520 | begin
|
|---|
| 521 | DrawPixelInlineWithAlphaCheck(pdest,Texture.ScanNextPixel,psrc^);
|
|---|
| 522 | inc(psrc);
|
|---|
| 523 | inc(pdest);
|
|---|
| 524 | dec(tx);
|
|---|
| 525 | end;
|
|---|
| 526 | end;
|
|---|
| 527 | end;
|
|---|
| 528 | end;
|
|---|
| 529 |
|
|---|
| 530 | procedure TBGRAFreeTypeDrawer.RenderDirectlyClearType(x, y, tx: integer; data: pointer);
|
|---|
| 531 | var xb: integer;
|
|---|
| 532 | psrc: pbyte;
|
|---|
| 533 | pdest: PBGRAPixel;
|
|---|
| 534 | begin
|
|---|
| 535 | if Destination <> nil then
|
|---|
| 536 | begin
|
|---|
| 537 | tx := tx div 3;
|
|---|
| 538 | if tx=0 then exit;
|
|---|
| 539 | if (FMask <> nil) and (FMask.Width <> tx) then
|
|---|
| 540 | FMask.SetSize(tx,1)
|
|---|
| 541 | else if FMask = nil then FMask := BGRABitmapFactory.create(tx,1);
|
|---|
| 542 |
|
|---|
| 543 | pdest := FMask.Data;
|
|---|
| 544 | psrc := pbyte(data);
|
|---|
| 545 | pdest^.red := (psrc^ + psrc^ + (psrc+1)^) div 3;
|
|---|
| 546 | pdest^.green := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
|
|---|
| 547 | if tx > 1 then
|
|---|
| 548 | pdest^.blue := ((psrc+1)^ + (psrc+2)^ + (psrc+3)^) div 3
|
|---|
| 549 | else
|
|---|
| 550 | pdest^.blue := ((psrc+1)^ + (psrc+2)^ + (psrc+2)^) div 3;
|
|---|
| 551 | inc(pdest);
|
|---|
| 552 | inc(psrc,3);
|
|---|
| 553 | for xb := 1 to tx-2 do
|
|---|
| 554 | begin
|
|---|
| 555 | pdest^.red := ((psrc-1)^+ psrc^ + (psrc+1)^) div 3;
|
|---|
| 556 | pdest^.green := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
|
|---|
| 557 | pdest^.blue := ((psrc+1)^ + (psrc+2)^ + (psrc+3)^) div 3;
|
|---|
| 558 | inc(pdest);
|
|---|
| 559 | inc(psrc,3);
|
|---|
| 560 | end;
|
|---|
| 561 | if tx > 1 then
|
|---|
| 562 | begin
|
|---|
| 563 | pdest^.red := ((psrc-1)^+ psrc^ + (psrc+1)^) div 3;
|
|---|
| 564 | pdest^.green := (psrc^+ (psrc+1)^ + (psrc+2)^) div 3;
|
|---|
| 565 | pdest^.blue := ((psrc+1)^ + (psrc+2)^ + (psrc+2)^) div 3;
|
|---|
| 566 | end;
|
|---|
| 567 | BGRAFillClearTypeRGBMask(Destination,x div 3,y,FMask,FColor,Texture,ClearTypeRGBOrder);
|
|---|
| 568 | end;
|
|---|
| 569 | end;
|
|---|
| 570 |
|
|---|
| 571 | function TBGRAFreeTypeDrawer.ShadowActuallyVisible: boolean;
|
|---|
| 572 | begin
|
|---|
| 573 | result := ShadowVisible and (ShadowColor.alpha <> 0);
|
|---|
| 574 | end;
|
|---|
| 575 |
|
|---|
| 576 | function TBGRAFreeTypeDrawer.OutlineActuallyVisible: boolean;
|
|---|
| 577 | begin
|
|---|
| 578 | result := ((OutlineTexture <> nil) or (OutlineColor.alpha <> 0)) and OutlineVisible;
|
|---|
| 579 | end;
|
|---|
| 580 |
|
|---|
| 581 | function TBGRAFreeTypeDrawer.ShaderActuallyActive: boolean;
|
|---|
| 582 | begin
|
|---|
| 583 | result := (Shader <> nil) and ShaderActive;
|
|---|
| 584 | end;
|
|---|
| 585 |
|
|---|
| 586 | constructor TBGRAFreeTypeDrawer.Create(ADestination: TBGRACustomBitmap);
|
|---|
| 587 | begin
|
|---|
| 588 | Destination := ADestination;
|
|---|
| 589 | ClearTypeRGBOrder:= true;
|
|---|
| 590 | ShaderActive := true;
|
|---|
| 591 | ShadowQuality:= rbFast;
|
|---|
| 592 | end;
|
|---|
| 593 |
|
|---|
| 594 | procedure TBGRAFreeTypeDrawer.DrawText(AText: string;
|
|---|
| 595 | AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor);
|
|---|
| 596 | var fx: TBGRACustomTextEffect;
|
|---|
| 597 | procedure DoOutline;
|
|---|
| 598 | begin
|
|---|
| 599 | if OutlineActuallyVisible then
|
|---|
| 600 | begin
|
|---|
| 601 | if OutlineTexture <> nil then
|
|---|
| 602 | fx.DrawOutline(Destination,round(x),round(y), OutlineTexture)
|
|---|
| 603 | else
|
|---|
| 604 | fx.DrawOutline(Destination,round(x),round(y), OutlineColor);
|
|---|
| 605 | end;
|
|---|
| 606 | end;
|
|---|
| 607 | begin
|
|---|
| 608 | if not FInCreateTextEffect and (ShadowActuallyVisible or OutlineActuallyVisible or ShaderActuallyActive) then
|
|---|
| 609 | begin
|
|---|
| 610 | fx := CreateTextEffect(AText, AFont);
|
|---|
| 611 | fx.ShadowQuality := ShadowQuality;
|
|---|
| 612 | y -= AFont.Ascent;
|
|---|
| 613 | if ShadowActuallyVisible then fx.DrawShadow(Destination, round(x+ShadowOffset.X),round(y+ShadowOffset.Y), ShadowRadius, ShadowColor);
|
|---|
| 614 | if OuterOutlineOnly then DoOutline;
|
|---|
| 615 |
|
|---|
| 616 | if texture <> nil then
|
|---|
| 617 | begin
|
|---|
| 618 | if ShaderActuallyActive then
|
|---|
| 619 | fx.DrawShaded(Destination,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), texture)
|
|---|
| 620 | else
|
|---|
| 621 | fx.Draw(Destination,round(x),round(y), texture);
|
|---|
| 622 | end else
|
|---|
| 623 | begin
|
|---|
| 624 | if ShaderActuallyActive then
|
|---|
| 625 | fx.DrawShaded(Destination,floor(x),floor(y), Shader, round(fx.TextSize.cy*0.05), FPColorToBGRA(AColor))
|
|---|
| 626 | else
|
|---|
| 627 | fx.Draw(Destination,round(x),round(y), FPColorToBGRA(AColor));
|
|---|
| 628 | end;
|
|---|
| 629 | if not OuterOutlineOnly then DoOutline;
|
|---|
| 630 | fx.Free;
|
|---|
| 631 | end else
|
|---|
| 632 | begin
|
|---|
| 633 | FColor := FPColorToBGRA(AColor);
|
|---|
| 634 | if AFont.ClearType then
|
|---|
| 635 | AFont.RenderText(AText, x, y, Destination.ClipRect, @RenderDirectlyClearType)
|
|---|
| 636 | else
|
|---|
| 637 | AFont.RenderText(AText, x, y, Destination.ClipRect, @RenderDirectly);
|
|---|
| 638 | end;
|
|---|
| 639 | end;
|
|---|
| 640 |
|
|---|
| 641 | procedure TBGRAFreeTypeDrawer.DrawText(AText: string;
|
|---|
| 642 | AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel);
|
|---|
| 643 | begin
|
|---|
| 644 | DrawText(AText, AFont, x,y, BGRAToFPColor(AColor));
|
|---|
| 645 | end;
|
|---|
| 646 |
|
|---|
| 647 | procedure TBGRAFreeTypeDrawer.DrawText(AText: string;
|
|---|
| 648 | AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel;
|
|---|
| 649 | AAlign: TFreeTypeAlignments);
|
|---|
| 650 | begin
|
|---|
| 651 | DrawText(AText, AFont, x,y, BGRAToFPColor(AColor), AAlign);
|
|---|
| 652 | end;
|
|---|
| 653 |
|
|---|
| 654 | {$IFDEF BGRABITMAP_USE_LCL12}
|
|---|
| 655 | procedure TBGRAFreeTypeDrawer.DrawTextWordBreak(AText: string;
|
|---|
| 656 | AFont: TFreeTypeRenderableFont; x, y, AMaxWidth: Single; AColor: TBGRAPixel;
|
|---|
| 657 | AAlign: TFreeTypeAlignments);
|
|---|
| 658 | begin
|
|---|
| 659 | DrawTextWordBreak(AText,AFont,x,y,AMaxWidth,BGRAToFPColor(AColor),AAlign);
|
|---|
| 660 | end;
|
|---|
| 661 |
|
|---|
| 662 | procedure TBGRAFreeTypeDrawer.DrawTextRect(AText: string;
|
|---|
| 663 | AFont: TFreeTypeRenderableFont; X1, Y1, X2, Y2: Single; AColor: TBGRAPixel;
|
|---|
| 664 | AAlign: TFreeTypeAlignments);
|
|---|
| 665 | begin
|
|---|
| 666 | DrawTextRect(AText,AFont,X1,Y1,X2,Y2,BGRAToFPColor(AColor),AAlign);
|
|---|
| 667 | end;
|
|---|
| 668 | {$ENDIF}
|
|---|
| 669 |
|
|---|
| 670 | {$IFDEF BGRABITMAP_USE_LCL15}
|
|---|
| 671 | procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer;
|
|---|
| 672 | AFont: TFreeTypeRenderableFont; x, y: single; AColor: TFPColor);
|
|---|
| 673 | var f: TFreeTypeFont;
|
|---|
| 674 | begin
|
|---|
| 675 | if not (AFont is TFreeTypeFont) then exit;
|
|---|
| 676 | f := TFreeTypeFont(Afont);
|
|---|
| 677 | FColor := FPColorToBGRA(AColor);
|
|---|
| 678 | if AFont.ClearType then
|
|---|
| 679 | f.RenderGlyph(AGlyph, x, y, Destination.ClipRect, @RenderDirectlyClearType)
|
|---|
| 680 | else
|
|---|
| 681 | f.RenderGlyph(AGlyph, x, y, Destination.ClipRect, @RenderDirectly);
|
|---|
| 682 | end;
|
|---|
| 683 |
|
|---|
| 684 | procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer;
|
|---|
| 685 | AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel);
|
|---|
| 686 | begin
|
|---|
| 687 | DrawGlyph(AGlyph, AFont, x,y, BGRAToFPColor(AColor));
|
|---|
| 688 | end;
|
|---|
| 689 |
|
|---|
| 690 | procedure TBGRAFreeTypeDrawer.DrawGlyph(AGlyph: integer;
|
|---|
| 691 | AFont: TFreeTypeRenderableFont; x, y: single; AColor: TBGRAPixel;
|
|---|
| 692 | AAlign: TFreeTypeAlignments);
|
|---|
| 693 | begin
|
|---|
| 694 | DrawGlyph(AGlyph, AFont, x,y, BGRAToFPColor(AColor), AAlign);
|
|---|
| 695 | end;
|
|---|
| 696 | {$ENDIF}
|
|---|
| 697 |
|
|---|
| 698 | function TBGRAFreeTypeDrawer.CreateTextEffect(AText: string;
|
|---|
| 699 | AFont: TFreeTypeRenderableFont): TBGRACustomTextEffect;
|
|---|
| 700 | var
|
|---|
| 701 | mask: TBGRACustomBitmap;
|
|---|
| 702 | tx,ty,marginHoriz,marginVert: integer;
|
|---|
| 703 | tempDest: TBGRACustomBitmap;
|
|---|
| 704 | tempTex: IBGRAScanner;
|
|---|
| 705 | tempClearType: boolean;
|
|---|
| 706 | begin
|
|---|
| 707 | FInCreateTextEffect:= True;
|
|---|
| 708 | try
|
|---|
| 709 | tx := ceil(AFont.TextWidth(AText));
|
|---|
| 710 | ty := ceil(AFont.TextHeight(AText));
|
|---|
| 711 | marginHoriz := ty div 2;
|
|---|
| 712 | marginVert := 1;
|
|---|
| 713 | mask := BGRABitmapFactory.Create(tx+2*marginHoriz,ty+2*marginVert,BGRABlack);
|
|---|
| 714 | tempDest := Destination;
|
|---|
| 715 | tempTex := Texture;
|
|---|
| 716 | tempClearType:= AFont.ClearType;
|
|---|
| 717 | Destination := mask;
|
|---|
| 718 | Texture := nil;
|
|---|
| 719 | AFont.ClearType := false;
|
|---|
| 720 | DrawText(AText,AFont,marginHoriz,marginVert,BGRAWhite,[ftaTop,ftaLeft]);
|
|---|
| 721 | Destination := tempDest;
|
|---|
| 722 | Texture := tempTex;
|
|---|
| 723 | AFont.ClearType := tempClearType;
|
|---|
| 724 | mask.ConvertToLinearRGB;
|
|---|
| 725 | result := TBGRACustomTextEffect.Create(mask, true,tx,ty,point(-marginHoriz,-marginVert));
|
|---|
| 726 | finally
|
|---|
| 727 | FInCreateTextEffect:= false;
|
|---|
| 728 | end;
|
|---|
| 729 | end;
|
|---|
| 730 |
|
|---|
| 731 | destructor TBGRAFreeTypeDrawer.Destroy;
|
|---|
| 732 | begin
|
|---|
| 733 | FMask.Free;
|
|---|
| 734 | inherited Destroy;
|
|---|
| 735 | end;
|
|---|
| 736 |
|
|---|
| 737 | end.
|
|---|