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. |
30f0ad28 |
53 | /*inline void moveX (NCollection_Vector<OpenGl_Font::Tile>& theRects, |
a174a3c5 |
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 | } |
30f0ad28 |
64 | }*/ |
a174a3c5 |
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), |
c24d4017 |
113 | myNewLineNb(0), |
a174a3c5 |
114 | myPenCurrLine (0.0f), |
115 | myLineLeft (0.0f), |
116 | myLineTail (0.0f), |
117 | myBndTop (0.0f), |
118 | myBndWidth (0.0f), |
119 | myMoveVec (0.0f, 0.0f) |
120 | { |
121 | // |
122 | } |
123 | |
124 | // ======================================================================= |
125 | // function : SetupAlignment |
126 | // purpose : |
127 | // ======================================================================= |
128 | void OpenGl_TextFormatter::SetupAlignment (const Graphic3d_HorizontalTextAlignment theAlignX, |
129 | const Graphic3d_VerticalTextAlignment theAlignY) |
130 | { |
131 | myAlignX = theAlignX; |
132 | myAlignY = theAlignY; |
133 | } |
134 | |
135 | // ======================================================================= |
136 | // function : Reset |
137 | // purpose : |
138 | // ======================================================================= |
139 | void OpenGl_TextFormatter::Reset() |
140 | { |
141 | myIsFormatted = false; |
142 | myString.Clear(); |
143 | myPen.x() = myPen.y() = 0.0f; |
144 | myRectsNb = 0; |
145 | myLineSpacing = myAscender = 0.0f; |
146 | myRects.Clear(); |
147 | myNewLines.Clear(); |
148 | } |
149 | |
150 | // ======================================================================= |
151 | // function : Result |
152 | // purpose : |
153 | // ======================================================================= |
154 | void OpenGl_TextFormatter::Result (NCollection_Vector<GLuint>& theTextures, |
155 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > >& theVertsPerTexture, |
156 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > >& theTCrdsPerTexture) const |
157 | { |
158 | OpenGl_Vec2 aVec (0.0f, 0.0f); |
159 | theTextures.Clear(); |
160 | theVertsPerTexture.Clear(); |
161 | theTCrdsPerTexture.Clear(); |
162 | for (Standard_Integer aRectIter = 0; aRectIter < myRectsNb; ++aRectIter) |
163 | { |
164 | const Font_FTFont::Rect& aRect = myRects.Value (aRectIter).px; |
165 | const Font_FTFont::Rect& aRectUV = myRects.Value (aRectIter).uv; |
166 | const GLuint aTexture = myRects.Value (aRectIter).texture; |
167 | |
168 | Standard_Integer aListId = 0; |
169 | for (aListId = 0; aListId < theTextures.Length(); ++aListId) |
170 | { |
171 | if (theTextures.Value (aListId) == aTexture) |
172 | { |
173 | break; |
174 | } |
175 | } |
176 | if (aListId >= theTextures.Length()) |
177 | { |
178 | theTextures.Append (aTexture); |
179 | theVertsPerTexture.Append (new NCollection_Vector<OpenGl_Vec2>()); |
180 | theTCrdsPerTexture.Append (new NCollection_Vector<OpenGl_Vec2>()); |
181 | } |
182 | |
183 | NCollection_Vector<OpenGl_Vec2>& aVerts = *theVertsPerTexture.ChangeValue (aListId); |
184 | NCollection_Vector<OpenGl_Vec2>& aTCrds = *theTCrdsPerTexture.ChangeValue (aListId); |
185 | |
186 | // apply floor on position to avoid blurring issues |
187 | // due to cross-pixel coordinates |
188 | aVerts.Append (floor(aRect.BottomLeft (aVec))); |
189 | aVerts.Append (floor(aRect.TopLeft (aVec))); |
190 | aVerts.Append (floor(aRect.TopRight (aVec))); |
191 | aTCrds.Append (aRectUV.BottomLeft (aVec)); |
192 | aTCrds.Append (aRectUV.TopLeft (aVec)); |
193 | aTCrds.Append (aRectUV.TopRight (aVec)); |
194 | |
195 | aVerts.Append (floor(aRect.BottomLeft (aVec))); |
196 | aVerts.Append (floor(aRect.TopRight (aVec))); |
197 | aVerts.Append (floor(aRect.BottomRight (aVec))); |
198 | aTCrds.Append (aRectUV.BottomLeft (aVec)); |
199 | aTCrds.Append (aRectUV.TopRight (aVec)); |
200 | aTCrds.Append (aRectUV.BottomRight (aVec)); |
201 | } |
202 | } |
203 | |
204 | // ======================================================================= |
205 | // function : Result |
206 | // purpose : |
207 | // ======================================================================= |
208 | void OpenGl_TextFormatter::Result (const Handle(OpenGl_Context)& theCtx, |
209 | NCollection_Vector<GLuint>& theTextures, |
210 | NCollection_Vector<Handle(OpenGl_VertexBuffer)>& theVertsPerTexture, |
211 | NCollection_Vector<Handle(OpenGl_VertexBuffer)>& theTCrdsPerTexture) const |
212 | { |
213 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aVertsPerTexture; |
214 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aTCrdsPerTexture; |
215 | Result (theTextures, aVertsPerTexture, aTCrdsPerTexture); |
216 | |
217 | if (theVertsPerTexture.Length() != theTextures.Length()) |
218 | { |
219 | for (Standard_Integer aTextureIter = 0; aTextureIter < theVertsPerTexture.Length(); ++aTextureIter) |
220 | { |
221 | theVertsPerTexture.Value (aTextureIter)->Release (theCtx.operator->()); |
222 | theTCrdsPerTexture.Value (aTextureIter)->Release (theCtx.operator->()); |
223 | } |
224 | theVertsPerTexture.Clear(); |
225 | theTCrdsPerTexture.Clear(); |
226 | |
227 | while (theVertsPerTexture.Length() < theTextures.Length()) |
228 | { |
229 | Handle(OpenGl_VertexBuffer) aVertsVbo = new OpenGl_VertexBuffer(); |
230 | Handle(OpenGl_VertexBuffer) aTcrdsVbo = new OpenGl_VertexBuffer(); |
231 | theVertsPerTexture.Append (aVertsVbo); |
232 | theTCrdsPerTexture.Append (aTcrdsVbo); |
233 | aVertsVbo->Create (theCtx); |
234 | aTcrdsVbo->Create (theCtx); |
235 | } |
236 | } |
237 | |
238 | for (Standard_Integer aTextureIter = 0; aTextureIter < theTextures.Length(); ++aTextureIter) |
239 | { |
240 | const NCollection_Vector<OpenGl_Vec2>& aVerts = *aVertsPerTexture.Value (aTextureIter); |
241 | Handle(OpenGl_VertexBuffer)& aVertsVbo = theVertsPerTexture.ChangeValue (aTextureIter); |
242 | if (!aVertsVbo->Init (theCtx, 2, aVerts.Length(), (GLfloat* )NULL) |
243 | || !myVboEditor.Init (theCtx, aVertsVbo)) |
244 | { |
245 | continue; |
246 | } |
247 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter, myVboEditor.Next()) |
248 | { |
249 | myVboEditor.Value() = aVerts.Value (aVertIter); |
250 | } |
251 | myVboEditor.Flush(); |
252 | |
253 | const NCollection_Vector<OpenGl_Vec2>& aTCrds = *aTCrdsPerTexture.Value (aTextureIter); |
254 | Handle(OpenGl_VertexBuffer)& aTCrdsVbo = theTCrdsPerTexture.ChangeValue (aTextureIter); |
255 | if (!aTCrdsVbo->Init (theCtx, 2, aVerts.Length(), (GLfloat* )NULL) |
256 | || !myVboEditor.Init (theCtx, aTCrdsVbo)) |
257 | { |
258 | continue; |
259 | } |
260 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter, myVboEditor.Next()) |
261 | { |
262 | myVboEditor.Value() = aTCrds.Value (aVertIter); |
263 | } |
264 | myVboEditor.Flush(); |
265 | } |
266 | myVboEditor.Init (NULL, NULL); |
267 | } |
268 | |
269 | // ======================================================================= |
270 | // function : Result |
271 | // purpose : |
272 | // ======================================================================= |
35e08fe8 |
273 | void OpenGl_TextFormatter::Result (const Handle(OpenGl_Context)& /*theCtx*/, |
a174a3c5 |
274 | NCollection_Vector<GLuint>& theTextures, |
275 | NCollection_Vector<Handle(OpenGl_Vec2Array)>& theVertsPerTexture, |
276 | NCollection_Vector<Handle(OpenGl_Vec2Array)>& theTCrdsPerTexture) const |
277 | { |
278 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aVertsPerTexture; |
279 | NCollection_Vector< NCollection_Handle <NCollection_Vector <OpenGl_Vec2> > > aTCrdsPerTexture; |
280 | Result (theTextures, aVertsPerTexture, aTCrdsPerTexture); |
281 | |
282 | theVertsPerTexture.Clear(); |
283 | theTCrdsPerTexture.Clear(); |
284 | |
285 | for (Standard_Integer aTextureIter = 0; aTextureIter < theTextures.Length(); ++aTextureIter) |
286 | { |
287 | const NCollection_Vector<OpenGl_Vec2>& aVerts = *aVertsPerTexture.Value (aTextureIter); |
288 | const NCollection_Vector<OpenGl_Vec2>& aTCrds = *aTCrdsPerTexture.Value (aTextureIter); |
289 | Handle(OpenGl_Vec2Array) aVertsArray = new OpenGl_Vec2Array (1, aVerts.Length()); |
290 | Handle(OpenGl_Vec2Array) aTCrdsArray = new OpenGl_Vec2Array (1, aVerts.Length()); |
291 | theVertsPerTexture.Append (aVertsArray); |
292 | theTCrdsPerTexture.Append (aTCrdsArray); |
293 | |
294 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter) |
295 | { |
296 | aVertsArray->ChangeValue (aVertIter + 1) = aVerts.Value (aVertIter); |
297 | } |
298 | for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter) |
299 | { |
300 | aTCrdsArray->ChangeValue (aVertIter + 1) = aTCrds.Value (aVertIter); |
301 | } |
302 | } |
303 | } |
304 | |
305 | // ======================================================================= |
306 | // function : Append |
307 | // purpose : |
308 | // ======================================================================= |
309 | void OpenGl_TextFormatter::Append (const Handle(OpenGl_Context)& theCtx, |
310 | const NCollection_String& theString, |
311 | OpenGl_Font& theFont) |
312 | { |
313 | if (theString.IsEmpty()) |
314 | { |
315 | return; |
316 | } |
317 | |
318 | myAscender = Max (myAscender, theFont.Ascender()); |
319 | myLineSpacing = Max (myLineSpacing, theFont.LineSpacing()); |
320 | myString += theString; |
321 | |
322 | int aSymbolsCounter = 0; // special counter to process tabulation symbols |
323 | |
324 | // first pass - render all symbols using associated font on single ZERO baseline |
325 | OpenGl_Font::Tile aTile; |
326 | memset (&aTile, 0, sizeof(aTile)); |
327 | for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;) |
328 | { |
329 | const Standard_Utf32Char aCharThis = *anIter; |
330 | const Standard_Utf32Char aCharNext = *++anIter; |
331 | |
332 | if (aCharThis == '\x0D' // CR (carriage return) |
333 | || aCharThis == '\a' // BEL (alarm) |
334 | || aCharThis == '\f' // FF (form feed) NP (new page) |
335 | || aCharThis == '\b' // BS (backspace) |
336 | || aCharThis == '\v') // VT (vertical tab) |
337 | { |
338 | continue; // skip unsupported carriage control codes |
339 | } |
340 | else if (aCharThis == '\x0A') // LF (line feed, new line) |
341 | { |
342 | aSymbolsCounter = 0; |
343 | myNewLines.Append (myPen.x()); |
344 | continue; // will be processed on second pass |
345 | } |
346 | else if (aCharThis == ' ') |
347 | { |
348 | ++aSymbolsCounter; |
349 | myPen.x() += theFont.AdvanceX (' ', aCharNext); |
350 | continue; |
351 | } |
352 | else if (aCharThis == '\t') |
353 | { |
354 | const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize); |
355 | myPen.x() += theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum); |
356 | aSymbolsCounter += aSpacesNum; |
357 | continue; |
358 | } |
359 | |
360 | ++aSymbolsCounter; |
361 | theFont.RenderGlyph (theCtx, |
362 | aCharThis, aCharNext, |
363 | aTile, myPen); |
364 | myRects.Append (aTile); |
365 | |
366 | ++myRectsNb; |
367 | } |
368 | } |
369 | |
370 | // ======================================================================= |
371 | // function : newLine |
372 | // purpose : |
373 | // ======================================================================= |
374 | void OpenGl_TextFormatter::newLine (const Standard_Integer theLastRect) |
375 | { |
376 | if (myRectLineStart >= myRectsNb) |
377 | { |
378 | ++myLinesNb; |
379 | myPenCurrLine -= myLineSpacing; |
380 | return; |
381 | } |
382 | |
383 | myMoveVec.y() = myPenCurrLine; |
384 | switch (myAlignX) |
385 | { |
386 | default: |
387 | case Graphic3d_HTA_LEFT: |
388 | { |
389 | myMoveVec.x() = (myNewLineNb > 0) ? -myNewLines.Value (myNewLineNb - 1) : 0.0f; |
390 | break; |
391 | } |
392 | case Graphic3d_HTA_RIGHT: |
393 | { |
394 | myMoveVec.x() = (myNewLineNb < myNewLines.Length()) |
395 | ? -myNewLines.Value (myNewLineNb) |
396 | : -myPen.x(); |
397 | break; |
398 | } |
399 | case Graphic3d_HTA_CENTER: |
400 | { |
401 | const Standard_ShortReal aFrom = (myNewLineNb > 0) |
402 | ? myNewLines.Value (myNewLineNb - 1) |
403 | : 0.0f; |
404 | const Standard_ShortReal aTo = (myNewLineNb < myNewLines.Length()) |
405 | ? myNewLines.Value (myNewLineNb) |
406 | : myPen.x(); |
407 | myMoveVec.x() = -0.5f * (aFrom + aTo); |
408 | break; |
409 | } |
410 | } |
411 | |
412 | move (myRects, myMoveVec, myRectLineStart, theLastRect); |
413 | |
414 | ++myLinesNb; |
415 | myPenCurrLine -= myLineSpacing; |
416 | myRectLineStart = myRectWordStart = theLastRect + 1; |
417 | if (myRectLineStart < myRectsNb) |
418 | { |
419 | myLineLeft = myRects.Value (myRectLineStart).px.Left; |
420 | } |
421 | } |
422 | |
423 | // ======================================================================= |
424 | // function : Format |
425 | // purpose : |
426 | // ======================================================================= |
427 | void OpenGl_TextFormatter::Format() |
428 | { |
429 | if (myRectsNb == 0 || myIsFormatted) |
430 | { |
431 | return; |
432 | } |
433 | |
434 | myIsFormatted = true; |
435 | myLinesNb = myRectLineStart = myRectWordStart = 0; |
436 | myLineLeft = 0.0f; |
437 | myLineTail = 0.0f; |
438 | myBndTop = 0.0f; |
439 | myBndWidth = 0.0f; |
440 | myMoveVec.x() = myMoveVec.y() = 0.0f; |
441 | |
442 | // split text into lines and apply horizontal alignment |
443 | myPenCurrLine = -myAscender; |
444 | Standard_Integer aRectIter = 0; |
445 | myNewLineNb = 0; |
446 | for (NCollection_Utf8Iter anIter = myString.Iterator(); *anIter != 0; ++anIter) |
447 | { |
448 | const Standard_Utf32Char aCharThis = *anIter; |
449 | if (aCharThis == '\x0D' // CR (carriage return) |
450 | || aCharThis == '\a' // BEL (alarm) |
451 | || aCharThis == '\f' // FF (form feed) NP (new page) |
452 | || aCharThis == '\b' // BS (backspace) |
453 | || aCharThis == '\v') // VT (vertical tab) |
454 | { |
455 | continue; // skip unsupported carriage control codes |
456 | } |
457 | else if (aCharThis == '\x0A') // LF (line feed, new line) |
458 | { |
459 | const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line |
460 | newLine (aLastRect); |
461 | ++myNewLineNb; |
462 | continue; |
463 | } |
464 | else if (aCharThis == ' ' |
465 | || aCharThis == '\t') |
466 | { |
467 | myRectWordStart = aRectIter; |
468 | continue; |
469 | } |
470 | |
471 | Standard_ShortReal aWidth = myRects.Value (aRectIter).px.Right - myLineLeft; |
472 | myBndWidth = Max (myBndWidth, aWidth); |
473 | |
474 | ++aRectIter; |
475 | } |
476 | |
477 | // move last line |
478 | newLine (myRectsNb - 1); |
479 | |
480 | // apply vertical alignment style |
481 | if (myAlignY == Graphic3d_VTA_BOTTOM) |
482 | { |
483 | myBndTop = -myLineSpacing - myPenCurrLine; |
484 | moveY (myRects, myBndTop, 0, myRectsNb - 1); |
485 | } |
486 | else if (myAlignY == Graphic3d_VTA_CENTER) |
487 | { |
488 | myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb)); |
489 | moveY (myRects, myBndTop, 0, myRectsNb - 1); |
490 | } |
491 | } |