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