0024717: TKOpenGl - globally defined clipping planes blink when operating with view
[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
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),
c24d4017 109 myNewLineNb(0),
a174a3c5 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
7d3e64ef 223 const bool isNormalMode = theCtx->ToUseVbo();
224 Handle(OpenGl_VertexBuffer) aVertsVbo, aTcrdsVbo;
a174a3c5 225 while (theVertsPerTexture.Length() < theTextures.Length())
226 {
7d3e64ef 227 if (isNormalMode)
228 {
229 aVertsVbo = new OpenGl_VertexBuffer();
230 aTcrdsVbo = new OpenGl_VertexBuffer();
231 }
232 else
233 {
234 aVertsVbo = new OpenGl_VertexBufferCompat();
235 aTcrdsVbo = new OpenGl_VertexBufferCompat();
236 }
a174a3c5 237 theVertsPerTexture.Append (aVertsVbo);
238 theTCrdsPerTexture.Append (aTcrdsVbo);
239 aVertsVbo->Create (theCtx);
240 aTcrdsVbo->Create (theCtx);
241 }
242 }
243
244 for (Standard_Integer aTextureIter = 0; aTextureIter < theTextures.Length(); ++aTextureIter)
245 {
246 const NCollection_Vector<OpenGl_Vec2>& aVerts = *aVertsPerTexture.Value (aTextureIter);
247 Handle(OpenGl_VertexBuffer)& aVertsVbo = theVertsPerTexture.ChangeValue (aTextureIter);
248 if (!aVertsVbo->Init (theCtx, 2, aVerts.Length(), (GLfloat* )NULL)
249 || !myVboEditor.Init (theCtx, aVertsVbo))
250 {
251 continue;
252 }
253 for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter, myVboEditor.Next())
254 {
255 myVboEditor.Value() = aVerts.Value (aVertIter);
256 }
257 myVboEditor.Flush();
258
259 const NCollection_Vector<OpenGl_Vec2>& aTCrds = *aTCrdsPerTexture.Value (aTextureIter);
260 Handle(OpenGl_VertexBuffer)& aTCrdsVbo = theTCrdsPerTexture.ChangeValue (aTextureIter);
261 if (!aTCrdsVbo->Init (theCtx, 2, aVerts.Length(), (GLfloat* )NULL)
262 || !myVboEditor.Init (theCtx, aTCrdsVbo))
263 {
264 continue;
265 }
266 for (Standard_Integer aVertIter = 0; aVertIter < aVerts.Length(); ++aVertIter, myVboEditor.Next())
267 {
268 myVboEditor.Value() = aTCrds.Value (aVertIter);
269 }
270 myVboEditor.Flush();
271 }
272 myVboEditor.Init (NULL, NULL);
273}
274
a174a3c5 275// =======================================================================
276// function : Append
277// purpose :
278// =======================================================================
279void OpenGl_TextFormatter::Append (const Handle(OpenGl_Context)& theCtx,
280 const NCollection_String& theString,
281 OpenGl_Font& theFont)
282{
283 if (theString.IsEmpty())
284 {
285 return;
286 }
287
288 myAscender = Max (myAscender, theFont.Ascender());
289 myLineSpacing = Max (myLineSpacing, theFont.LineSpacing());
290 myString += theString;
291
292 int aSymbolsCounter = 0; // special counter to process tabulation symbols
293
294 // first pass - render all symbols using associated font on single ZERO baseline
295 OpenGl_Font::Tile aTile;
296 memset (&aTile, 0, sizeof(aTile));
297 for (NCollection_Utf8Iter anIter = theString.Iterator(); *anIter != 0;)
298 {
299 const Standard_Utf32Char aCharThis = *anIter;
300 const Standard_Utf32Char aCharNext = *++anIter;
301
302 if (aCharThis == '\x0D' // CR (carriage return)
303 || aCharThis == '\a' // BEL (alarm)
304 || aCharThis == '\f' // FF (form feed) NP (new page)
305 || aCharThis == '\b' // BS (backspace)
306 || aCharThis == '\v') // VT (vertical tab)
307 {
308 continue; // skip unsupported carriage control codes
309 }
310 else if (aCharThis == '\x0A') // LF (line feed, new line)
311 {
312 aSymbolsCounter = 0;
313 myNewLines.Append (myPen.x());
314 continue; // will be processed on second pass
315 }
316 else if (aCharThis == ' ')
317 {
318 ++aSymbolsCounter;
319 myPen.x() += theFont.AdvanceX (' ', aCharNext);
320 continue;
321 }
322 else if (aCharThis == '\t')
323 {
324 const Standard_Integer aSpacesNum = (myTabSize - (aSymbolsCounter - 1) % myTabSize);
325 myPen.x() += theFont.AdvanceX (' ', aCharNext) * Standard_ShortReal(aSpacesNum);
326 aSymbolsCounter += aSpacesNum;
327 continue;
328 }
329
330 ++aSymbolsCounter;
331 theFont.RenderGlyph (theCtx,
332 aCharThis, aCharNext,
333 aTile, myPen);
334 myRects.Append (aTile);
335
336 ++myRectsNb;
337 }
338}
339
340// =======================================================================
341// function : newLine
342// purpose :
343// =======================================================================
344void OpenGl_TextFormatter::newLine (const Standard_Integer theLastRect)
345{
346 if (myRectLineStart >= myRectsNb)
347 {
348 ++myLinesNb;
349 myPenCurrLine -= myLineSpacing;
350 return;
351 }
352
353 myMoveVec.y() = myPenCurrLine;
354 switch (myAlignX)
355 {
356 default:
357 case Graphic3d_HTA_LEFT:
358 {
359 myMoveVec.x() = (myNewLineNb > 0) ? -myNewLines.Value (myNewLineNb - 1) : 0.0f;
360 break;
361 }
362 case Graphic3d_HTA_RIGHT:
363 {
364 myMoveVec.x() = (myNewLineNb < myNewLines.Length())
365 ? -myNewLines.Value (myNewLineNb)
366 : -myPen.x();
367 break;
368 }
369 case Graphic3d_HTA_CENTER:
370 {
371 const Standard_ShortReal aFrom = (myNewLineNb > 0)
372 ? myNewLines.Value (myNewLineNb - 1)
373 : 0.0f;
374 const Standard_ShortReal aTo = (myNewLineNb < myNewLines.Length())
375 ? myNewLines.Value (myNewLineNb)
376 : myPen.x();
377 myMoveVec.x() = -0.5f * (aFrom + aTo);
378 break;
379 }
380 }
381
382 move (myRects, myMoveVec, myRectLineStart, theLastRect);
383
384 ++myLinesNb;
385 myPenCurrLine -= myLineSpacing;
386 myRectLineStart = myRectWordStart = theLastRect + 1;
387 if (myRectLineStart < myRectsNb)
388 {
389 myLineLeft = myRects.Value (myRectLineStart).px.Left;
390 }
391}
392
393// =======================================================================
394// function : Format
395// purpose :
396// =======================================================================
397void OpenGl_TextFormatter::Format()
398{
399 if (myRectsNb == 0 || myIsFormatted)
400 {
401 return;
402 }
403
404 myIsFormatted = true;
405 myLinesNb = myRectLineStart = myRectWordStart = 0;
406 myLineLeft = 0.0f;
407 myLineTail = 0.0f;
408 myBndTop = 0.0f;
409 myBndWidth = 0.0f;
410 myMoveVec.x() = myMoveVec.y() = 0.0f;
411
412 // split text into lines and apply horizontal alignment
413 myPenCurrLine = -myAscender;
414 Standard_Integer aRectIter = 0;
415 myNewLineNb = 0;
416 for (NCollection_Utf8Iter anIter = myString.Iterator(); *anIter != 0; ++anIter)
417 {
418 const Standard_Utf32Char aCharThis = *anIter;
419 if (aCharThis == '\x0D' // CR (carriage return)
420 || aCharThis == '\a' // BEL (alarm)
421 || aCharThis == '\f' // FF (form feed) NP (new page)
422 || aCharThis == '\b' // BS (backspace)
423 || aCharThis == '\v') // VT (vertical tab)
424 {
425 continue; // skip unsupported carriage control codes
426 }
427 else if (aCharThis == '\x0A') // LF (line feed, new line)
428 {
429 const Standard_Integer aLastRect = aRectIter - 1; // last rect on current line
430 newLine (aLastRect);
431 ++myNewLineNb;
432 continue;
433 }
434 else if (aCharThis == ' '
435 || aCharThis == '\t')
436 {
437 myRectWordStart = aRectIter;
438 continue;
439 }
440
441 Standard_ShortReal aWidth = myRects.Value (aRectIter).px.Right - myLineLeft;
442 myBndWidth = Max (myBndWidth, aWidth);
443
444 ++aRectIter;
445 }
446
447 // move last line
448 newLine (myRectsNb - 1);
449
450 // apply vertical alignment style
451 if (myAlignY == Graphic3d_VTA_BOTTOM)
452 {
453 myBndTop = -myLineSpacing - myPenCurrLine;
454 moveY (myRects, myBndTop, 0, myRectsNb - 1);
455 }
456 else if (myAlignY == Graphic3d_VTA_CENTER)
457 {
458 myBndTop = 0.5f * (myLineSpacing * Standard_ShortReal(myLinesNb));
459 moveY (myRects, myBndTop, 0, myRectsNb - 1);
460 }
461}