Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

394 lignes
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 SetDrawPos(const aValue: TtsPosition); override;
  63. function GetDrawPos: TtsPosition; override;
  64. procedure MoveDrawPos(const aOffset: TtsPosition); override;
  65. procedure SetColor(const aValue: TtsColor4f); override;
  66. public
  67. property TextureSize: Integer read fTextureSize write fTextureSize;
  68. constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
  69. destructor Destroy; override;
  70. end;
  71. EtsRendererOpenGL = class(EtsRenderer);
  72. implementation
  73. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  74. //TtsOpenGLRenderRef////////////////////////////////////////////////////////////////////////////////////////////////////
  75. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  76. constructor TtsOpenGLRenderRef.Create;
  77. begin
  78. inherited Create;
  79. TextureID := 0;
  80. FillChar(TexMat, SizeOf(TexMat), #0);
  81. FillChar(VertMat, SizeOf(VertMat), #0);
  82. FillChar(Size, SizeOf(Size), #0);
  83. end;
  84. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  85. //TtsBaseOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////////
  86. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  87. procedure TtsBaseOpenGL.FreeTextures(var aTexture: PtsFontTexture);
  88. begin
  89. if not Assigned(aTexture) then
  90. exit;
  91. FreeTextures(aTexture^.Next);
  92. FreeTexture(aTexture);
  93. end;
  94. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  95. procedure TtsBaseOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  96. begin
  97. if not Assigned(aItem) then
  98. exit;
  99. FreeTextureTreeItem(aItem^.children[0]);
  100. FreeTextureTreeItem(aItem^.children[1]);
  101. Dispose(aItem);
  102. aItem := nil;
  103. end;
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. procedure TtsBaseOpenGL.PushTexture(const aTexture: PtsFontTexture);
  106. begin
  107. aTexture^.Prev := fLastTexture;
  108. if Assigned(fLastTexture) then
  109. fLastTexture^.Next := aTexture
  110. else
  111. fFirstTexture := aTexture;
  112. fLastTexture := aTexture;
  113. end;
  114. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  115. function TtsBaseOpenGL.CreateNewTexture: PtsFontTexture;
  116. begin
  117. result := nil;
  118. end;
  119. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  120. procedure TtsBaseOpenGL.FreeTexture(var aTexture: PtsFontTexture);
  121. begin
  122. if not Assigned(aTexture) then
  123. exit;
  124. FreeTextureTreeItem(aTexture^.Usage);
  125. if Assigned(aTexture^.Prev) then
  126. aTexture^.Prev^.Next := aTexture^.Next;
  127. if Assigned(aTexture^.Next) then
  128. aTexture^.Next^.Prev := aTexture^.Prev;
  129. Dispose(aTexture);
  130. aTexture := nil;
  131. end;
  132. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  133. procedure TtsBaseOpenGL.UploadTexData(const aCharRef: TtsOpenGLRenderRef; const aCharImage: TtsImage; const X,
  134. Y: Integer);
  135. begin
  136. // DUMMY
  137. end;
  138. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  139. function TtsBaseOpenGL.CreateRenderRef(const aChar: TtsChar; const aImage: TtsImage): TtsRenderRef;
  140. var
  141. GlyphWidth, GlyphHeight: Integer;
  142. function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
  143. var
  144. w, h: Integer;
  145. begin
  146. result := nil;
  147. w := X2 - X1;
  148. h := Y2 - Y1;
  149. if not Assigned(aItem) or
  150. Assigned(aItem^.ref) or
  151. (w < GlyphWidth) or
  152. (h < GlyphHeight) then
  153. exit;
  154. if (aItem^.value > 0) then begin
  155. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  156. if not Assigned(result) then
  157. result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
  158. end else if (aItem^.value < 0) then begin
  159. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
  160. if not Assigned(result) then
  161. result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
  162. end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
  163. X := X1;
  164. Y := Y1;
  165. result := aItem;
  166. end else begin
  167. new(aItem^.children[0]);
  168. new(aItem^.children[1]);
  169. FillChar(aItem^.children[0]^, SizeOf(aItem^.children[0]^), #0);
  170. FillChar(aItem^.children[1]^, SizeOf(aItem^.children[1]^), #0);
  171. if (w - GlyphWidth) < (h - GlyphHeight) then begin
  172. aItem^.value := Y1 + GlyphHeight;
  173. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  174. end else begin
  175. aItem^.value := -(X1 + GlyphWidth);
  176. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
  177. end;
  178. end;
  179. end;
  180. function AddToTexture(const aTexture: PtsFontTexture): TtsOpenGLRenderRef;
  181. var
  182. x, y, wChar, hChar, l, t: Integer;
  183. item: PtsTextureTreeItem;
  184. begin
  185. item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
  186. if not Assigned(item) then
  187. raise EtsRendererOpenGL.Create('unable to add glyph to texture');
  188. item^.ref := TtsOpenGLRenderRef.Create;
  189. result := item^.ref;
  190. wChar := aChar.GlyphMetric.GlyphRect.Right - aChar.GlyphMetric.GlyphRect.Left;
  191. hChar := aChar.GlyphMetric.GlyphRect.Bottom - aChar.GlyphMetric.GlyphRect.Top;
  192. l := aChar.GlyphMetric.GlyphRect.Left + x;
  193. t := aChar.GlyphMetric.GlyphRect.Top + y;
  194. result.TextureID := aTexture^.ID;
  195. result.Size := tsPosition(aImage.Width, aImage.Height);
  196. result.TexMat := tsMatrix4f(
  197. tsVector4f(wChar / aTexture^.Size, 0.0, 0.0, 0.0),
  198. tsVector4f(0.0, hChar / aTexture^.Size, 0.0, 0.0),
  199. tsVector4f(0.0, 0.0, 1.0, 0.0),
  200. tsVector4f(l / aTexture^.Size, t / aTexture^.Size, 0.0, 1.0));
  201. result.VertMat := tsMatrix4f(
  202. tsVector4f(wChar, 0.0, 0.0, 0.0),
  203. tsVector4f(0.0, hChar, 0.0, 0.0),
  204. tsVector4f(0.0, 0.0, 1.0, 0.0),
  205. tsVector4f(aChar.GlyphMetric.GlyphOrigin.x, -aChar.GlyphMetric.GlyphOrigin.y, 0.0, 1.0));
  206. UploadTexData(result, aImage, x, y);
  207. end;
  208. var
  209. tex: PtsFontTexture;
  210. begin
  211. result := nil;
  212. if aImage.IsEmpty then
  213. exit;
  214. GlyphWidth := aImage.Width + 1;
  215. GlyphHeight := aImage.Height + 1;
  216. // try to add to existing texture
  217. tex := fFirstTexture;
  218. while Assigned(tex) and not Assigned(result) do begin
  219. result := AddToTexture(tex);
  220. tex := tex^.Next;
  221. end;
  222. // create new texture
  223. if not Assigned(result) then begin
  224. if (aImage.Width > TextureSize) or (aImage.Height > TextureSize) then
  225. raise EtsRendererOpenGL.Create('char is to large to fit into a texture: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  226. tex := CreateNewTexture;
  227. result := AddToTexture(tex);
  228. end;
  229. if not Assigned(result) then
  230. raise EtsRendererOpenGL.Create('unable to creat render reference for char: (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  231. end;
  232. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  233. procedure TtsBaseOpenGL.FreeRenderRef(const aRenderRef: TtsRenderRef);
  234. var
  235. ref: TtsOpenGLRenderRef;
  236. tex: PtsFontTexture;
  237. function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
  238. begin
  239. result :=
  240. Assigned(aItem) and
  241. not Assigned(aItem^.children[0]) and
  242. not Assigned(aItem^.children[1]) and
  243. not Assigned(aItem^.ref);
  244. end;
  245. function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
  246. var
  247. w, h: Integer;
  248. begin
  249. result := false;
  250. w := X2 - X1;
  251. h := Y2 - Y1;
  252. if not Assigned(aItem) or
  253. (w < ref.Size.x) or
  254. (h < ref.Size.y) then
  255. exit;
  256. result := (aItem^.ref = ref);
  257. if not result then begin
  258. if (aItem^.value > 0) then begin
  259. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
  260. result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
  261. end else if (aItem^.value < 0) then begin
  262. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  263. result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  264. end;
  265. end else
  266. aItem^.ref := nil;
  267. if result and
  268. IsEmtpy(aItem^.children[0]) and
  269. IsEmtpy(aItem^.children[1]) then
  270. begin
  271. FreeTextureTreeItem(aItem^.children[0]);
  272. FreeTextureTreeItem(aItem^.children[1]);
  273. FillChar(aItem^, SizeOf(aItem^), #0);
  274. end;
  275. end;
  276. begin
  277. ref := nil;
  278. try
  279. if not Assigned(aRenderRef) then
  280. exit;
  281. ref := TtsOpenGLRenderRef(aRenderRef);
  282. tex := fFirstTexture;
  283. while Assigned(tex) do begin
  284. if (tex^.ID = ref.TextureID) then begin
  285. if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
  286. raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
  287. if IsEmtpy(tex^.Usage) then begin
  288. if (tex = fFirstTexture) then
  289. fFirstTexture := nil;
  290. FreeTexture(tex);
  291. end;
  292. tex := nil;
  293. end else
  294. tex := tex^.Next;
  295. end;
  296. finally
  297. if Assigned(ref) then
  298. ref.Free;
  299. end;
  300. end;
  301. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  302. procedure TtsBaseOpenGL.BeginRender;
  303. begin
  304. fRenderPos.x := 0;
  305. fRenderPos.y := 0;
  306. end;
  307. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  308. procedure TtsBaseOpenGL.SetDrawPos(const aValue: TtsPosition);
  309. begin
  310. fRenderPos := aValue;
  311. end;
  312. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  313. function TtsBaseOpenGL.GetDrawPos: TtsPosition;
  314. begin
  315. result := fRenderPos;
  316. end;
  317. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  318. procedure TtsBaseOpenGL.MoveDrawPos(const aOffset: TtsPosition);
  319. begin
  320. fRenderPos.x := fRenderPos.x + aOffset.x;
  321. fRenderPos.y := fRenderPos.y + aOffset.y;
  322. end;
  323. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  324. procedure TtsBaseOpenGL.SetColor(const aValue: TtsColor4f);
  325. begin
  326. fColor := aValue;
  327. end;
  328. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  329. constructor TtsBaseOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  330. begin
  331. inherited Create(aContext, aFormat);
  332. fFirstTexture := nil;
  333. fLastTexture := nil;
  334. fTextureSize := 2048;
  335. fColor := tsColor4f(1, 1, 1, 1);
  336. fRenderPos := tsPosition(0, 0);
  337. end;
  338. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  339. destructor TtsBaseOpenGL.Destroy;
  340. begin
  341. FreeTextures(fFirstTexture);
  342. inherited Destroy;
  343. end;
  344. end.