summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/datavisualization/data/qcustom3dvolume.cpp130
-rw-r--r--src/datavisualization/data/qcustom3dvolume.h4
-rw-r--r--tests/volumetrictest/logo_no_padding.pngbin0 -> 2278 bytes
-rw-r--r--tests/volumetrictest/main.cpp10
-rw-r--r--tests/volumetrictest/volumetrictest.cpp74
-rw-r--r--tests/volumetrictest/volumetrictest.h3
-rw-r--r--tests/volumetrictest/volumetrictest.qrc1
7 files changed, 183 insertions, 39 deletions
diff --git a/src/datavisualization/data/qcustom3dvolume.cpp b/src/datavisualization/data/qcustom3dvolume.cpp
index ec5129ff..393533c0 100644
--- a/src/datavisualization/data/qcustom3dvolume.cpp
+++ b/src/datavisualization/data/qcustom3dvolume.cpp
@@ -428,7 +428,8 @@ QVector<QRgb> QCustom3DVolume::colorTable() const
* \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is
* QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need
* to be added to each X-line of the \a data. You can get the padded byte count with
- * textureDataWidth() function.
+ * textureDataWidth() function. The padding bytes should indicate an fully transparent color
+ * to avoid rendering artifacts.
*
* Defaults to \c{0}.
*
@@ -531,29 +532,76 @@ QVector<uchar> *QCustom3DVolume::textureData() const
}
/*!
- * This function allows setting a single 2D subtexture of the 3D texture.
- * The \a depthIndex parameter specifies the subtexture to set.
- * The texture\a data must be in the format specified by textureFormat property and have size of
- * (\c{textureDataWidth * textureHeight * texture format color depth in bytes}).
+ * This function allows setting a single 2D subtexture of the 3D texture along the specified
+ * \a axis of the volume.
+ * The \a index parameter specifies the subtexture to set.
+ * The texture \a data must be in the format specified by textureFormat property and have size of
+ * the cross-section of the volume texture along the specified axis multiplied by
+ * the texture format color depth in bytes.
+ * The \a data is expected to be ordered similarly to the data in images produced by renderSlice()
+ * method along the same axis.
*
- * \note Each X-line of the data needs to be 32bit aligned. If the textureFormat is
- * QImage::Format_Indexed8 and textureWidth is not divisible by four, padding bytes need
- * to be added to each X-line of the \a data.
+ * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis.
+ * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four,
+ * padding bytes need to be added to each X-line of the \a data in cases it is not already
+ * properly aligned. The padding bytes should indicate an fully transparent color to avoid
+ * rendering artifacts.
*
- * \sa textureData
+ * \sa textureData, renderSlice()
*/
-void QCustom3DVolume::setSubTextureData(int depthIndex, const uchar *data)
+void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const uchar *data)
{
if (data) {
- int frameSize = textureDataWidth() * dptr()->m_textureHeight;
- int startIndex = depthIndex * frameSize;
+ int lineSize = textureDataWidth();
+ int frameSize = lineSize * dptr()->m_textureHeight;
+ int dataSize = dptr()->m_textureData->size();
+ int pixelWidth = (dptr()->m_textureFormat == QImage::Format_Indexed8) ? 1 : 4;
+ int targetIndex;
+ uchar *dataPtr = dptr()->m_textureData->data();
+ bool invalid = (index < 0);
+ if (axis == Qt::XAxis) {
+ targetIndex = index * pixelWidth;
+ if (index >= dptr()->m_textureWidth
+ || (frameSize * (dptr()->m_textureDepth - 1) + targetIndex) > dataSize) {
+ invalid = true;
+ }
+ } else if (axis == Qt::YAxis) {
+ targetIndex = (index * lineSize) + (frameSize * (dptr()->m_textureDepth - 1));
+ if (index >= dptr()->m_textureHeight || (targetIndex + lineSize > dataSize))
+ invalid = true;
+ } else {
+ targetIndex = index * frameSize;
+ if (index >= dptr()->m_textureDepth || ((targetIndex + frameSize) > dataSize))
+ invalid = true;
+ }
- if (depthIndex >= dptr()->m_textureDepth
- || (startIndex + frameSize) > dptr()->m_textureData->size()) {
+ if (invalid) {
qWarning() << __FUNCTION__ << "Attempted to set invalid subtexture.";
} else {
- void *subTexPtr = dptr()->m_textureData->data() + startIndex;
- memcpy(subTexPtr, static_cast<const void *>(data), frameSize);
+ const uchar *sourcePtr = data;
+ uchar *targetPtr = dataPtr + targetIndex;
+ if (axis == Qt::XAxis) {
+ int targetWidth = dptr()->m_textureDepth;
+ int targetHeight = dptr()->m_textureHeight;
+ for (int i = 0; i < targetHeight; i++) {
+ targetPtr = dataPtr + targetIndex + (lineSize * i);
+ for (int j = 0; j < targetWidth; j++) {
+ for (int k = 0; k < pixelWidth; k++)
+ *targetPtr++ = *sourcePtr++;
+ targetPtr += (frameSize - pixelWidth);
+ }
+ }
+ } else if (axis == Qt::YAxis) {
+ int targetHeight = dptr()->m_textureDepth;
+ for (int i = 0; i < targetHeight; i++){
+ for (int j = 0; j < lineSize; j++)
+ *targetPtr++ = *sourcePtr++;
+ targetPtr -= (frameSize + lineSize);
+ }
+ } else {
+ void *subTexPtr = dataPtr + targetIndex;
+ memcpy(subTexPtr, static_cast<const void *>(data), frameSize);
+ }
dptr()->m_dirtyBitsVolume.textureDataDirty = true;
emit textureDataChanged(dptr()->m_textureData);
emit dptr()->needUpdate();
@@ -564,18 +612,42 @@ void QCustom3DVolume::setSubTextureData(int depthIndex, const uchar *data)
}
/*!
- * This function allows setting a single 2D subtexture of the 3D texture to a source \a image;
- * The \a depthIndex parameter specifies the subtexture to set.
- * The image must be in the format specified by the textureFormat property if the textureFormat
- * is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted to that format.
- * The image must have the size of (\c{textureWidth * textureHeight}).
- *
- * \sa textureData
- */
-void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image)
-{
- if (image.width() == dptr()->m_textureWidth
- && image.height() == dptr()->m_textureHeight
+ * This function allows setting a single 2D subtexture of the 3D texture along the specified
+ * \a axis of the volume.
+ * The \a index parameter specifies the subtexture to set.
+ * The source \a image must be in the format specified by the textureFormat property if the
+ * textureFormat is indexed. If the textureFormat is QImage::Format_ARGB32, the image is converted
+ * to that format. The image must have the size of the cross-section of the volume texture along
+ * the specified axis. The orientation of the image should correspond to the orientation of
+ * the slice image produced by renderSlice() method along the same axis.
+ *
+ * \note Each X-line of the data needs to be 32bit aligned when targeting Y-axis or Z-axis.
+ * If the textureFormat is QImage::Format_Indexed8 and textureWidth is not divisible by four,
+ * padding bytes need to be added to each X-line of the \a data in cases it is not already
+ * properly aligned. The padding bytes should indicate an fully transparent color to avoid
+ * rendering artifacts. It is not guaranteed QImage will do this automatically.
+ *
+ * \sa textureData, renderSlice()
+ */
+void QCustom3DVolume::setSubTextureData(Qt::Axis axis, int index, const QImage &image)
+{
+ int sourceWidth = image.width();
+ int sourceHeight = image.height();
+ int targetWidth;
+ int targetHeight;
+ if (axis == Qt::XAxis) {
+ targetWidth = dptr()->m_textureDepth;
+ targetHeight = dptr()->m_textureHeight;
+ } else if (axis == Qt::YAxis) {
+ targetWidth = dptr()->m_textureWidth;
+ targetHeight = dptr()->m_textureDepth;
+ } else {
+ targetWidth = dptr()->m_textureWidth;
+ targetHeight = dptr()->m_textureHeight;
+ }
+
+ if (sourceWidth == targetWidth
+ && sourceHeight == targetHeight
&& (image.format() == dptr()->m_textureFormat
|| dptr()->m_textureFormat == QImage::Format_ARGB32)) {
QImage convertedImage;
@@ -585,7 +657,7 @@ void QCustom3DVolume::setSubTextureData(int depthIndex, const QImage &image)
} else {
convertedImage = image;
}
- setSubTextureData(depthIndex, convertedImage.bits());
+ setSubTextureData(axis, index, convertedImage.bits());
} else {
qWarning() << __FUNCTION__ << "Invalid image size or format.";
}
diff --git a/src/datavisualization/data/qcustom3dvolume.h b/src/datavisualization/data/qcustom3dvolume.h
index 23ae07d9..04434263 100644
--- a/src/datavisualization/data/qcustom3dvolume.h
+++ b/src/datavisualization/data/qcustom3dvolume.h
@@ -76,8 +76,8 @@ public:
void setTextureData(QVector<uchar> *data);
QVector<uchar> *createTextureData(const QVector<QImage *> &images);
QVector<uchar> *textureData() const;
- void setSubTextureData(int depthIndex, const uchar *data);
- void setSubTextureData(int depthIndex, const QImage &image);
+ void setSubTextureData(Qt::Axis axis, int index, const uchar *data);
+ void setSubTextureData(Qt::Axis axis, int index, const QImage &image);
void setTextureFormat(QImage::Format format);
QImage::Format textureFormat() const;
diff --git a/tests/volumetrictest/logo_no_padding.png b/tests/volumetrictest/logo_no_padding.png
new file mode 100644
index 00000000..714234aa
--- /dev/null
+++ b/tests/volumetrictest/logo_no_padding.png
Binary files differ
diff --git a/tests/volumetrictest/main.cpp b/tests/volumetrictest/main.cpp
index e838c43a..ba5ba6d3 100644
--- a/tests/volumetrictest/main.cpp
+++ b/tests/volumetrictest/main.cpp
@@ -26,6 +26,7 @@
#include <QtWidgets/QSlider>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QLabel>
+#include <QtWidgets/QPushButton>
#include <QtGui/QScreen>
int main(int argc, char **argv)
@@ -93,6 +94,9 @@ int main(int argc, char **argv)
sliceImageYLabel->setScaledContents(true);
sliceImageZLabel->setScaledContents(true);
+ QPushButton *testSubTextureSetting = new QPushButton(widget);
+ testSubTextureSetting->setText(QStringLiteral("Test subtexture settings"));
+
vLayout->addWidget(fpsLabel);
vLayout->addWidget(sliceXCheckBox);
vLayout->addWidget(sliceXSlider);
@@ -102,8 +106,8 @@ int main(int argc, char **argv)
vLayout->addWidget(sliceImageYLabel);
vLayout->addWidget(sliceZCheckBox);
vLayout->addWidget(sliceZSlider);
- vLayout->addWidget(sliceImageZLabel, 1, Qt::AlignTop);
-
+ vLayout->addWidget(sliceImageZLabel);
+ vLayout->addWidget(testSubTextureSetting, 1, Qt::AlignTop);
VolumetricModifier *modifier = new VolumetricModifier(graph);
modifier->setFpsLabel(fpsLabel);
@@ -121,6 +125,8 @@ int main(int argc, char **argv)
&VolumetricModifier::adjustSliceY);
QObject::connect(sliceZSlider, &QSlider::valueChanged, modifier,
&VolumetricModifier::adjustSliceZ);
+ QObject::connect(testSubTextureSetting, &QPushButton::clicked, modifier,
+ &VolumetricModifier::testSubtextureSetting);
widget->show();
return app.exec();
diff --git a/tests/volumetrictest/volumetrictest.cpp b/tests/volumetrictest/volumetrictest.cpp
index 53b0a875..555cc286 100644
--- a/tests/volumetrictest/volumetrictest.cpp
+++ b/tests/volumetrictest/volumetrictest.cpp
@@ -168,7 +168,67 @@ void VolumetricModifier::handleFpsChange()
// const QString sceneDimensionsFormat = QStringLiteral("%1 x %2");
// m_fpsLabel->setText(sceneDimensionsFormat
// .arg(m_graph->scene()->viewport().width())
-// .arg(m_graph->scene()->viewport().height()));
+ // .arg(m_graph->scene()->viewport().height()));
+}
+
+void VolumetricModifier::testSubtextureSetting()
+{
+ // Setting the rendered Slice as subtexture should result in identical volume
+ QVector<uchar> dataBefore = *m_volumeItem->textureData();
+ dataBefore[0] = dataBefore.at(0); // Make sure we are detached
+
+ checkRenderCase(1, Qt::XAxis, 56, dataBefore, m_volumeItem);
+ checkRenderCase(2, Qt::YAxis, 64, dataBefore, m_volumeItem);
+ checkRenderCase(3, Qt::ZAxis, 87, dataBefore, m_volumeItem);
+
+ checkRenderCase(4, Qt::XAxis, 0, dataBefore, m_volumeItem);
+ checkRenderCase(5, Qt::YAxis, 0, dataBefore, m_volumeItem);
+ checkRenderCase(6, Qt::ZAxis, 0, dataBefore, m_volumeItem);
+
+ checkRenderCase(7, Qt::XAxis, m_volumeItem->textureWidth() - 1, dataBefore, m_volumeItem);
+ checkRenderCase(8, Qt::YAxis, m_volumeItem->textureHeight() - 1, dataBefore, m_volumeItem);
+ checkRenderCase(9, Qt::ZAxis, m_volumeItem->textureDepth() - 1, dataBefore, m_volumeItem);
+
+ dataBefore = *m_volumeItem2->textureData();
+ dataBefore[0] = dataBefore.at(0); // Make sure we are detached
+
+ checkRenderCase(11, Qt::XAxis, 56, dataBefore, m_volumeItem2);
+ checkRenderCase(12, Qt::YAxis, 64, dataBefore, m_volumeItem2);
+ checkRenderCase(13, Qt::ZAxis, 87, dataBefore, m_volumeItem2);
+
+ checkRenderCase(14, Qt::XAxis, 0, dataBefore, m_volumeItem2);
+ checkRenderCase(15, Qt::YAxis, 0, dataBefore, m_volumeItem2);
+ checkRenderCase(16, Qt::ZAxis, 0, dataBefore, m_volumeItem2);
+
+ checkRenderCase(17, Qt::XAxis, m_volumeItem2->textureWidth() - 1, dataBefore, m_volumeItem2);
+ checkRenderCase(18, Qt::YAxis, m_volumeItem2->textureHeight() - 1, dataBefore, m_volumeItem2);
+ checkRenderCase(19, Qt::ZAxis, m_volumeItem2->textureDepth() - 1, dataBefore, m_volumeItem2);
+
+ // Do some visible swaps on volume 3
+ QImage slice = m_volumeItem3->renderSlice(Qt::XAxis, 144);
+ slice = slice.mirrored();
+ m_volumeItem3->setSubTextureData(Qt::XAxis, 144, slice);
+
+ slice = m_volumeItem3->renderSlice(Qt::YAxis, 80);
+ slice = slice.mirrored();
+ m_volumeItem3->setSubTextureData(Qt::YAxis, 80, slice);
+
+ slice = m_volumeItem3->renderSlice(Qt::ZAxis, 190);
+ slice = slice.mirrored(true, false);
+ m_volumeItem3->setSubTextureData(Qt::ZAxis, 190, slice);
+}
+
+void VolumetricModifier::checkRenderCase(int id, Qt::Axis axis, int index,
+ const QVector<uchar> &dataBefore,
+ QCustom3DVolume *volumeItem)
+{
+ QImage slice = volumeItem->renderSlice(axis, index);
+ volumeItem->setSubTextureData(axis, index, slice);
+
+ if (dataBefore == *volumeItem->textureData())
+ qDebug() << __FUNCTION__ << "Case:" << id << "Vectors identical";
+ else
+ qDebug() << __FUNCTION__ << "Case:" << id << "BUG: VECTORS DIFFER!";
}
void VolumetricModifier::createVolume()
@@ -179,7 +239,8 @@ void VolumetricModifier::createVolume()
m_volumeItem->setPosition(QVector3D(-0.5f, 0.6f, 0.0f));
QImage logo;
- logo.load(QStringLiteral(":/logo.png"));
+ logo.load(QStringLiteral(":/logo_no_padding.png"));
+ //logo.load(QStringLiteral(":/logo.png"));
qDebug() << "image dimensions:" << logo.width() << logo.height()
<< logo.byteCount() << (logo.width() * logo.height())
<< logo.bytesPerLine();
@@ -233,7 +294,7 @@ void VolumetricModifier::createVolume()
// Change one picture using subtexture replacement
QImage flipped = logo.mirrored();
- m_volumeItem->setSubTextureData(100, flipped);
+ m_volumeItem->setSubTextureData(Qt::ZAxis, 100, flipped);
// Clean up the two extra pixels
p = data + width - 1;
@@ -331,7 +392,8 @@ void VolumetricModifier::createAnotherVolume()
m_volumeItem2->setPosition(QVector3D(0.5f, -0.5f, 0.0f));
QImage logo;
- logo.load(QStringLiteral(":/logo.png"));
+ logo.load(QStringLiteral(":/logo_no_padding.png"));
+ //logo.load(QStringLiteral(":/logo.png"));
logo = logo.convertToFormat(QImage::Format_ARGB8555_Premultiplied);
qDebug() << "second image dimensions:" << logo.width() << logo.height()
<< logo.byteCount() << (logo.width() * logo.height())
@@ -361,8 +423,8 @@ void VolumetricModifier::createAnotherVolume()
// Change one picture using subtexture replacement
QImage flipped = logo.mirrored();
- m_volumeItem2->setSubTextureData(100, flipped);
- m_volumeItem2->setAlphaMultiplier(0.2f);
+ m_volumeItem2->setSubTextureData(Qt::ZAxis, 100, flipped);
+ //m_volumeItem2->setAlphaMultiplier(0.2f);
m_volumeItem2->setPreserveOpacity(false);
}
diff --git a/tests/volumetrictest/volumetrictest.h b/tests/volumetrictest/volumetrictest.h
index f21fd528..b1b98455 100644
--- a/tests/volumetrictest/volumetrictest.h
+++ b/tests/volumetrictest/volumetrictest.h
@@ -45,11 +45,14 @@ public slots:
void adjustSliceY(int value);
void adjustSliceZ(int value);
void handleFpsChange();
+ void testSubtextureSetting();
private:
void createVolume();
void createAnotherVolume();
void createYetAnotherVolume();
+ void checkRenderCase(int id, Qt::Axis axis, int index, const QVector<uchar> &dataBefore,
+ QCustom3DVolume *volumeItem);
Q3DScatter *m_graph;
QCustom3DVolume *m_volumeItem;
diff --git a/tests/volumetrictest/volumetrictest.qrc b/tests/volumetrictest/volumetrictest.qrc
index 5b9623f0..7cd8533b 100644
--- a/tests/volumetrictest/volumetrictest.qrc
+++ b/tests/volumetrictest/volumetrictest.qrc
@@ -2,5 +2,6 @@
<qresource prefix="/">
<file>logo.png</file>
<file alias="mesh">cubeFilledFlat.obj</file>
+ <file>logo_no_padding.png</file>
</qresource>
</RCC>