You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1169 lines
36 KiB
1169 lines
36 KiB
/*=========================================================================
|
|
|
|
Program: Visualization Toolkit
|
|
Module: $RCSfile: vtkImageBlend.cxx,v $
|
|
|
|
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 "vtkImageBlend.h"
|
|
|
|
#include "vtkImageData.h"
|
|
#include "vtkImageStencilData.h"
|
|
#include "vtkInformation.h"
|
|
#include "vtkInformationVector.h"
|
|
#include "vtkObjectFactory.h"
|
|
#include "vtkStreamingDemandDrivenPipeline.h"
|
|
#include "vtkPointData.h"
|
|
|
|
vtkCxxRevisionMacro(vtkImageBlend, "$Revision: 1.38.4.2 $");
|
|
vtkStandardNewMacro(vtkImageBlend);
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageBlend::vtkImageBlend()
|
|
{
|
|
this->Opacity = 0;
|
|
this->OpacityArrayLength = 0;
|
|
this->BlendMode = VTK_IMAGE_BLEND_MODE_NORMAL;
|
|
this->CompoundThreshold = 0.0;
|
|
this->DataWasPassed = 0;
|
|
|
|
// we have the image inputs and the optional stencil input
|
|
this->SetNumberOfInputPorts(2);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageBlend::~vtkImageBlend()
|
|
{
|
|
if (this->Opacity)
|
|
{
|
|
delete [] this->Opacity;
|
|
}
|
|
this->OpacityArrayLength = 0;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// The default vtkImageAlgorithm semantics are that SetInput() puts
|
|
// each input on a different port, we want all the image inputs to
|
|
// go on the first port.
|
|
void vtkImageBlend::SetInput(int idx, vtkDataObject *input)
|
|
{
|
|
// Ask the superclass to connect the input.
|
|
this->SetNthInputConnection(0, idx, (input ? input->GetProducerPort() : 0));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkDataObject *vtkImageBlend::GetInput(int idx)
|
|
{
|
|
if (this->GetNumberOfInputConnections(0) <= idx)
|
|
{
|
|
return 0;
|
|
}
|
|
return vtkImageData::SafeDownCast(
|
|
this->GetExecutive()->GetInputData(0, idx));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageBlend::SetStencil(vtkImageStencilData *stencil)
|
|
{
|
|
// if stencil is null, then set the input port to null
|
|
this->SetNthInputConnection(1, 0,
|
|
(stencil ? stencil->GetProducerPort() : 0));
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
vtkImageStencilData *vtkImageBlend::GetStencil()
|
|
{
|
|
if (this->GetNumberOfInputConnections(1) < 1)
|
|
{
|
|
return 0;
|
|
}
|
|
return vtkImageStencilData::SafeDownCast(
|
|
this->GetExecutive()->GetInputData(1, 0));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageBlend::SetOpacity(int idx, double opacity)
|
|
{
|
|
int i;
|
|
int newLength;
|
|
double *newArray;
|
|
|
|
if (opacity < 0.0)
|
|
{
|
|
opacity = 0.0;
|
|
}
|
|
if (opacity > 1.0)
|
|
{
|
|
opacity = 1.0;
|
|
}
|
|
|
|
if (idx >= this->OpacityArrayLength)
|
|
{
|
|
newLength = idx + 1;
|
|
newArray = new double[newLength];
|
|
for (i = 0; i < this->OpacityArrayLength; i++)
|
|
{
|
|
newArray[i] = this->Opacity[i];
|
|
}
|
|
for (; i < newLength; i++)
|
|
{
|
|
newArray[i] = 1.0;
|
|
}
|
|
if (this->Opacity)
|
|
{
|
|
delete [] this->Opacity;
|
|
}
|
|
this->Opacity = newArray;
|
|
this->OpacityArrayLength = newLength;
|
|
}
|
|
|
|
if (this->Opacity[idx] != opacity)
|
|
{
|
|
this->Opacity[idx] = opacity;
|
|
this->Modified();
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
double vtkImageBlend::GetOpacity(int idx)
|
|
{
|
|
if (idx >= this->OpacityArrayLength)
|
|
{
|
|
return 1.0;
|
|
}
|
|
return this->Opacity[idx];
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkImageBlend::RequestInformation (
|
|
vtkInformation * vtkNotUsed(request),
|
|
vtkInformationVector **inputVector,
|
|
vtkInformationVector *vtkNotUsed( outputVector ))
|
|
{
|
|
// get the info objects
|
|
vtkInformation *inInfo = inputVector[0]->GetInformationObject(0);
|
|
|
|
// this is some scary stuff, propagating info upstream??? - Ken
|
|
vtkImageStencilData *stencil = this->GetStencil();
|
|
if (stencil)
|
|
{
|
|
stencil->SetSpacing(inInfo->Get(vtkDataObject::SPACING()));
|
|
stencil->SetOrigin(inInfo->Get(vtkDataObject::ORIGIN()));
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This method computes the extent of the input region necessary to generate
|
|
// an output region. Before this method is called "region" should have the
|
|
// extent of the output region. After this method finishes, "region" should
|
|
// have the extent of the required input region. The default method assumes
|
|
// the required input extent are the same as the output extent.
|
|
// Note: The splitting methods call this method with outRegion = inRegion.
|
|
void vtkImageBlend::InternalComputeInputUpdateExtent(int inExt[6],
|
|
int outExt[6],
|
|
int wholeExtent[6])
|
|
{
|
|
memcpy(inExt,outExt,sizeof(int)*6);
|
|
|
|
int i;
|
|
|
|
// clip with the whole extent
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
if (inExt[2*i] < wholeExtent[2*i])
|
|
{
|
|
inExt[2*i] = wholeExtent[2*i];
|
|
}
|
|
if (inExt[2*i+1] > wholeExtent[2*i+1])
|
|
{
|
|
inExt[2*i+1] = wholeExtent[2*i+1];
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkImageBlend::RequestUpdateExtent(
|
|
vtkInformation * vtkNotUsed(request),
|
|
vtkInformationVector **inputVector,
|
|
vtkInformationVector *outputVector)
|
|
{
|
|
// get the info objects
|
|
vtkInformation* outInfo = outputVector->GetInformationObject(0);
|
|
|
|
// default input extent will be that of output extent
|
|
int inExt[6];
|
|
int *outExt =
|
|
outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT());
|
|
|
|
int whichInput;
|
|
for (whichInput = 0; whichInput < this->GetNumberOfInputConnections(0);
|
|
whichInput++)
|
|
{
|
|
int *inWextent;
|
|
vtkInformation *inInfo = inputVector[0]->GetInformationObject(whichInput);
|
|
inWextent = inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
|
|
this->InternalComputeInputUpdateExtent(inExt, outExt, inWextent);
|
|
inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(),inExt,6);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
int vtkImageBlend::RequestData(
|
|
vtkInformation* request,
|
|
vtkInformationVector** inputVector,
|
|
vtkInformationVector* outputVector)
|
|
{
|
|
// check to see if we have more than one input
|
|
if (this->GetNumberOfInputConnections(0) == 1)
|
|
{
|
|
vtkDebugMacro("RequestData: single input, passing data");
|
|
|
|
vtkInformation* info = outputVector->GetInformationObject(0);
|
|
vtkImageData *outData = static_cast<vtkImageData *>(
|
|
info->Get(vtkDataObject::DATA_OBJECT()));
|
|
info = inputVector[0]->GetInformationObject(0);
|
|
vtkImageData *inData =
|
|
static_cast<vtkImageData*>(info->Get(vtkDataObject::DATA_OBJECT()));
|
|
|
|
outData->SetExtent(inData->GetExtent());
|
|
outData->GetPointData()->PassData(inData->GetPointData());
|
|
this->DataWasPassed = 1;
|
|
}
|
|
else // multiple inputs
|
|
{
|
|
vtkInformation* info = outputVector->GetInformationObject(0);
|
|
vtkImageData *outData = static_cast<vtkImageData *>(
|
|
info->Get(vtkDataObject::DATA_OBJECT()));
|
|
if (this->DataWasPassed)
|
|
{
|
|
outData->GetPointData()->SetScalars(NULL);
|
|
this->DataWasPassed = 0;
|
|
}
|
|
return this->Superclass::RequestData(request,inputVector,outputVector);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// helper function for the stencil
|
|
template <class T>
|
|
inline int vtkBlendGetNextExtent(vtkImageStencilData *stencil,
|
|
int &r1, int &r2, int rmin, int rmax,
|
|
int yIdx, int zIdx,
|
|
T *&outPtr, T *&inPtr,
|
|
int outScalars, int inScalars,
|
|
int &iter)
|
|
{
|
|
// trivial case if stencil is not set
|
|
if (!stencil)
|
|
{
|
|
if (iter++ == 0)
|
|
{
|
|
r1 = rmin;
|
|
r2 = rmax;
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// save r2
|
|
int oldr2 = r2;
|
|
if (iter == 0)
|
|
{ // if no 'last time', start just before rmin
|
|
oldr2 = rmin - 1;
|
|
}
|
|
|
|
int rval = stencil->GetNextExtent(r1, r2, rmin, rmax, yIdx, zIdx, iter);
|
|
int incr = r1 - oldr2 - 1;
|
|
if (rval == 0)
|
|
{
|
|
incr = rmax - oldr2;
|
|
}
|
|
|
|
outPtr += incr*outScalars;
|
|
inPtr += incr*inScalars;
|
|
|
|
return rval;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This templated function executes the filter for any type of data.
|
|
template <class T>
|
|
void vtkImageBlendExecute(vtkImageBlend *self, int extent[6],
|
|
vtkImageData *inData, T *inPtr,
|
|
vtkImageData *outData, T *outPtr,
|
|
double opacity, int id)
|
|
{
|
|
int idxX, idxY, idxZ;
|
|
int minX = 0;
|
|
int maxX = 0;
|
|
int iter;
|
|
vtkIdType inIncX, inIncY, inIncZ;
|
|
vtkIdType outIncX, outIncY, outIncZ;
|
|
int inC, outC;
|
|
double minA, maxA;
|
|
double r, f;
|
|
unsigned long count = 0;
|
|
unsigned long target;
|
|
|
|
vtkImageStencilData *stencil = self->GetStencil();
|
|
|
|
if (inData->GetScalarType() == VTK_DOUBLE ||
|
|
inData->GetScalarType() == VTK_FLOAT)
|
|
{
|
|
minA = 0.0;
|
|
maxA = 1.0;
|
|
}
|
|
else
|
|
{
|
|
minA = inData->GetScalarTypeMin();
|
|
maxA = inData->GetScalarTypeMax();
|
|
}
|
|
|
|
r = opacity;
|
|
f = 1.0 - r;
|
|
|
|
opacity = opacity/(maxA-minA);
|
|
|
|
inC = inData->GetNumberOfScalarComponents();
|
|
outC = outData->GetNumberOfScalarComponents();
|
|
|
|
target = (unsigned long)((extent[3] - extent[2] + 1)*
|
|
(extent[5] - extent[4] + 1)/50.0);
|
|
target++;
|
|
|
|
// Get increments to march through data
|
|
inData->GetContinuousIncrements(extent, inIncX, inIncY, inIncZ);
|
|
outData->GetContinuousIncrements(extent, outIncX, outIncY, outIncZ);
|
|
|
|
// Loop through output pixels
|
|
for (idxZ = extent[4]; idxZ <= extent[5]; idxZ++)
|
|
{
|
|
for (idxY = extent[2]; !self->AbortExecute && idxY <= extent[3]; idxY++)
|
|
{
|
|
if (!id)
|
|
{
|
|
if (!(count%target))
|
|
{
|
|
self->UpdateProgress(count/(50.0*target));
|
|
}
|
|
count++;
|
|
}
|
|
|
|
iter = 0;
|
|
if (outC >= 3 && inC >= 4)
|
|
{ // RGB(A) blended with RGBA
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
r = opacity*(inPtr[3]-minA);
|
|
f = 1.0-r;
|
|
outPtr[0] = T(outPtr[0]*f + inPtr[0]*r);
|
|
outPtr[1] = T(outPtr[1]*f + inPtr[1]*r);
|
|
outPtr[2] = T(outPtr[2]*f + inPtr[2]*r);
|
|
outPtr += outC;
|
|
inPtr += inC;
|
|
}
|
|
}
|
|
}
|
|
else if (outC >= 3 && inC == 3)
|
|
{ // RGB(A) blended with RGB
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
outPtr[0] = T(outPtr[0]*f + inPtr[0]*r);
|
|
outPtr[1] = T(outPtr[1]*f + inPtr[1]*r);
|
|
outPtr[2] = T(outPtr[2]*f + inPtr[2]*r);
|
|
outPtr += outC;
|
|
inPtr += inC;
|
|
}
|
|
}
|
|
}
|
|
else if (outC >= 3 && inC == 2)
|
|
{ // RGB(A) blended with luminance+alpha
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
r = opacity*(inPtr[1]-minA);
|
|
f = 1.0-r;
|
|
outPtr[0] = T(outPtr[0]*f + (*inPtr)*r);
|
|
outPtr[1] = T(outPtr[1]*f + (*inPtr)*r);
|
|
outPtr[2] = T(outPtr[2]*f + (*inPtr)*r);
|
|
outPtr += outC;
|
|
inPtr += 2;
|
|
}
|
|
}
|
|
}
|
|
else if (outC >= 3 && inC == 1)
|
|
{ // RGB(A) blended with luminance
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
outPtr[0] = T(outPtr[0]*f + (*inPtr)*r);
|
|
outPtr[1] = T(outPtr[1]*f + (*inPtr)*r);
|
|
outPtr[2] = T(outPtr[2]*f + (*inPtr)*r);
|
|
outPtr += outC;
|
|
inPtr++;
|
|
}
|
|
}
|
|
}
|
|
else if (inC == 2)
|
|
{ // luminance(+alpha) blended with luminance+alpha
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
r = opacity*(inPtr[1]-minA);
|
|
f = 1.0-r;
|
|
*outPtr = T((*outPtr)*f + (*inPtr)*r);
|
|
outPtr += outC;
|
|
inPtr += 2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // luminance(+alpha) blended with luminance
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
*outPtr = T((*outPtr)*f + (*inPtr)*r);
|
|
outPtr += outC;
|
|
inPtr++;
|
|
}
|
|
}
|
|
}
|
|
outPtr += outIncY;
|
|
inPtr += inIncY;
|
|
}
|
|
outPtr += outIncZ;
|
|
inPtr += inIncZ;
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This templated function executes the filter specifically for char data
|
|
template <class T>
|
|
void vtkImageBlendExecuteChar(vtkImageBlend *self, int extent[6],
|
|
vtkImageData *inData, T *inPtr,
|
|
vtkImageData *outData, T *outPtr,
|
|
double opacity, int id)
|
|
{
|
|
int idxX, idxY, idxZ;
|
|
int minX = 0;
|
|
int maxX = 0;
|
|
int iter;
|
|
vtkIdType inIncX, inIncY, inIncZ;
|
|
vtkIdType outIncX, outIncY, outIncZ;
|
|
int inC, outC;
|
|
unsigned short r, f, o;
|
|
int v0, v1, v2;
|
|
unsigned long count = 0;
|
|
unsigned long target;
|
|
|
|
vtkImageStencilData *stencil = self->GetStencil();
|
|
|
|
// round opacity to a value in the range [0,256], because division
|
|
// by 256 can be efficiently achieved by bit-shifting by 8 bits
|
|
o = (unsigned short)(256*opacity + 0.5);
|
|
r = o;
|
|
f = 256 - o;
|
|
|
|
inC = inData->GetNumberOfScalarComponents();
|
|
outC = outData->GetNumberOfScalarComponents();
|
|
|
|
target = (unsigned long)((extent[3] - extent[2] + 1)*
|
|
(extent[5] - extent[4] + 1)/50.0);
|
|
target++;
|
|
|
|
// Get increments to march through data
|
|
inData->GetContinuousIncrements(extent, inIncX, inIncY, inIncZ);
|
|
outData->GetContinuousIncrements(extent, outIncX, outIncY, outIncZ);
|
|
|
|
// Loop through ouput pixels
|
|
for (idxZ = extent[4]; idxZ <= extent[5]; idxZ++)
|
|
{
|
|
for (idxY = extent[2]; !self->AbortExecute && idxY <= extent[3]; idxY++)
|
|
{
|
|
if (!id)
|
|
{
|
|
if (!(count%target))
|
|
{
|
|
self->UpdateProgress(count/(50.0*target));
|
|
}
|
|
count++;
|
|
}
|
|
|
|
iter = 0;
|
|
if (outC >= 3 && inC >= 4)
|
|
{ // RGB(A) blended with RGBA
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
// multiply to get a number in the range [0,65280]
|
|
// where 65280 = 255*256 = range of inPtr[3] * range of o
|
|
r = inPtr[3]*o;
|
|
f = 65280 - r;
|
|
v0 = outPtr[0]*f + inPtr[0]*r;
|
|
v1 = outPtr[1]*f + inPtr[1]*r;
|
|
v2 = outPtr[2]*f + inPtr[2]*r;
|
|
// do some math tricks to achieve division by 65280:
|
|
// this is not an approximation, it gives exactly the
|
|
// same result as an integer division by 65280
|
|
outPtr[0] = (v0 + (v0 >> 8) + (v0 >> 16) + 1) >> 16;
|
|
outPtr[1] = (v1 + (v1 >> 8) + (v1 >> 16) + 1) >> 16;
|
|
outPtr[2] = (v2 + (v2 >> 8) + (v2 >> 16) + 1) >> 16;
|
|
inPtr += inC;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
}
|
|
else if (outC >= 3 && inC == 3)
|
|
{ // RGB(A) blended with RGB
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
// the bit-shift achieves a division by 256
|
|
outPtr[0] = (outPtr[0]*f + inPtr[0]*r) >> 8;
|
|
outPtr[1] = (outPtr[1]*f + inPtr[1]*r) >> 8;
|
|
outPtr[2] = (outPtr[2]*f + inPtr[2]*r) >> 8;
|
|
inPtr += 3;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
}
|
|
else if (outC >= 3 && inC == 2)
|
|
{ // RGB(A) blended with luminance+alpha
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
// multiply to get a number in the range [0,65280]
|
|
// where 65280 = 255*256 = range of inPtr[1] * range of o
|
|
r = inPtr[1]*o;
|
|
f = 65280 - r;
|
|
v0 = outPtr[0]*f + inPtr[0]*r;
|
|
v1 = outPtr[1]*f + inPtr[0]*r;
|
|
v2 = outPtr[2]*f + inPtr[0]*r;
|
|
// do some math tricks to achieve division by 65280:
|
|
// this is not an approximation, it gives exactly the
|
|
// same result as an integer division by 65280
|
|
outPtr[0] = (v0 + (v0 >> 8) + (v0 >> 16) + 1) >> 16;
|
|
outPtr[1] = (v1 + (v1 >> 8) + (v1 >> 16) + 1) >> 16;
|
|
outPtr[2] = (v2 + (v2 >> 8) + (v2 >> 16) + 1) >> 16;
|
|
inPtr += 2;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
}
|
|
else if (outC >= 3 && inC == 1)
|
|
{ // RGB(A) blended with luminance
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
// the bit-shift achieves a division by 256
|
|
outPtr[0] = (outPtr[0]*f + inPtr[0]*r) >> 8;
|
|
outPtr[1] = (outPtr[1]*f + inPtr[0]*r) >> 8;
|
|
outPtr[2] = (outPtr[2]*f + inPtr[0]*r) >> 8;
|
|
inPtr++;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
}
|
|
else if (inC == 2)
|
|
{ // luminance(+alpha) blended with luminance+alpha
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
// multiply to get a number in the range [0,65280]
|
|
// where 65280 = 255*256 = range of inPtr[1] * range of o
|
|
r = inPtr[1]*o;
|
|
f = 65280 - r;
|
|
v0 = outPtr[0]*f + inPtr[0]*r;
|
|
// do some math tricks to achieve division by 65280:
|
|
// this is not an approximation, it gives exactly the
|
|
// same result as an integer division by 65280
|
|
outPtr[0] = (v0 + (v0 >> 8) + (v0 >> 16) + 1) >> 16;
|
|
inPtr += 2;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // luminance(+alpha) blended with luminance
|
|
while (vtkBlendGetNextExtent(stencil, minX, maxX, extent[0], extent[1],
|
|
idxY, idxZ,
|
|
outPtr, inPtr, outC, inC, iter))
|
|
{
|
|
for (idxX = minX; idxX <= maxX; idxX++)
|
|
{
|
|
// the bit-shift achieves a division by 256
|
|
outPtr[0] = (outPtr[0]*f + inPtr[0]*r) >> 8;
|
|
inPtr++;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
}
|
|
outPtr += outIncY;
|
|
inPtr += inIncY;
|
|
}
|
|
outPtr += outIncZ;
|
|
inPtr += inIncZ;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This function simply does a copy (for the first input)
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageBlendCopyData(vtkImageData *inData, vtkImageData *outData,
|
|
int *ext)
|
|
{
|
|
int idxY, idxZ, maxY, maxZ;
|
|
vtkIdType inIncX, inIncY, inIncZ;
|
|
int rowLength;
|
|
unsigned char *inPtr, *inPtr1, *outPtr;
|
|
|
|
inPtr = (unsigned char *) inData->GetScalarPointerForExtent(ext);
|
|
outPtr = (unsigned char *) outData->GetScalarPointerForExtent(ext);
|
|
|
|
// Get increments to march through inData
|
|
inData->GetIncrements(inIncX, inIncY, inIncZ);
|
|
|
|
// find the region to loop over
|
|
rowLength = (ext[1] - ext[0]+1)*inIncX*inData->GetScalarSize();
|
|
maxY = ext[3] - ext[2];
|
|
maxZ = ext[5] - ext[4];
|
|
|
|
inIncY *= inData->GetScalarSize();
|
|
inIncZ *= inData->GetScalarSize();
|
|
|
|
// Loop through outData pixels
|
|
for (idxZ = 0; idxZ <= maxZ; idxZ++)
|
|
{
|
|
inPtr1 = inPtr + idxZ*inIncZ;
|
|
for (idxY = 0; idxY <= maxY; idxY++)
|
|
{
|
|
memcpy(outPtr,inPtr1,rowLength);
|
|
inPtr1 += inIncY;
|
|
outPtr += rowLength;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This templated function executes the filter for any type of data.
|
|
template <class T>
|
|
void vtkImageBlendCompoundExecute(vtkImageBlend *self,
|
|
int extent[6],
|
|
vtkImageData *inData,
|
|
T *inPtr,
|
|
vtkImageData *tmpData,
|
|
double opacity,
|
|
double threshold)
|
|
{
|
|
unsigned long count = 0;
|
|
unsigned long target;
|
|
|
|
target = (unsigned long)((extent[3] - extent[2] + 1)*
|
|
(extent[5] - extent[4] + 1)/50.0);
|
|
target++;
|
|
|
|
// Get increments to march through data
|
|
|
|
vtkIdType inIncX, inIncY, inIncZ;
|
|
int inC;
|
|
|
|
inData->GetContinuousIncrements(extent, inIncX, inIncY, inIncZ);
|
|
inC = inData->GetNumberOfScalarComponents();
|
|
|
|
vtkIdType tmpIncX, tmpIncY, tmpIncZ;
|
|
int tmpC;
|
|
|
|
tmpData->GetContinuousIncrements(extent, tmpIncX, tmpIncY, tmpIncZ);
|
|
tmpC = tmpData->GetNumberOfScalarComponents();
|
|
|
|
double* tmpPtr = (double *)tmpData->GetScalarPointerForExtent(extent);
|
|
|
|
// Opacity
|
|
double minA, maxA;
|
|
double r;
|
|
|
|
if (inData->GetScalarType() == VTK_DOUBLE ||
|
|
inData->GetScalarType() == VTK_FLOAT)
|
|
{
|
|
minA = 0.0;
|
|
maxA = 1.0;
|
|
}
|
|
else
|
|
{
|
|
minA = (double)inData->GetScalarTypeMin();
|
|
maxA = (double)inData->GetScalarTypeMax();
|
|
}
|
|
|
|
r = opacity;
|
|
opacity = opacity/(maxA-minA);
|
|
|
|
if ((inC == 3 || inC == 1) && r <= threshold)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Loop through output pixels
|
|
|
|
for (int idxZ = extent[4]; idxZ <= extent[5]; idxZ++)
|
|
{
|
|
for (int idxY = extent[2]; !self->AbortExecute && idxY<=extent[3]; idxY++)
|
|
{
|
|
if (!(count%target))
|
|
{
|
|
self->UpdateProgress(count/(50.0*target));
|
|
}
|
|
count++;
|
|
|
|
if (tmpC >= 3)
|
|
{
|
|
|
|
// RGB(A) blended with RGBA
|
|
if (inC >= 4)
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
r = opacity * ((double)inPtr[3] - minA);
|
|
if (r > threshold)
|
|
{
|
|
tmpPtr[0] += (double)inPtr[0] * r;
|
|
tmpPtr[1] += (double)inPtr[1] * r;
|
|
tmpPtr[2] += (double)inPtr[2] * r;
|
|
tmpPtr[3] += r;
|
|
}
|
|
tmpPtr += 4;
|
|
inPtr += inC;
|
|
}
|
|
}
|
|
|
|
// RGB(A) blended with RGB
|
|
else if (inC == 3)
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
tmpPtr[0] += (double)inPtr[0] * r;
|
|
tmpPtr[1] += (double)inPtr[1] * r;
|
|
tmpPtr[2] += (double)inPtr[2] * r;
|
|
tmpPtr[3] += r;
|
|
tmpPtr += 4;
|
|
inPtr += inC;
|
|
}
|
|
}
|
|
|
|
// RGB(A) blended with luminance+alpha
|
|
else if (inC == 2)
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
r = opacity * ((double)inPtr[1] - minA);
|
|
if (r > threshold)
|
|
{
|
|
tmpPtr[0] += (double)(*inPtr) * r;
|
|
tmpPtr[1] += (double)(*inPtr) * r;
|
|
tmpPtr[2] += (double)(*inPtr) * r;
|
|
tmpPtr[3] += r;
|
|
}
|
|
tmpPtr += 4;
|
|
inPtr += 2;
|
|
}
|
|
}
|
|
|
|
// RGB(A) blended with luminance
|
|
else if (inC == 1)
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
tmpPtr[0] += (double)(*inPtr) * r;
|
|
tmpPtr[1] += (double)(*inPtr) * r;
|
|
tmpPtr[2] += (double)(*inPtr) * r;
|
|
tmpPtr[3] += r;
|
|
tmpPtr += 4;
|
|
inPtr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// luminance(+alpha) blended with luminance+alpha
|
|
else if (inC == 2)
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
r = opacity * ((double)inPtr[1] - minA);
|
|
if (r > threshold)
|
|
{
|
|
tmpPtr[0] = (double)(*inPtr) * r;
|
|
tmpPtr[1] += r;
|
|
}
|
|
tmpPtr += 2;
|
|
inPtr += 2;
|
|
}
|
|
}
|
|
|
|
// luminance(+alpha) blended with luminance
|
|
else
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
tmpPtr[0] = (double)(*inPtr) * r;
|
|
tmpPtr[1] += r;
|
|
tmpPtr += 2;
|
|
inPtr++;
|
|
}
|
|
}
|
|
|
|
tmpPtr += tmpIncY;
|
|
inPtr += inIncY;
|
|
}
|
|
tmpPtr += tmpIncZ;
|
|
inPtr += inIncZ;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This templated function executes the filter for any type of data.
|
|
template <class T>
|
|
void vtkImageBlendCompoundTransferExecute(vtkImageBlend *self,
|
|
int extent[6],
|
|
vtkImageData *outData,
|
|
T *outPtr,
|
|
vtkImageData *tmpData)
|
|
{
|
|
// Get increments to march through data
|
|
|
|
vtkIdType outIncX, outIncY, outIncZ;
|
|
int outC;
|
|
|
|
outData->GetContinuousIncrements(extent, outIncX, outIncY, outIncZ);
|
|
outC = outData->GetNumberOfScalarComponents();
|
|
|
|
vtkIdType tmpIncX, tmpIncY, tmpIncZ;
|
|
int tmpC;
|
|
|
|
tmpData->GetContinuousIncrements(extent, tmpIncX, tmpIncY, tmpIncZ);
|
|
tmpC = tmpData->GetNumberOfScalarComponents();
|
|
|
|
double* tmpPtr = (double *)tmpData->GetScalarPointerForExtent(extent);
|
|
|
|
// Loop through output pixels
|
|
|
|
for (int idxZ = extent[4]; idxZ <= extent[5]; idxZ++)
|
|
{
|
|
for (int idxY = extent[2]; !self->AbortExecute && idxY<=extent[3]; idxY++)
|
|
{
|
|
if (tmpC >= 3)
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
double factor = 0.0;
|
|
if (tmpPtr[3] != 0)
|
|
{
|
|
factor = 1.0/tmpPtr[3];
|
|
}
|
|
outPtr[0] = T(tmpPtr[0]*factor);
|
|
outPtr[1] = T(tmpPtr[1]*factor);
|
|
outPtr[2] = T(tmpPtr[2]*factor);
|
|
tmpPtr += 4;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int idxX = extent[0]; idxX <= extent[1]; idxX++)
|
|
{
|
|
double factor = 0.0;
|
|
if (tmpPtr[1] != 0)
|
|
{
|
|
factor = 1.0/tmpPtr[1];
|
|
}
|
|
outPtr[0] = T(tmpPtr[0]*factor);
|
|
tmpPtr += 2;
|
|
outPtr += outC;
|
|
}
|
|
}
|
|
|
|
tmpPtr += tmpIncY;
|
|
outPtr += outIncY;
|
|
}
|
|
tmpPtr += tmpIncZ;
|
|
outPtr += outIncZ;
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// This method is passed a input and output regions, and executes the filter
|
|
// algorithm to fill the output from the inputs.
|
|
// It just executes a switch statement to call the correct function for
|
|
// the regions data types.
|
|
void vtkImageBlend::ThreadedRequestData (
|
|
vtkInformation * vtkNotUsed( request ),
|
|
vtkInformationVector** inputVector,
|
|
vtkInformationVector * vtkNotUsed( outputVector ),
|
|
vtkImageData ***inData,
|
|
vtkImageData **outData,
|
|
int outExt[6], int id)
|
|
{
|
|
int extent[6];
|
|
void *inPtr;
|
|
void *outPtr;
|
|
|
|
double opacity;
|
|
|
|
vtkImageData *tmpData = NULL;
|
|
|
|
// check
|
|
if (inData[0][0]->GetNumberOfScalarComponents() > 4)
|
|
{
|
|
vtkErrorMacro("The first input can have a maximum of four components");
|
|
return;
|
|
}
|
|
|
|
// init
|
|
switch (this->BlendMode)
|
|
{
|
|
case VTK_IMAGE_BLEND_MODE_NORMAL:
|
|
// copy the first image directly to the output
|
|
vtkDebugMacro("Execute: copy input 0 to the output.");
|
|
vtkImageBlendCopyData(inData[0][0], outData[0], outExt);
|
|
break;
|
|
|
|
case VTK_IMAGE_BLEND_MODE_COMPOUND:
|
|
tmpData = vtkImageData::New();
|
|
if (tmpData == NULL)
|
|
{
|
|
vtkErrorMacro(<< "Execute: Unable to allocate memory");
|
|
return;
|
|
}
|
|
tmpData->SetExtent(outExt);
|
|
tmpData->SetNumberOfScalarComponents(
|
|
(outData[0]->GetNumberOfScalarComponents() >= 3 ? 3 : 1) + 1);
|
|
tmpData->SetScalarType(VTK_DOUBLE);
|
|
tmpData->AllocateScalars();
|
|
memset((void *)tmpData->GetScalarPointer(), 0,
|
|
(outExt[1] - outExt[0] + 1) *
|
|
(outExt[3] - outExt[2] + 1) *
|
|
(outExt[5] - outExt[4] + 1) *
|
|
tmpData->GetNumberOfScalarComponents() *
|
|
tmpData->GetScalarSize());
|
|
break;
|
|
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown blending mode");
|
|
}
|
|
|
|
// process each input
|
|
int first_index = (this->BlendMode == VTK_IMAGE_BLEND_MODE_NORMAL ? 1 : 0);
|
|
for (int idx1 = first_index;
|
|
idx1 < this->GetNumberOfInputConnections(0); ++idx1)
|
|
{
|
|
if (inData[0][idx1] != NULL)
|
|
{
|
|
|
|
// RGB with RGB, greyscale with greyscale
|
|
if ((inData[0][idx1]->GetNumberOfScalarComponents()+1)/2 == 2 &&
|
|
(inData[0][0]->GetNumberOfScalarComponents()+1)/2 == 1)
|
|
{
|
|
vtkErrorMacro("input has too many components, can't blend RGB data \
|
|
into greyscale data");
|
|
continue;
|
|
}
|
|
|
|
// this filter expects that input is the same type as output.
|
|
if (inData[0][idx1]->GetScalarType() != outData[0]->GetScalarType())
|
|
{
|
|
vtkErrorMacro(<< "Execute: input" << idx1 << " ScalarType (" <<
|
|
inData[0][idx1]->GetScalarType() <<
|
|
"), must match output ScalarType (" << outData[0]->GetScalarType()
|
|
<< ")");
|
|
continue;
|
|
}
|
|
|
|
// input extents
|
|
vtkInformation *inInfo =
|
|
inputVector[0]->GetInformationObject(idx1);
|
|
int *inWextent =
|
|
inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
|
|
this->InternalComputeInputUpdateExtent(extent, outExt, inWextent);
|
|
|
|
int skip = 0;
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
if (outExt[2*i+1] < extent[2*i] || outExt[2*i] > extent[2*i+1])
|
|
{
|
|
// extents don't overlap, skip this input
|
|
skip = 1;
|
|
}
|
|
}
|
|
|
|
if (skip)
|
|
{
|
|
vtkDebugMacro("Execute: skipping input.");
|
|
continue;
|
|
}
|
|
|
|
opacity = this->GetOpacity(idx1);
|
|
|
|
inPtr = inData[0][idx1]->GetScalarPointerForExtent(extent);
|
|
|
|
// vtkDebugMacro("Execute: " << idx1 << "=>" << extent[0] << ", " << extent[1] << " / " << extent[2] << ", " << extent[3] << " / " << extent[4] << ", " << extent[5]);
|
|
|
|
switch (this->BlendMode)
|
|
{
|
|
case VTK_IMAGE_BLEND_MODE_NORMAL:
|
|
outPtr = outData[0]->GetScalarPointerForExtent(extent);
|
|
// for performance reasons, use a special method for unsigned char
|
|
if (inData[0][idx1]->GetScalarType() == VTK_UNSIGNED_CHAR)
|
|
{
|
|
vtkImageBlendExecuteChar(this, extent,
|
|
inData[0][idx1], (unsigned char *)(inPtr),
|
|
outData[0], (unsigned char *)(outPtr),
|
|
opacity, id);
|
|
}
|
|
else
|
|
{
|
|
switch (inData[0][idx1]->GetScalarType())
|
|
{
|
|
vtkTemplateMacro(
|
|
vtkImageBlendExecute(this, extent,
|
|
inData[0][idx1], (VTK_TT *)(inPtr),
|
|
outData[0], (VTK_TT *)(outPtr),
|
|
opacity, id));
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case VTK_IMAGE_BLEND_MODE_COMPOUND:
|
|
switch (inData[0][idx1]->GetScalarType())
|
|
{
|
|
vtkTemplateMacro(
|
|
vtkImageBlendCompoundExecute(this,
|
|
extent,
|
|
inData[0][idx1],
|
|
(VTK_TT *)(inPtr),
|
|
tmpData,
|
|
opacity,
|
|
this->CompoundThreshold));
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown blending mode");
|
|
}
|
|
}
|
|
}
|
|
|
|
// conclude
|
|
switch (this->BlendMode)
|
|
{
|
|
case VTK_IMAGE_BLEND_MODE_NORMAL:
|
|
break;
|
|
|
|
case VTK_IMAGE_BLEND_MODE_COMPOUND:
|
|
outPtr = outData[0]->GetScalarPointerForExtent(outExt);
|
|
switch (outData[0]->GetScalarType())
|
|
{
|
|
vtkTemplateMacro(
|
|
vtkImageBlendCompoundTransferExecute(this,
|
|
outExt,
|
|
outData[0],
|
|
(VTK_TT *)(outPtr),
|
|
tmpData));
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown ScalarType");
|
|
return;
|
|
}
|
|
tmpData->Delete();
|
|
break;
|
|
|
|
default:
|
|
vtkErrorMacro(<< "Execute: Unknown blending mode");
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
void vtkImageBlend::PrintSelf(ostream& os, vtkIndent indent)
|
|
{
|
|
this->Superclass::PrintSelf(os, indent);
|
|
int i;
|
|
for (i = 0; i < this->OpacityArrayLength; i++)
|
|
{
|
|
os << indent << "Opacity(" << i << "): " << this->GetOpacity(i) << endl;
|
|
}
|
|
os << indent << "Stencil: " << this->GetStencil() << endl;
|
|
os << indent << "BlendMode: " << this->GetBlendModeAsString() << endl
|
|
<< indent << "CompoundThreshold: " << this->CompoundThreshold << endl;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
int vtkImageBlend::FillInputPortInformation(int port, vtkInformation* info)
|
|
{
|
|
if (port == 0)
|
|
{
|
|
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageData");
|
|
info->Set(vtkAlgorithm::INPUT_IS_REPEATABLE(), 1);
|
|
}
|
|
if (port == 1)
|
|
{
|
|
info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkImageStencilData");
|
|
// the stencil input is optional
|
|
info->Set(vtkAlgorithm::INPUT_IS_OPTIONAL(), 1);
|
|
}
|
|
return 1;
|
|
}
|
|
|