a174a3c5 |
1 | // Created on: 2013-01-29 |
2 | // Created by: Kirill GAVRILOV |
3 | // Copyright (c) 2013 OPEN CASCADE SAS |
4 | // |
5 | // The content of this file is subject to the Open CASCADE Technology Public |
6 | // License Version 6.5 (the "License"). You may not use the content of this file |
7 | // except in compliance with the License. Please obtain a copy of the License |
8 | // at http://www.opencascade.org and read it completely before using this file. |
9 | // |
10 | // The Initial Developer of the Original Code is Open CASCADE S.A.S., having its |
11 | // main offices at: 1, place des Freres Montgolfier, 78280 Guyancourt, France. |
12 | // |
13 | // The Original Code and all software distributed under the License is |
14 | // distributed on an "AS IS" basis, without warranty of any kind, and the |
15 | // Initial Developer hereby disclaims all such warranties, including without |
16 | // limitation, any warranties of merchantability, fitness for a particular |
17 | // purpose or non-infringement. Please see the License for the specific terms |
18 | // and conditions governing the rights and limitations under the License. |
19 | |
20 | #include <OpenGl_TextFormatter.hxx> |
21 | |
22 | #include <OpenGl_VertexBuffer.hxx> |
23 | |
24 | #include <cmath> |
25 | |
26 | namespace |
27 | { |
28 | |
29 | //! Auxiliary function to translate rectangle by the vector. |
30 | inline void move (Font_FTFont::Rect& theRect, |
31 | const OpenGl_Vec2& theVec) |
32 | { |
33 | theRect.Left += theVec.x(); |
34 | theRect.Right += theVec.x(); |
35 | theRect.Top += theVec.y(); |
36 | theRect.Bottom += theVec.y(); |
37 | } |
38 | |
39 | //! Auxiliary function to translate rectangles by the vector. |
40 | inline void move (NCollection_Vector<OpenGl_Font::Tile>& theRects, |
41 | const OpenGl_Vec2& theMoveVec, |
42 | Standard_Integer theCharLower, |
43 | const Standard_Integer theCharUpper) |
44 | { |
45 | for(; theCharLower <= theCharUpper; ++theCharLower) |
46 | { |
47 | Font_FTFont::Rect& aRect = theRects.ChangeValue (theCharLower).px; |
48 | move (aRect, theMoveVec); |
49 | } |
50 | } |
51 | |
52 | //! Auxiliary function to translate rectangles in horizontal direction. |
53 | inline void moveX (NCollection_Vector<OpenGl_Font::Tile>& theRects, |
54 | const Standard_ShortReal theMoveVec, |
55 | Standard_Integer theCharLower, |
56 | const Standard_Integer theCharUpper) |
57 | { |
58 | for (; theCharLower <= theCharUpper; ++theCharLower) |
59 | { |
60 | Font_FTFont::Rect& aRect = theRects.ChangeValue (theCharLower).px; |
61 | aRect.Left += theMoveVec; |
62 | aRect.Right += theMoveVec; |
63 | } |
64 | } |
65 | |
66 | //! Auxiliary function to translate rectangles in vertical direction. |
67 | inline void moveY (NCollection_Vector<OpenGl_Font::Tile>& theRects, |
68 | const Standard_ShortReal theMoveVec, |
69 | Standard_Integer theCharLower, |
70 | const Standard_Integer theCharUpper) |
71 | { |
72 | for(; theCharLower <= theCharUpper; ++theCharLower) |
73 | { |
74 | Font_FTFont::Rect& aRect = theRects.ChangeValue (theCharLower).px; |
75 | aRect.Top += theMoveVec; |
76 | aRect.Bottom += theMoveVec; |
77 | } |
78 | } |
79 | |
80 | //! Apply floor to vector components. |
81 | //! @param theVec - vector to change (by reference!) |
82 | //! @return modified vector |
83 | inline OpenGl_Vec2& floor (OpenGl_Vec2& theVec) |
84 | { |
85 | theVec.x() = std::floor (theVec.x()); |
86 | theVec.y() = std::floor (theVec.y()); |
87 | return theVec; |
88 | } |
89 | |
90 | }; |
91 | |
92 | IMPLEMENT_STANDARD_HANDLE (OpenGl_TextFormatter, Standard_Transient) |
93 | IMPLEMENT_STANDARD_RTTIEXT(OpenGl_TextFormatter, Standard_Transient) |
94 | |
95 | // ======================================================================= |
96 | // function : OpenGl_TextFormatter |
97 | // purpose : |
98 | // ======================================================================= |
99 | OpenGl_TextFormatter::OpenGl_TextFormatter() |
100 | : myAlignX (Graphic3d_HTA_LEFT), |
101 | myAlignY (Graphic3d_VTA_TOP), |
102 | myTabSize (8), |
103 | // |
104 | myPen (0.0f, 0.0f), |
105 | myRectsNb (0), |
106 | myLineSpacing (0.0f), |
107 | myAscender (0.0f), |
108 | myIsFormatted (false), |
109 | // |
110 | myLinesNb (0), |
111 | myRectLineStart (0), |
112 | myRectWordStart (0), |
113 | myPenCurrLine (0.0f), |
114 | myLineLeft (0.0f), |
115 | myLineTail (0.0f), |
116 | myBndTop (0.0f), |
117 | myBndWidth (0.0f), |
118 | myMoveVec (0.0f, 0.0f) |
119 | { |
120 | // |
121 | } |
122 | |
123 | // ======================================================================= |
124 | // function : SetupAlignment |
125 | // purpose : |
126 | // ======================================================================= |
127 | void OpenGl_TextFormatter::SetupAlignment (const Graphic3d_HorizontalTextAlignment theAlignX, |
128 | const Graphic3d_VerticalTextAlignment theAlignY) |
129 | { |
130 | myAlignX = theAlignX; |
131 | myAlignY = theAlignY; |
132 | } |
133 | |
134 | // ======================================================================= |
135 | // function : Reset |
136 | // purpose : |
137 | // ======================================================================= |
138 | void OpenGl_TextFormatter::Reset() |
139 | { |
140 | myIsFormatted = false; |
141 | myString.Clear(); |
142 | myPen.x() = myPen.y() = 0.0f; |
143 | myRectsNb = 0; |
144 | myLineSpacing = myAscender = 0.0f; |
145 | myRects.Clear(); |
146 | myNewLines.Clear(); |
147 | } |
148 | |
149 | // ======================================================================= |
150 | // function : Result |
151 | // purpose : |
152 | // ======================================================================= |
153 | void OpenGl_TextFormatter::Result (NCollection_Vector<GLuint>& theTextures, |
154 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > >& theVertsPerTexture, |
155 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > >& theTCrdsPerTexture) const |
156 | { |
157 | OpenGl_Vec2 aVec (0.0f, 0.0f); |
158 | theTextures.Clear(); |
159 | theVertsPerTexture.Clear(); |
160 | theTCrdsPerTexture.Clear(); |
161 | for (Standard_Integer aRectIter = 0; aRectIter < myRectsNb; ++aRectIter) |
162 | { |
163 | const Font_FTFont::Rect& aRect = myRects.Value (aRectIter).px; |
164 | const Font_FTFont::Rect& aRectUV = myRects.Value (aRectIter).uv; |
165 | const GLuint aTexture = myRects.Value (aRectIter).texture; |
166 | |
167 | Standard_Integer aListId = 0; |
168 | for (aListId = 0; aListId < theTextures.Length(); ++aListId) |
169 | { |
170 | if (theTextures.Value (aListId) == aTexture) |
171 | { |
172 | break; |
173 | } |
174 | } |
175 | if (aListId >= theTextures.Length()) |
176 | { |
177 | theTextures.Append (aTexture); |
178 | theVertsPerTexture.Append (new NCollection_Vector<OpenGl_Vec2>()); |
179 | theTCrdsPerTexture.Append (new NCollection_Vector<OpenGl_Vec2>()); |
180 | } |
181 | |
182 | NCollection_Vector<OpenGl_Vec2>& aVerts = *theVertsPerTexture.ChangeValue (aListId); |
183 | NCollection_Vector<OpenGl_Vec2>& aTCrds = *theTCrdsPerTexture.ChangeValue (aListId); |
184 | |
185 | // apply floor on position to avoid blurring issues |
186 | // due to cross-pixel coordinates |
187 | aVerts.Append (floor(aRect.BottomLeft (aVec))); |
188 | aVerts.Append (floor(aRect.TopLeft (aVec))); |
189 | aVerts.Append (floor(aRect.TopRight (aVec))); |
190 | aTCrds.Append (aRectUV.BottomLeft (aVec)); |
191 | aTCrds.Append (aRectUV.TopLeft (aVec)); |
192 | aTCrds.Append (aRectUV.TopRight (aVec)); |
193 | |
194 | aVerts.Append (floor(aRect.BottomLeft (aVec))); |
195 | aVerts.Append (floor(aRect.TopRight (aVec))); |
196 | aVerts.Append (floor(aRect.BottomRight (aVec))); |
197 | aTCrds.Append (aRectUV.BottomLeft (aVec)); |
198 | aTCrds.Append (aRectUV.TopRight (aVec)); |
199 | aTCrds.Append (aRectUV.BottomRight (aVec)); |
200 | } |
201 | } |
202 | |
203 | // ======================================================================= |
204 | // function : Result |
205 | // purpose : |
206 | // ======================================================================= |
207 | void OpenGl_TextFormatter::Result (const Handle(OpenGl_Context)& theCtx, |
208 | NCollection_Vector<GLuint>& theTextures, |
209 | NCollection_Vector<Handle(OpenGl_VertexBuffer)>& theVertsPerTexture, |
210 | NCollection_Vector<Handle(OpenGl_VertexBuffer)>& theTCrdsPerTexture) const |
211 | { |
212 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aVertsPerTexture; |
213 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aTCrdsPerTexture; |
214 | Result (theTextures, aVertsPerTexture, aTCrdsPerTexture); |
215 | |
216 | if (theVertsPerTexture.Length() != theTextures.Length()) |
217 | { |
218 | for (Standard_Integer aTextureIter = 0; aTextureIter < theVertsPerTexture.Length(); ++aTextureIter) |
219 | { |
220 | theVertsPerTexture.Value (aTextureIter)->Release (theCtx.operator->()); |
221 | theTCrdsPerTexture.Value (aTextureIter)->Release (theCtx.operator->()); |
222 | } |
223 | theVertsPerTexture.Clear(); |
224 | theTCrdsPerTexture.Clear(); |
225 | |
226 | while (theVertsPerTexture.Length() < theTextures.Length()) |
227 | { |
228 | Handle(OpenGl_VertexBuffer) aVertsVbo = new OpenGl_VertexBuffer(); |
229 | Handle(OpenGl_VertexBuffer) aTcrdsVbo = new OpenGl_VertexBuffer(); |
230 | theVertsPerTexture.Append (aVertsVbo); |
231 | theTCrdsPerTexture.Append (aTcrdsVbo); |
232 | aVertsVbo->Create (theCtx); |
233 | aTcrdsVbo->Create (theCtx); |
234 | } |
235 | } |
236 | |
237 | for (Standard_Integer aTextureIter = 0; aTextureIter < theTextures.Length(); ++aTextureIter) |
238 | { |
239 | const NCollection_Vector<OpenGl_Vec2>& aVerts = *aVertsPerTexture.Value (aTextureIter); |
240 | Handle(OpenGl_VertexBuffer)& aVertsVbo = theVertsPerTexture.ChangeValue (aTextureIter); |
241 | if (!aVertsVbo->Init (theCtx, 2, aVerts.Length(), (GLfloat* )NULL) |
242 | || !myVboEditor.Init (theCtx, aVertsVbo)) |
243 | { |
244 | continue; |
245 | } |
246 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter, myVboEditor.Next()) |
247 | { |
248 | myVboEditor.Value() = aVerts.Value (aVertIter); |
249 | } |
250 | myVboEditor.Flush(); |
251 | |
252 | const NCollection_Vector<OpenGl_Vec2>& aTCrds = *aTCrdsPerTexture.Value (aTextureIter); |
253 | Handle(OpenGl_VertexBuffer)& aTCrdsVbo = theTCrdsPerTexture.ChangeValue (aTextureIter); |
254 | if (!aTCrdsVbo->Init (theCtx, 2, aVerts.Length(), (GLfloat* )NULL) |
255 | || !myVboEditor.Init (theCtx, aTCrdsVbo)) |
256 | { |
257 | continue; |
258 | } |
259 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter, myVboEditor.Next()) |
260 | { |
261 | myVboEditor.Value() = aTCrds.Value (aVertIter); |
262 | } |
263 | myVboEditor.Flush(); |
264 | } |
265 | myVboEditor.Init (NULL, NULL); |
266 | } |
267 | |
268 | // ======================================================================= |
269 | // function : Result |
270 | // purpose : |
271 | // ======================================================================= |
35e08fe8 |
272 | void OpenGl_TextFormatter::Result (const Handle(OpenGl_Context)& /*theCtx*/, |
a174a3c5 |
273 | NCollection_Vector<GLuint>& theTextures, |
274 | NCollection_Vector<Handle(OpenGl_Vec2Array)>& theVertsPerTexture, |
275 | NCollection_Vector<Handle(OpenGl_Vec2Array)>& theTCrdsPerTexture) const |
276 | { |
277 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aVertsPerTexture; |
278 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aTCrdsPerTexture; |
279 | Result (theTextures, aVertsPerTexture, aTCrdsPerTexture); |
280 | |
281 | theVertsPerTexture.Clear(); |
282 | theTCrdsPerTexture.Clear(); |
283 | |
284 | for (Standard_Integer aTextureIter = 0; aTextureIter < theTextures.Length(); ++aTextureIter) |
285 | { |
286 | const NCollection_Vector<OpenGl_Vec2>& aVerts = *aVertsPerTexture.Value (aTextureIter); |
287 | const NCollection_Vector<OpenGl_Vec2>& aTCrds = *aTCrdsPerTexture.Value (aTextureIter); |
288 | Handle(OpenGl_Vec2Array) aVertsArray = new OpenGl_Vec2Array (1, aVerts.Length()); |
289 | Handle(OpenGl_Vec2Array) aTCrdsArray = new OpenGl_Vec2Array (1, aVerts.Length()); |
290 | theVertsPerTexture.Append (aVertsArray); |
291 | theTCrdsPerTexture.Append (aTCrdsArray); |
292 | |
293 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter) |
294 | { |
295 | aVertsArray->ChangeValue (aVertIter + 1) = aVerts.Value (aVertIter); |
296 | } |
297 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter) |
298 | { |
299 | aTCrdsArray->ChangeValue (aVertIter + 1) = aTCrds.Value (aVertIter); |
300 | } |
301 | } |
302 | } |
303 | |
304 | // ======================================================================= |
305 | // function : Append |
306 | // purpose : |
307 | // ======================================================================= |
308 | void OpenGl_TextFormatter::Append (const Handle(OpenGl_Context)& theCtx, |
309 | const NCollection_String& theString, |
310 | OpenGl_Font& theFont) |
311 | { |
312 | if (theString.IsEmpty()) |
313 | { |
314 | return; |
315 | } |
316 | |
317 | myAscender = Max (myAscender, theFont.Ascender()); |
318 | myLineSpacing = Max (myLineSpacing, theFont.LineSpacing()); |
319 | myString += theString; |
320 | |
321 | int aSymbolsCounter = 0; // special counter to process tabulation symbols |
322 | |
323 | // first pass - render all symbols using associated font on single ZERO baseline |
324 | OpenGl_Font::Tile aTile; |
325 | memset (&aTile, 0, sizeof(aTile)); |
326 | for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;) |
327 | { |
328 | const Standard_Utf32Char aCharThis = *anIter; |
329 | const Standard_Utf32Char aCharNext = *++anIter; |
330 | |
331 | if (aCharThis == '\x0D' // CR (carriage return) |
332 | || aCharThis == '\a' // BEL (alarm) |
333 | || aCharThis == '\f' // FF (form feed) NP (new page) |
334 | || aCharThis == '\b' // BS (backspace) |
335 | || aCharThis == '\v') // VT (vertical tab) |
336 | { |
337 | continue; // skip unsupported carriage control codes |
338 | } |
339 | else if (aCharThis == '\x0A') // LF (line feed, new line) |
340 | { |
341 | aSymbolsCounter = 0; |
342 | myNewLines.Append (myPen.x()); |
343 | continue; // will be processed on second pass |
344 | } |
345 | else if (aCharThis == ' ') |
346 | { |
347 | ++aSymbolsCounter; |
348 | myPen.x() += theFont.AdvanceX (' ', aCharNext); |
349 | continue; |
350 | } |
351 | else if (aCharThis == '\t') |
352 | { |
353 | const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize); |
354 | myPen.x() += theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum); |
355 | aSymbolsCounter += aSpacesNum; |
356 | continue; |
357 | } |
358 | |
359 | ++aSymbolsCounter; |
360 | theFont.RenderGlyph (theCtx, |
361 | aCharThis, aCharNext, |
362 | aTile, myPen); |
363 | myRects.Append (aTile); |
364 | |
365 | ++myRectsNb; |
366 | } |
367 | } |
368 | |
369 | // ======================================================================= |
370 | // function : newLine |
371 | // purpose : |
372 | // ======================================================================= |
373 | void OpenGl_TextFormatter::newLine (const Standard_Integer theLastRect) |
374 | { |
375 | if (myRectLineStart >= myRectsNb) |
376 | { |
377 | ++myLinesNb; |
378 | myPenCurrLine -= myLineSpacing; |
379 | return; |
380 | } |
381 | |
382 | myMoveVec.y() = myPenCurrLine; |
383 | switch (myAlignX) |
384 | { |
385 | default: |
386 | case Graphic3d_HTA_LEFT: |
387 | { |
388 | myMoveVec.x() = (myNewLineNb > 0) ? -myNewLines.Value (myNewLineNb - 1) : 0.0f; |
389 | break; |
390 | } |
391 | case Graphic3d_HTA_RIGHT: |
392 | { |
393 | myMoveVec.x() = (myNewLineNb < myNewLines.Length()) |
394 | ? -myNewLines.Value (myNewLineNb) |
395 | : -myPen.x(); |
396 | break; |
397 | } |
398 | case Graphic3d_HTA_CENTER: |
399 | { |
400 | const Standard_ShortReal aFrom = (myNewLineNb > 0) |
401 | ? myNewLines.Value (myNewLineNb - 1) |
402 | : 0.0f; |
403 | const Standard_ShortReal aTo = (myNewLineNb < myNewLines.Length()) |
404 | ? myNewLines.Value (myNewLineNb) |
405 | : myPen.x(); |
406 | myMoveVec.x() = -0.5f * (aFrom + aTo); |
407 | break; |
408 | } |
409 | } |
410 | |
411 | move (myRects, myMoveVec, myRectLineStart, theLastRect); |
412 | |
413 | ++myLinesNb; |
414 | myPenCurrLine -= myLineSpacing; |
415 | myRectLineStart = myRectWordStart = theLastRect + 1; |
416 | if (myRectLineStart < myRectsNb) |
417 | { |
418 | myLineLeft = myRects.Value (myRectLineStart).px.Left; |
419 | } |
420 | } |
421 | |
422 | // ======================================================================= |
423 | // function : Format |
424 | // purpose : |
425 | // ======================================================================= |
426 | void OpenGl_TextFormatter::Format() |
427 | { |
428 | if (myRectsNb == 0 || myIsFormatted) |
429 | { |
430 | return; |
431 | } |
432 | |
433 | myIsFormatted = true; |
434 | myLinesNb = myRectLineStart = myRectWordStart = 0; |
435 | myLineLeft = 0.0f; |
436 | myLineTail = 0.0f; |
437 | myBndTop = 0.0f; |
438 | myBndWidth = 0.0f; |
439 | myMoveVec.x() = myMoveVec.y() = 0.0f; |
440 | |
441 | // split text into lines and apply horizontal alignment |
442 | myPenCurrLine = -myAscender; |
443 | Standard_Integer aRectIter = 0; |
444 | myNewLineNb = 0; |
445 | for (NCollection_Utf8Iter anIter = myString.Iterator(); *anIter != 0; ++anIter) |
446 | { |
447 | const Standard_Utf32Char aCharThis = *anIter; |
448 | if (aCharThis == '\x0D' // CR (carriage return) |
449 | || aCharThis == '\a' // BEL (alarm) |
450 | || aCharThis == '\f' // FF (form feed) NP (new page) |
451 | || aCharThis == '\b' // BS (backspace) |
452 | || aCharThis == '\v') // VT (vertical tab) |
453 | { |
454 | continue; // skip unsupported carriage control codes |
455 | } |
456 | else if (aCharThis == '\x0A') // LF (line feed, new line) |
457 | { |
458 | const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line |
459 | newLine (aLastRect); |
460 | ++myNewLineNb; |
461 | continue; |
462 | } |
463 | else if (aCharThis == ' ' |
464 | || aCharThis == '\t') |
465 | { |
466 | myRectWordStart = aRectIter; |
467 | continue; |
468 | } |
469 | |
470 | Standard_ShortReal aWidth = myRects.Value (aRectIter).px.Right - myLineLeft; |
471 | myBndWidth = Max (myBndWidth, aWidth); |
472 | |
473 | ++aRectIter; |
474 | } |
475 | |
476 | // move last line |
477 | newLine (myRectsNb - 1); |
478 | |
479 | // apply vertical alignment style |
480 | if (myAlignY == Graphic3d_VTA_BOTTOM) |
481 | { |
482 | myBndTop = -myLineSpacing - myPenCurrLine; |
483 | moveY (myRects, myBndTop, 0, myRectsNb - 1); |
484 | } |
485 | else if (myAlignY == Graphic3d_VTA_CENTER) |
486 | { |
487 | myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb)); |
488 | moveY (myRects, myBndTop, 0, myRectsNb - 1); |
489 | } |
490 | } |