/*========================================================================= Program: Visualization Toolkit Module: vtkImageMapper3D.cxx Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ #include "vtkImageMapper3D.h" #include "vtkAbstractTransform.h" #include "vtkDataArray.h" #include "vtkExecutive.h" #include "vtkGarbageCollector.h" #include "vtkGraphicsFactory.h" #include "vtkImageData.h" #include "vtkImageProperty.h" #include "vtkImageSlice.h" #include "vtkInformation.h" #include "vtkInformationVector.h" #include "vtkMath.h" #include "vtkMatrix3x3.h" #include "vtkMatrix4x4.h" #include "vtkMultiThreader.h" #include "vtkPlane.h" #include "vtkRenderer.h" #include "vtkScalarsToColors.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtkTemplateAliasMacro.h" //------------------------------------------------------------------------------ vtkImageMapper3D::vtkImageMapper3D() { // Default color conversion this->DefaultLookupTable = vtkScalarsToColors::New(); this->DefaultLookupTable->SetVectorModeToRGBColors(); this->Threader = vtkMultiThreader::New(); this->NumberOfThreads = this->Threader->GetNumberOfThreads(); this->Streaming = 0; this->Border = 0; this->Background = 0; this->SlicePlane = vtkPlane::New(); this->SliceFacesCamera = 0; this->SliceAtFocalPoint = 0; this->DataToWorldMatrix = vtkMatrix4x4::New(); this->CurrentProp = nullptr; this->CurrentRenderer = nullptr; this->MatteEnable = true; this->ColorEnable = true; this->DepthEnable = true; this->DataOrigin[0] = 0.0; this->DataOrigin[1] = 0.0; this->DataOrigin[2] = 0.0; this->DataSpacing[0] = 1.0; this->DataSpacing[1] = 1.0; this->DataSpacing[2] = 1.0; vtkMatrix3x3::Identity(this->DataDirection); this->DataWholeExtent[0] = 0; this->DataWholeExtent[1] = 0; this->DataWholeExtent[2] = 0; this->DataWholeExtent[3] = 0; this->DataWholeExtent[4] = 0; this->DataWholeExtent[5] = 0; } //------------------------------------------------------------------------------ vtkImageMapper3D::~vtkImageMapper3D() { if (this->DefaultLookupTable) { this->DefaultLookupTable->Delete(); } if (this->Threader) { this->Threader->Delete(); } if (this->SlicePlane) { this->SlicePlane->Delete(); } if (this->DataToWorldMatrix) { this->DataToWorldMatrix->Delete(); } } //------------------------------------------------------------------------------ void vtkImageMapper3D::SetInputData(vtkImageData* input) { this->SetInputDataInternal(0, input); } //------------------------------------------------------------------------------ vtkImageData* vtkImageMapper3D::GetInput() { if (this->GetNumberOfInputConnections(0) < 1) { return nullptr; } return vtkImageData::SafeDownCast(this->GetExecutive()->GetInputData(0, 0)); } //------------------------------------------------------------------------------ void vtkImageMapper3D::ReleaseGraphicsResources(vtkWindow*) { // see subclass for implementation } //------------------------------------------------------------------------------ void vtkImageMapper3D::Render(vtkRenderer*, vtkImageSlice*) { // see subclass for implementation } //------------------------------------------------------------------------------ vtkTypeBool vtkImageMapper3D::ProcessRequest( vtkInformation* request, vtkInformationVector** inputVector, vtkInformationVector* outputVector) { if (request->Has(vtkStreamingDemandDrivenPipeline::REQUEST_INFORMATION())) { vtkInformation* inInfo = inputVector[0]->GetInformationObject(0); inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT(), this->DataWholeExtent); inInfo->Get(vtkDataObject::SPACING(), this->DataSpacing); inInfo->Get(vtkDataObject::ORIGIN(), this->DataOrigin); if (inInfo->Has(vtkDataObject::DIRECTION())) { inInfo->Get(vtkDataObject::DIRECTION(), this->DataDirection); } else { vtkMatrix3x3::Identity(this->DataDirection); } return 1; } return this->Superclass::ProcessRequest(request, inputVector, outputVector); } //------------------------------------------------------------------------------ void vtkImageMapper3D::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "SlicePlane: " << this->SlicePlane << "\n"; os << indent << "SliceAtFocalPoint: " << (this->SliceAtFocalPoint ? "On\n" : "Off\n"); os << indent << "SliceFacesCamera: " << (this->SliceFacesCamera ? "On\n" : "Off\n"); os << indent << "Border: " << (this->Border ? "On\n" : "Off\n"); os << indent << "Background: " << (this->Background ? "On\n" : "Off\n"); os << indent << "NumberOfThreads: " << this->NumberOfThreads << "\n"; os << indent << "Streaming: " << (this->Streaming ? "On\n" : "Off\n"); } //------------------------------------------------------------------------------ vtkDataObject* vtkImageMapper3D::GetDataObjectInput() { if (this->GetNumberOfInputConnections(0) < 1) { return nullptr; } return this->GetInputDataObject(0, 0); } //------------------------------------------------------------------------------ vtkDataSet* vtkImageMapper3D::GetDataSetInput() { if (this->GetNumberOfInputConnections(0) < 1) { return nullptr; } return vtkDataSet::SafeDownCast(this->GetInputDataObject(0, 0)); } //------------------------------------------------------------------------------ int vtkImageMapper3D::FillInputPortInformation(int vtkNotUsed(port), vtkInformation* info) { info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData"); return 1; } //------------------------------------------------------------------------------ int vtkImageMapper3D::FillOutputPortInformation(int vtkNotUsed(port), vtkInformation* info) { info->Set(vtkDataObject::DATA_TYPE_NAME(), "vtkImageData"); return 1; } //------------------------------------------------------------------------------ static vtkRenderer* vtkImageMapper3DFindRenderer(vtkProp* prop, int& count) { vtkRenderer* ren = nullptr; int n = prop->GetNumberOfConsumers(); for (int i = 0; i < n; i++) { vtkObjectBase* o = prop->GetConsumer(i); vtkProp3D* a = nullptr; if ((ren = vtkRenderer::SafeDownCast(o))) { count++; } else if ((a = vtkProp3D::SafeDownCast(o))) { ren = vtkImageMapper3DFindRenderer(a, count); } } return ren; } //------------------------------------------------------------------------------ static void vtkImageMapper3DComputeMatrix(vtkProp* prop, double mat[16]) { vtkMatrix4x4* propmat = prop->GetMatrix(); vtkMatrix4x4::DeepCopy(mat, propmat); int n = prop->GetNumberOfConsumers(); for (int i = 0; i < n; i++) { vtkObjectBase* o = prop->GetConsumer(i); vtkProp3D* a = nullptr; if ((a = vtkProp3D::SafeDownCast(o))) { vtkImageMapper3DComputeMatrix(a, mat); if (a->IsA("vtkAssembly") || a->IsA("vtkImageStack")) { vtkMatrix4x4::Multiply4x4(mat, *propmat->Element, mat); } } } } //------------------------------------------------------------------------------ vtkRenderer* vtkImageMapper3D::GetCurrentRenderer() { vtkImageSlice* prop = this->CurrentProp; vtkRenderer* ren = this->CurrentRenderer; int count = 0; if (ren) { return ren; } if (!prop) { return nullptr; } ren = vtkImageMapper3DFindRenderer(prop, count); if (count > 1) { vtkErrorMacro("Cannot follow camera, mapper is associated with " "multiple renderers"); ren = nullptr; } return ren; } //------------------------------------------------------------------------------ vtkMatrix4x4* vtkImageMapper3D::GetDataToWorldMatrix() { vtkProp3D* prop = this->CurrentProp; if (prop) { if (this->CurrentRenderer) { prop->GetModelToWorldMatrix(this->DataToWorldMatrix); } else { double mat[16]; vtkImageMapper3DComputeMatrix(prop, mat); this->DataToWorldMatrix->DeepCopy(mat); } } return this->DataToWorldMatrix; } //------------------------------------------------------------------------------ // Convert char data without changing format static void vtkImageMapperCopy(const unsigned char* inPtr, unsigned char* outPtr, int ncols, int nrows, int numComp, vtkIdType inIncX, vtkIdType inIncY, vtkIdType outIncY) { // loop through the data and copy it for the texture if (numComp == 1) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { *outPtr = *inPtr; outPtr++; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else if (numComp == 2) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { outPtr[0] = inPtr[0]; outPtr[1] = inPtr[1]; outPtr += 2; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else if (numComp == 3) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { outPtr[0] = inPtr[0]; outPtr[1] = inPtr[1]; outPtr[2] = inPtr[2]; outPtr += 3; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else // if (numComp == 4) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { outPtr[0] = inPtr[0]; outPtr[1] = inPtr[1]; outPtr[2] = inPtr[2]; outPtr[3] = inPtr[3]; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } } //------------------------------------------------------------------------------ // Convert char data to RGBA static void vtkImageMapperConvertToRGBA(const unsigned char* inPtr, unsigned char* outPtr, int ncols, int nrows, int numComp, vtkIdType inIncX, vtkIdType inIncY, vtkIdType outIncY) { unsigned char alpha = 255; // loop through the data and copy it for the texture if (numComp == 1) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { unsigned char val = inPtr[0]; outPtr[0] = val; outPtr[1] = val; outPtr[2] = val; outPtr[3] = alpha; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else if (numComp == 2) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { unsigned char val = inPtr[0]; unsigned char a = inPtr[1]; outPtr[0] = val; outPtr[1] = val; outPtr[2] = val; outPtr[3] = a; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else if (numComp == 3) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { outPtr[0] = inPtr[0]; outPtr[1] = inPtr[1]; outPtr[2] = inPtr[2]; outPtr[3] = alpha; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else // if (numComp == 4) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { outPtr[0] = inPtr[0]; outPtr[1] = inPtr[1]; outPtr[2] = inPtr[2]; outPtr[3] = inPtr[3]; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } } //------------------------------------------------------------------------------ // Convert data to unsigned char template inline F vtkImageMapperClamp(F x, F xmin, F xmax) { // do not change this code: it compiles into min/max opcodes x = (x > xmin ? x : xmin); x = (x < xmax ? x : xmax); return x; } template void vtkImageMapperShiftScale(const T* inPtr, unsigned char* outPtr, int ncols, int nrows, int numComp, vtkIdType inIncX, vtkIdType inIncY, vtkIdType outIncY, F shift, F scale) { const F vmin = static_cast(0); const F vmax = static_cast(255); unsigned char alpha = 255; // loop through the data and copy it for the texture if (numComp == 1) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { // Pixel operation F val = (inPtr[0] + shift) * scale; val = vtkImageMapperClamp(val, vmin, vmax); unsigned char cval = static_cast(val + 0.5); outPtr[0] = cval; outPtr[1] = cval; outPtr[2] = cval; outPtr[3] = alpha; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else if (numComp == 2) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { // Pixel operation F val = (inPtr[0] + shift) * scale; val = vtkImageMapperClamp(val, vmin, vmax); unsigned char cval = static_cast(val + 0.5); val = (inPtr[1] + shift) * scale; val = vtkImageMapperClamp(val, vmin, vmax); unsigned char aval = static_cast(val + 0.5); outPtr[0] = cval; outPtr[1] = cval; outPtr[2] = cval; outPtr[3] = aval; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else if (numComp == 3) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { // Pixel operation F r = (inPtr[0] + shift) * scale; F g = (inPtr[1] + shift) * scale; F b = (inPtr[2] + shift) * scale; r = vtkImageMapperClamp(r, vmin, vmax); g = vtkImageMapperClamp(g, vmin, vmax); b = vtkImageMapperClamp(b, vmin, vmax); outPtr[0] = static_cast(r + 0.5); outPtr[1] = static_cast(g + 0.5); outPtr[2] = static_cast(b + 0.5); outPtr[3] = alpha; outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } else // if (numComp == 4) { for (int idy = 0; idy < nrows; idy++) { for (int idx = 0; idx < ncols; idx++) { // Pixel operation F r = (inPtr[0] + shift) * scale; F g = (inPtr[1] + shift) * scale; F b = (inPtr[2] + shift) * scale; F a = (inPtr[3] + shift) * scale; r = vtkImageMapperClamp(r, vmin, vmax); g = vtkImageMapperClamp(g, vmin, vmax); b = vtkImageMapperClamp(b, vmin, vmax); a = vtkImageMapperClamp(a, vmin, vmax); outPtr[0] = static_cast(r + 0.5); outPtr[1] = static_cast(g + 0.5); outPtr[2] = static_cast(b + 0.5); outPtr[3] = static_cast(a + 0.5); outPtr += 4; inPtr += inIncX; } outPtr += outIncY; inPtr += inIncY; } } } //------------------------------------------------------------------------------ static void vtkImageMapperConvertImageScalarsToRGBA(void* inPtr, unsigned char* outPtr, int ncols, int nrows, int numComp, vtkIdType inIncX, vtkIdType inIncY, vtkIdType outIncY, int scalarType, double scalarRange[2]) { double shift = -scalarRange[0]; double scale = 255.0; if (scalarRange[0] < scalarRange[1]) { scale /= (scalarRange[1] - scalarRange[0]); } else { scale = 1e+32; } // Check if the data can be simply copied if (scalarType == VTK_UNSIGNED_CHAR && static_cast(shift * scale) == 0 && static_cast((255 + shift) * scale) == 255) { vtkImageMapperConvertToRGBA( static_cast(inPtr), outPtr, ncols, nrows, numComp, inIncX, inIncY, outIncY); } else { switch (scalarType) { vtkTemplateAliasMacro(vtkImageMapperShiftScale(static_cast(inPtr), outPtr, ncols, nrows, numComp, inIncX, inIncY, outIncY, shift, scale)); default: vtkGenericWarningMacro("ConvertImageScalarsToRGBA: Unknown input ScalarType"); } } } //------------------------------------------------------------------------------ template void vtkImageMapperMakeContiguous( const T* inPtr, T* outPtr, int ncols, int numComp, vtkIdType inIncX) { if (numComp == 1) { for (int idx = 0; idx < ncols; idx++) { *outPtr = *inPtr; outPtr++; inPtr += inIncX; } } else { inIncX -= numComp; for (int idx = 0; idx < ncols; idx++) { int idc = numComp; do { *outPtr++ = *inPtr++; } while (--idc); inPtr += inIncX; } } } static void vtkImageMapperApplyLookupTableToImageScalars(void* inPtr, unsigned char* outPtr, int ncols, int nrows, int numComp, vtkIdType inIncX, vtkIdType inIncY, vtkIdType outIncY, int scalarType, vtkScalarsToColors* lookupTable) { // number of values per row of input image int scalarSize = vtkDataArray::GetDataTypeSize(scalarType); // convert incs from continuous increments to regular increment outIncY += 4 * ncols; inIncY += inIncX * ncols; inIncY *= scalarSize; // if data not contiguous, make a temporary array void* newPtr = nullptr; if (inIncX > static_cast(numComp)) { newPtr = malloc(scalarSize * numComp * ncols); } // loop through the data and copy it for the texture for (int idy = 0; idy < nrows; idy++) { void* tmpPtr = inPtr; // make contiguous if necessary if (inIncX > static_cast(numComp)) { tmpPtr = newPtr; if (scalarSize == 1) { vtkImageMapperMakeContiguous( static_cast(inPtr), static_cast(tmpPtr), ncols, numComp, inIncX); } else if (scalarSize == 2) { vtkImageMapperMakeContiguous( static_cast(inPtr), static_cast(tmpPtr), ncols, numComp, inIncX); } else if (scalarSize == 4) { vtkImageMapperMakeContiguous( static_cast(inPtr), static_cast(tmpPtr), ncols, numComp, inIncX); } else { vtkImageMapperMakeContiguous(static_cast(inPtr), static_cast(tmpPtr), ncols, numComp * (scalarSize >> 3), inIncX * (scalarSize >> 3)); } } // pass the data through the lookup table if (numComp == 1) { lookupTable->MapScalarsThroughTable(tmpPtr, outPtr, scalarType, ncols, numComp, VTK_RGBA); } else { lookupTable->MapVectorsThroughTable(tmpPtr, outPtr, scalarType, ncols, numComp, VTK_RGBA); } outPtr += outIncY; inPtr = static_cast(static_cast(inPtr) + inIncY); } if (newPtr) { free(newPtr); } } //------------------------------------------------------------------------------ struct vtkImageMapperThreadStruct { void* InputPtr; unsigned char* OutputPtr; int ImageSize[2]; int ScalarType; int NumComp; vtkIdType InIncX; vtkIdType InIncY; vtkIdType OutIncX; vtkIdType OutIncY; double Range[2]; vtkScalarsToColors* LookupTable; }; static VTK_THREAD_RETURN_TYPE vtkImageMapperMapColors(void* arg) { vtkMultiThreader::ThreadInfo* mtti = static_cast(arg); int threadId = mtti->ThreadID; int threadCount = mtti->NumberOfThreads; vtkImageMapperThreadStruct* imts = static_cast(mtti->UserData); int ncols = imts->ImageSize[0]; int nrows = imts->ImageSize[1]; int scalarSize = vtkDataArray::GetDataTypeSize(imts->ScalarType); // only split in vertical direction if (threadCount > nrows) { threadCount = nrows; if (threadId >= threadCount) { return VTK_THREAD_RETURN_VALUE; } } // adjust pointers int firstRow = threadId * nrows / threadCount; int lastRow = (threadId + 1) * nrows / threadCount; void* inputPtr = static_cast(imts->InputPtr) + (imts->InIncX * ncols + imts->InIncY) * firstRow * scalarSize; unsigned char* outputPtr = imts->OutputPtr + (imts->OutIncX * ncols + imts->OutIncY) * firstRow; nrows = lastRow - firstRow; // reformat the data for use as a texture if (imts->LookupTable) { // apply a lookup table vtkImageMapperApplyLookupTableToImageScalars(inputPtr, outputPtr, ncols, nrows, imts->NumComp, imts->InIncX, imts->InIncY, imts->OutIncY, imts->ScalarType, imts->LookupTable); } else { // no lookup table, do a shift/scale calculation vtkImageMapperConvertImageScalarsToRGBA(inputPtr, outputPtr, ncols, nrows, imts->NumComp, imts->InIncX, imts->InIncY, imts->OutIncY, imts->ScalarType, imts->Range); } return VTK_THREAD_RETURN_VALUE; } //------------------------------------------------------------------------------ // Given an image and an extent that describes a single slice, this method // will return a contiguous block of unsigned char data that can be loaded // into a texture. // The values of xsize, ysize, bytesPerPixel, and reuseTexture must be // pre-loaded with the current texture size and depth, with subTexture // set to 1 if only a subTexture is to be generated. // When the method returns, these values will be set to the dimensions // of the data that was produced, and subTexture will remain set to 1 // if xsize,ysize describe a subtexture size. // If subTexture is not set to one upon return, then xsize,ysize will // describe the full texture size, with the assumption that the full // texture must be reloaded. // If reuseData is false upon return, then the returned array must be // freed after use with delete []. unsigned char* vtkImageMapper3D::MakeTextureData(vtkImageProperty* property, vtkImageData* input, int extent[6], int& xsize, int& ysize, int& bytesPerPixel, bool& reuseTexture, bool& reuseData) { int xdim, ydim; int imageSize[2]; int textureSize[2]; // compute image size and texture size from extent this->ComputeTextureSize(extent, xdim, ydim, imageSize, textureSize); // number of components int numComp = input->GetNumberOfScalarComponents(); int scalarType = input->GetScalarType(); int textureBytesPerPixel = 4; // lookup table and window/level double colorWindow = 255.0; double colorLevel = 127.5; vtkScalarsToColors* lookupTable = nullptr; if (property) { colorWindow = property->GetColorWindow(); colorLevel = property->GetColorLevel(); lookupTable = property->GetLookupTable(); } // check if the input is pre-formatted as colors int inputIsColors = false; if (lookupTable == nullptr && scalarType == VTK_UNSIGNED_CHAR && colorLevel == 127.5 && colorWindow == 255.0) { inputIsColors = true; if (reuseData && numComp < 4) { textureBytesPerPixel = numComp; } } // reuse texture if texture size has not changed if (xsize == textureSize[0] && ysize == textureSize[1] && bytesPerPixel == textureBytesPerPixel && reuseTexture) { // if texture is reused, only reload the image portion xsize = imageSize[0]; ysize = imageSize[1]; } else { xsize = textureSize[0]; ysize = textureSize[1]; bytesPerPixel = textureBytesPerPixel; reuseTexture = false; } // if the image is already of the desired size and type if (xsize == imageSize[0] && ysize == imageSize[1]) { // Check if the data needed for the texture is a contiguous region // of the input data: this requires that xdim = 0 and ydim = 1 // OR xextent = 1 pixel and xdim = 1 and ydim = 2 // OR xdim = 0 and ydim = 2 and yextent = 1 pixel. // In addition the corresponding x display extents must match the // extent of the data int* dataExtent = input->GetExtent(); if ((xdim == 0 && ydim == 1 && extent[0] == dataExtent[0] && extent[1] == dataExtent[1]) || (xdim == 1 && ydim == 2 && dataExtent[0] == dataExtent[1] && extent[2] == dataExtent[2] && extent[3] == dataExtent[3]) || (xdim == 0 && ydim == 2 && dataExtent[2] == dataExtent[3] && extent[0] == dataExtent[0] && extent[1] == dataExtent[1])) { // if contiguous and correct data type, use data as-is if (inputIsColors && reuseData) { return static_cast(input->GetScalarPointerForExtent(extent)); } } } // could not directly use input data, so allocate a new array reuseData = false; unsigned char* outPtr = new unsigned char[ysize * xsize * bytesPerPixel]; // output increments vtkIdType outIncY = bytesPerPixel * (xsize - imageSize[0]); // input pointer and increments vtkIdType inInc[3]; void* inPtr = input->GetScalarPointerForExtent(extent); input->GetIncrements(inInc); vtkIdType inIncX = inInc[xdim]; vtkIdType inIncY = inInc[ydim] - inInc[xdim] * imageSize[0]; // convert Window/Level to a scalar range double range[2]; range[0] = colorLevel - 0.5 * colorWindow; range[1] = colorLevel + 0.5 * colorWindow; if (lookupTable) { if (property && !property->GetUseLookupTableScalarRange()) { // no way to do this without modifying the table lookupTable->SetRange(range); } // make sure table is up to date lookupTable->Build(); } if (inputIsColors && !lookupTable) { // just copy the data vtkImageMapperCopy(static_cast(inPtr), outPtr, imageSize[0], imageSize[1], numComp, inIncX, inIncY, outIncY); } else { // do a multi-threaded conversion vtkImageMapperThreadStruct imts; imts.InputPtr = inPtr; imts.OutputPtr = outPtr; imts.ImageSize[0] = imageSize[0]; imts.ImageSize[1] = imageSize[1]; imts.ScalarType = scalarType; imts.NumComp = numComp; imts.InIncX = inIncX; imts.InIncY = inIncY; imts.OutIncX = 4; imts.OutIncY = outIncY; imts.Range[0] = range[0]; imts.Range[1] = range[1]; imts.LookupTable = lookupTable; int numThreads = this->NumberOfThreads; numThreads = ((numThreads <= imageSize[1]) ? numThreads : imageSize[1]); this->Threader->SetNumberOfThreads(numThreads); this->Threader->SetSingleMethod(&vtkImageMapperMapColors, &imts); this->Threader->SingleMethodExecute(); } return outPtr; } //------------------------------------------------------------------------------ // Compute the coords and tcoords for the image void vtkImageMapper3D::MakeTextureGeometry( const int extent[6], double coords[12], double tcoords[8]) { int xdim, ydim; int imageSize[2]; int textureSize[2]; // compute image size and texture size from extent this->ComputeTextureSize(extent, xdim, ydim, imageSize, textureSize); // get spacing/origin for the quad coordinates double* spacing = this->DataSpacing; double* origin = this->DataOrigin; double* direction = this->DataDirection; // stretch the geometry one half-pixel double dext[6]{ static_cast(extent[0]), static_cast(extent[1]), static_cast(extent[2]), static_cast(extent[3]), static_cast(extent[4]), static_cast(extent[5]) }; if (this->Border) { dext[xdim * 2] -= 0.5; dext[xdim * 2 + 1] += 0.5; dext[ydim * 2] -= 0.5; dext[ydim * 2 + 1] += 0.5; } // compute the world coordinates of the quad vtkImageData::TransformContinuousIndexToPhysicalPoint( dext[0], dext[2], dext[4], origin, spacing, direction, coords); vtkImageData::TransformContinuousIndexToPhysicalPoint( dext[1], dext[2 + (xdim == 1)], dext[4], origin, spacing, direction, coords + 3); vtkImageData::TransformContinuousIndexToPhysicalPoint( dext[1], dext[3], dext[5], origin, spacing, direction, coords + 6); vtkImageData::TransformContinuousIndexToPhysicalPoint( dext[0], dext[2 + (ydim == 1)], dext[5], origin, spacing, direction, coords + 9); if (tcoords) { // compute the tcoords double textureBorder = 0.5 * (this->Border == 0); tcoords[0] = textureBorder / textureSize[0]; tcoords[1] = textureBorder / textureSize[1]; tcoords[2] = (imageSize[0] - textureBorder) / textureSize[0]; tcoords[3] = tcoords[1]; tcoords[4] = tcoords[2]; tcoords[5] = (imageSize[1] - textureBorder) / textureSize[1]; tcoords[6] = tcoords[0]; tcoords[7] = tcoords[5]; } } //------------------------------------------------------------------------------ void vtkImageMapper3D::ComputeTextureSize( const int extent[6], int& xdim, int& ydim, int imageSize[2], int textureSize[2]) { // find dimension indices that will correspond to the // columns and rows of the 2D texture xdim = 1; ydim = 2; if (extent[0] != extent[1]) { xdim = 0; if (extent[2] != extent[3]) { ydim = 1; } } // compute the image dimensions imageSize[0] = (extent[xdim * 2 + 1] - extent[xdim * 2] + 1); imageSize[1] = (extent[ydim * 2 + 1] - extent[ydim * 2] + 1); textureSize[0] = imageSize[0]; textureSize[1] = imageSize[1]; } //------------------------------------------------------------------------------ void vtkImageMapper3D::GetSlicePlaneInDataCoords(vtkMatrix4x4* propMatrix, double normal[4]) { double point[3]; this->SlicePlane->GetNormal(normal); this->SlicePlane->GetOrigin(point); // The plane has a transform, though most people forget vtkAbstractTransform* planeTransform = this->SlicePlane->GetTransform(); if (planeTransform) { planeTransform->TransformNormalAtPoint(point, normal, normal); planeTransform->TransformPoint(point, point); } // Convert to a homogeneous normal in data coords normal[3] = -vtkMath::Dot(point, normal); // Transform to data coordinates if (propMatrix) { double mat[16]; vtkMatrix4x4::Transpose(*propMatrix->Element, mat); vtkMatrix4x4::MultiplyPoint(mat, normal, normal); } // Normalize the "normal" part for good measure double l = vtkMath::Norm(normal); normal[0] /= l; normal[1] /= l; normal[2] /= l; normal[3] /= l; } //------------------------------------------------------------------------------ void vtkImageMapper3D::GetBackgroundColor(vtkImageProperty* property, double color[4]) { color[0] = 0.0; color[1] = 0.0; color[2] = 0.0; color[3] = 1.0; if (property) { vtkScalarsToColors* table = property->GetLookupTable(); if (table) { double v = property->GetColorLevel() - 0.5 * property->GetColorWindow(); if (property->GetUseLookupTableScalarRange()) { v = table->GetRange()[0]; } table->GetColor(v, color); color[3] = table->GetOpacity(v); } } } //------------------------------------------------------------------------------ void vtkImageMapper3D::CheckerboardRGBA(unsigned char* data, int xsize, int ysize, double originx, double originy, double spacingx, double spacingy) { static double tol = 7.62939453125e-06; static double maxval = 2147483647; static double minval = -2147483647; originx += 1.0 + tol; originy += 1.0 + tol; originx = (originx > minval ? originx : minval); originx = (originx < maxval ? originx : maxval); originy = (originy > minval ? originy : minval); originy = (originy < maxval ? originy : maxval); spacingx = fabs(spacingx); spacingy = fabs(spacingy); spacingx = (spacingx < maxval ? spacingx : maxval); spacingy = (spacingy < maxval ? spacingy : maxval); spacingx = (spacingx != 0 ? spacingx : maxval); spacingy = (spacingy != 0 ? spacingy : maxval); int xn = static_cast(spacingx + tol); int yn = static_cast(spacingy + tol); double fx = spacingx - xn; double fy = spacingy - yn; int state = 0; int tmpstate = ~state; double spacing2x = 2 * spacingx; double spacing2y = 2 * spacingy; originx -= ceil(originx / spacing2x) * spacing2x; while (originx < 0) { originx += spacing2x; } originy -= ceil(originy / spacing2y) * spacing2y; while (originy < 0) { originy += spacing2y; } double tmporiginx = originx - spacingx; originx = (tmporiginx < 0 ? originx : tmporiginx); state = (tmporiginx < 0 ? state : tmpstate); tmpstate = ~state; double tmporiginy = originy - spacingy; originy = (tmporiginy < 0 ? originy : tmporiginy); state = (tmporiginy < 0 ? state : tmpstate); int xm = static_cast(originx); int savexm = xm; int ym = static_cast(originy); double gx = originx - xm; double savegx = gx; double gy = originy - ym; int inc = 4; data += (inc - 1); for (int j = 0; j < ysize;) { double tmpy = gy - 1.0; gy = (tmpy < 0 ? gy : tmpy); int yextra = (tmpy >= 0); ym += yextra; int ry = ysize - j; ym = (ym < ry ? ym : ry); j += ym; for (; ym; --ym) { tmpstate = state; xm = savexm; gx = savegx; for (int i = 0; i < xsize;) { double tmpx = gx - 1.0; gx = (tmpx < 0 ? gx : tmpx); int xextra = (tmpx >= 0); xm += xextra; int rx = xsize - i; xm = (xm < rx ? xm : rx); i += xm; if ((tmpstate & xm)) { do { *data = 0; data += inc; } while (--xm); } data += inc * xm; xm = xn; tmpstate = ~tmpstate; gx += fx; } } ym = yn; state = ~state; gy += fy; } }