1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkOpenGLImageGradient.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkOpenGLImageGradient.h"
16 
17 #include "vtkOpenGLImageAlgorithmHelper.h"
18 
19 #include "vtkCellData.h"
20 #include "vtkDataArray.h"
21 #include "vtkImageData.h"
22 #include "vtkInformation.h"
23 #include "vtkInformationVector.h"
24 #include "vtkObjectFactory.h"
25 #include "vtkPointData.h"
26 #include "vtkShaderProgram.h"
27 #include "vtkStreamingDemandDrivenPipeline.h"
28 
29 #include <algorithm> // for std::nth_element
30 
31 vtkStandardNewMacro(vtkOpenGLImageGradient);
32 
33 //------------------------------------------------------------------------------
34 // Construct an instance of vtkOpenGLImageGradient filter.
vtkOpenGLImageGradient()35 vtkOpenGLImageGradient::vtkOpenGLImageGradient()
36 {
37   // for GPU we do not want threading
38   this->NumberOfThreads = 1;
39   this->EnableSMP = false;
40   this->Helper = vtkOpenGLImageAlgorithmHelper::New();
41 }
42 
43 //------------------------------------------------------------------------------
~vtkOpenGLImageGradient()44 vtkOpenGLImageGradient::~vtkOpenGLImageGradient()
45 {
46   if (this->Helper)
47   {
48     this->Helper->Delete();
49     this->Helper = nullptr;
50   }
51 }
52 
SetRenderWindow(vtkRenderWindow * renWin)53 void vtkOpenGLImageGradient::SetRenderWindow(vtkRenderWindow* renWin)
54 {
55   this->Helper->SetRenderWindow(renWin);
56 }
57 
58 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)59 void vtkOpenGLImageGradient::PrintSelf(ostream& os, vtkIndent indent)
60 {
61   this->Superclass::PrintSelf(os, indent);
62 
63   os << indent << "Helper: ";
64   this->Helper->PrintSelf(os, indent);
65 }
66 
67 // this is used as a callback by the helper to set shader parameters
68 // before running and to update them on each slice
69 class vtkOpenGLGradientCB : public vtkOpenGLImageAlgorithmCallback
70 {
71 public:
72   // initialize the spacing
InitializeShaderUniforms(vtkShaderProgram * program)73   void InitializeShaderUniforms(vtkShaderProgram* program) override
74   {
75     float sp[3];
76     sp[0] = this->Spacing[0];
77     sp[1] = this->Spacing[1];
78     sp[2] = this->Spacing[2];
79     program->SetUniform3f("spacing", sp);
80   }
81 
82   // no uniforms change on a per slice basis so empty
UpdateShaderUniforms(vtkShaderProgram *,int)83   void UpdateShaderUniforms(vtkShaderProgram* /* program */, int /* zExtent */) override {}
84 
85   double* Spacing;
86   vtkOpenGLGradientCB() = default;
87   ~vtkOpenGLGradientCB() override = default;
88 
89 private:
90   vtkOpenGLGradientCB(const vtkOpenGLGradientCB&) = delete;
91   void operator=(const vtkOpenGLGradientCB&) = delete;
92 };
93 
94 //------------------------------------------------------------------------------
95 // This method contains the first switch statement that calls the correct
96 // templated function for the input and output region types.
ThreadedRequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** inputVector,vtkInformationVector * vtkNotUsed (outputVector),vtkImageData *** inData,vtkImageData ** outData,int outExt[6],int vtkNotUsed (id))97 void vtkOpenGLImageGradient::ThreadedRequestData(vtkInformation* vtkNotUsed(request),
98   vtkInformationVector** inputVector, vtkInformationVector* vtkNotUsed(outputVector),
99   vtkImageData*** inData, vtkImageData** outData, int outExt[6], int vtkNotUsed(id))
100 {
101   vtkDataArray* inArray = this->GetInputArrayToProcess(0, inputVector);
102   outData[0]->GetPointData()->GetScalars()->SetName(inArray->GetName());
103 
104   // The output scalar type must be double to store proper gradients.
105   if (outData[0]->GetScalarType() != VTK_DOUBLE)
106   {
107     vtkErrorMacro(
108       "Execute: output ScalarType is " << outData[0]->GetScalarType() << "but must be double.");
109     return;
110   }
111 
112   // Gradient makes sense only with one input component.  This is not
113   // a Jacobian filter.
114   if (inArray->GetNumberOfComponents() != 1)
115   {
116     vtkErrorMacro("Execute: input has more than one component. "
117                   "The input to gradient should be a single component image. "
118                   "Think about it. If you insist on using a color image then "
119                   "run it though RGBToHSV then ExtractComponents to get the V "
120                   "components. That's probably what you want anyhow.");
121     return;
122   }
123 
124   vtkOpenGLGradientCB cb;
125   cb.Spacing = inData[0][0]->GetSpacing();
126 
127   // build the fragment shader for 2D or 3D gradient
128   std::string fragShader =
129     "//VTK::System::Dec\n"
130     "varying vec2 tcoordVSOutput;\n"
131     "uniform sampler3D inputTex1;\n"
132     "uniform float zPos;\n"
133     "uniform vec3 spacing;\n"
134     "uniform float inputScale;\n"
135     "uniform float inputShift;\n"
136     "//VTK::Output::Dec\n"
137     "void main(void) {\n"
138     "  float dx = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(1,0,0)).r\n"
139     "    - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(-1,0,0)).r;\n"
140     "  dx = inputScale*0.5*dx/spacing.x;\n"
141     "  float dy = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,1,0)).r\n"
142     "    - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,-1,0)).r;\n"
143     "  dy = inputScale*0.5*dy/spacing.y;\n";
144 
145   if (this->Dimensionality == 3)
146   {
147     fragShader +=
148       "  float dz = textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,0,1)).r\n"
149       "    - textureOffset(inputTex1, vec3(tcoordVSOutput, zPos), ivec3(0,0,-1)).r;\n"
150       "  dz = inputScale*0.5*dz/spacing.z;\n"
151       "  gl_FragData[0] = vec4(dx, dy, dz, 1.0);\n"
152       "}\n";
153   }
154   else
155   {
156     fragShader += "  gl_FragData[0] = vec4(dx, dy, 0.0, 1.0);\n"
157                   "}\n";
158   }
159 
160   // call the helper to execte this code
161   this->Helper->Execute(&cb, inData[0][0], inArray, outData[0], outExt,
162 
163     "//VTK::System::Dec\n"
164     "attribute vec4 vertexMC;\n"
165     "attribute vec2 tcoordMC;\n"
166     "varying vec2 tcoordVSOutput;\n"
167     "void main() {\n"
168     "  tcoordVSOutput = tcoordMC;\n"
169     "  gl_Position = vertexMC;\n"
170     "}\n",
171 
172     fragShader.c_str(),
173 
174     "");
175 }
176