No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

522 líneas
17 KiB

  1. unit utsRendererOpenGL;
  2. {$mode objfpc}{$H+}
  3. interface
  4. uses
  5. Classes, SysUtils, syncobjs, dglOpenGL,
  6. utsTextSuite, utsTypes;
  7. type
  8. TtsQuadPosF = array[0..3] of TtsPositionF;
  9. TtsCharRenderRefOpenGL = class(TtsCharRenderRef)
  10. private
  11. TextureID: GLint; // ID of OpenGL texture where the char is stored in
  12. TexCoordSize: TtsPositionF; // size of the char in texture coords (0.0 - 1.0)
  13. TexCoordPos: TtsPositionF; // position of the char in texture coords (0.0 - 1.0)
  14. VertexSize: TtsPositionF; // size of the char in world coords
  15. VertexPos: TtsPositionF; // size of the char in world coords
  16. public
  17. constructor Create;
  18. end;
  19. PtsTextureUsageItem = ^TtsTextureUsageItem;
  20. TtsTextureUsageItem = packed record
  21. children: array[0..3] of PtsTextureUsageItem;
  22. end;
  23. PtsTextureTreeItem = ^TtsTextureTreeItem;
  24. TtsTextureTreeItem = packed record
  25. value: SmallInt;
  26. children: array[0..1] of PtsTextureTreeItem;
  27. ref: TtsCharRenderRefOpenGL;
  28. end;
  29. PtsFontTexture = ^TtsFontTexture;
  30. TtsFontTexture = packed record
  31. ID: GLint; // OpenGL texture ID
  32. Usage: PtsTextureTreeItem ; // tree of used texture space
  33. Next: PtsFontTexture; // next texture in list
  34. Prev: PtsFontTexture; // previouse texture in list
  35. Size: Integer; // size of this texture
  36. Count: Integer; // number of chars stored in this texture
  37. end;
  38. TtsRendererOpenGL = class(TtsRenderer)
  39. private
  40. fVBO: GLuint;
  41. fTextureSize: Integer;
  42. fColor: TtsColor4f;
  43. fRenderPos: TtsPosition;
  44. fIsRendering: Boolean;
  45. fFirstTexture: PtsFontTexture;
  46. fLastTexture: PtsFontTexture;
  47. function CreateNewTexture: PtsFontTexture;
  48. procedure FreeTexture(var aTexture: PtsFontTexture);
  49. procedure FreeTextures(var aTexture: PtsFontTexture);
  50. procedure FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  51. protected
  52. function CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef; override;
  53. procedure FreeRenderRef(const aCharRef: TtsCharRenderRef); override;
  54. procedure BeginRender; override;
  55. procedure EndRender; override;
  56. procedure SetDrawPos(const X, Y: Integer); override;
  57. function GetDrawPos: TtsPosition; override;
  58. procedure MoveDrawPos(const X, Y: Integer); override;
  59. procedure SetColor(const aColor: TtsColor4f); override;
  60. procedure Render(const aCharRef: TtsCharRenderRef); override;
  61. public
  62. property TextureSize: Integer read fTextureSize write fTextureSize;
  63. constructor Create(const aContext: TtsContext; const aFormat: TtsFormat);
  64. destructor Destroy; override;
  65. end;
  66. EtsRendererOpenGL = class(EtsRenderer);
  67. implementation
  68. type
  69. TVertex = packed record
  70. pos: array[0..1] of GLfloat;
  71. tex: array[0..1] of GLfloat;
  72. end;
  73. const
  74. FORMAT_TYPES: array[TtsFormat] of packed record
  75. InternalFormat: GLenum;
  76. Format: GLenum;
  77. DataFormat: GLenum;
  78. end = (
  79. ( //tsFormatEmpty
  80. InternalFormat: 0;
  81. Format: 0;
  82. DataFormat: 0),
  83. ( //tsFormatRGBA8
  84. InternalFormat: GL_RGBA8;
  85. Format: GL_RGBA;
  86. DataFormat: GL_UNSIGNED_BYTE),
  87. ( //tsFormatLumAlpha8
  88. InternalFormat: GL_LUMINANCE8_ALPHA8;
  89. Format: GL_LUMINANCE_ALPHA;
  90. DataFormat: GL_UNSIGNED_BYTE),
  91. ( //tsFormatAlpha8
  92. InternalFormat: GL_ALPHA8;
  93. Format: GL_ALPHA;
  94. DataFormat: GL_UNSIGNED_BYTE)
  95. );
  96. VBO_DATA: array[0..3] of TVertex = (
  97. (pos: (0.0, 0.0); tex: (0.0, 0.0)),
  98. (pos: (0.0, 1.0); tex: (0.0, 1.0)),
  99. (pos: (1.0, 1.0); tex: (1.0, 1.0)),
  100. (pos: (1.0, 0.0); tex: (1.0, 0.0))
  101. );
  102. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  103. //TtsCharRenderRefOpenGL////////////////////////////////////////////////////////////////////////////////////////////////
  104. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  105. constructor TtsCharRenderRefOpenGL.Create;
  106. begin
  107. inherited Create;
  108. TextureID := 0;
  109. FillByte(TexCoordPos, SizeOf(TexCoordPos), 0);
  110. FillByte(TexCoordSize, SizeOf(TexCoordSize), 0);
  111. FillByte(VertexPos, SizeOf(VertexPos), 0);
  112. FillByte(VertexSize, SizeOf(VertexSize), 0);
  113. end;
  114. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  115. //TtsRendererOpenGL/////////////////////////////////////////////////////////////////////////////////////////////////////
  116. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  117. function TtsRendererOpenGL.CreateNewTexture: PtsFontTexture;
  118. begin
  119. new(result);
  120. try
  121. FillByte(result^, SizeOf(result^), 0);
  122. new(result^.Usage);
  123. FillByte(result^.Usage^, SizeOf(result^.Usage^), 0);
  124. result^.Size := TextureSize;
  125. glGenTextures(1, @result^.ID);
  126. glBindTexture(GL_TEXTURE_2D, result^.ID);
  127. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  128. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  129. glTexImage2D(
  130. GL_TEXTURE_2D,
  131. 0,
  132. FORMAT_TYPES[Format].InternalFormat,
  133. result^.Size,
  134. result^.Size,
  135. 0,
  136. FORMAT_TYPES[Format].Format,
  137. FORMAT_TYPES[Format].DataFormat,
  138. nil);
  139. result^.Prev := fLastTexture;
  140. if Assigned(fLastTexture) then
  141. fLastTexture^.Next := result
  142. else
  143. fFirstTexture := result;
  144. fLastTexture := result;
  145. except
  146. if Assigned(result^.Usage) then
  147. Dispose(result^.Usage);
  148. Dispose(result);
  149. end;
  150. end;
  151. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  152. procedure TtsRendererOpenGL.FreeTexture(var aTexture: PtsFontTexture);
  153. begin
  154. if not Assigned(aTexture) then
  155. exit;
  156. glDeleteTextures(1, @aTexture^.ID);
  157. FreeTextureTreeItem(aTexture^.Usage);
  158. if Assigned(aTexture^.Prev) then
  159. aTexture^.Prev^.Next := aTexture^.Next;
  160. if Assigned(aTexture^.Next) then
  161. aTexture^.Next^.Prev := aTexture^.Prev;
  162. Dispose(aTexture);
  163. aTexture := nil;
  164. end;
  165. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  166. procedure TtsRendererOpenGL.FreeTextures(var aTexture: PtsFontTexture);
  167. begin
  168. if not Assigned(aTexture) then
  169. exit;
  170. FreeTextures(aTexture^.Next);
  171. FreeTexture(aTexture);
  172. end;
  173. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  174. procedure TtsRendererOpenGL.FreeTextureTreeItem(var aItem: PtsTextureTreeItem);
  175. begin
  176. if not Assigned(aItem) then
  177. exit;
  178. FreeTextureTreeItem(aItem^.children[0]);
  179. FreeTextureTreeItem(aItem^.children[1]);
  180. Dispose(aItem);
  181. aItem := nil;
  182. end;
  183. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  184. function TtsRendererOpenGL.CreateRenderRef(const aChar: TtsChar; const aCharImage: TtsImage): TtsCharRenderRef;
  185. var
  186. GlyphWidth, GlyphHeight: Integer;
  187. function InsertToTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: SmallInt; out X, Y: Integer): PtsTextureTreeItem;
  188. var
  189. w, h: Integer;
  190. begin
  191. result := nil;
  192. w := X2 - X1;
  193. h := Y2 - Y1;
  194. if not Assigned(aItem) or
  195. Assigned(aItem^.ref) or
  196. (w < GlyphWidth) or
  197. (h < GlyphHeight) then
  198. exit;
  199. if (aItem^.value > 0) then begin
  200. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  201. if not Assigned(result) then
  202. result := InsertToTree(aItem^.children[1], X1, aItem^.value, X2, Y2, X, Y);
  203. end else if (aItem^.value < 0) then begin
  204. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y);
  205. if not Assigned(result) then
  206. result := InsertToTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2, X, Y);
  207. end else if (w = GlyphWidth) and (h = GlyphHeight) then begin
  208. X := X1;
  209. Y := Y1;
  210. result := aItem;
  211. end else begin
  212. new(aItem^.children[0]);
  213. new(aItem^.children[1]);
  214. FillByte(aItem^.children[0]^, SizeOf(aItem^.children[0]^), 0);
  215. FillByte(aItem^.children[1]^, SizeOf(aItem^.children[1]^), 0);
  216. if (w - GlyphWidth) < (h - GlyphHeight) then begin
  217. aItem^.value := Y1 + GlyphHeight;
  218. result := InsertToTree(aItem^.children[0], X1, Y1, X2, aItem^.value, X, Y);
  219. end else begin
  220. aItem^.value := -(X1 + GlyphWidth);
  221. result := InsertToTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2, X, Y)
  222. end;
  223. end;
  224. end;
  225. function AddToTexture(const aTexture: PtsFontTexture): TtsCharRenderRefOpenGL;
  226. var
  227. x, y: Integer;
  228. item: PtsTextureTreeItem;
  229. begin
  230. item := InsertToTree(aTexture^.Usage, 0, 0, aTexture^.Size, aTexture^.Size, x, y);
  231. if not Assigned(item) then
  232. raise EtsRendererOpenGL.Create('unable to add glyph to texture');
  233. item^.ref := TtsCharRenderRefOpenGL.Create;
  234. result := item^.ref;
  235. // Text Coords
  236. result.TextureID := aTexture^.ID;
  237. result.TexCoordPos.x := x / aTexture^.Size;
  238. result.TexCoordPos.y := y / aTexture^.Size;
  239. result.TexCoordSize.x := aCharImage.Width / aTexture^.Size;
  240. result.TexCoordSize.y := aCharImage.Height / aTexture^.Size;
  241. // Vertex Coords
  242. result.VertexPos.x := -aChar.GlyphRect.Left;
  243. result.VertexPos.y := -aChar.GlyphRect.Top - aChar.GlyphOrigin.y;
  244. result.VertexSize.x := aCharImage.Width;
  245. result.VertexSize.y := aCharImage.Height;
  246. glBindTexture(GL_TEXTURE_2D, result.TextureID);
  247. glTexSubImage2D(GL_TEXTURE_2D, 0,
  248. x, y, aCharImage.Width, aCharImage.Height,
  249. FORMAT_TYPES[aCharImage.Format].Format,
  250. FORMAT_TYPES[aCharImage.Format].DataFormat,
  251. aCharImage.Data);
  252. end;
  253. var
  254. tex: PtsFontTexture;
  255. begin
  256. result := nil;
  257. if aCharImage.IsEmpty then
  258. exit;
  259. GlyphWidth := aCharImage.Width + 1;
  260. GlyphHeight := aCharImage.Height + 1;
  261. // try to add to existing texture
  262. tex := fFirstTexture;
  263. while Assigned(tex) and not Assigned(result) do begin
  264. result := AddToTexture(tex);
  265. tex := tex^.Next;
  266. end;
  267. // create new texture
  268. if not Assigned(result) then begin
  269. if (aCharImage.Width > TextureSize) or (aCharImage.Height > TextureSize) then
  270. raise EtsRendererOpenGL.Create('char is to large to fit into a texture: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  271. tex := CreateNewTexture;
  272. result := AddToTexture(tex);
  273. end;
  274. if not Assigned(result) then
  275. raise EtsRendererOpenGL.Create('unable to creat render reference for char: ' + aChar.CharCode + ' (0x' + IntToHex(Ord(aChar.CharCode), 4) + ')');
  276. end;
  277. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  278. procedure TtsRendererOpenGL.FreeRenderRef(const aCharRef: TtsCharRenderRef);
  279. var
  280. ref: TtsCharRenderRefOpenGL;
  281. tex: PtsFontTexture;
  282. function IsEmtpy(const aItem: PtsTextureTreeItem): Boolean;
  283. begin
  284. result :=
  285. Assigned(aItem) and
  286. not Assigned(aItem^.children[0]) and
  287. not Assigned(aItem^.children[1]) and
  288. not Assigned(aItem^.ref);
  289. end;
  290. function RemoveFromTree(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer): Boolean;
  291. var
  292. w, h: Integer;
  293. begin
  294. w := X2 - X1;
  295. h := Y2 - Y1;
  296. if not Assigned(aItem) or
  297. (w < ref.VertexSize.x) or
  298. (h < ref.VertexSize.y) then
  299. exit;
  300. result := (aItem^.ref = ref);
  301. if not result then begin
  302. if (aItem^.value > 0) then begin
  303. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, X2, aItem^.value);
  304. result := result or RemoveFromTree(aItem^.children[1], X1, aItem^.value, X2, Y2);
  305. end else if (aItem^.value < 0) then begin
  306. result := result or RemoveFromTree(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  307. result := result or RemoveFromTree(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  308. end;
  309. end else
  310. aItem^.ref := nil;
  311. if result and
  312. IsEmtpy(aItem^.children[0]) and
  313. IsEmtpy(aItem^.children[1]) then
  314. begin
  315. FreeTextureTreeItem(aItem^.children[0]);
  316. FreeTextureTreeItem(aItem^.children[1]);
  317. FillByte(aItem^, SizeOf(aItem^), 0);
  318. end;
  319. end;
  320. begin
  321. try
  322. if not Assigned(aCharRef) or not (aCharRef is TtsCharRenderRefOpenGL) then
  323. exit;
  324. ref := (aCharRef as TtsCharRenderRefOpenGL);
  325. tex := fFirstTexture;
  326. while Assigned(tex) do begin
  327. if (tex^.ID = ref.TextureID) then begin
  328. if not RemoveFromTree(tex^.Usage, 0, 0, tex^.Size, tex^.Size) then
  329. raise EtsRendererOpenGL.Create('unable to remove render ref from texture');
  330. if IsEmtpy(tex^.Usage) then begin
  331. if (tex = fFirstTexture) then
  332. fFirstTexture := nil;
  333. FreeTexture(tex);
  334. end;
  335. tex := nil;
  336. end else
  337. tex := tex^.Next;
  338. end;
  339. finally
  340. if Assigned(aCharRef) then
  341. aCharRef.Free;
  342. end;
  343. end;
  344. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  345. procedure TtsRendererOpenGL.BeginRender;
  346. begin
  347. inherited BeginRender;
  348. fIsRendering := true;
  349. fRenderPos.x := 0;
  350. fRenderPos.y := 0;
  351. glPushMatrix;
  352. glColor4fv(@fColor.arr[0]);
  353. end;
  354. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  355. procedure TtsRendererOpenGL.EndRender;
  356. begin
  357. if fIsRendering then begin
  358. glPopMatrix;
  359. fIsRendering := false;
  360. end;
  361. inherited EndRender;
  362. end;
  363. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  364. procedure TtsRendererOpenGL.SetDrawPos(const X, Y: Integer);
  365. begin
  366. fRenderPos.x := X;
  367. fRenderPos.y := Y;
  368. glPopMatrix;
  369. glPushMatrix;
  370. glTranslatef(X, Y, 0);
  371. end;
  372. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  373. function TtsRendererOpenGL.GetDrawPos: TtsPosition;
  374. begin
  375. result := fRenderPos;
  376. end;
  377. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  378. procedure TtsRendererOpenGL.MoveDrawPos(const X, Y: Integer);
  379. begin
  380. fRenderPos.x := fRenderPos.x + X;
  381. fRenderPos.y := fRenderPos.y + Y;
  382. glTranslatef(X, Y, 0);
  383. end;
  384. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  385. procedure TtsRendererOpenGL.SetColor(const aColor: TtsColor4f);
  386. begin
  387. fColor := aColor;
  388. glColor4fv(@fColor.arr[0]);
  389. end;
  390. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  391. procedure TtsRendererOpenGL.Render(const aCharRef: TtsCharRenderRef);
  392. var
  393. ref: TtsCharRenderRefOpenGL;
  394. procedure RenderTreeItem(const aItem: PtsTextureTreeItem; const X1, Y1, X2, Y2: Integer);
  395. begin
  396. glBegin(GL_LINE_LOOP);
  397. glVertex2f(X1, Y1);
  398. glVertex2f(X2, Y1);
  399. glVertex2f(X2, Y2);
  400. glVertex2f(X1, Y2);
  401. glEnd;
  402. if (aItem^.value > 0) then begin
  403. RenderTreeItem(aItem^.children[0], X1, Y1, X2, aItem^.value);
  404. RenderTreeItem(aItem^.children[1], X1, aItem^.value, X2, Y2);
  405. end else if (aItem^.value < 0) then begin
  406. RenderTreeItem(aItem^.children[0], X1, Y1, -aItem^.value, Y2);
  407. RenderTreeItem(aItem^.children[1], -aItem^.value, Y1, X2, Y2);
  408. end;
  409. end;
  410. begin
  411. if Assigned(aCharRef) and (aCharRef is TtsCharRenderRefOpenGL) then begin
  412. ref := (aCharRef as TtsCharRenderRefOpenGL);
  413. glEnable(GL_TEXTURE_2D);
  414. glBindTexture(GL_TEXTURE_2D, ref.TextureID);
  415. glMatrixMode(GL_TEXTURE);
  416. glPushMatrix;
  417. glLoadIdentity;
  418. glTranslatef(ref.TexCoordPos.x, ref.TexCoordPos.y, 0);
  419. glScalef(ref.TexCoordSize.x, ref.TexCoordSize.y, 1);
  420. glMatrixMode(GL_MODELVIEW);
  421. glPushMatrix;
  422. glTranslatef(ref.VertexPos.x, ref.VertexPos.y, 0);
  423. glScalef(ref.VertexSize.x, ref.VertexSize.y, 1);
  424. glBindBuffer(GL_ARRAY_BUFFER, fVBO);
  425. glEnableClientState(GL_VERTEX_ARRAY);
  426. glVertexPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(0));
  427. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  428. glTexCoordPointer(2, GL_FLOAT, SizeOf(TVertex), Pointer(8));
  429. glDrawArrays(GL_QUADS, 0, 4);
  430. glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  431. glDisableClientState(GL_VERTEX_ARRAY);
  432. glMatrixMode(GL_TEXTURE);
  433. glPopMatrix;
  434. glMatrixMode(GL_MODELVIEW);
  435. glPopMatrix;
  436. end;
  437. end;
  438. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  439. constructor TtsRendererOpenGL.Create(const aContext: TtsContext; const aFormat: TtsFormat);
  440. begin
  441. inherited Create(aContext, aFormat);
  442. fIsRendering := false;
  443. fFirstTexture := nil;
  444. fLastTexture := nil;
  445. fTextureSize := 2048;
  446. fColor := tsColor4f(1, 1, 1, 1);
  447. fRenderPos := tsPosition(0, 0);
  448. glGenBuffers(1, @fVBO);
  449. glBindBuffer(GL_ARRAY_BUFFER, fVBO);
  450. glBufferData(GL_ARRAY_BUFFER, SizeOf(TVertex) * Length(VBO_DATA), @VBO_DATA[0].pos[0], GL_STATIC_DRAW);
  451. glBindBuffer(GL_ARRAY_BUFFER, 0);
  452. end;
  453. destructor TtsRendererOpenGL.Destroy;
  454. begin
  455. glDeleteBuffers(1, @fVBO);
  456. FreeTextures(fFirstTexture);
  457. inherited Destroy;
  458. end;
  459. end.