1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkQtLabelRenderStrategy.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 "vtkQtLabelRenderStrategy.h"
16 #include "vtkQtLabelRenderStrategyInternals.h"
17 
18 #include "vtkCoordinate.h"
19 #include "vtkImageData.h"
20 #include "vtkLabeledDataMapper.h"
21 #include "vtkLabelSizeCalculator.h"
22 #include "vtkMath.h"
23 #include "vtkObjectFactory.h"
24 #include "vtkPointData.h"
25 #include "vtkPlaneSource.h"
26 #include "vtkPolyDataMapper2D.h"
27 #include "vtkProperty2D.h"
28 #include "vtkQImageToImageSource.h"
29 #include "vtkRenderer.h"
30 #include "vtkRenderWindow.h"
31 #include "vtkSmartPointer.h"
32 #include "vtkTextProperty.h"
33 #include "vtkTexture.h"
34 #include "vtkTexturedActor2D.h"
35 #include "vtkTextureMapToPlane.h"
36 #include "vtkTimerLog.h"
37 
38 #include <QApplication>
39 #include <QFont>
40 #include <QFontMetrics>
41 #include <QImage>
42 #include <QMap>
43 #include <QPainter>
44 #include <QPainterPath>
45 #include <QPair>
46 #include <QPixmap>
47 #include <QTextDocument>
48 #include <QTextStream>
49 
50 vtkStandardNewMacro(vtkQtLabelRenderStrategy);
51 
operator <(const vtkQtLabelMapEntry & a,const vtkQtLabelMapEntry & other)52 bool operator <(const vtkQtLabelMapEntry& a, const vtkQtLabelMapEntry& other)
53 {
54   if (a.Text != other.Text)
55   {
56     return a.Text < other.Text;
57   }
58   if (a.Color.red() != other.Color.red())
59   {
60     return a.Color.red() < other.Color.red();
61   }
62   if (a.Color.green() != other.Color.green())
63   {
64     return a.Color.green() < other.Color.green();
65   }
66   if (a.Color.blue() != other.Color.blue())
67   {
68     return a.Color.blue() < other.Color.blue();
69   }
70   if (a.Color.alpha() != other.Color.alpha())
71   {
72     return a.Color.alpha() < other.Color.alpha();
73   }
74   return a.Font < other.Font;
75 }
76 
77 //----------------------------------------------------------------------------
vtkQtLabelRenderStrategy()78 vtkQtLabelRenderStrategy::vtkQtLabelRenderStrategy()
79 {
80   this->Implementation = new Internals();
81   this->Implementation->Image = new QImage(1, 1, QImage::Format_ARGB32_Premultiplied);
82   this->Implementation->Painter = new QPainter(this->Implementation->Image);
83 
84   this->QImageToImage = vtkQImageToImageSource::New();
85   this->PlaneSource = vtkPlaneSource::New();
86   this->TextureMapToPlane = vtkTextureMapToPlane::New();
87   this->Texture = vtkTexture::New();
88   this->Mapper = vtkPolyDataMapper2D::New();
89   this->Actor = vtkTexturedActor2D::New();
90 
91   this->QImageToImage->SetQImage(this->Implementation->Image);
92 
93   this->PlaneSource->SetOrigin(0, 0, 0);
94 
95   this->TextureMapToPlane->SetInputConnection(this->PlaneSource->GetOutputPort());
96   this->TextureMapToPlane->AutomaticPlaneGenerationOn();
97   this->TextureMapToPlane->SetSRange(0., 1.);
98   this->TextureMapToPlane->SetTRange(0., 1.);
99 
100   this->Mapper->SetInputConnection(this->TextureMapToPlane->GetOutputPort());
101   this->Texture->SetInputConnection(this->QImageToImage->GetOutputPort());
102   this->Texture->PremultipliedAlphaOn();
103   this->Actor->SetTexture(this->Texture);
104   this->Actor->SetMapper(this->Mapper);
105 }
106 
107 //----------------------------------------------------------------------------
~vtkQtLabelRenderStrategy()108 vtkQtLabelRenderStrategy::~vtkQtLabelRenderStrategy()
109 {
110   delete this->Implementation->Painter;
111   delete this->Implementation->Image;
112   delete this->Implementation;
113   this->QImageToImage->Delete();
114   this->PlaneSource->Delete();
115   this->TextureMapToPlane->Delete();
116   this->Texture->Delete();
117   this->Mapper->Delete();
118   this->Actor->Delete();
119 }
120 
ReleaseGraphicsResources(vtkWindow * window)121 void vtkQtLabelRenderStrategy::ReleaseGraphicsResources(vtkWindow *window)
122 {
123   this->Texture->ReleaseGraphicsResources(window);
124   this->Mapper->ReleaseGraphicsResources(window);
125   this->Actor->ReleaseGraphicsResources(window);
126 }
127 
128 //double start_frame_time = 0;
129 //int start_frame_iter = 0;
130 //----------------------------------------------------------------------------
StartFrame()131 void vtkQtLabelRenderStrategy::StartFrame()
132 {
133   //vtkTimerLog* timer = vtkTimerLog::New();
134   //timer->StartTimer();
135 
136   if (!this->Renderer)
137   {
138     vtkErrorMacro("Renderer must be set.");
139     return;
140   }
141 
142   if (!this->Renderer->GetRenderWindow())
143   {
144     vtkErrorMacro("RenderWindow must be set.");
145     return;
146   }
147 
148   int *size = this->Renderer->GetRenderWindow()->GetSize();
149   int width = size[0];
150   int height = size[1];
151   // If the render window is not antialiased then the text should not be
152   this->AntialiasText = this->Renderer->GetRenderWindow()->GetMultiSamples() > 0;
153 
154   if (this->Implementation->Image->width() != width ||
155       this->Implementation->Image->height() != height)
156   {
157     this->Implementation->Painter->end();
158     delete this->Implementation->Image;
159     this->Implementation->Image = new QImage(width, height,
160                                              QImage::Format_ARGB32_Premultiplied);
161     this->Implementation->Painter->begin(this->Implementation->Image);
162     this->Implementation->Painter->setRenderHint(QPainter::TextAntialiasing,
163                                                  this->AntialiasText);
164     this->Implementation->Painter->setRenderHint(QPainter::Antialiasing,
165                                                  this->AntialiasText);
166     this->QImageToImage->SetQImage(this->Implementation->Image);
167     this->PlaneSource->SetPoint1(width, 0, 0);
168     this->PlaneSource->SetPoint2(0, height, 0);
169   }
170 
171   this->Implementation->Image->fill(qRgba(0,0,0,0));
172   this->QImageToImage->Modified();
173 
174   //timer->StopTimer();
175   //start_frame_time += timer->GetElapsedTime();
176   //start_frame_iter++;
177   //if (start_frame_iter % 10 == 0)
178   //  {
179   //  cerr << "StartFrame time: " << (start_frame_time / start_frame_iter) << endl;
180   //  }
181 }
182 
183 //double compute_bounds_time = 0;
184 //int compute_bounds_iter = 0;
185 //----------------------------------------------------------------------------
ComputeLabelBounds(vtkTextProperty * tprop,vtkUnicodeString label,double bds[4])186 void vtkQtLabelRenderStrategy::ComputeLabelBounds(
187   vtkTextProperty* tprop, vtkUnicodeString label, double bds[4])
188 {
189   if (!QApplication::instance())
190   {
191     vtkErrorMacro("You must initialize a QApplication before using this class.");
192     return;
193   }
194 
195   //vtkTimerLog* timer = vtkTimerLog::New();
196   //timer->StartTimer();
197 
198   if (!tprop)
199   {
200     tprop = this->DefaultTextProperty;
201   }
202 
203   QFont fontSpec = this->Implementation->TextPropertyToFont(tprop);
204 
205   // This is the recommended Qt way of controlling text antialiasing.
206   if (this->AntialiasText)
207   {
208     fontSpec.setStyleStrategy(QFont::PreferAntialias);
209   }
210   else
211   {
212     fontSpec.setStyleStrategy(QFont::NoAntialias);
213   }
214 
215   QString text = QString::fromUtf8(label.utf8_str());
216   QColor textColor = this->Implementation->TextPropertyToColor(tprop->GetColor(),
217                                                                tprop->GetOpacity());
218   vtkQtLabelMapEntry key;
219   key.Font = fontSpec;
220   key.Text = text;
221   key.Color = textColor;
222 
223   QRectF rect;
224   if (this->Implementation->Cache.contains(key))
225   {
226     rect = this->Implementation->Cache[key].Bounds;
227   }
228   else
229   {
230     QPainterPath path;
231     path.addText(0, 0, fontSpec, text);
232     rect = path.boundingRect();
233     this->Implementation->Cache[key].Bounds = rect;
234   }
235 
236   bds[0] = 0;
237   bds[1] = rect.width();
238   bds[2] = 0;
239   bds[3] = rect.height();
240 
241   bds[2] -= tprop->GetLineOffset();
242   bds[3] -= tprop->GetLineOffset();
243 
244   // Take justification into account
245   double sz[2] = {bds[1] - bds[0], bds[3] - bds[2]};
246   switch (tprop->GetJustification())
247   {
248     case VTK_TEXT_LEFT:
249       break;
250     case VTK_TEXT_CENTERED:
251       bds[0] -= sz[0]/2;
252       bds[1] -= sz[0]/2;
253       break;
254     case VTK_TEXT_RIGHT:
255       bds[0] -= sz[0];
256       bds[1] -= sz[0];
257       break;
258   }
259   switch (tprop->GetVerticalJustification())
260   {
261     case VTK_TEXT_BOTTOM:
262       break;
263     case VTK_TEXT_CENTERED:
264       bds[2] -= sz[1]/2;
265       bds[3] -= sz[1]/2;
266       break;
267     case VTK_TEXT_TOP:
268       bds[2] -= sz[1];
269       bds[3] -= sz[1];
270       break;
271   }
272 
273   //timer->StopTimer();
274   //compute_bounds_time += timer->GetElapsedTime();
275   //compute_bounds_iter++;
276   //if (compute_bounds_iter % 10000 == 0)
277   //  {
278   //  cerr << "ComputeLabelBounds time: " << (compute_bounds_time / compute_bounds_iter) << endl;
279   //  }
280 }
281 
282 //double render_label_time = 0;
283 //int render_label_iter = 0;
284 //----------------------------------------------------------------------------
RenderLabel(int x[2],vtkTextProperty * tprop,vtkUnicodeString label,int maxWidth)285 void vtkQtLabelRenderStrategy::RenderLabel(
286   int x[2], vtkTextProperty* tprop, vtkUnicodeString label, int maxWidth)
287 {
288   if (!QApplication::instance())
289   {
290     vtkErrorMacro("You must initialize a QApplication before using this class.");
291     return;
292   }
293 
294   //vtkTimerLog* timer = vtkTimerLog::New();
295   //timer->StartTimer();
296 
297   // Determine if we can render the label to fit the width
298   QString origText = QString::fromUtf8(label.utf8_str());
299   QFont fontSpec = this->Implementation->TextPropertyToFont(tprop);
300 
301   // This is the recommended Qt way of controlling text antialiasing.
302   if (this->AntialiasText)
303   {
304     fontSpec.setStyleStrategy(QFont::PreferAntialias);
305   }
306   else
307   {
308     fontSpec.setStyleStrategy(QFont::NoAntialias);
309   }
310 
311   QFontMetrics fontMetric(fontSpec);
312   QString text = fontMetric.elidedText(origText, Qt::ElideRight, maxWidth);
313   if (origText.length() >= 8 && text.length() < 8)
314   {
315     // Too small to render.
316     return;
317   }
318 
319   // Get properties from text property
320   double rotation = -tprop->GetOrientation();
321   QColor textColor = this->Implementation->TextPropertyToColor(tprop->GetColor(), tprop->GetOpacity());
322   int *size = this->Renderer->GetRenderWindow()->GetSize();
323   double h = size[1]-1;
324   double line_offset = tprop->GetLineOffset();
325   int shOff[2];
326   tprop->GetShadowOffset(shOff);
327   double sc[3];
328   tprop->GetShadowColor(sc);
329   QColor shadowColor = this->Implementation->TextPropertyToColor(sc, tprop->GetOpacity());
330 
331   // Compute bounds and justification
332   QPainterPath path;
333   path.addText(0, 0, fontSpec, text);
334   QRectF bounds = path.boundingRect();
335   double delta_x = 0., delta_y = 0.;
336 
337   switch( tprop->GetJustification() )
338   {
339     case VTK_TEXT_LEFT:
340       break;
341     case VTK_TEXT_CENTERED:
342       delta_x = -bounds.width()/2.0;
343       break;
344     case VTK_TEXT_RIGHT:
345       delta_x = -bounds.width();
346       break;
347   }
348   switch (tprop->GetVerticalJustification())
349   {
350     case VTK_TEXT_TOP:
351       delta_y = bounds.height() - bounds.bottom();
352       break;
353     case VTK_TEXT_CENTERED:
354       delta_y = bounds.height()/2.0 - bounds.bottom();
355       break;
356     case VTK_TEXT_BOTTOM:
357       delta_y = -bounds.bottom();
358       break;
359   }
360 
361   QPainter* painter = this->Implementation->Painter;
362   painter->save();
363   painter->translate(x[0], h-x[1]);
364   painter->rotate(rotation);
365   painter->translate(delta_x, delta_y);
366   painter->translate(0., line_offset);
367 
368   if (tprop->GetShadow())
369   {
370     painter->save();
371     painter->translate(shOff[0], -shOff[1]);
372     painter->fillPath(path, shadowColor);
373     painter->restore();
374   }
375 
376   painter->fillPath(path, textColor);
377   painter->restore();
378 
379   //timer->StopTimer();
380   //render_label_time += timer->GetElapsedTime();
381   //render_label_iter++;
382   //if (render_label_iter % 100 == 0)
383   //  {
384   //  cerr << "RenderLabel time: " << (render_label_time / render_label_iter) << endl;
385   //  }
386 }
387 
388 //----------------------------------------------------------------------------
RenderLabel(int x[2],vtkTextProperty * tprop,vtkUnicodeString label)389 void vtkQtLabelRenderStrategy::RenderLabel(
390   int x[2], vtkTextProperty* tprop, vtkUnicodeString label)
391 {
392   if (!QApplication::instance())
393   {
394     vtkErrorMacro("You must initialize a QApplication before using this class.");
395     return;
396   }
397 
398   if (!this->Renderer)
399   {
400     vtkErrorMacro("Renderer must be set.");
401     return;
402   }
403 
404   //vtkTimerLog* timer = vtkTimerLog::New();
405   //timer->StartTimer();
406 
407   QString text = QString::fromUtf8(label.utf8_str());
408   QFont fontSpec = this->Implementation->TextPropertyToFont(tprop);
409 
410   // This is the recommended Qt way of controlling text antialiasing.
411   if (this->AntialiasText)
412   {
413     fontSpec.setStyleStrategy(QFont::PreferAntialias);
414   }
415   else
416   {
417     fontSpec.setStyleStrategy(QFont::NoAntialias);
418   }
419 
420   double rotation = -tprop->GetOrientation();
421   QColor textColor = this->Implementation->TextPropertyToColor(tprop->GetColor(), tprop->GetOpacity());
422 
423   int shOff[2];
424   tprop->GetShadowOffset(shOff);
425   double pixelPadding = 2;
426   double pixelPaddingX = pixelPadding + shOff[0];
427   double pixelPaddingY = pixelPadding - shOff[1];
428 
429   // Get image from cache
430   QImage* img = nullptr;
431   QRectF bounds;
432   vtkQtLabelMapEntry key;
433   key.Font = fontSpec;
434   key.Text = text;
435   key.Color = textColor;
436   if (this->Implementation->Cache.contains(key) && this->Implementation->Cache[key].Image.width() > 0)
437   {
438     img = &this->Implementation->Cache[key].Image;
439     bounds = this->Implementation->Cache[key].Bounds;
440   }
441   else
442   {
443     QPainterPath path;
444     path.addText(0, 0, fontSpec, text);
445     bounds = path.boundingRect();
446     this->Implementation->Cache[key].Bounds = bounds;
447     bounds.setWidth(bounds.width() + pixelPaddingX);
448     bounds.setHeight(bounds.height() + pixelPaddingY);
449     QTransform trans;
450     trans.rotate(rotation);
451     QRectF rotBounds = trans.mapRect(bounds);
452     this->Implementation->Cache[key].Image = QImage(static_cast<int>(rotBounds.width()), static_cast<int>(rotBounds.height()), QImage::Format_ARGB32_Premultiplied);
453     img = &this->Implementation->Cache[key].Image;
454     img->fill(qRgba(0,0,0,0));
455     QPainter p(img);
456     p.translate(-rotBounds.left(), -rotBounds.top());
457     p.rotate(rotation);
458     p.setRenderHint(QPainter::TextAntialiasing, this->AntialiasText);
459     p.setRenderHint(QPainter::Antialiasing, this->AntialiasText);
460 
461     if (tprop->GetShadow())
462     {
463       p.save();
464       p.translate(shOff[0], -shOff[1]);
465       double sc[3];
466       tprop->GetShadowColor(sc);
467       QColor shadowColor = this->Implementation->TextPropertyToColor(sc, tprop->GetOpacity());
468       p.fillPath(path, shadowColor);
469       p.restore();
470     }
471 
472     p.fillPath(path, textColor);
473   }
474 
475   QPainter* painter = this->Implementation->Painter;
476 
477   double delta_x = 0.;
478   switch( tprop->GetJustification() )
479   {
480     case VTK_TEXT_LEFT:
481       delta_x = bounds.width()/2.0;
482       break;
483     case VTK_TEXT_CENTERED:
484       break;
485     case VTK_TEXT_RIGHT:
486       delta_x = -bounds.width()/2.0;
487       break;
488   }
489 
490   double delta_y = pixelPadding / 2.0;
491   switch (tprop->GetVerticalJustification())
492   {
493     case VTK_TEXT_TOP:
494       delta_y += bounds.height()/2.0;
495       break;
496     case VTK_TEXT_CENTERED:
497       break;
498     case VTK_TEXT_BOTTOM:
499       delta_y += -bounds.height()/2.0;
500       break;
501   }
502 
503   int *size = this->Renderer->GetRenderWindow()->GetSize();
504   double h = size[1]-1;
505   double line_offset = tprop->GetLineOffset();
506 
507   QRectF imgRect;
508   imgRect.setSize(img->size());
509 
510   painter->save();
511   painter->translate(x[0], h-x[1]);
512   painter->translate(-imgRect.width()/2.0, -imgRect.height()/2.0);
513   painter->rotate(rotation);
514   painter->translate(delta_x, delta_y);
515   painter->rotate(-rotation);
516   painter->translate(0., line_offset);
517   painter->drawImage(imgRect, *img, imgRect);
518   painter->restore();
519 
520   //timer->StopTimer();
521   //render_label_time += timer->GetElapsedTime();
522   //render_label_iter++;
523   //if (render_label_iter % 100 == 0)
524   //  {
525   //  cerr << "RenderLabel time: " << (render_label_time / render_label_iter) << endl;
526   //  }
527 }
528 
529 //double end_frame_time = 0;
530 //int end_frame_iter = 0;
531 //----------------------------------------------------------------------------
EndFrame()532 void vtkQtLabelRenderStrategy::EndFrame()
533 {
534   //vtkTimerLog* timer = vtkTimerLog::New();
535   //timer->StartTimer();
536   this->Actor->RenderOverlay(this->Renderer);
537   //timer->StopTimer();
538   //end_frame_time += timer->GetElapsedTime();
539   //end_frame_iter++;
540   //if (end_frame_iter % 10 == 0)
541   //  {
542   //  cerr << "EndFrame time: " << (end_frame_time / end_frame_iter) << endl;
543   //  }
544 }
545 
546 //----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)547 void vtkQtLabelRenderStrategy::PrintSelf(ostream& os, vtkIndent indent)
548 {
549   this->Superclass::PrintSelf(os,indent);
550 }
551