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