1 unit TAOpenGL;
2
3 {$mode objfpc}{$H+}
4
5 interface
6
7 uses
8 Classes, LazFileUtils, FPCanvas, FPImage, gl,
9 EasyLazFreeType, LazFreeTypeFPImageDrawer, LazFreeTypeFontCollection, TAFonts;
10
11 type
12 TTextureCacheItem = class
13 TextureID: Gluint;
14 TextWidth: Integer;
15 TextHeight: Integer;
16 end;
17
18 TGLFreeTypeHelper = class
19 private
20 FFont: TFreeTypeFont;
21 FImg: TFPMemoryImage;
22 FDrawer: TFPImageFreeTypeDrawer;
23 FTextureCache: TStringList;
24 protected
BuildTextureNamenull25 function BuildTextureName(AText: String): String;
26 procedure CreateTexture(AText: String; out ATextWidth, ATextHeight,
27 ATextureWidth, ATextureHeight: Integer; out ATextureID: GLuint);
FindTexturenull28 function FindTexture(AText: String; out ATextWidth, ATextHeight,
29 ATextureWidth, ATextureHeight: Integer): GLuint;
30 public
31 constructor Create;
32 destructor Destroy; override;
33 procedure RenderText(AText: String; Alignments: TFreeTypeAlignments);
34 procedure SetFont(AFontName: String; AFontSize: Integer;
35 ABold: Boolean = false; AItalic: Boolean = false;
36 AUnderline: Boolean = false; AStrikethrough: Boolean = false);
37 procedure TextExtent(AText: String; out AWidth, AHeight: Integer);
38 end;
39
40 var
41 GLFreeTypeHelper: TGLFreeTypeHelper = nil;
42
43
44 implementation
45
46 uses
47 SysUtils;
48
NextPowerOf2null49 function NextPowerOf2(n: Integer): Integer;
50 begin
51 Result := 1;
52 while Result < n do
53 Result := Result * 2;
54 end;
55
56
57 { TGLFreeTypeHelper }
58
59 constructor TGLFreeTypeHelper.Create;
60 begin
61 FImg := TFPMemoryImage.Create(8, 8); // dummy size, will be updated when needed
62 FDrawer := TFPImageFreeTypeDrawer.Create(FImg);
63 FTextureCache := TStringList.Create;
64 FTextureCache.Sorted := true;
65 end;
66
67 destructor TGLFreeTypeHelper.Destroy;
68 var
69 i: Integer;
70 item: TTextureCacheItem;
71 begin
72 for i:=0 to FTextureCache.Count-1 do begin
73 item := TTextureCacheItem(FTextureCache.Objects[i]);
74 glDeleteTextures(1, @item.TextureID);
75 item.Free;
76 end;
77 FTextureCache.Free;
78 if FFont <> nil then FFont.Free;
79 FDrawer.Free;
80 FImg.Free;
81 inherited;
82 end;
83
84 { The texture items are stored in the FTextureCache list and can be identified
85 by means of their name which is composed of the text and font parameters.
86 The name of the texture items is calculated here. }
TGLFreeTypeHelper.BuildTextureNamenull87 function TGLFreeTypeHelper.BuildTextureName(AText: String): String;
88 begin
89 Result := Format('%s|%s|%d|%s', [
90 AText, FFont.Family, round(FFont.SizeInPoints*100), FFont.StyleAsString
91 ]);
92 end;
93
94 procedure TGLFreeTypeHelper.CreateTexture(AText: String; out ATextWidth, ATextHeight,
95 ATextureWidth, ATextureHeight: Integer; out ATextureID: GLuint);
96 var
97 expanded_data: packed array of byte = nil;
98 i, j: Integer;
99 c: TFPColor;
100 begin
101 if FFont = nil then
102 raise Exception.Create('No font selected.');
103
104 ATextWidth := round(FFont.TextWidth(AText));
105 ATextHeight := round(FFont.TextHeight(AText));
106 ATextureWidth := NextPowerOf2(ATextWidth);
107 ATextureHeight := NextPowerOf2(ATextHeight);
108
109 FImg.SetSize(ATextureWidth, ATextureHeight);
110 FDrawer.FillPixels(colTransparent);
111 FDrawer.DrawText(AText, FFont, 0,0, colRed, [ftaLeft, ftaTop]);
112
113 SetLength(expanded_data, 2*ATextureWidth * ATextureHeight);
114 for j:=0 to ATextureHeight-1 do
115 for i:=0 to ATextureWidth-1 do
116 begin
117 expanded_data[2*(i + j*ATextureWidth)] := 255; // Luminosity
118 if (i > ATextWidth) or (j > ATextHeight) then
119 expanded_data[2*(i + j*ATextureWidth) + 1] := 0 // Alpha
120 else begin
121 c := FImg.Colors[i,j];
122 expanded_data[2*(i + j*ATextureWidth) + 1] := c.Alpha shr 8;
123 end;
124 end;
125
126 // Set up texture parameters
127 glGenTextures(1, @ATextureID);
128 glBindTexture(GL_TEXTURE_2D, ATextureID);
129
130 // Create the texture
131 // Note that we are using GL_LUMINANCE_ALPHA to indicate that we are using
132 // two-channel data
133 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, ATextureWidth, ATextureHeight, 0,
134 GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, @expanded_data[0]);
135
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
138 end;
139
140 { Finds the texture id for the given text. Returns the texture id and the
141 size of text and texture. Note that the texture size must be a power of 2 and
142 thus can be different from the text size. }
TGLFreeTypeHelper.FindTexturenull143 function TGLFreeTypeHelper.FindTexture(AText: String;
144 out ATextWidth, ATextHeight, ATextureWidth, ATextureHeight: Integer): GLuint;
145 var
146 idx: Integer;
147 item: TTextureCacheItem;
148 txname: String;
149 begin
150 txname := BuildTextureName(AText);
151 idx := FTextureCache.IndexOf(txname);
152 if idx = -1 then begin
153 CreateTexture(AText, ATextWidth, ATextHeight, ATextureWidth, ATextureHeight, Result);
154 item := TTextureCacheItem.Create;
155 item.TextureID := Result;
156 item.TextWidth := ATextWidth;
157 item.TextHeight := ATextHeight;
158 FTextureCache.AddObject(txname, item);
159 end else begin
160 item := TTextureCacheItem(FTextureCache.Objects[idx]);
161 result := item.TextureID;
162 ATextWidth := item.TextWidth;
163 ATextHeight := item.TextHeight;
164 ATextureWidth := NextPowerOf2(ATextWidth);
165 ATextureHeight := NextPowerOf2(ATextHeight);
166 end;
167 end;
168
169 procedure TGLFreeTypeHelper.RenderText(AText: String; Alignments: TFreeTypeAlignments);
170 var
171 textureID: GLuint;
172 w, h: Integer;
173 w2, h2: Integer;
174 sx, sy: Double;
175 dx, dy: Integer;
176 begin
177 textureID := FindTexture(AText, w, h, w2, h2);
178 sx := w / w2;
179 sy := h / h2;
180
181 glMatrixMode(GL_MODELVIEW);
182 glPushMatrix;
183 // Note: We don't support ftaJustify)
184 if (ftaCenter in Alignments) then dx := -w div 2
185 else if (ftaRight in ALignments) then dx := -w
186 else dx := 0;
187
188 if (ftaVerticalCenter in Alignments) then dy := -h div 2
189 else if (ftaBottom in Alignments) then dy := -h
190 else if (ftaBaseline in Alignments) then dy := - h + round(FFont.Descent)
191 else dy := 0;
192
193 glTranslatef(dx, dy, 0);
194 glEnable(GL_TEXTURE_2D);
195 glBindTexture(GL_TEXTURE_2D, textureID);
196 glBegin(GL_QUADS);
197 glTexCoord2f(0.0, sy); glVertex2f(0, h);
198 glTexCoord2f(sx, sy); glVertex2f(w, h);
199 glTexCoord2f(sx, 0.0); glVertex2f(w, 0);
200 glTexCoord2f(0.0, 0.0); glVertex2f(0, 0);
201 glEnd();
202 glDisable(GL_TEXTURE_2D);
203 glPopMatrix;
204 end;
205
206 procedure TGLFreeTypeHelper.SetFont(AFontName: String; AFontSize: Integer;
207 ABold: Boolean = false; AItalic: Boolean = false;
208 AUnderline: Boolean = false; AStrikethrough: Boolean = false);
209 var
210 style: TFreeTypeStyles;
211 begin
212 if GLFreeTypeHelper = nil then
213 raise Exception.Create('InitFonts has not been called.');
214
215 style := [];
216 if ABold then Include(style, ftsBold);
217 if AItalic then Include(style, ftsItalic);
218
219 // Create a new font if not yet loaded
220 if (FFont = nil) or (FFont.Family <> AFontName) or (FFont.Style <> style) then
221 begin
222 FreeAndNil(FFont);
223 FFont := LoadFont(AFontName, style);
224 if FFont = nil then
225 raise Exception.CreateFmt('Font "%s" not found.', [AFontName]);
226 end;
227
228 // Set the requested font attributes.
229 FFont.SizeInPoints := AFontSize;
230 FFont.UnderlineDecoration := AUnderline;
231 FFont.StrikeoutDecoration := AStrikethrough;
232 FFont.Hinted := true;
233 FFont.Quality := grqHighQuality;
234 //FFont.ClearType := true;
235 end;
236
237 { Returns the width and height of the specified text. If the text already has
238 been handled with the same font parameters it is stored in the FTextureCache
239 list. If not, the size is determined from the font. }
240 procedure TGLFreeTypeHelper.TextExtent(AText: String; out AWidth, AHeight: Integer);
241 var
242 txname: String;
243 idx: Integer;
244 item: TTextureCacheItem;
245 textureID: Gluint;
246 w2, h2: Integer;
247 begin
248 txname := BuildTextureName(AText);
249 idx := FTextureCache.IndexOf(txname);
250 if idx = -1 then begin
251 CreateTexture(AText, AWidth, AHeight, w2, h2, textureID);
252 item := TTextureCacheItem.Create;
253 item.TextureID := textureID;
254 item.TextWidth := AWidth;
255 item.TextHeight := AHeight;
256 idx := FTextureCache.AddObject(txname, item);
257 end;
258
259 item := TTextureCacheItem(FTextureCache.Objects[idx]);
260 AWidth := item.TextWidth;
261 AHeight := item.TextHeight;
262 end;
263
264 end.
265
266