You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

401 lines
14 KiB

  1. unit utsOpenGLUtils;
  2. {$IFDEF FPC}
  3. {$mode objfpc}{$H+}
  4. {$ENDIF}
  5. interface
  6. uses
  7. Classes, SysUtils,
  8. utsTypes, utsRenderer, utsImage, utsContext, utsUtils, utsChar;
  9. type
  10. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  11. TtsOpenGLRenderRef = class(TObject)
  12. public
  13. TextureID: Integer;
  14. Size: TtsPosition;
  15. TexMat: TtsMatrix4f;
  16. VertMat: TtsMatrix4f;
  17. constructor Create;
  18. end;
  19. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  20. PtsTextureUsageItem = ^TtsTextureUsageItem;
  21. TtsTextureUsageItem = packed record
  22. children: array[0..3] of PtsTextureUsageItem;
  23. end;
  24. PtsTextureTreeItem = ^TtsTextureTreeItem;
  25. TtsTextureTreeItem = packed record
  26. value: SmallInt;
  27. children: array[0..1] of PtsTextureTreeItem;
  28. ref: TtsOpenGLRenderRef;
  29. end;
  30. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  31. PtsFontTexture = ^TtsFontTexture;
  32. TtsFontTexture = packed record
  33. ID: Integer; // OpenGL texture ID
  34. Usage: PtsTextureTreeItem ; // tree of used texture space
  35. Next: PtsFontTexture; // next texture in list
  36. Prev: PtsFontTexture; // previouse texture in list
  37. Size: Integer; // size of this texture
  38. Count: Integer; // number of chars stored in this texture
  39. end;
  40. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  41. TtsBaseOpenGL = class(TtsRenderer)
  42. private
  43. fTextureSize: Integer;
  44. fColor: TtsColor4f;
  45. fRenderPos: TtsPosition;
  46. fFirstTexture: PtsFontTexture;
  47. fLastTexture: PtsFontTexture;
  48. procedure FreeTextures(var aTexture: PtsFontTexture);
  49. procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  50. protected
  51. property Color: TtsColor4f read fColor;
  52. property RenderPos: TtsPosition read fRenderPos;
  53. procedure PushTexture(const aTexture: PtsFontTexture);
  54. function CreateNewTexture: PtsFontTexture; virtual;
  55. procedure FreeTexture(var aTexture: PtsFontTexture); virtual;
  56. procedure UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X, Y: Integer); virtual;
  57. public
  58. function CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef; override;
  59. procedure FreeRenderRef(const aRenderRef: TtsRenderRef); override;
  60. protected
  61. procedure BeginRender; override;
  62. procedure EndRender; override;
  63. procedure SetDrawPos(const aValue: TtsPosition); override;
  64. function GetDrawPos: TtsPosition; override;
  65. procedure MoveDrawPos(const aOffset: TtsPosition); override;
  66. procedure SetColor(const aValue: TtsColor4f); override;
  67. public
  68. property TextureSize: Integer read fTextureSize write fTextureSize;
  69. constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
  70. destructor Destroy; override;
  71. end;
  72. EtsRendererOpenGL = class(EtsRenderer);
  73. implementation
  74. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  75. //TtsOpenGLRenderRef////////////////////////////////////////////////////////////////////////////////////////////////////
  76. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  77. constructor TtsOpenGLRenderRef.Create;
  78. begin
  79. inherited Create;
  80. TextureID := 0;
  81. FillChar(TexMat, SizeOf(TexMat), #0);
  82. FillChar(VertMat, SizeOf(VertMat), #0);
  83. FillChar(Size, SizeOf(Size), #0);
  84. end;
  85. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  86. //TtsBaseOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////////
  87. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  88. procedure TtsBaseOpenGL.FreeTextures(var aTexture: PtsFontTexture);
  89. begin
  90. if not Assigned(aTexture) then
  91. exit;
  92. FreeTextures(aTexture^.Next);
  93. FreeTexture(aTexture);
  94. end;
  95. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  96. procedure TtsBaseOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  97. begin
  98. if not Assigned(aItem) then
  99. exit;
  100. FreeTextureTreeItem(aItem^.children[0]);
  101. FreeTextureTreeItem(aItem^.children[1]);
  102. Dispose(aItem);
  103. aItem := nil;
  104. end;
  105. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  106. procedure TtsBaseOpenGL.PushTexture(const aTexture: PtsFontTexture);
  107. begin
  108. aTexture^.Prev := fLastTexture;
  109. if Assigned(fLastTexture) then
  110. fLastTexture^.Next := aTexture
  111. else
  112. fFirstTexture := aTexture;
  113. fLastTexture := aTexture;
  114. end;
  115. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  116. function TtsBaseOpenGL.CreateNewTexture: PtsFontTexture;
  117. begin
  118. result := nil;
  119. end;
  120. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  121. procedure TtsBaseOpenGL.FreeTexture(var aTexture: PtsFontTexture);
  122. begin
  123. if not Assigned(aTexture) then
  124. exit;
  125. FreeTextureTreeItem(aTexture^.Usage);
  126. if Assigned(aTexture^.Prev) then
  127. aTexture^.Prev^.Next := aTexture^.Next;
  128. if Assigned(aTexture^.Next) then
  129. aTexture^.Next^.Prev := aTexture^.Prev;
  130. Dispose(aTexture);
  131. if (fFirstTexture = aTexture) then begin
  132. fFirstTexture := nil;
  133. fLastTexture := nil;
  134. end;
  135. aTexture := nil;
  136. end;
  137. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  138. procedure TtsBaseOpenGL.UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X,
  139. Y: Integer);
  140. begin
  141. // DUMMY
  142. end;
  143. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  144. function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef;
  145. var
  146. GlyphWidth, GlyphHeight: Integer;
  147. function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
  148. var
  149. w, h: Integer;
  150. begin
  151. result := nil;
  152. w := X2 - X1;
  153. h := Y2 - Y1;
  154. if not Assigned(aItem) or
  155. Assigned(aItem^.ref) or
  156. (w < GlyphWidth) or
  157. (h < GlyphHeight) then
  158. exit;
  159. if (aItem^.value > 0) then begin
  160. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  161. if not Assigned(result) then
  162. result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
  163. end else if (aItem^.value < 0) then begin
  164. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
  165. if not Assigned(result) then
  166. result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
  167. end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
  168. X := X1;
  169. Y := Y1;
  170. result := aItem;
  171. end else begin
  172. new(aItem^.children[0]);
  173. new(aItem^.children[1]);
  174. FillChar(aItem^.children[0]^, SizeOf(aItem^.children[0]^), #0);
  175. FillChar(aItem^.children[1]^, SizeOf(aItem^.children[1]^), #0);
  176. if (w - GlyphWidth) < (h - GlyphHeight) then begin
  177. aItem^.value := Y1 + GlyphHeight;
  178. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  179. end else begin
  180. aItem^.value := -(X1 + GlyphWidth);
  181. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
  182. end;
  183. end;
  184. end;
  185. function AddToTexture(const aTexture: PtsFontTexture): TtsOpenGLRenderRef;
  186. var
  187. x, y, wChar, hChar, l, t: Integer;
  188. item: PtsTextureTreeItem;
  189. begin
  190. item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
  191. if not Assigned(item) then
  192. raise EtsRendererOpenGL.Create('unable to add glyph to texture');
  193. item^.ref := TtsOpenGLRenderRef.Create;
  194. result := item^.ref;
  195. wChar := aChar.GlyphMetric.GlyphRect.Right - aChar.GlyphMetric.GlyphRect.Left;
  196. hChar := aChar.GlyphMetric.GlyphRect.Bottom - aChar.GlyphMetric.GlyphRect.Top;
  197. l := aChar.GlyphMetric.GlyphRect.Left + x;
  198. t := aChar.GlyphMetric.GlyphRect.Top + y;
  199. result.TextureID := aTexture^.ID;
  200. result.Size := tsPosition(aImage.Width, aImage.Height);
  201. result.TexMat := tsMatrix4f(
  202. tsVector4f(wChar / aTexture^.Size, 0.0, 0.0, 0.0),
  203. tsVector4f(0.0, hChar / aTexture^.Size, 0.0, 0.0),
  204. tsVector4f(0.0, 0.0, 1.0, 0.0),
  205. tsVector4f(l / aTexture^.Size, t / aTexture^.Size, 0.0, 1.0));
  206. result.VertMat := tsMatrix4f(
  207. tsVector4f(wChar, 0.0, 0.0, 0.0),
  208. tsVector4f(0.0, hChar, 0.0, 0.0),
  209. tsVector4f(0.0, 0.0, 1.0, 0.0),
  210. tsVector4f(aChar.GlyphMetric.GlyphOrigin.x, -aChar.GlyphMetric.GlyphOrigin.y, 0.0, 1.0));
  211. UploadTexData(result, aImage, x, y);
  212. end;
  213. var
  214. tex: PtsFontTexture;
  215. begin
  216. result := nil;
  217. if aImage.IsEmpty then
  218. exit;
  219. GlyphWidth := aImage.Width + 1;
  220. GlyphHeight := aImage.Height + 1;
  221. // try to add to existing texture
  222. tex := fFirstTexture;
  223. while Assigned(tex) and not Assigned(result) do begin
  224. result := AddToTexture(tex);
  225. tex := tex^.Next;
  226. end;
  227. // create new texture
  228. if not Assigned(result) then begin
  229. if (aImage.Width > TextureSize) or (aImage.Height > TextureSize) then
  230. raise EtsRendererOpenGL.Create('char is to large to fit into a texture: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  231. tex := CreateNewTexture;
  232. result := AddToTexture(tex);
  233. end;
  234. if not Assigned(result) then
  235. raise EtsRendererOpenGL.Create('unable to creat render reference for char: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  236. end;
  237. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  238. procedure TtsBaseOpenGL.FreeRenderRef(const aRenderRef: TtsRenderRef);
  239. var
  240. ref: TtsOpenGLRenderRef;
  241. tex: PtsFontTexture;
  242. function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
  243. begin
  244. result :=
  245. Assigned(aItem) and
  246. not Assigned(aItem^.children[0]) and
  247. not Assigned(aItem^.children[1]) and
  248. not Assigned(aItem^.ref);
  249. end;
  250. function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
  251. var
  252. w, h: Integer;
  253. begin
  254. result := false;
  255. w := X2 - X1;
  256. h := Y2 - Y1;
  257. if not Assigned(aItem) or
  258. (w < ref.Size.x) or
  259. (h < ref.Size.y) then
  260. exit;
  261. result := (aItem^.ref = ref);
  262. if not result then begin
  263. if (aItem^.value > 0) then begin
  264. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
  265. result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
  266. end else if (aItem^.value < 0) then begin
  267. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  268. result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  269. end;
  270. end else
  271. aItem^.ref := nil;
  272. if result and
  273. IsEmtpy(aItem^.children[0]) and
  274. IsEmtpy(aItem^.children[1]) then
  275. begin
  276. FreeTextureTreeItem(aItem^.children[0]);
  277. FreeTextureTreeItem(aItem^.children[1]);
  278. FillChar(aItem^, SizeOf(aItem^), #0);
  279. end;
  280. end;
  281. begin
  282. ref := nil;
  283. try
  284. if not Assigned(aRenderRef) then
  285. exit;
  286. ref := TtsOpenGLRenderRef(aRenderRef);
  287. tex := fFirstTexture;
  288. while Assigned(tex) do begin
  289. if (tex^.ID = ref.TextureID) then begin
  290. if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
  291. raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
  292. if IsEmtpy(tex^.Usage) then
  293. FreeTexture(tex);
  294. tex := nil;
  295. end else
  296. tex := tex^.Next;
  297. end;
  298. finally
  299. FreeAndNil(ref);
  300. end;
  301. end;
  302. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  303. procedure TtsBaseOpenGL.BeginRender;
  304. begin
  305. fRenderPos.x := 0;
  306. fRenderPos.y := 0;
  307. end;
  308. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  309. procedure TtsBaseOpenGL.EndRender;
  310. begin
  311. // DUMMY
  312. end;
  313. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  314. procedure TtsBaseOpenGL.SetDrawPos(const aValue: TtsPosition);
  315. begin
  316. fRenderPos := aValue;
  317. end;
  318. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  319. function TtsBaseOpenGL.GetDrawPos: TtsPosition;
  320. begin
  321. result := fRenderPos;
  322. end;
  323. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  324. procedure TtsBaseOpenGL.MoveDrawPos(const aOffset: TtsPosition);
  325. begin
  326. fRenderPos.x := fRenderPos.x + aOffset.x;
  327. fRenderPos.y := fRenderPos.y + aOffset.y;
  328. end;
  329. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  330. procedure TtsBaseOpenGL.SetColor(const aValue: TtsColor4f);
  331. begin
  332. fColor := aValue;
  333. end;
  334. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  335. constructor TtsBaseOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  336. begin
  337. inherited Create(aContext, aFormat);
  338. fFirstTexture := nil;
  339. fLastTexture := nil;
  340. fTextureSize := 2048;
  341. fColor := tsColor4f(1, 1, 1, 1);
  342. fRenderPos := tsPosition(0, 0);
  343. end;
  344. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  345. destructor TtsBaseOpenGL.Destroy;
  346. begin
  347. FreeTextures(fFirstTexture);
  348. inherited Destroy;
  349. end;
  350. end.