ctkVTKMagnifyWidget.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. /*=========================================================================
  2. Library: CTK
  3. Copyright (c) Kitware Inc.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.commontk.org/LICENSE
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. =========================================================================*/
  14. // Qt includes
  15. #include <QEvent>
  16. #include <QMouseEvent>
  17. #include <QPointF>
  18. // CTK includes
  19. #include "ctkVTKMagnifyWidget.h"
  20. #include "ctkLogger.h"
  21. // VTK includes
  22. #include <QVTKWidget.h>
  23. #include <vtkRenderWindow.h>
  24. #include <vtkUnsignedCharArray.h>
  25. // Convenient macro
  26. #define VTK_CREATE(type, name) \
  27. vtkSmartPointer<type> name = vtkSmartPointer<type>::New()
  28. //--------------------------------------------------------------------------
  29. static ctkLogger logger("org.commontk.visualization.vtk.widgets.ctkVTKMagnifyWidget");
  30. //--------------------------------------------------------------------------
  31. //-----------------------------------------------------------------------------
  32. class ctkVTKMagnifyWidgetPrivate
  33. {
  34. Q_DECLARE_PUBLIC(ctkVTKMagnifyWidget);
  35. protected:
  36. ctkVTKMagnifyWidget* const q_ptr;
  37. public:
  38. ctkVTKMagnifyWidgetPrivate(ctkVTKMagnifyWidget& object);
  39. void init();
  40. void removePixmap();
  41. void observe(QVTKWidget * widget);
  42. void remove(QVTKWidget * widget);
  43. void updatePixmap(QVTKWidget * widget, QPointF pos);
  44. QList<QVTKWidget *> ObservedQVTKWidgets;
  45. double Magnification;
  46. };
  47. // --------------------------------------------------------------------------
  48. // ctkVTKMagnifyWidgetPrivate methods
  49. // --------------------------------------------------------------------------
  50. ctkVTKMagnifyWidgetPrivate::ctkVTKMagnifyWidgetPrivate(ctkVTKMagnifyWidget& object)
  51. :q_ptr(&object)
  52. {
  53. this->ObservedQVTKWidgets = QList<QVTKWidget *>();
  54. this->Magnification = 1.0;
  55. }
  56. // --------------------------------------------------------------------------
  57. void ctkVTKMagnifyWidgetPrivate::init()
  58. {
  59. this->removePixmap();
  60. }
  61. // --------------------------------------------------------------------------
  62. void ctkVTKMagnifyWidgetPrivate::removePixmap()
  63. {
  64. Q_Q(ctkVTKMagnifyWidget);
  65. QPixmap nullPixmap;
  66. q->setPixmap(nullPixmap);
  67. }
  68. // --------------------------------------------------------------------------
  69. void ctkVTKMagnifyWidgetPrivate::observe(QVTKWidget * widget)
  70. {
  71. Q_ASSERT(widget);
  72. // If we are not already observing the widget, add it to the list and install
  73. // the public implementation as the event filter to handle mousing
  74. if (!this->ObservedQVTKWidgets.contains(widget))
  75. {
  76. this->ObservedQVTKWidgets.append(widget);
  77. Q_Q(ctkVTKMagnifyWidget);
  78. widget->installEventFilter(q);
  79. }
  80. }
  81. // --------------------------------------------------------------------------
  82. void ctkVTKMagnifyWidgetPrivate::remove(QVTKWidget * widget)
  83. {
  84. Q_ASSERT(widget);
  85. // If we are observing the widget, remove it from the list and remove the
  86. // public implementations event filtering
  87. if (this->ObservedQVTKWidgets.contains(widget))
  88. {
  89. Q_ASSERT(this->ObservedQVTKWidgets.count(widget) == 1);
  90. this->ObservedQVTKWidgets.removeOne(widget);
  91. Q_Q(ctkVTKMagnifyWidget);
  92. widget->removeEventFilter(q);
  93. }
  94. }
  95. // --------------------------------------------------------------------------
  96. void ctkVTKMagnifyWidgetPrivate::updatePixmap(QVTKWidget * widget, QPointF pos)
  97. {
  98. Q_ASSERT(widget);
  99. Q_Q(ctkVTKMagnifyWidget);
  100. // Retrieve buffer of given QVTKWidget from its render window
  101. vtkRenderWindow * renderWindow = widget->GetRenderWindow();
  102. if (!renderWindow)
  103. {
  104. this->removePixmap();
  105. return;
  106. }
  107. // Get the window size and mouse position, and do error checking
  108. int * windowSize = renderWindow->GetSize();
  109. QPointF mouseWindowPos(pos.x(), static_cast<double>(windowSize[1]-1)-pos.y());
  110. if (mouseWindowPos.x() < 0 || mouseWindowPos.x() >= windowSize[0] ||
  111. mouseWindowPos.y() < 0 || mouseWindowPos.y() >= windowSize[1])
  112. {
  113. this->removePixmap();
  114. return;
  115. }
  116. // Compute indices into the render window's data array
  117. // Given a magnification and the label's widget size, compute the number of
  118. // pixels we can show. We should round to get a larger integer extent, since
  119. // we will later adjust the pixmap's location in paintEvent().
  120. QSizeF sizeToMagnify = QSizeF(q->size()) / this->Magnification;
  121. double dx0 = (mouseWindowPos.x() - ((sizeToMagnify.width()-1.0) / 2.0));
  122. double dx1 = (mouseWindowPos.x() + ((sizeToMagnify.width()-1.0) / 2.0));
  123. double dy0 = (mouseWindowPos.y() - ((sizeToMagnify.height()-1.0) / 2.0));
  124. double dy1 = (mouseWindowPos.y() + ((sizeToMagnify.height()-1.0) / 2.0));
  125. // Round to ints, for indexing into the pixel array
  126. int ix0 = floor(dx0);
  127. int ix1 = ceil(dx1);
  128. int iy0 = floor(dy0);
  129. int iy1 = ceil(dy1);
  130. // Handle when mouse is near the border
  131. int min_x0 = 0;
  132. int max_x1 = windowSize[0]-1;
  133. int min_y0 = 0;
  134. int max_y1 = windowSize[1]-1;
  135. bool overLeft = ix0 < min_x0;
  136. bool overRight = ix1 > max_x1;
  137. bool overBottom = iy0 < min_y0;
  138. bool overTop = iy1 > max_y1;
  139. // Ensure we don't access nonexistant indices
  140. if (overLeft)
  141. {
  142. ix0 = min_x0;
  143. dx0 = min_x0;
  144. }
  145. if (overRight)
  146. {
  147. ix1 = max_x1;
  148. dx1 = max_x1;
  149. }
  150. if (overBottom)
  151. {
  152. iy0 = min_y0;
  153. dy0 = min_y0;
  154. }
  155. if (overTop)
  156. {
  157. iy1 = max_y1;
  158. dy1 = max_y1;
  159. }
  160. // Error case
  161. if (ix0 > ix1 || iy0 > iy1)
  162. {
  163. this->removePixmap();
  164. return;
  165. }
  166. // Setup the pixelmap's position in the label
  167. Qt::Alignment alignment;
  168. if (overLeft && !overRight)
  169. {
  170. alignment = Qt::AlignRight;
  171. }
  172. else if (overRight && !overLeft)
  173. {
  174. alignment = Qt::AlignLeft;
  175. }
  176. else
  177. {
  178. alignment = Qt::AlignLeft;
  179. }
  180. if (overBottom && !overTop)
  181. {
  182. alignment = alignment | Qt::AlignTop;
  183. }
  184. else if (overTop && !overBottom)
  185. {
  186. alignment = alignment | Qt::AlignBottom;
  187. }
  188. else
  189. {
  190. alignment = alignment | Qt::AlignTop;
  191. }
  192. q->setAlignment(alignment);
  193. // Retrieve the pixel data into a QImage
  194. QSize actualSize(ix1-ix0+1, iy1-iy0+1);
  195. QImage image(actualSize.width(), actualSize.height(), QImage::Format_RGB32);
  196. vtkUnsignedCharArray * pixelData = vtkUnsignedCharArray::New();
  197. pixelData->SetArray(image.bits(), actualSize.width() * actualSize.height() * 4, 1);
  198. int front = renderWindow->GetDoubleBuffer();
  199. int success = renderWindow->GetRGBACharPixelData(ix0, iy0, ix1, iy1, front, pixelData);
  200. if (!success)
  201. {
  202. this->removePixmap();
  203. return;
  204. }
  205. pixelData->Delete();
  206. image = image.rgbSwapped();
  207. image = image.mirrored();
  208. // Scale the image to zoom, using FastTransformation to prevent smoothing
  209. QSize imageSize = actualSize * this->Magnification;
  210. image = image.scaled(imageSize, Qt::KeepAspectRatioByExpanding, Qt::FastTransformation);
  211. // Crop the magnified image to solve the problem of magnified partial pixels
  212. int x0diff = round((dx0 - static_cast<double>(ix0)) * this->Magnification);
  213. int x1diff = imageSize.width()
  214. - round((static_cast<double>(ix1) - dx1) * this->Magnification);
  215. int y0diff = round((dy0 - static_cast<double>(iy0)) * this->Magnification);
  216. int y1diff = imageSize.height()
  217. - round((static_cast<double>(iy1) - dy1) * this->Magnification);
  218. QRect cropRect(QPoint(x0diff, y0diff), QPoint(x1diff, y1diff));
  219. image = image.copy(cropRect);
  220. // Finally, set the pixelmap to the new one we have created
  221. q->setPixmap(QPixmap::fromImage(image));
  222. }
  223. //---------------------------------------------------------------------------
  224. // ctkVTKMagnifyWidget methods
  225. // --------------------------------------------------------------------------
  226. ctkVTKMagnifyWidget::ctkVTKMagnifyWidget(QWidget* parentWidget)
  227. : Superclass(parentWidget)
  228. , d_ptr(new ctkVTKMagnifyWidgetPrivate(*this))
  229. {
  230. Q_D(ctkVTKMagnifyWidget);
  231. d->init();
  232. }
  233. // --------------------------------------------------------------------------
  234. ctkVTKMagnifyWidget::~ctkVTKMagnifyWidget()
  235. {
  236. }
  237. // --------------------------------------------------------------------------
  238. CTK_GET_CPP(ctkVTKMagnifyWidget, double, magnification, Magnification);
  239. // --------------------------------------------------------------------------
  240. void ctkVTKMagnifyWidget::setMagnification(double newMagnification)
  241. {
  242. if (newMagnification > 0)
  243. {
  244. Q_D(ctkVTKMagnifyWidget);
  245. d->Magnification = newMagnification;
  246. }
  247. }
  248. // --------------------------------------------------------------------------
  249. void ctkVTKMagnifyWidget::observe(QVTKWidget * widget)
  250. {
  251. Q_D(ctkVTKMagnifyWidget);
  252. if (widget)
  253. {
  254. d->observe(widget);
  255. }
  256. }
  257. // --------------------------------------------------------------------------
  258. void ctkVTKMagnifyWidget::observe(QList<QVTKWidget *> widgets)
  259. {
  260. foreach(QVTKWidget * widget, widgets)
  261. {
  262. this->observe(widget);
  263. }
  264. }
  265. // --------------------------------------------------------------------------
  266. bool ctkVTKMagnifyWidget::isObserved(QVTKWidget * widget) const
  267. {
  268. if (!widget)
  269. {
  270. return false;
  271. }
  272. Q_D(const ctkVTKMagnifyWidget);
  273. return d->ObservedQVTKWidgets.contains(widget);
  274. }
  275. // --------------------------------------------------------------------------
  276. int ctkVTKMagnifyWidget::numberObserved() const
  277. {
  278. Q_D(const ctkVTKMagnifyWidget);
  279. return d->ObservedQVTKWidgets.length();
  280. }
  281. // --------------------------------------------------------------------------
  282. void ctkVTKMagnifyWidget::remove(QVTKWidget * widget)
  283. {
  284. Q_D(ctkVTKMagnifyWidget);
  285. if (widget)
  286. {
  287. d->remove(widget);
  288. }
  289. }
  290. // --------------------------------------------------------------------------
  291. void ctkVTKMagnifyWidget::remove(QList<QVTKWidget *> widgets)
  292. {
  293. foreach(QVTKWidget * widget, widgets)
  294. {
  295. this->remove(widget);
  296. }
  297. }
  298. // --------------------------------------------------------------------------
  299. bool ctkVTKMagnifyWidget::eventFilter(QObject * obj, QEvent * event)
  300. {
  301. // The given object should be a QVTKWidget in our list
  302. QVTKWidget * widget = static_cast<QVTKWidget *>(obj);
  303. Q_ASSERT(widget);
  304. Q_D(ctkVTKMagnifyWidget);
  305. Q_ASSERT(d->ObservedQVTKWidgets.contains(widget));
  306. QEvent::Type eventType = event->type();
  307. // On mouse move, update the pixmap with the zoomed image
  308. if (eventType == QEvent::MouseMove)
  309. {
  310. QMouseEvent * mouseEvent = static_cast<QMouseEvent *>(event);
  311. Q_ASSERT(mouseEvent);
  312. d->updatePixmap(widget, mouseEvent->posF());
  313. this->update();
  314. return false;
  315. }
  316. // On enter, update the pixmap with the zoomed image (required for zooming when widget
  317. // is created with mouse already within it), and emit signal of enter event
  318. else if (eventType == QEvent::Enter)
  319. {
  320. QPointF posF = widget->mapFromGlobal(QCursor::pos());
  321. d->updatePixmap(widget, posF);
  322. this->update();
  323. emit enteredObservedWidget(widget);
  324. return false;
  325. }
  326. // On leave, fill the pixmap with a solid color and emit signal of leave event
  327. else if (eventType == QEvent::Leave)
  328. {
  329. d->removePixmap();
  330. this->update();
  331. emit leftObservedWidget(widget);
  332. return false;
  333. }
  334. // For other event types, use standard event processing
  335. else
  336. {
  337. return QObject::eventFilter(obj, event);
  338. }
  339. }