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