// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkFixedPointVolumeRayCastMIPHelper.h" #include "vtkArrayDispatch.h" #include "vtkCommand.h" #include "vtkDataArray.h" #include "vtkDataArrayRange.h" #include "vtkFixedPointRayCastImage.h" #include "vtkFixedPointVolumeRayCastMapper.h" #include "vtkImageData.h" #include "vtkObjectFactory.h" #include "vtkRectilinearGrid.h" #include "vtkRenderWindow.h" #include "vtkVolume.h" #include "vtkVolumeProperty.h" VTK_ABI_NAMESPACE_BEGIN vtkStandardNewMacro(vtkFixedPointVolumeRayCastMIPHelper); // Construct a new vtkFixedPointVolumeRayCastMIPHelper with default values vtkFixedPointVolumeRayCastMIPHelper::vtkFixedPointVolumeRayCastMIPHelper() = default; // Destruct a vtkFixedPointVolumeRayCastMIPHelper - clean up any memory used vtkFixedPointVolumeRayCastMIPHelper::~vtkFixedPointVolumeRayCastMIPHelper() = default; // This method is called when the interpolation type is nearest neighbor and // the data contains one component. In the inner loop we will compute the // maximum value (in native type). After we have a maximum value for the ray // we will convert it to unsigned short using the scale/shift, then use this // index to lookup the final color/opacity. struct vtkFixedPointMIPHelperGenerateImageOneNNFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol)) { using T = vtk::GetAPIType; auto data = vtk::DataArrayValueRange<1>(dataArray).begin(); VTKKWRCHelper_InitializationAndLoopStartNN VTKKWRCHelper_InitializeMIPOneNN VTKKWRCHelper_SpaceLeapSetup if (cropping) { int maxValueDefined = 0; unsigned short maxIdx = 0; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison()) if (!mapper->CheckIfCropped(pos)) { mapper->ShiftVectorDown(pos, spos); dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2]; if (!maxValueDefined || ((mapper->GetFlipMIPComparison() && *dptr < maxValue) || (!mapper->GetFlipMIPComparison() && *dptr > maxValue))) { maxValue = *dptr; maxIdx = static_cast((maxValue + shift[0]) * scale[0]); maxValueDefined = 1; } } } if (maxValueDefined) { VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr) } else { imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; } } else { unsigned short maxIdx = static_cast((maxValue + shift[0]) * scale[0]); for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, 1, mapper->GetFlipMIPComparison()) mapper->ShiftVectorDown(pos, spos); dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2]; if (mapper->GetFlipMIPComparison()) { maxValue = (*dptr < maxValue) ? (*dptr) : (maxValue); } else { maxValue = (*dptr > maxValue) ? (*dptr) : (maxValue); } maxIdx = static_cast((maxValue + shift[0]) * scale[0]); } VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr) } VTKKWRCHelper_IncrementAndLoopEnd } }; // This method is called when the interpolation type is nearest neighbor and // the data has two or four dependent components. If it is four, they must be // unsigned char components. Compute max of last components in native type, // then use first component to look up a color (2 component data) or first three // as the color directly (four component data). Lookup alpha off the last component. struct vtkFixedPointMIPHelperGenerateImageDependentNNFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol)) { using T = vtk::GetAPIType; auto data = vtk::DataArrayValueRange(dataArray).begin(); VTKKWRCHelper_InitializationAndLoopStartNN VTKKWRCHelper_InitializeMIPMultiNN VTKKWRCHelper_SpaceLeapSetup int maxValueDefined = 0; unsigned short maxIdxS = 0; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_MIPSpaceLeapCheck(maxIdxS, maxValueDefined, mapper->GetFlipMIPComparison()) VTKKWRCHelper_CroppingCheckNN(pos) mapper->ShiftVectorDown(pos, spos); dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2]; if (!maxValueDefined || ((mapper->GetFlipMIPComparison() && *(dptr + components - 1) < maxValue[components - 1]) || (!mapper->GetFlipMIPComparison() && *(dptr + components - 1) > maxValue[components - 1]))) { for (c = 0; c < components; c++) { maxValue[c] = *(dptr + c); } maxIdxS = static_cast( (maxValue[components - 1] + shift[components - 1]) * scale[components - 1]); maxValueDefined = 1; } } if (maxValueDefined) { unsigned short maxIdx[4] = { 0, 0, 0, 0 }; if (components == 2) { maxIdx[0] = static_cast((maxValue[0] + shift[0]) * scale[0]); maxIdx[1] = static_cast((maxValue[1] + shift[1]) * scale[1]); } else { maxIdx[0] = static_cast(maxValue[0]); maxIdx[1] = static_cast(maxValue[1]); maxIdx[2] = static_cast(maxValue[2]); maxIdx[3] = static_cast((maxValue[3] + shift[3]) * scale[3]); } for (c = 0; c < components; c++) { } VTKKWRCHelper_LookupDependentColorUS( colorTable[0], scalarOpacityTable[0], maxIdx, components, imagePtr) } else { imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; } VTKKWRCHelper_IncrementAndLoopEnd } }; // This method is called when the interpolation type is nearest neighbor and // the data has more than one independent components. We compute the max of // each component along the ray in native type, then use the scale/shift to // convert this into an unsigned short index value. We use the index values // to lookup the color/opacity per component, then use the component weights to // blend these into one final color. struct vtkFixedPointMIPHelperGenerateImageIndependentNNFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vol) { using T = vtk::GetAPIType; auto data = vtk::DataArrayValueRange(dataArray).begin(); VTKKWRCHelper_InitializeWeights VTKKWRCHelper_InitializationAndLoopStartNN VTKKWRCHelper_InitializeMIPMultiNN VTKKWRCHelper_SpaceLeapSetupMulti int maxValueDefined = 0; unsigned short maxIdx[4] = { 0, 0, 0, 0 }; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_CroppingCheckNN(pos) VTKKWRCHelper_MIPSpaceLeapPopulateMulti(maxIdx, mapper->GetFlipMIPComparison()) mapper->ShiftVectorDown(pos, spos); dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2]; if (!maxValueDefined) { for (c = 0; c < components; c++) { maxValue[c] = *(dptr + c); maxIdx[c] = static_cast((maxValue[c] + shift[c]) * scale[c]); } maxValueDefined = 1; } else { for (c = 0; c < components; c++) { if (VTKKWRCHelper_MIPSpaceLeapCheckMulti(c, mapper->GetFlipMIPComparison()) && ((mapper->GetFlipMIPComparison() && *(dptr + c) < maxValue[c]) || (!mapper->GetFlipMIPComparison() && *(dptr + c) > maxValue[c]))) { maxValue[c] = *(dptr + c); maxIdx[c] = static_cast((maxValue[c] + shift[c]) * scale[c]); } } } } imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; if (maxValueDefined) { VTKKWRCHelper_LookupAndCombineIndependentColorsMax( colorTable, scalarOpacityTable, maxIdx, weights, components, imagePtr) } VTKKWRCHelper_IncrementAndLoopEnd } }; // This method is called when the interpolation type is linear, the // data contains one component and scale = 1.0 and shift = 0.0. This is // the simple case were we do not need to apply scale/shift in the // inner loop. In the inner loop we compute the eight cell vertex values // (if we have changed cells). We compute our weights within the cell // according to our fractional position within the cell, and apply trilinear // interpolation to compute the index. We find the maximum index along // the ray, and then use this to look up a final color. struct vtkFixedPointMIPHelperGenerateImageOneSimpleTrilinFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol)) { auto data = vtk::DataArrayValueRange<1>(dataArray).begin(); VTKKWRCHelper_InitializationAndLoopStartTrilin VTKKWRCHelper_InitializeMIPOneTrilin VTKKWRCHelper_SpaceLeapSetup int maxValueDefined = 0; unsigned short maxIdx = 0; unsigned int maxScalar = 0; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison()) VTKKWRCHelper_CroppingCheckTrilin(pos) mapper->ShiftVectorDown(pos, spos); if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2]) { oldSPos[0] = spos[0]; oldSPos[1] = spos[1]; oldSPos[2] = spos[2]; dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2]; VTKKWRCHelper_GetCellScalarValuesSimple(dptr) if (mapper->GetFlipMIPComparison()) { maxScalar = (A < B) ? (A) : (B); maxScalar = (C < maxScalar) ? (C) : (maxScalar); maxScalar = (D < maxScalar) ? (D) : (maxScalar); maxScalar = (E < maxScalar) ? (E) : (maxScalar); maxScalar = (F < maxScalar) ? (F) : (maxScalar); maxScalar = (G < maxScalar) ? (G) : (maxScalar); maxScalar = (H < maxScalar) ? (H) : (maxScalar); } else { maxScalar = (A > B) ? (A) : (B); maxScalar = (C > maxScalar) ? (C) : (maxScalar); maxScalar = (D > maxScalar) ? (D) : (maxScalar); maxScalar = (E > maxScalar) ? (E) : (maxScalar); maxScalar = (F > maxScalar) ? (F) : (maxScalar); maxScalar = (G > maxScalar) ? (G) : (maxScalar); maxScalar = (H > maxScalar) ? (H) : (maxScalar); } } if (!maxValueDefined || ((mapper->GetFlipMIPComparison() && maxScalar < static_cast(maxValue)) || (!mapper->GetFlipMIPComparison() && maxScalar > static_cast(maxValue)))) { VTKKWRCHelper_ComputeWeights(pos) VTKKWRCHelper_InterpolateScalar(val) if (!maxValueDefined || ((mapper->GetFlipMIPComparison() && val < maxValue) || (!mapper->GetFlipMIPComparison() && val > maxValue))) { maxValue = val; maxIdx = maxValue; maxValueDefined = 1; } } } if (maxValueDefined) { VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr) } else { imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; } VTKKWRCHelper_IncrementAndLoopEnd } }; // This method is called when the interpolation type is linear, the // data contains one component and scale != 1.0 or shift != 0.0. This // means that we need to apply scale/shift in the inner loop to compute // an unsigned short index value. In the inner loop we compute the eight cell // vertex values (as unsigned short indices, if we have changed cells). We // compute our weights within the cell according to our fractional position // within the cell, and apply trilinear interpolation to compute the index. // We find the maximum index along the ray, and then use this to look up a // final color. struct vtkFixedPointMIPHelperGenerateImageOneTrilinFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol)) { auto data = vtk::DataArrayValueRange<1>(dataArray).begin(); VTKKWRCHelper_InitializationAndLoopStartTrilin VTKKWRCHelper_InitializeMIPOneTrilin VTKKWRCHelper_SpaceLeapSetup int maxValueDefined = 0; unsigned short maxIdx = 0; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_CroppingCheckTrilin(pos) VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison()) mapper->ShiftVectorDown(pos, spos); if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2]) { oldSPos[0] = spos[0]; oldSPos[1] = spos[1]; oldSPos[2] = spos[2]; dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2]; VTKKWRCHelper_GetCellScalarValues(dptr, scale[0], shift[0]) } VTKKWRCHelper_ComputeWeights(pos) VTKKWRCHelper_InterpolateScalar(val) if (!maxValueDefined || ((mapper->GetFlipMIPComparison() && val < maxValue) || (!mapper->GetFlipMIPComparison() && val > maxValue))) { maxValue = val; maxIdx = maxValue; maxValueDefined = 1; } } if (maxValueDefined) { VTKKWRCHelper_LookupColorMax(colorTable[0], scalarOpacityTable[0], maxIdx, imagePtr) } else { imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; } VTKKWRCHelper_IncrementAndLoopEnd } }; // This method is used when the interpolation type is linear, the data has // two or four components and the components are not considered independent. // For four component d>>(VTKKW_FP_SHIFT - 8));ata, the data must be unsigned char in type. In the // inner loop we get the data value for the eight cell corners (if we have // changed cells) for all components as unsigned shorts (we use the // scale/shift to ensure the correct range). We compute our weights within // the cell according to our fractional position within the cell, and apply // trilinear interpolation to compute the index values. For two component data, // We use the first index to lookup a color and the second to look up an opacity // for this sample. For four component data we use the first three components // directly as a color, then we look up the opacity using the fourth component. // We then composite this into the color computed so far along the ray, and // check if we can terminate at this point (if the accumulated opacity is // higher than some threshold). struct vtkFixedPointMIPHelperGenerateImageDependentTrilinFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vtkNotUsed(vol)) { auto data = vtk::DataArrayValueRange(dataArray).begin(); VTKKWRCHelper_InitializationAndLoopStartTrilin VTKKWRCHelper_InitializeMIPMultiTrilin VTKKWRCHelper_SpaceLeapSetup int maxValueDefined = 0; unsigned short maxIdx = 0; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_CroppingCheckTrilin(pos) VTKKWRCHelper_MIPSpaceLeapCheck(maxIdx, maxValueDefined, mapper->GetFlipMIPComparison()) mapper->ShiftVectorDown(pos, spos); if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2]) { oldSPos[0] = spos[0]; oldSPos[1] = spos[1]; oldSPos[2] = spos[2]; if (components == 2) { for (c = 0; c < components; c++) { dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c; VTKKWRCHelper_GetCellComponentScalarValues(dptr, c, scale[c], shift[c]) } } else { for (c = 0; c < 3; c++) { dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c; VTKKWRCHelper_GetCellComponentRawScalarValues(dptr, c) } dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c; VTKKWRCHelper_GetCellComponentScalarValues(dptr, 3, scale[3], shift[3]) } } VTKKWRCHelper_ComputeWeights(pos) VTKKWRCHelper_InterpolateScalarComponent(val, c, components) if (!maxValueDefined || ((mapper->GetFlipMIPComparison() && (val[components - 1] < maxValue[components - 1])) || (!mapper->GetFlipMIPComparison() && (val[components - 1] > maxValue[components - 1])))) { for (c = 0; c < components; c++) { maxValue[c] = val[c]; } maxIdx = static_cast( (maxValue[components - 1] + shift[components - 1]) * scale[components - 1]); maxValueDefined = 1; } } if (maxValueDefined) { VTKKWRCHelper_LookupDependentColorUS( colorTable[0], scalarOpacityTable[0], maxValue, components, imagePtr) } else { imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; } VTKKWRCHelper_IncrementAndLoopEnd } }; // This method is used when the interpolation type is linear, the data has // more than one component and the components are considered independent. In // the inner loop we get the data value for the eight cell corners (if we have // changed cells) for all components as an unsigned shorts (we have to use the // scale/shift to ensure that we obtained unsigned short indices) We compute // our weights within the cell according to our fractional position within the // cell, and apply trilinear interpolation to compute a value for each // component. We do this for each sample along the ray to find a maximum value // per component, then we look up a color/opacity for each component and blend // them according to the component weights. struct vtkFixedPointMIPHelperGenerateImageIndependentTrilinFunctor { template void operator()(TArray* dataArray, int threadID, int threadCount, vtkFixedPointVolumeRayCastMapper* mapper, vtkVolume* vol) { auto data = vtk::DataArrayValueRange(dataArray).begin(); VTKKWRCHelper_InitializeWeights VTKKWRCHelper_InitializationAndLoopStartTrilin VTKKWRCHelper_InitializeMIPMultiTrilin int maxValueDefined = 0; for (k = 0; k < numSteps; k++) { if (k) { mapper->FixedPointIncrement(pos, dir); } VTKKWRCHelper_CroppingCheckTrilin(pos) mapper->ShiftVectorDown(pos, spos); if (spos[0] != oldSPos[0] || spos[1] != oldSPos[1] || spos[2] != oldSPos[2]) { oldSPos[0] = spos[0]; oldSPos[1] = spos[1]; oldSPos[2] = spos[2]; for (c = 0; c < components; c++) { dptr = data + spos[0] * inc[0] + spos[1] * inc[1] + spos[2] * inc[2] + c; VTKKWRCHelper_GetCellComponentScalarValues(dptr, c, scale[c], shift[c]) } } VTKKWRCHelper_ComputeWeights(pos) VTKKWRCHelper_InterpolateScalarComponent(val, c, components) if (!maxValueDefined) { for (c = 0; c < components; c++) { maxValue[c] = val[c]; } maxValueDefined = 1; } else { for (c = 0; c < components; c++) { if ((mapper->GetFlipMIPComparison() && val[c] < maxValue[c]) || (!mapper->GetFlipMIPComparison() && val[c] > maxValue[c])) { maxValue[c] = val[c]; } } } } imagePtr[0] = imagePtr[1] = imagePtr[2] = imagePtr[3] = 0; if (maxValueDefined) { VTKKWRCHelper_LookupAndCombineIndependentColorsMax( colorTable, scalarOpacityTable, maxValue, weights, components, imagePtr) } VTKKWRCHelper_IncrementAndLoopEnd } }; void vtkFixedPointVolumeRayCastMIPHelper::GenerateImage( int threadID, int threadCount, vtkVolume* vol, vtkFixedPointVolumeRayCastMapper* mapper) { auto* dataArray = mapper->GetCurrentScalars(); // Nearest Neighbor interpolate if (mapper->ShouldUseNearestNeighborInterpolation(vol)) { // One component data if (mapper->GetCurrentScalars()->GetNumberOfComponents() == 1) { vtkFixedPointMIPHelperGenerateImageOneNNFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } // More that one independent components else if (vol->GetProperty()->GetIndependentComponents()) { vtkFixedPointMIPHelperGenerateImageIndependentNNFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } // Dependent (color) components else { vtkFixedPointMIPHelperGenerateImageDependentNNFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } } // Trilinear Interpolation else { // One component if (mapper->GetCurrentScalars()->GetNumberOfComponents() == 1) { // Scale == 1.0 and shift == 0.0 - simple case (faster) if (mapper->GetTableScale()[0] == 1.0 && mapper->GetTableShift()[0] == 0.0) { vtkFixedPointMIPHelperGenerateImageOneSimpleTrilinFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } // Scale != 1.0 or shift != 0.0 - must apply scale/shift in inner loop else { vtkFixedPointMIPHelperGenerateImageOneTrilinFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } } // Independent components (more than one) else if (vol->GetProperty()->GetIndependentComponents()) { vtkFixedPointMIPHelperGenerateImageIndependentTrilinFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } // Dependent components else { vtkFixedPointMIPHelperGenerateImageDependentTrilinFunctor functor; if (!vtkArrayDispatch::Dispatch::Execute( dataArray, functor, threadID, threadCount, mapper, vol)) { functor(dataArray, threadID, threadCount, mapper, vol); } } } } // Print method for vtkFixedPointVolumeRayCastMIPHelper void vtkFixedPointVolumeRayCastMIPHelper::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); } VTK_ABI_NAMESPACE_END