diff options
author | Miikka Heikkinen <miikka.heikkinen@digia.com> | 2014-08-22 16:40:52 +0300 |
---|---|---|
committer | Miikka Heikkinen <miikka.heikkinen@digia.com> | 2014-08-25 08:17:45 +0300 |
commit | f9bb71fd11cce59d74e78202a1117c8abb3a2e44 (patch) | |
tree | 3fcc832dfece19f6158b8b56e395a6c289e26bbe | |
parent | ae411d84b9eac08c217bdda3aa5fbc6f39d03d85 (diff) |
Implement API function for rendering volume slice to an image.
Change-Id: Iea18967c3b525a8d4507a06e6541c85ed3abb470
Reviewed-by: Tomi Korpipää <tomi.korpipaa@digia.com>
-rw-r--r-- | examples/datavisualization/volumetric/main.cpp | 22 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/volumetric.cpp | 56 | ||||
-rw-r--r-- | examples/datavisualization/volumetric/volumetric.h | 4 | ||||
-rw-r--r-- | src/datavisualization/data/qcustom3dvolume.cpp | 83 | ||||
-rw-r--r-- | src/datavisualization/data/qcustom3dvolume.h | 2 | ||||
-rw-r--r-- | src/datavisualization/engine/shaders/texture3dslice.frag | 4 | ||||
-rw-r--r-- | tests/volumetrictest/main.cpp | 23 | ||||
-rw-r--r-- | tests/volumetrictest/volumetrictest.cpp | 25 | ||||
-rw-r--r-- | tests/volumetrictest/volumetrictest.h | 4 |
9 files changed, 204 insertions, 19 deletions
diff --git a/examples/datavisualization/volumetric/main.cpp b/examples/datavisualization/volumetric/main.cpp index 66f62779..5a90070e 100644 --- a/examples/datavisualization/volumetric/main.cpp +++ b/examples/datavisualization/volumetric/main.cpp @@ -106,22 +106,42 @@ int main(int argc, char **argv) colorTableCheckBox->setText(QStringLiteral("Alternate color table")); colorTableCheckBox->setChecked(false); + QLabel *sliceImageXLabel = new QLabel(widget); + QLabel *sliceImageYLabel = new QLabel(widget); + QLabel *sliceImageZLabel = new QLabel(widget); + sliceImageXLabel->setMinimumSize(QSize(200, 100)); + sliceImageYLabel->setMinimumSize(QSize(200, 200)); + sliceImageZLabel->setMinimumSize(QSize(200, 100)); + sliceImageXLabel->setMaximumSize(QSize(200, 100)); + sliceImageYLabel->setMaximumSize(QSize(200, 200)); + sliceImageZLabel->setMaximumSize(QSize(200, 100)); + sliceImageXLabel->setFrameShape(QFrame::Box); + sliceImageYLabel->setFrameShape(QFrame::Box); + sliceImageZLabel->setFrameShape(QFrame::Box); + sliceImageXLabel->setScaledContents(true); + sliceImageYLabel->setScaledContents(true); + sliceImageZLabel->setScaledContents(true); + vLayout->addWidget(fpsCheckBox); vLayout->addWidget(fpsLabel); vLayout->addWidget(textureDetailGroupBox); vLayout->addWidget(colorTableCheckBox); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); + vLayout->addWidget(sliceImageXLabel); vLayout->addWidget(sliceYCheckBox); vLayout->addWidget(sliceYSlider); + vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); - vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop); + vLayout->addWidget(sliceZSlider); + vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); modifier->setMediumDetailRB(mediumDetailRB); modifier->setHighDetailRB(highDetailRB); modifier->setSliceSliders(sliceXSlider, sliceYSlider, sliceZSlider); + modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel); QObject::connect(fpsCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::setFpsMeasurement); diff --git a/examples/datavisualization/volumetric/volumetric.cpp b/examples/datavisualization/volumetric/volumetric.cpp index b4ea769c..a553ccf8 100644 --- a/examples/datavisualization/volumetric/volumetric.cpp +++ b/examples/datavisualization/volumetric/volumetric.cpp @@ -51,7 +51,10 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_highDetailIndex(0), m_sliceSliderX(0), m_sliceSliderY(0), - m_sliceSliderZ(0) + m_sliceSliderZ(0), + m_sliceLabelX(0), + m_sliceLabelY(0), + m_sliceLabelZ(0) { m_graph->activeTheme()->setType(Q3DTheme::ThemeQt); m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualityNone); @@ -95,12 +98,12 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) } // The alternate color table. - // The first visible layer is a thin yellow one, and rest of the volume uses a smooth gradient. + // The first visible layer is a thin single color, and rest of the volume uses a smooth gradient. for (int i = 1; i < colorTableSize; i++) { if (i < cutOffColorIndex) m_colorTable2[i] = qRgba(0, 0, 0, 0); else if (i < cutOffColorIndex + 4) - m_colorTable2[i] = qRgba(255, 255, 0, 255); + m_colorTable2[i] = qRgba(75, 150, 0, 255); else m_colorTable2[i] = qRgba(i, 0, 255 - i, 255); } @@ -108,6 +111,7 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_volumeItem->setColorTable(m_colorTable1); m_graph->addCustomItem(m_volumeItem); + m_timer.start(0); #else // OpenGL ES2 doesn't support 3D textures, so show a warning label instead @@ -152,6 +156,17 @@ void VolumetricModifier::setHighDetailRB(QRadioButton *button) m_highDetailRB = button; } +void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel) +{ + m_sliceLabelX = xLabel; + m_sliceLabelY = yLabel; + m_sliceLabelZ = zLabel; + + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); +} + void VolumetricModifier::sliceX(int enabled) { if (m_volumeItem) @@ -173,22 +188,40 @@ void VolumetricModifier::sliceZ(int enabled) void VolumetricModifier::adjustSliceX(int value) { m_sliceIndexX = value / (1024 / m_volumeItem->textureWidth()); - if (m_volumeItem && m_volumeItem->sliceIndexX() != -1) - m_volumeItem->setSliceIndexX(m_sliceIndexX); + if (m_sliceIndexX == m_volumeItem->textureWidth()) + m_sliceIndexX--; + if (m_volumeItem) { + if (m_volumeItem->sliceIndexX() != -1) + m_volumeItem->setSliceIndexX(m_sliceIndexX); + m_sliceLabelX->setPixmap(QPixmap::fromImage( + m_volumeItem->renderSlice(Qt::XAxis, m_sliceIndexX))); + } } void VolumetricModifier::adjustSliceY(int value) { m_sliceIndexY = value / (1024 / m_volumeItem->textureHeight()); - if (m_volumeItem && m_volumeItem->sliceIndexY() != -1) - m_volumeItem->setSliceIndexY(m_sliceIndexY); + if (m_sliceIndexY == m_volumeItem->textureHeight()) + m_sliceIndexY--; + if (m_volumeItem) { + if (m_volumeItem->sliceIndexY() != -1) + m_volumeItem->setSliceIndexY(m_sliceIndexY); + m_sliceLabelY->setPixmap(QPixmap::fromImage( + m_volumeItem->renderSlice(Qt::YAxis, m_sliceIndexY))); + } } void VolumetricModifier::adjustSliceZ(int value) { m_sliceIndexZ = value / (1024 / m_volumeItem->textureDepth()); - if (m_volumeItem && m_volumeItem->sliceIndexZ() != -1) - m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + if (m_sliceIndexZ == m_volumeItem->textureDepth()) + m_sliceIndexZ--; + if (m_volumeItem) { + if (m_volumeItem->sliceIndexZ() != -1) + m_volumeItem->setSliceIndexZ(m_sliceIndexZ); + m_sliceLabelZ->setPixmap(QPixmap::fromImage( + m_volumeItem->renderSlice(Qt::ZAxis, m_sliceIndexZ))); + } } void VolumetricModifier::handleZoomLevelChange() @@ -282,6 +315,11 @@ void VolumetricModifier::changeColorTable(int enabled) m_volumeItem->setColorTable(m_colorTable2); else m_volumeItem->setColorTable(m_colorTable1); + + // Rerender image labels + adjustSliceX(m_sliceSliderX->value()); + adjustSliceY(m_sliceSliderY->value()); + adjustSliceZ(m_sliceSliderZ->value()); } int VolumetricModifier::createVolume(int textureSize, int startIndex, int count, diff --git a/examples/datavisualization/volumetric/volumetric.h b/examples/datavisualization/volumetric/volumetric.h index bb7fb3fe..eb8a4172 100644 --- a/examples/datavisualization/volumetric/volumetric.h +++ b/examples/datavisualization/volumetric/volumetric.h @@ -40,6 +40,7 @@ public: void setFpsLabel(QLabel *fpsLabel); void setMediumDetailRB(QRadioButton *button); void setHighDetailRB(QRadioButton *button); + void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel); public slots: void sliceX(int enabled); @@ -82,6 +83,9 @@ private: QSlider *m_sliceSliderZ; QVector<QRgb> m_colorTable1; QVector<QRgb> m_colorTable2; + QLabel *m_sliceLabelX; + QLabel *m_sliceLabelY; + QLabel *m_sliceLabelZ; }; #endif diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp index a7bbae9b..cab79ac0 100644 --- a/src/datavisualization/data/qcustom3dvolume.cpp +++ b/src/datavisualization/data/qcustom3dvolume.cpp @@ -577,6 +577,86 @@ QImage::Format QCustom3DVolume::textureFormat() const return dptrc()->m_textureFormat; } +/*! + * Renders the slice specified by \a index along \a axis into an image. + * The texture format of this object is used. + * + * \return the rendered image of the slice, or a null image if invalid index is specified. + * + * \sa textureFormat + */ +QImage QCustom3DVolume::renderSlice(Qt::Axis axis, int index) +{ + if (index < 0) + return QImage(); + + int x; + int y; + if (axis == Qt::XAxis) { + if (index >= textureWidth()) + return QImage(); + x = textureDepth(); + y = textureHeight(); + } else if (axis == Qt::YAxis) { + if (index >= textureHeight()) + return QImage(); + x = textureWidth(); + y = textureDepth(); + } else { + if (index >= textureDepth()) + return QImage(); + x = textureWidth(); + y = textureHeight(); + } + + int padding = 0; + int pixelWidth = 4; + if (textureFormat() == QImage::Format_Indexed8) { + padding = x % 4; + pixelWidth = 1; + } + QVector<uchar> data((x + padding) * y * pixelWidth); + int frameSize = textureDataWidth() * textureHeight(); + + int dataIndex = 0; + if (axis == Qt::XAxis) { + for (int i = 0; i < y; i++) { + const uchar *p = textureData()->constData() + + (index * pixelWidth) + (textureDataWidth() * i); + for (int j = 0; j < x; j++) { + data[dataIndex++] = *p; + for (int k = 1; k < pixelWidth; k++) + data[dataIndex++] = *(p + k); + p += frameSize; + } + } + } else if (axis == Qt::YAxis) { + for (int i = 0; i < y; i++) { + const uchar *p = textureData()->constData() + (index * textureDataWidth()) + + (frameSize * i); + for (int j = 0; j < (x * pixelWidth); j++) { + data[dataIndex++] = *p; + p++; + } + } + } else { + for (int i = 0; i < y; i++) { + const uchar *p = textureData()->constData() + (index * frameSize) + + (textureDataWidth() * i); + for (int j = 0; j < (x * pixelWidth); j++) { + data[dataIndex++] = *p; + p++; + } + } + } + + QImage image(data.constData(), x, y, x * pixelWidth, textureFormat()); + image.bits(); // Call bits() to detach the new image from local data + if (textureFormat() == QImage::Format_Indexed8) + image.setColorTable(colorTable()); + + return image; +} /*! * \internal @@ -637,9 +717,6 @@ QCustom3DVolumePrivate::QCustom3DVolumePrivate(QCustom3DVolume *q, const QVector if (m_textureDepth < 0) m_textureDepth = 0; - if (m_colorTable.size() != 256) - m_colorTable.clear(); - if (m_textureFormat != QImage::Format_Indexed8) m_textureFormat = QImage::Format_ARGB32; diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h index 498922e8..00733d17 100644 --- a/src/datavisualization/data/qcustom3dvolume.h +++ b/src/datavisualization/data/qcustom3dvolume.h @@ -80,6 +80,8 @@ public: void setTextureFormat(QImage::Format format); QImage::Format textureFormat() const; + QImage renderSlice(Qt::Axis axis, int index); + signals: void textureWidthChanged(int value); void textureHeightChanged(int value); diff --git a/src/datavisualization/engine/shaders/texture3dslice.frag b/src/datavisualization/engine/shaders/texture3dslice.frag index 409ab41d..8870b26d 100644 --- a/src/datavisualization/engine/shaders/texture3dslice.frag +++ b/src/datavisualization/engine/shaders/texture3dslice.frag @@ -33,8 +33,8 @@ void main() { if (rayDir.z < 0) boxBounds.z = -1.0; highp vec3 t = (boxBounds - rayStart) * invRayDir; - tFar = max(t.x, t.y); - tFar = max(tFar, t.z); + tFar = min(t.x, t.y); + tFar = min(tFar, t.z); } highp vec3 xPoint = vec3(volumeSliceIndices.x, 0, 0); diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp index a15caf88..e838c43a 100644 --- a/tests/volumetrictest/main.cpp +++ b/tests/volumetrictest/main.cpp @@ -77,16 +77,37 @@ int main(int argc, char **argv) QLabel *fpsLabel = new QLabel(QStringLiteral("Fps: "), widget); + QLabel *sliceImageXLabel = new QLabel(widget); + QLabel *sliceImageYLabel = new QLabel(widget); + QLabel *sliceImageZLabel = new QLabel(widget); + sliceImageXLabel->setMinimumSize(QSize(200, 100)); + sliceImageYLabel->setMinimumSize(QSize(200, 200)); + sliceImageZLabel->setMinimumSize(QSize(200, 100)); + sliceImageXLabel->setMaximumSize(QSize(200, 100)); + sliceImageYLabel->setMaximumSize(QSize(200, 200)); + sliceImageZLabel->setMaximumSize(QSize(200, 100)); + sliceImageXLabel->setFrameShape(QFrame::Box); + sliceImageYLabel->setFrameShape(QFrame::Box); + sliceImageZLabel->setFrameShape(QFrame::Box); + sliceImageXLabel->setScaledContents(true); + sliceImageYLabel->setScaledContents(true); + sliceImageZLabel->setScaledContents(true); + vLayout->addWidget(fpsLabel); vLayout->addWidget(sliceXCheckBox); vLayout->addWidget(sliceXSlider); + vLayout->addWidget(sliceImageXLabel); vLayout->addWidget(sliceYCheckBox); vLayout->addWidget(sliceYSlider); + vLayout->addWidget(sliceImageYLabel); vLayout->addWidget(sliceZCheckBox); - vLayout->addWidget(sliceZSlider, 1, Qt::AlignTop); + vLayout->addWidget(sliceZSlider); + vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop); + VolumetricModifier *modifier = new VolumetricModifier(graph); modifier->setFpsLabel(fpsLabel); + modifier->setSliceLabels(sliceImageXLabel, sliceImageYLabel, sliceImageZLabel); QObject::connect(sliceXCheckBox, &QCheckBox::stateChanged, modifier, &VolumetricModifier::sliceX); diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp index 701cfc04..3485dd24 100644 --- a/tests/volumetrictest/volumetrictest.cpp +++ b/tests/volumetrictest/volumetrictest.cpp @@ -66,6 +66,8 @@ VolumetricModifier::VolumetricModifier(Q3DScatter *scatter) m_graph->addCustomItem(m_plainItem); //m_graph->setMeasureFps(true); + + QObject::connect(m_graph, &QAbstract3DGraph::currentFpsChanged, this, &VolumetricModifier::handleFpsChange); // QObject::connect(m_graph->scene(), &Q3DScene::viewportChanged, this, @@ -110,6 +112,9 @@ void VolumetricModifier::adjustSliceX(int value) m_volumeItem->setSliceIndexX(m_sliceIndexX); m_volumeItem2->setSliceIndexX(m_sliceIndexX); } + m_sliceLabelX->setPixmap(QPixmap::fromImage( + m_volumeItem2->renderSlice(Qt::XAxis, m_sliceIndexX))); + } void VolumetricModifier::adjustSliceY(int value) @@ -122,6 +127,8 @@ void VolumetricModifier::adjustSliceY(int value) m_volumeItem->setSliceIndexY(m_sliceIndexY); m_volumeItem2->setSliceIndexY(m_sliceIndexY); } + m_sliceLabelY->setPixmap(QPixmap::fromImage( + m_volumeItem2->renderSlice(Qt::YAxis, m_sliceIndexY))); } void VolumetricModifier::adjustSliceZ(int value) @@ -134,6 +141,8 @@ void VolumetricModifier::adjustSliceZ(int value) m_volumeItem->setSliceIndexZ(m_sliceIndexZ); m_volumeItem2->setSliceIndexZ(m_sliceIndexZ); } + m_sliceLabelZ->setPixmap(QPixmap::fromImage( + m_volumeItem2->renderSlice(Qt::ZAxis, m_sliceIndexZ))); } void VolumetricModifier::handleFpsChange() @@ -208,8 +217,8 @@ void VolumetricModifier::createVolume() uchar *p = data; // Change one picture using subtexture replacement -// QImage flipped = logo.mirrored(); -// m_volumeItem->setSubTextureData(101, flipped); + QImage flipped = logo.mirrored(); + m_volumeItem->setSubTextureData(100, flipped); // Clean up the two extra pixels p = data + width - 1; @@ -337,7 +346,7 @@ void VolumetricModifier::createAnotherVolume() // Change one picture using subtexture replacement QImage flipped = logo.mirrored(); - m_volumeItem2->setSubTextureData(101, flipped); + m_volumeItem2->setSubTextureData(100, flipped); } void VolumetricModifier::createYetAnotherVolume() @@ -485,3 +494,13 @@ void VolumetricModifier::createYetAnotherVolume() } +void VolumetricModifier::setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel) +{ + m_sliceLabelX = xLabel; + m_sliceLabelY = yLabel; + m_sliceLabelZ = zLabel; + + adjustSliceX(512); + adjustSliceY(512); + adjustSliceZ(512); +} diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h index 40d88192..f21fd528 100644 --- a/tests/volumetrictest/volumetrictest.h +++ b/tests/volumetrictest/volumetrictest.h @@ -35,6 +35,7 @@ public: ~VolumetricModifier(); void setFpsLabel(QLabel *fpsLabel); + void setSliceLabels(QLabel *xLabel, QLabel *yLabel, QLabel *zLabel); public slots: void sliceX(int enabled); @@ -59,6 +60,9 @@ private: int m_sliceIndexY; int m_sliceIndexZ; QLabel *m_fpsLabel; + QLabel *m_sliceLabelX; + QLabel *m_sliceLabelY; + QLabel *m_sliceLabelZ; }; #endif |