0023457: Slow text rendering
[occt.git] / src / OpenGl / OpenGl_TextFormatter.cxx
CommitLineData
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
26namespace
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
92IMPLEMENT_STANDARD_HANDLE (OpenGl_TextFormatter, Standard_Transient)
93IMPLEMENT_STANDARD_RTTIEXT(OpenGl_TextFormatter, Standard_Transient)
94
95// =======================================================================
96// function : OpenGl_TextFormatter
97// purpose :
98// =======================================================================
99OpenGl_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// =======================================================================
127void 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// =======================================================================
138void 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// =======================================================================
153void 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// =======================================================================
207void 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// =======================================================================
272void OpenGl_TextFormatter::Result (const Handle(OpenGl_Context)& theCtx,
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// =======================================================================
308void 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// =======================================================================
373void 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// =======================================================================
426void 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}