1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkOpenGLFreeTypeTextMapper.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15 #include "vtkOpenGLFreeTypeTextMapper.h"
16
17 #include "vtkActor2D.h"
18 #include "vtkObjectFactory.h"
19 #include "vtkProperty2D.h"
20 #include "vtkTextProperty.h"
21 #include "vtkViewport.h"
22 #include "vtkWindow.h"
23 #include "vtkOpenGLError.h"
24
25 #include "vtkFreeTypeUtilities.h"
26 #include "vtkftglConfig.h"
27
28 #include "vtkgluPickMatrix.h"
29
30 #include "FTFont.h"
31
32 #ifdef FTGL_USE_NAMESPACE
33 using namespace ftgl;
34 #endif
35
36 namespace {
GetNumberOfLinesImpl(const char * str)37 inline int GetNumberOfLinesImpl(const char *str)
38 {
39 if (str == NULL || *str == '\0')
40 {
41 return 0;
42 }
43
44 int result = 1;
45 while (str != NULL)
46 {
47 if ((str = strstr(str, "\n")) != NULL)
48 {
49 result++;
50 str++; // Skip '\n'
51 }
52 }
53 return result;
54 }
55 }
56
57 //----------------------------------------------------------------------------
58 // Print debug info
59
60 #define VTK_FTTM_DEBUG 0
61
62 //----------------------------------------------------------------------------
63 // GL2PS related internal helper functions.
64
65 //----------------------------------------------------------------------------
66 vtkStandardNewMacro(vtkOpenGLFreeTypeTextMapper);
67
68 //----------------------------------------------------------------------------
vtkOpenGLFreeTypeTextMapper()69 vtkOpenGLFreeTypeTextMapper::vtkOpenGLFreeTypeTextMapper()
70 {
71 this->LastSize[0] = 0;
72 this->LastSize[1] = 0;
73 this->TextLines = NULL;
74 this->NumberOfLines = 0;
75 this->NumberOfLinesAllocated = 0;
76 }
77
78 //----------------------------------------------------------------------------
~vtkOpenGLFreeTypeTextMapper()79 vtkOpenGLFreeTypeTextMapper::~vtkOpenGLFreeTypeTextMapper()
80 {
81 if (this->TextLines != NULL)
82 {
83 for (int i=0; i < this->NumberOfLinesAllocated; i++)
84 {
85 this->TextLines[i]->Delete();
86 }
87 delete [] this->TextLines;
88 }
89
90 if (this->LastWindow)
91 {
92 this->ReleaseGraphicsResources(this->LastWindow);
93 }
94 }
95
96 //----------------------------------------------------------------------------
NextLine(const char * input,int lineNum)97 char *vtkOpenGLFreeTypeTextMapper::NextLine(const char *input, int lineNum)
98 {
99 const char *ptr, *ptrEnd;
100 int strLen;
101 char *line;
102
103 ptr = input;
104 for (int i=0; i != lineNum; i++)
105 {
106 ptr = strstr(ptr,"\n");
107 ptr++;
108 }
109 ptrEnd = strstr(ptr,"\n");
110 if ( ptrEnd == NULL )
111 {
112 ptrEnd = strchr(ptr, '\0');
113 }
114
115 strLen = ptrEnd - ptr;
116 line = new char[strLen+1];
117 strncpy(line, ptr, strLen);
118 line[strLen] = '\0';
119
120 return line;
121 }
122
123 //----------------------------------------------------------------------------
GetMultiLineSize(vtkViewport * viewport,int size[])124 void vtkOpenGLFreeTypeTextMapper::GetMultiLineSize(vtkViewport *viewport,
125 int size[])
126 {
127 int i;
128 int lineSize[2];
129
130 vtkTextProperty *tprop = this->GetTextProperty();
131 if (!tprop)
132 {
133 vtkErrorMacro(<<"Need text property to get multiline size of mapper");
134 size[0] = size[1] = 0;
135 return;
136 }
137
138 lineSize[0] = lineSize[1] = size[0] = size[1] = 0;
139 for ( i=0; i < this->NumberOfLines; i++ )
140 {
141 this->TextLines[i]->GetTextProperty()->ShallowCopy(tprop);
142 this->TextLines[i]->GetSize(viewport, lineSize);
143 size[0] = (lineSize[0] > size[0] ? lineSize[0] : size[0]);
144 size[1] = (lineSize[1] > size[1] ? lineSize[1] : size[1]);
145 }
146
147 // add in the line spacing
148 this->LineSize = size[1];
149 size[1] = static_cast<int>(
150 size[1] * (1.0 + (this->NumberOfLines - 1) * tprop->GetLineSpacing()));
151 }
152
153 //----------------------------------------------------------------------------
RenderOverlayMultipleLines(vtkViewport * viewport,vtkActor2D * actor)154 void vtkOpenGLFreeTypeTextMapper::RenderOverlayMultipleLines(
155 vtkViewport *viewport, vtkActor2D *actor)
156 {
157 float offset = 0.0f;
158 int size[2];
159 // make sure LineSize is up to date
160 this->GetMultiLineSize(viewport,size);
161
162 vtkTextProperty *tprop = this->GetTextProperty();
163 if (!tprop)
164 {
165 vtkErrorMacro(<<"Need text property to render multiple lines of mapper");
166 return;
167 }
168
169 switch (tprop->GetVerticalJustification())
170 {
171 case VTK_TEXT_TOP:
172 offset = 0.0f;
173 break;
174 case VTK_TEXT_CENTERED:
175 offset = (-this->NumberOfLines + 1.0f) / 2.0f;
176 break;
177 case VTK_TEXT_BOTTOM:
178 offset = -this->NumberOfLines + 1.0f;
179 break;
180 }
181
182 for (int lineNum=0; lineNum < this->NumberOfLines; lineNum++)
183 {
184 this->TextLines[lineNum]->GetTextProperty()->ShallowCopy(tprop);
185 this->TextLines[lineNum]->GetTextProperty()->SetLineOffset
186 (tprop->GetLineOffset() +
187 static_cast<int>(this->LineSize * (lineNum + offset)
188 * tprop->GetLineSpacing()));
189 this->TextLines[lineNum]->RenderOverlay(viewport,actor);
190 }
191 }
192
193 //----------------------------------------------------------------------------
ReleaseGraphicsResources(vtkWindow *)194 void vtkOpenGLFreeTypeTextMapper::ReleaseGraphicsResources(vtkWindow *)
195 {
196 #if VTK_FTTM_DEBUG
197 printf("vtkOpenGLFreeTypeTextMapper::ReleaseGraphicsResources\n");
198 #endif
199
200 this->LastWindow = NULL;
201
202 // Very important
203 // the release of graphics resources indicates that significant changes have
204 // occurred. Old fonts, cached sizes etc are all no longer valid, so we send
205 // ourselves a general modified message.
206
207 // this->Modified();
208 }
209
210 //----------------------------------------------------------------------------
GetSize(vtkViewport * viewport,int * size)211 void vtkOpenGLFreeTypeTextMapper::GetSize(vtkViewport* viewport, int *size)
212 {
213 // Check for multiline
214
215 if (this->NumberOfLines > 1)
216 {
217 this->GetMultiLineSize(viewport, size);
218 return;
219 }
220
221 // Check for input
222
223 if (this->Input == NULL || this->Input[0] == '\0')
224 {
225 size[0] = size[1] = 0;
226 return;
227 }
228
229 vtkTextProperty *tprop = this->GetTextProperty();
230 if (!tprop)
231 {
232 vtkErrorMacro(<< "Need a text property to get size");
233 size[0] = size[1] = 0;
234 return;
235 }
236
237 // Check to see whether we have to rebuild anything
238
239 if (this->GetMTime() < this->SizeBuildTime &&
240 tprop->GetMTime() < this->SizeBuildTime)
241 {
242 #if VTK_FTTM_DEBUG
243 printf("vtkOpenGLFreeTypeTextMapper::GetSize: In cache!\n");
244 #endif
245
246 size[0] = this->LastSize[0];
247 size[1] = this->LastSize[1];
248 return;
249 }
250
251 // Check for font and try to set the size
252
253 vtkFreeTypeUtilities::Entry *entry =
254 vtkFreeTypeUtilities::GetInstance()->GetFont(tprop);
255 FTFont *font = entry ? entry->Font : NULL;
256 if (!font)
257 {
258 vtkErrorMacro(<< "Render - No font");
259 size[0] = size[1] = 0;
260 return;
261 }
262
263 // The font global ascender and descender might just be too high
264 // for given a face. Let's get a compromise by computing these values
265 // from some usual ascii chars.
266
267 if (entry->LargestAscender < 0 || entry->LargestDescender < 0)
268 {
269 float llx, lly, llz, urx, ury, urz;
270 font->BBox("_/7Agfy", llx, lly, llz, urx, ury, urz);
271 entry->LargestAscender = ury;
272 entry->LargestDescender = lly;
273 }
274
275 this->LastSize[0] = size[0] = static_cast<int>(font->Advance(this->Input));
276 this->LastSize[1] = size[1] =
277 static_cast<int>(entry->LargestAscender - entry->LargestDescender);
278 this->LastLargestDescender = static_cast<int>(entry->LargestDescender);
279
280 this->SizeBuildTime.Modified();
281 }
282
283 //----------------------------------------------------------------------------
RenderOverlay(vtkViewport * viewport,vtkActor2D * actor)284 void vtkOpenGLFreeTypeTextMapper::RenderOverlay(vtkViewport* viewport,
285 vtkActor2D* actor)
286 {
287 vtkDebugMacro (<< "RenderOverlay");
288
289 // Check for input
290
291 if (this->Input == NULL || this->Input[0] == '\0')
292 {
293 return;
294 }
295
296 // Check for multi-lines
297
298 if (this->NumberOfLines > 1)
299 {
300 this->RenderOverlayMultipleLines(viewport, actor);
301 return;
302 }
303
304 // Get text property
305
306 vtkTextProperty *tprop = this->GetTextProperty();
307 if (!tprop)
308 {
309 vtkErrorMacro(<< "Need a text property to render mapper");
310 return;
311 }
312
313 vtkOpenGLClearErrorMacro();
314
315 // Get the window information for display
316
317 vtkWindow* window = viewport->GetVTKWindow();
318 if (this->LastWindow && this->LastWindow != window)
319 {
320 this->ReleaseGraphicsResources(this->LastWindow);
321 }
322 this->LastWindow = window;
323
324 // Get size of text
325
326 int size[2];
327 this->GetSize(viewport, size);
328
329 // Get the position of the text actor
330
331 int* actorPos;
332 actorPos =
333 actor->GetActualPositionCoordinate()->GetComputedViewportValue(viewport);
334
335 // Define bounding rectangle
336
337 int pos[2];
338 pos[0] = actorPos[0];
339 pos[1] = static_cast<int>(actorPos[1] - tprop->GetLineOffset());
340
341 switch (tprop->GetJustification())
342 {
343 case VTK_TEXT_LEFT:
344 break;
345 case VTK_TEXT_CENTERED:
346 pos[0] = pos[0] - size[0] / 2;
347 break;
348 case VTK_TEXT_RIGHT:
349 pos[0] = pos[0] - size[0];
350 break;
351 }
352
353 switch (tprop->GetVerticalJustification())
354 {
355 case VTK_TEXT_TOP:
356 pos[1] = pos[1] - size[1] - this->LastLargestDescender;
357 break;
358 case VTK_TEXT_CENTERED:
359 pos[1] = pos[1] - size[1] / 2 - this->LastLargestDescender / 2;
360 break;
361 case VTK_TEXT_BOTTOM:
362 break;
363 }
364
365 // Push a 2D matrix on the stack
366
367 int *vsize = viewport->GetSize();
368 double *vport = viewport->GetViewport();
369 double *tileViewport = viewport->GetVTKWindow()->GetTileViewport();
370 double visVP[4];
371
372 visVP[0] = (vport[0] >= tileViewport[0]) ? vport[0] : tileViewport[0];
373 visVP[1] = (vport[1] >= tileViewport[1]) ? vport[1] : tileViewport[1];
374 visVP[2] = (vport[2] <= tileViewport[2]) ? vport[2] : tileViewport[2];
375 visVP[3] = (vport[3] <= tileViewport[3]) ? vport[3] : tileViewport[3];
376
377 if (visVP[0] == visVP[2] || visVP[1] == visVP[3])
378 {
379 return;
380 }
381
382 glMatrixMode(GL_PROJECTION);
383 glPushMatrix();
384 glLoadIdentity();
385
386 if(viewport->GetIsPicking())
387 {
388 vtkgluPickMatrix(viewport->GetPickX(), viewport->GetPickY(),
389 viewport->GetPickWidth(),
390 viewport->GetPickHeight(),
391 viewport->GetOrigin(), viewport->GetSize());
392 }
393
394 glMatrixMode(GL_MODELVIEW);
395 glPushMatrix();
396 glLoadIdentity();
397
398 // Store the state of the attributes we are about to change
399 GLboolean lightingEnabled = glIsEnabled(GL_LIGHTING);
400 GLint depthFunc;
401 glGetIntegerv(GL_DEPTH_FUNC, &depthFunc);
402 glDisable(GL_LIGHTING);
403 glDepthFunc(GL_ALWAYS);
404
405 if (actor->GetProperty()->GetDisplayLocation() == VTK_FOREGROUND_LOCATION)
406 {
407 glOrtho(0, vsize[0] - 1, 0, vsize[1] - 1, 0, 1);
408 }
409 else
410 {
411 glOrtho(0, vsize[0] - 1, 0, vsize[1] - 1, -1, 0);
412 }
413
414 int *winSize = viewport->GetVTKWindow()->GetSize();
415
416 int xoff = static_cast<int>(pos[0] - winSize[0] * (visVP[0] - vport[0]));
417 int yoff = static_cast<int>(pos[1] - winSize[1] * (visVP[1] - vport[1]));
418
419 // When picking draw the bounds of the text as a rectangle,
420 // as text only picks when the pick point is exactly on the
421 // origin of the text
422
423 if (viewport->GetIsPicking())
424 {
425 float x1 = (2.0 * actorPos[0]) / vsize[0] - 1.0;
426 float y1 = 2.0 * (actorPos[1] - tprop->GetLineOffset())/vsize[1] - 1.0;
427 float width = (2.0 * size[0]) / vsize[0];
428 float height = (2.0 * size[1]) / vsize[1];
429 glRectf(x1, y1, x1 + width, y1 + height);
430
431 // Clean up and return after drawing the rectangle
432 // Restore the original state
433 glMatrixMode(GL_PROJECTION);
434 glPopMatrix();
435 glMatrixMode(GL_MODELVIEW);
436 glPopMatrix();
437 if (lightingEnabled)
438 {
439 glEnable(GL_LIGHTING);
440 }
441 glDepthFunc(depthFunc);
442
443 return;
444 }
445
446 double* tprop_color = tprop->GetColor();
447 double tprop_opacity = tprop->GetOpacity();
448
449 // Get the font
450
451 vtkFreeTypeUtilities::Entry *entry =
452 vtkFreeTypeUtilities::GetInstance()->GetFont(tprop, tprop_color);
453 FTFont *font = entry ? entry->Font : NULL;
454 if (!font)
455 {
456 vtkErrorMacro(<< "Render - No font");
457 return;
458 }
459
460 struct FTGLRenderContext *ftgl_context = 0;
461
462 // Set up the shadow color
463
464 if (tprop->GetShadow())
465 {
466 double shadow_color[3], rgb;
467 rgb = ((tprop_color[0] + tprop_color[1] + tprop_color[2]) / 3.0 > 0.5)
468 ? 0.0 : 1.0;
469 shadow_color[0] = shadow_color[1] = shadow_color[2] = rgb;
470
471 // Get the shadow font
472
473 vtkFreeTypeUtilities::Entry *shadow_entry =
474 vtkFreeTypeUtilities::GetInstance()->GetFont(tprop, shadow_color);
475 FTFont *shadow_font = shadow_entry ? shadow_entry->Font : NULL;
476 if (!shadow_font)
477 {
478 vtkErrorMacro(<< "Render - No shadow font");
479 return;
480 }
481
482 // Set the color here since load/render glyphs is done
483 // on demand and this color has to be consistent for a given font entry.
484
485 glColor4ub(static_cast<unsigned char>(shadow_color[0] * 255.0),
486 static_cast<unsigned char>(shadow_color[1] * 255.0),
487 static_cast<unsigned char>(shadow_color[2] * 255.0),
488 static_cast<unsigned char>(tprop_opacity * 255.0));
489
490 // Required for clipping to work correctly
491
492 glRasterPos2i(0, 0);
493 glBitmap(0, 0, 0, 0,
494 xoff + tprop->GetShadowOffset()[0],
495 yoff + tprop->GetShadowOffset()[1], NULL);
496
497 // Draw the shadow text
498
499 shadow_font->render(this->Input, ftgl_context);
500
501 // Get the font again, Duh, since it may have been freed from the
502 // cache by the shadow font
503
504 font = vtkFreeTypeUtilities::GetInstance()->GetFont(
505 tprop, tprop_color)->Font;
506 if (!font)
507 {
508 vtkErrorMacro(<< "Render - No font");
509 return;
510 }
511 }
512
513 // Set the color here since load/render glyphs is done
514 // on demand and this color has to be consistent for a given font entry.
515
516 glColor4ub(static_cast<unsigned char>(tprop_color[0] * 255.0),
517 static_cast<unsigned char>(tprop_color[1] * 255.0),
518 static_cast<unsigned char>(tprop_color[2] * 255.0),
519 static_cast<unsigned char>(tprop_opacity * 255.0));
520
521 // Required for clipping to work correctly
522
523 glRasterPos2i(0, 0);
524 glBitmap(0, 0, 0, 0, xoff, yoff, NULL);
525
526 // Display a string
527
528 font->render(this->Input, ftgl_context);
529
530 glFlush();
531
532 // Restore the original GL state
533 glMatrixMode(GL_PROJECTION);
534 glPopMatrix();
535 glMatrixMode(GL_MODELVIEW);
536 glPopMatrix();
537 if (lightingEnabled)
538 {
539 glEnable(GL_LIGHTING);
540 }
541 glDepthFunc(depthFunc);
542
543 vtkOpenGLCheckErrorMacro("failed after RenderOverlay");
544 }
545
546 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)547 void vtkOpenGLFreeTypeTextMapper::PrintSelf(ostream& os, vtkIndent indent)
548 {
549 this->Superclass::PrintSelf(os,indent);
550 os << indent << "NumberOfLines: " << this->NumberOfLines << "\n";
551 }
552
553 //----------------------------------------------------------------------------
SetInput(const char * input)554 void vtkOpenGLFreeTypeTextMapper::SetInput(const char *input)
555 {
556 if ( this->Input && input && (!strcmp(this->Input,input)))
557 {
558 return;
559 }
560 delete [] this->Input;
561 if (input)
562 {
563 this->Input = new char[strlen(input)+1];
564 strcpy(this->Input,input);
565 }
566 else
567 {
568 this->Input = NULL;
569 }
570 this->Modified();
571
572 int numLines = GetNumberOfLinesImpl(input);
573
574 if ( numLines <= 1) // a line with no "\n"
575 {
576 this->NumberOfLines = numLines;
577 }
578
579 else //multiple lines
580 {
581 char *line;
582 int i;
583
584 if ( numLines > this->NumberOfLinesAllocated )
585 {
586 // delete old stuff
587 if ( this->TextLines )
588 {
589 for (i=0; i < this->NumberOfLinesAllocated; i++)
590 {
591 this->TextLines[i]->Delete();
592 }
593 delete [] this->TextLines;
594 }
595
596 // allocate new text mappers
597 this->NumberOfLinesAllocated = numLines;
598 this->TextLines = new vtkTextMapper *[numLines];
599 for (i=0; i < numLines; i++)
600 {
601 this->TextLines[i] = vtkTextMapper::New();
602 }
603 } //if we need to reallocate
604
605 // set the input strings
606 this->NumberOfLines = numLines;
607 for (i=0; i < this->NumberOfLines; i++)
608 {
609 line = this->NextLine(input, i);
610 this->TextLines[i]->SetInput( line );
611 delete [] line;
612 }
613 }
614 }
615