GLWindowsFont.pas 10 KB

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