0024001: Stereographic rendering support
[occt.git] / src / OpenGl / OpenGl_TextFormatter.cxx
... / ...
CommitLineData
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
22namespace
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
88IMPLEMENT_STANDARD_HANDLE (OpenGl_TextFormatter, Standard_Transient)
89IMPLEMENT_STANDARD_RTTIEXT(OpenGl_TextFormatter, Standard_Transient)
90
91// =======================================================================
92// function : OpenGl_TextFormatter
93// purpose :
94// =======================================================================
95OpenGl_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// =======================================================================
124void 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// =======================================================================
135void 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// =======================================================================
150void 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// =======================================================================
204void 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// =======================================================================
269void 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// =======================================================================
305void 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// =======================================================================
370void 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// =======================================================================
423void 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}