2
0

GXS.WindowsFont.pas 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. //
  2. // The graphics engine GLXEngine. The unit of GXScene for Delphi
  3. //
  4. unit GXS.WindowsFont;
  5. (* TFont Import into a BitmapFont using variable width...*)
  6. interface
  7. {$I Stage.Defines.inc}
  8. uses
  9. Winapi.OpenGL,
  10. Winapi.OpenGLext,
  11. Winapi.Windows,
  12. System.Classes,
  13. System.Math,
  14. System.SysUtils,
  15. System.Types,
  16. System.UITypes,
  17. FMX.Graphics,
  18. GXS.Scene,
  19. GXS.Texture,
  20. GXS.BitmapFont,
  21. GXS.RenderContextInfo,
  22. GXS.VectorLists,
  23. Stage.Utils,
  24. Stage.VectorGeometry,
  25. GXS.ApplicationFileIO,
  26. Stage.VectorTypes;
  27. type
  28. (* A bitmap font automatically built from a TFont.
  29. It works like a TgxBitmapfont, you set ranges and which chars are assigned
  30. to which indexes, however here you also set the Font property to any TFont
  31. available to the system and it renders as close to that font
  32. as possible, on some font types this is 100% on some a slight difference
  33. in spacing can occur at most 1 pixel per char on some char combinations.
  34. Ranges must be sorted in ascending ASCII order and should not overlap.
  35. As the font texture is automatically layed out, the Ranges StartGlyphIdx
  36. property is ignored and replaced appropriately. *)
  37. TgxWindowsBitmapFont = class(TgxCustomBitmapFont)
  38. private
  39. FFont: TFont;
  40. procedure SetList(const AList : TgxIntegerList);
  41. protected
  42. procedure SetFont(value: TFont);
  43. procedure LoadWindowsFont; virtual;
  44. function StoreRanges: Boolean;
  45. procedure PrepareImage(var ARci: TgxRenderContextInfo); override;
  46. function TextureFormat: Integer; override;
  47. procedure StreamlineRanges;
  48. public
  49. constructor Create(AOwner: TComponent); override;
  50. destructor Destroy; override;
  51. procedure NotifyChange(Sender: TObject); override;
  52. function FontTextureWidth: Integer;
  53. function FontTextureHeight: Integer;
  54. procedure EnsureString(const s : String); overload;
  55. procedure EnsureChars(const AStart, AEnd: widechar);
  56. property Glyphs;
  57. published
  58. (* The font used to prepare the texture.
  59. Note: the font color is ignored. *)
  60. property Font: TFont read FFont write SetFont;
  61. property HSpace;
  62. property VSpace;
  63. property MagFilter;
  64. property MinFilter;
  65. property Ranges stored StoreRanges;
  66. end;
  67. // ------------------------------------------------------------------
  68. implementation
  69. // ------------------------------------------------------------------
  70. const
  71. cDefaultLast = '}';
  72. {$IFDEF MSWINDOWS}
  73. Var
  74. Win32PlatformIsUnicode : Boolean;
  75. {$ENDIF}
  76. // ------------------
  77. // ------------------ TgxWindowsBitmapFont ------------------
  78. // ------------------
  79. constructor TgxWindowsBitmapFont.Create(AOwner: TComponent);
  80. begin
  81. inherited;
  82. FFont := TFont.Create;
  83. // TODO : E2003 Undeclared identifier: 'Color'
  84. (*FFont.Color := TColors.White;*)
  85. FFont.OnChanged := NotifyChange;
  86. GlyphsAlpha := tiaAlphaFromIntensity;
  87. EnsureChars(' ', cDefaultLast);
  88. end;
  89. destructor TgxWindowsBitmapFont.Destroy;
  90. begin
  91. FFont.Free;
  92. Ranges.Clear;
  93. inherited;
  94. end;
  95. function TgxWindowsBitmapFont.FontTextureWidth: Integer;
  96. begin
  97. Result := Glyphs.Bitmap.Width;
  98. end;
  99. function TgxWindowsBitmapFont.FontTextureHeight: Integer;
  100. begin
  101. Result := Glyphs.Bitmap.Height;
  102. end;
  103. procedure TgxWindowsBitmapFont.SetFont(value: TFont);
  104. begin
  105. FFont.Assign(value);
  106. end;
  107. procedure TgxWindowsBitmapFont.NotifyChange(Sender: TObject);
  108. begin
  109. StreamlineRanges;
  110. FreeTextureHandle;
  111. InvalidateUsers;
  112. inherited;
  113. end;
  114. procedure TgxWindowsBitmapFont.LoadWindowsFont;
  115. procedure ComputeCharRects(bitmap: TBitmap);
  116. var
  117. px, py, cw, n, x, y: Integer;
  118. PaddedHeight : integer;
  119. buffer : array[0..2] of WideChar;
  120. p : PCharInfo;
  121. r : TRect;
  122. begin
  123. buffer[1] := WideChar(#32);
  124. buffer[2] := WideChar(#0);
  125. PaddedHeight:= CharHeight + GlyphsIntervalY;
  126. x := bitmap.Width; y := bitmap.Height;
  127. px := 0;
  128. py := 0;
  129. if y < CharHeight then px := x;
  130. p := @FChars[0];
  131. for n := 0 to CharacterCount - 1 do
  132. begin
  133. cw := p.w;
  134. if cw > 0 then
  135. begin
  136. Inc(cw, GlyphsIntervalX);
  137. if px + cw > x then
  138. begin
  139. px := 0;
  140. Inc(py, PaddedHeight);
  141. if py + PaddedHeight > y then
  142. begin
  143. py := bitmap.Height;
  144. y := py + TextureHeight;
  145. bitmap.Height := y;
  146. with bitmap.Canvas do
  147. begin
  148. { TODO : E2003 Undeclared identifier: 'Brush' }
  149. (*
  150. Brush.Style := bsSolid;
  151. Brush.Color := TColors.Black;
  152. *)
  153. { TODO : E2250 There is no overloaded version of 'FillRect', not enouph arguments }
  154. (*FillRect(Rect(0, py, x, y));*)
  155. end;
  156. end;
  157. end;
  158. if Assigned(bitmap) then
  159. begin
  160. //+1 makes right align (padding left);
  161. // I prefer padding right for compatibility with bitmap font...
  162. p.l := px;
  163. //should make it consistent, same as above
  164. p.t := py;
  165. r.Left := px;
  166. r.Top := py;
  167. r.Right := px + cw;
  168. r.Bottom := py + PaddedHeight;
  169. buffer[0] := TileIndexToChar(n);
  170. // Draw the Char, the trailing space is to properly handle the italics.
  171. // credits to the Unicode version of SynEdit for this function call. GPL/MPL as GLScene
  172. // TODO : E2003 Undeclared identifier: 'Handle'
  173. (*ExtTextOutW(bitmap.Canvas.Handle, p.l, p.t, ETO_CLIPPED, @r, buffer, 1, nil);*)
  174. end;
  175. Inc(px, cw);
  176. end
  177. else
  178. begin
  179. p.l := 0;
  180. p.t := 0;
  181. end;
  182. inc(p);
  183. end;
  184. end;
  185. // credits to the Unicode version of SynEdit for this function. GPL/MPL as GLScene
  186. function GetTextSize(DC: HDC; Str: PWideChar; Count: Integer): TSize;
  187. var tm: TTextMetricW;
  188. begin
  189. Result.cx := 0;
  190. Result.cy := 0;
  191. GetTextExtentPoint32W(DC, Str, Count, Result);
  192. if not Win32PlatformIsUnicode then
  193. begin
  194. GetTextMetricsW(DC, tm);
  195. if tm.tmPitchAndFamily and TMPF_TRUETYPE <> 0 then
  196. Result.cx := Result.cx - tm.tmOverhang
  197. else
  198. Result.cx := tm.tmAveCharWidth * Count;
  199. end;
  200. end;
  201. var
  202. bitmap: TBitmap;
  203. ch: widechar;
  204. i, cw, nbChars, n: Integer;
  205. begin
  206. InvalidateUsers;
  207. Glyphs.Bitmap.OnChange := nil;
  208. //accessing Bitmap might trigger onchange
  209. bitmap := Glyphs.Bitmap;
  210. bitmap.Height := 0;
  211. //due to lazarus doesn't properly support pixel formats
  212. // TODO : E2129 Cannot assign to a read-only property
  213. (*bitmap.PixelFormat := TPixelFormat.RGBA; //in VCL glpf32bit;*)
  214. with bitmap.Canvas do
  215. begin
  216. // TODO : E2129 Cannot assign to a read-only property
  217. (*
  218. Font := Self.Font;
  219. Font.Color := TColors.White;
  220. *)
  221. // get characters dimensions for the font
  222. // character size without padding; paddings are used from GlyphsInterval
  223. { TODO : E2250 There is no overloaded version of 'MaxInteger' that can be called with these arguments }
  224. (*
  225. CharWidth := Round(MaxInteger(TextWidth('M'), TextWidth('W'), TextWidth('_')));
  226. CharHeight := TextHeight('"_pI|,');
  227. *)
  228. // used for padding
  229. GlyphsIntervalX := 1;
  230. GlyphsIntervalY := 1;
  231. if TFontStyle.fsItalic in Font.Style then
  232. begin
  233. // italics aren't properly acknowledged in font width
  234. HSpaceFix := -(CharWidth div 3);
  235. CharWidth := CharWidth - HSpaceFix;
  236. end
  237. else
  238. HSpaceFix := 0;
  239. end;
  240. nbChars := CharacterCount;
  241. // Retrieve width of all characters (texture width)
  242. ResetCharWidths(0);
  243. n := 0;
  244. for i := 0 to nbChars - 1 do
  245. begin
  246. ch := TileIndexToChar(i);
  247. // TODO : E2003 Undeclared identifier: 'Handle'
  248. (*cw := GetTextSize(bitmap.canvas.Handle, @ch, 1).cx-HSpaceFix;*)
  249. n := n + cw + GlyphsIntervalX;
  250. SetCharWidths(i, cw);
  251. end;
  252. //try to make best guess...
  253. //~total pixels, including some waste (10%)
  254. n := n * (CharHeight + GlyphsIntervalY) * 11 div 10;
  255. TextureWidth := min(512, RoundUpToPowerOf2( round(sqrt(n)) ));
  256. TextureHeight := min(512, RoundUpToPowerOf2( n div TextureWidth));
  257. bitmap.Width := TextureWidth;
  258. ComputeCharRects(bitmap);
  259. FCharsLoaded := true;
  260. Glyphs.Bitmap.OnChange := OnGlyphsChanged;
  261. end;
  262. function TgxWindowsBitmapFont.StoreRanges: Boolean;
  263. begin
  264. Result := (Ranges.Count <> 1) or (Ranges[0].StartASCII[1] <> ' ') or (Ranges[0].StopASCII[1] <> cDefaultLast);
  265. end;
  266. type
  267. TFriendlyRange = class(TgxBitmapFontRange);
  268. procedure TgxWindowsBitmapFont.StreamlineRanges;
  269. var
  270. I, C: Integer;
  271. begin
  272. C := 0;
  273. for I := 0 to Ranges.Count - 1 do
  274. begin
  275. TFriendlyRange(Ranges[I]).FStartGlyphIdx := C;
  276. Inc(C, Ranges[I].CharCount);
  277. TFriendlyRange(Ranges[I]).FStopGlyphIdx := MaxInteger(C - 1, 0);
  278. end;
  279. end;
  280. procedure TgxWindowsBitmapFont.SetList(const AList: TgxIntegerList);
  281. var
  282. i : integer;
  283. f, n, s : integer;
  284. begin
  285. //add existing ranges
  286. for I := 0 to Ranges.Count - 1 do
  287. with Ranges.Items[I] do
  288. AList.AddSerie(integer(StartASCII[1]), 1, CharCount);
  289. AList.SortAndRemoveDuplicates;
  290. Ranges.Clear;
  291. Ranges.BeginUpdate;
  292. if AList.Count > 0 then
  293. begin
  294. i := 0;
  295. while (i < AList.Count) and (AList[i] < 32) do inc(i);
  296. while i < AList.Count do
  297. begin
  298. f := AList[i]; n := f; s := Ranges.CharacterCount;
  299. while (i < AList.Count) and (n = AList[i]) do
  300. begin
  301. inc(i);
  302. inc(n);
  303. end;
  304. Ranges.Add(widechar(f), widechar(pred(n))).StartGlyphIdx := s;
  305. end;
  306. end;
  307. Ranges.EndUpdate;
  308. TextureChanged;
  309. InvalidateUsers;
  310. end;
  311. //add characters to internal list
  312. procedure TgxWindowsBitmapFont.EnsureChars(const AStart, AEnd: widechar);
  313. var
  314. c : WideChar;
  315. ACharList : TgxIntegerList;
  316. begin
  317. ACharList := TgxIntegerList.Create;
  318. for c := AStart to AEnd do
  319. ACharList.Add(integer(c));
  320. SetList(ACharList);
  321. ACharList.Free;
  322. end;
  323. //add characters to internal list
  324. procedure TgxWindowsBitmapFont.EnsureString(const s: String);
  325. var
  326. i : Integer;
  327. ACharList : TgxIntegerList;
  328. begin
  329. ACharList := TgxIntegerList.Create;
  330. for i := Low(s) to High(s) do
  331. ACharList.Add(integer(s[i]));
  332. SetList(ACharList);
  333. ACharList.Free;
  334. end;
  335. procedure TgxWindowsBitmapFont.PrepareImage(var ARci: TgxRenderContextInfo);
  336. begin
  337. LoadWindowsFont;
  338. inherited PrepareImage(ARci);
  339. end;
  340. function TgxWindowsBitmapFont.TextureFormat: Integer;
  341. begin
  342. Result := GL_ALPHA;
  343. end;
  344. // ------------------------------------------------------------------
  345. initialization
  346. // ------------------------------------------------------------------
  347. Win32PlatformIsUnicode := (Win32Platform = VER_PLATFORM_WIN32_NT);
  348. RegisterClasses([TgxWindowsBitmapFont]);
  349. end.