0024947: Redesign OCCT legacy type system -- automatic
[occt.git] / src / OpenGl / OpenGl_TextFormatter.cxx
CommitLineData
a174a3c5 1// Created on: 2013-01-29
2// Created by: Kirill GAVRILOV
d5f74e42 3// Copyright (c) 2013-2014 OPEN CASCADE SAS
a174a3c5 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
a174a3c5 6//
d5f74e42 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
973c2be1 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.
a174a3c5 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
a174a3c5 15
16#include <OpenGl_TextFormatter.hxx>
17
7d3e64ef 18#include <OpenGl_VertexBufferCompat.hxx>
a174a3c5 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.
30f0ad28 49 /*inline void moveX (NCollection_Vector<OpenGl_Font::Tile>& theRects,
a174a3c5 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 }
30f0ad28 60 }*/
a174a3c5 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
a174a3c5 88
89// =======================================================================
90// function : OpenGl_TextFormatter
91// purpose :
92// =======================================================================
93OpenGl_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),
c24d4017 107 myNewLineNb(0),
a174a3c5 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// =======================================================================
122void 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// =======================================================================
133void 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// =======================================================================
148void 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
a174a3c5 182 aVerts.Append (floor(aRect.TopRight (aVec)));
348ff753 183 aVerts.Append (floor(aRect.TopLeft (aVec)));
184 aVerts.Append (floor(aRect.BottomLeft (aVec)));
a174a3c5 185 aTCrds.Append (aRectUV.TopRight (aVec));
348ff753 186 aTCrds.Append (aRectUV.TopLeft (aVec));
187 aTCrds.Append (aRectUV.BottomLeft (aVec));
a174a3c5 188
a174a3c5 189 aVerts.Append (floor(aRect.BottomRight (aVec)));
348ff753 190 aVerts.Append (floor(aRect.TopRight (aVec)));
191 aVerts.Append (floor(aRect.BottomLeft (aVec)));
a174a3c5 192 aTCrds.Append (aRectUV.BottomRight (aVec));
348ff753 193 aTCrds.Append (aRectUV.TopRight (aVec));
194 aTCrds.Append (aRectUV.BottomLeft (aVec));
a174a3c5 195 }
196}
197
198// =======================================================================
199// function : Result
200// purpose :
201// =======================================================================
202void 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
7d3e64ef 221 const bool isNormalMode = theCtx->ToUseVbo();
222 Handle(OpenGl_VertexBuffer) aVertsVbo, aTcrdsVbo;
a174a3c5 223 while (theVertsPerTexture.Length() < theTextures.Length())
224 {
7d3e64ef 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 }
a174a3c5 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
a174a3c5 273// =======================================================================
274// function : Append
275// purpose :
276// =======================================================================
277void 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// =======================================================================
342void 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// =======================================================================
395void 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}