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