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