vtk9/Rendering/ContextOpenGL2/vtkOpenGLContextDevice2D.cxx

2930 lines
87 KiB
C++

/*=========================================================================
Program: Visualization Toolkit
Module: vtkOpenGLContextDevice2D.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.
=========================================================================*/
// Hide VTK_DEPRECATED_IN_9_1_0() warnings for this class.
#define VTK_DEPRECATION_LEVEL 0
#include "vtkOpenGLContextDevice2D.h"
#include "vtkAbstractContextBufferId.h"
#include "vtkBrush.h"
#include "vtkContext2D.h"
#include "vtkFloatArray.h"
#include "vtkImageData.h"
#include "vtkImageResize.h"
#include "vtkMath.h"
#include "vtkMatrix3x3.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLError.h"
#include "vtkOpenGLGL2PSHelper.h"
#include "vtkOpenGLHelper.h"
#include "vtkOpenGLIndexBufferObject.h"
#include "vtkOpenGLRenderWindow.h"
#include "vtkOpenGLRenderer.h"
#include "vtkOpenGLShaderCache.h"
#include "vtkOpenGLState.h"
#include "vtkOpenGLTexture.h"
#include "vtkOpenGLVertexArrayObject.h"
#include "vtkOpenGLVertexBufferObject.h"
#include "vtkPath.h"
#include "vtkPen.h"
#include "vtkPointData.h"
#include "vtkPoints2D.h"
#include "vtkPolyData.h"
#include "vtkRect.h"
#include "vtkShaderProgram.h"
#include "vtkSmartPointer.h"
#include "vtkTextProperty.h"
#include "vtkTextRenderer.h"
#include "vtkTexture.h"
#include "vtkTextureUnitManager.h"
#include "vtkTransform.h"
#include "vtkTransformFeedback.h"
#include "vtkVector.h"
#include "vtkViewport.h"
#include "vtkWindow.h"
#include "vtkObjectFactory.h"
#include "vtkOpenGLContextDevice2DPrivate.h"
#include <algorithm>
#include <cassert>
#include <limits>
#include <sstream>
#define BUFFER_OFFSET(i) (reinterpret_cast<char*>(i))
namespace
{
void copyColors(std::vector<unsigned char>& newColors, unsigned char* colors, int nc)
{
for (int j = 0; j < nc; j++)
{
newColors.push_back(colors[j]);
}
}
const char* myVertShader = "in vec2 vertexMC;\n"
"uniform mat4 WCDCMatrix;\n"
"uniform mat4 MCWCMatrix;\n"
"#ifdef haveColors\n"
"in vec4 vertexScalar;\n"
"out vec4 vertexColor;\n"
"#endif\n"
"#ifdef haveTCoords\n"
"in vec2 tcoordMC;\n"
"out vec2 tcoord;\n"
"#endif\n"
"#ifdef haveLines\n"
"in vec2 tcoordMC;\n"
"out float ldistance;\n"
"#endif\n"
"void main() {\n"
"#ifdef haveColors\n"
"vertexColor = vertexScalar;\n"
"#endif\n"
"#ifdef haveTCoords\n"
"tcoord = tcoordMC;\n"
"#endif\n"
"#ifdef haveLines\n"
"ldistance = tcoordMC.x;\n"
"#endif\n"
"vec4 vertex = vec4(vertexMC.xy, 0.0, 1.0);\n"
"gl_Position = vertex*MCWCMatrix*WCDCMatrix; }\n";
const char* myFragShader = "//VTK::Output::Dec\n"
"#ifdef haveColors\n"
"in vec4 vertexColor;\n"
"#else\n"
"uniform vec4 vertexColor;\n"
"#endif\n"
"#ifdef haveTCoords\n"
"in vec2 tcoord;\n"
"uniform sampler2D texture1;\n"
"#endif\n"
"#ifdef haveLines\n"
"in float ldistance;\n"
"uniform int stipple;\n"
"#endif\n"
"void main() {\n"
"#ifdef haveLines\n"
"if ((0x01 << int(mod(ldistance,16.0)) & stipple) == 0) { discard; }\n"
"#endif\n"
"#ifdef haveTCoords\n"
" gl_FragData[0] = texture2D(texture1, tcoord);\n"
"#else\n"
" gl_FragData[0] = vertexColor;\n"
"#endif\n"
"}\n";
//------------------------------------------------------------------------------
// Returns true when rendering the GL2PS background raster image. Vectorizable
// primitives should not be drawn during these passes.
bool SkipDraw()
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Background)
{
return true;
}
return false;
}
//------------------------------------------------------------------------------
// Releases the current shader program if it is inconsistent with the GL2PS
// capture state. Returns the current OpenGLGL2PSHelper instance if one exists.
vtkOpenGLGL2PSHelper* PrepProgramForGL2PS(vtkOpenGLHelper& helper)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
// Always recreate the program when doing GL2PS capture.
if (helper.Program)
{
helper.ReleaseGraphicsResources(nullptr);
}
}
else
{
// If there is a feedback transform capturer set on the current shader
// program and we're not capturing, recreate the program.
if (helper.Program && helper.Program->GetTransformFeedback())
{
helper.ReleaseGraphicsResources(nullptr);
}
}
return gl2ps;
}
//------------------------------------------------------------------------------
// Call before glDraw* commands to ensure that vertices are properly captured
// for GL2PS export.
void PreDraw(vtkOpenGLHelper& helper, int drawMode, size_t numVerts)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture && helper.Program)
{
if (vtkTransformFeedback* tfc = helper.Program->GetTransformFeedback())
{
tfc->SetNumberOfVertices(drawMode, numVerts);
tfc->BindBuffer();
}
}
}
//------------------------------------------------------------------------------
// Call after glDraw* commands to ensure that vertices are properly captured
// for GL2PS export.
void PostDraw(vtkOpenGLHelper& helper, vtkRenderer* ren, unsigned char col[4])
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture && helper.Program)
{
if (vtkTransformFeedback* tfc = helper.Program->GetTransformFeedback())
{
tfc->ReadBuffer();
tfc->ReleaseGraphicsResources();
gl2ps->ProcessTransformFeedback(tfc, ren, col);
tfc->ReleaseBufferData();
}
}
}
//------------------------------------------------------------------------------
// Returns true if the startAngle and stopAngle (as used in the ellipse drawing
// functions) describe a full circle.
inline bool IsFullCircle(float startAngle, float stopAngle)
{
// A small number practical for rendering purposes.
const float TOL = 1e-5f;
return std::fabs(stopAngle - startAngle) + TOL >= 360.f;
}
} // end anon namespace
//------------------------------------------------------------------------------
vtkStandardNewMacro(vtkOpenGLContextDevice2D);
//------------------------------------------------------------------------------
vtkOpenGLContextDevice2D::vtkOpenGLContextDevice2D()
{
this->Renderer = nullptr;
this->InRender = false;
this->Storage = new vtkOpenGLContextDevice2D::Private;
this->PolyDataImpl = new vtkOpenGLContextDevice2D::CellArrayHelper(this);
this->RenderWindow = nullptr;
this->MaximumMarkerCacheSize = 20;
this->ProjectionMatrix = vtkTransform::New();
this->ModelMatrix = vtkTransform::New();
this->VBO = new vtkOpenGLHelper;
this->VCBO = new vtkOpenGLHelper;
this->LinesBO = new vtkOpenGLHelper;
this->LinesCBO = new vtkOpenGLHelper;
this->VTBO = new vtkOpenGLHelper;
this->SBO = new vtkOpenGLHelper;
this->SCBO = new vtkOpenGLHelper;
this->LinePattern = 0xFFFF;
}
//------------------------------------------------------------------------------
vtkOpenGLContextDevice2D::~vtkOpenGLContextDevice2D()
{
delete this->VBO;
this->VBO = nullptr;
delete this->VCBO;
this->VCBO = nullptr;
delete this->LinesBO;
this->LinesBO = nullptr;
delete this->LinesCBO;
this->LinesCBO = nullptr;
delete this->SBO;
this->SBO = nullptr;
delete this->SCBO;
this->SCBO = nullptr;
delete this->VTBO;
this->VTBO = nullptr;
while (!this->MarkerCache.empty())
{
this->MarkerCache.back().Value->Delete();
this->MarkerCache.pop_back();
}
this->ProjectionMatrix->Delete();
this->ModelMatrix->Delete();
delete this->Storage;
delete this->PolyDataImpl;
}
vtkMatrix4x4* vtkOpenGLContextDevice2D::GetProjectionMatrix()
{
return this->ProjectionMatrix->GetMatrix();
}
vtkMatrix4x4* vtkOpenGLContextDevice2D::GetModelMatrix()
{
return this->ModelMatrix->GetMatrix();
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::Begin(vtkViewport* viewport)
{
vtkOpenGLClearErrorMacro();
// Need the actual pixel size of the viewport - ask OpenGL.
GLint vp[4];
glGetIntegerv(GL_VIEWPORT, vp);
this->Storage->Offset.Set(static_cast<int>(vp[0]), static_cast<int>(vp[1]));
this->Storage->Dim.Set(static_cast<int>(vp[2]), static_cast<int>(vp[3]));
// push a 2D matrix on the stack
this->ProjectionMatrix->Push();
this->ProjectionMatrix->Identity();
this->PushMatrix();
this->ModelMatrix->Identity();
double offset = 0.5;
double xmin = offset;
double xmax = vp[2] + offset - 1.0;
double ymin = offset;
double ymax = vp[3] + offset - 1.0;
double znear = -2000;
double zfar = 2000;
double matrix[4][4];
vtkMatrix4x4::Identity(*matrix);
matrix[0][0] = 2 / (xmax - xmin);
matrix[1][1] = 2 / (ymax - ymin);
matrix[2][2] = -2 / (zfar - znear);
matrix[0][3] = -(xmin + xmax) / (xmax - xmin);
matrix[1][3] = -(ymin + ymax) / (ymax - ymin);
matrix[2][3] = -(znear + zfar) / (zfar - znear);
this->ProjectionMatrix->SetMatrix(*matrix);
// Store the previous state before changing it
this->Renderer = vtkRenderer::SafeDownCast(viewport);
this->RenderWindow = vtkOpenGLRenderWindow::SafeDownCast(this->Renderer->GetRenderWindow());
vtkOpenGLState* ostate = this->RenderWindow->GetState();
this->Storage->SaveGLState(ostate);
ostate->vtkglDisable(GL_DEPTH_TEST);
ostate->vtkglEnable(GL_BLEND);
this->RenderWindow->GetShaderCache()->ReleaseCurrentShader();
// Enable simple line smoothing if multisampling is on.
#ifdef GL_LINE_SMOOTH
if (this->Renderer->GetRenderWindow()->GetMultiSamples())
{
this->RenderWindow->GetState()->vtkglEnable(GL_LINE_SMOOTH);
}
#endif
this->InRender = true;
vtkOpenGLCheckErrorMacro("failed after Begin");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::End()
{
if (!this->InRender)
{
return;
}
this->ProjectionMatrix->Pop();
this->PopMatrix();
vtkOpenGLClearErrorMacro();
// Restore the GL state that we changed
vtkOpenGLState* ostate = this->RenderWindow->GetState();
this->Storage->RestoreGLState(ostate);
// Disable simple line smoothing if multisampling is on.
#ifdef GL_LINE_SMOOTH
if (this->Renderer->GetRenderWindow()->GetMultiSamples())
{
this->RenderWindow->GetState()->vtkglDisable(GL_LINE_SMOOTH);
}
#endif
this->PolyDataImpl->HandleEndFrame();
this->RenderWindow = nullptr;
this->InRender = false;
vtkOpenGLCheckErrorMacro("failed after End");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::BufferIdModeBegin(vtkAbstractContextBufferId* bufferId)
{
assert("pre: not_yet" && !this->GetBufferIdMode());
assert("pre: bufferId_exists" && bufferId != nullptr);
vtkOpenGLClearErrorMacro();
this->BufferId = bufferId;
// Save OpenGL state.
vtkOpenGLState* ostate = this->RenderWindow->GetState();
this->Storage->SaveGLState(ostate, true);
int lowerLeft[2];
int usize, vsize;
this->Renderer->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft + 1);
// push a 2D matrix on the stack
this->ProjectionMatrix->Push();
this->ProjectionMatrix->Identity();
this->PushMatrix();
this->ModelMatrix->Identity();
double xmin = 0.5;
double xmax = usize + 0.5;
double ymin = 0.5;
double ymax = vsize + 0.5;
double znear = -1;
double zfar = 1;
double matrix[4][4];
vtkMatrix4x4::Identity(*matrix);
matrix[0][0] = 2 / (xmax - xmin);
matrix[1][1] = 2 / (ymax - ymin);
matrix[2][2] = -2 / (zfar - znear);
matrix[0][3] = -(xmin + xmax) / (xmax - xmin);
matrix[1][3] = -(ymin + ymax) / (ymax - ymin);
matrix[2][3] = -(znear + zfar) / (zfar - znear);
this->ProjectionMatrix->SetMatrix(*matrix);
ostate->vtkglDrawBuffer(GL_BACK_LEFT);
ostate->vtkglClearColor(0.0, 0.0, 0.0, 0.0); // id=0 means no hit, just background
ostate->vtkglClear(GL_COLOR_BUFFER_BIT);
ostate->vtkglDisable(GL_STENCIL_TEST);
ostate->vtkglDisable(GL_DEPTH_TEST);
ostate->vtkglDisable(GL_BLEND);
vtkOpenGLCheckErrorMacro("failed after BufferIdModeBegin");
assert("post: started" && this->GetBufferIdMode());
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::BufferIdModeEnd()
{
assert("pre: started" && this->GetBufferIdMode());
vtkOpenGLClearErrorMacro();
// Assume the renderer has been set previously during rendering (sse Begin())
int lowerLeft[2];
int usize, vsize;
this->Renderer->GetTiledSizeAndOrigin(&usize, &vsize, lowerLeft, lowerLeft + 1);
this->BufferId->SetValues(lowerLeft[0], lowerLeft[1]);
this->ProjectionMatrix->Pop();
this->PopMatrix();
this->Storage->RestoreGLState(this->RenderWindow->GetState(), true);
this->BufferId = nullptr;
vtkOpenGLCheckErrorMacro("failed after BufferIdModeEnd");
assert("post: done" && !this->GetBufferIdMode());
}
void vtkOpenGLContextDevice2D::SetMatrices(vtkShaderProgram* prog)
{
prog->SetUniformMatrix("WCDCMatrix", this->ProjectionMatrix->GetMatrix());
prog->SetUniformMatrix("MCWCMatrix", this->ModelMatrix->GetMatrix());
}
void vtkOpenGLContextDevice2D::BuildVBO(
vtkOpenGLHelper* cellBO, float* f, int nv, unsigned char* colors, int nc, float* tcoords)
{
int stride = 2;
int cOffset = 0;
int tOffset = 0;
if (colors)
{
cOffset = stride;
stride++;
}
if (tcoords)
{
tOffset = stride;
stride += 2;
}
std::vector<float> va;
va.resize(nv * stride);
vtkFourByteUnion c;
for (int i = 0; i < nv; i++)
{
va[i * stride] = f[i * 2];
va[i * stride + 1] = f[i * 2 + 1];
if (colors)
{
c.c[0] = colors[nc * i];
c.c[1] = colors[nc * i + 1];
c.c[2] = colors[nc * i + 2];
if (nc == 4)
{
c.c[3] = colors[nc * i + 3];
}
else
{
c.c[3] = 255;
}
va[i * stride + cOffset] = c.f;
}
if (tcoords)
{
va[i * stride + tOffset] = tcoords[i * 2];
va[i * stride + tOffset + 1] = tcoords[i * 2 + 1];
}
}
// upload the data
cellBO->IBO->Upload(va, vtkOpenGLBufferObject::ArrayBuffer);
cellBO->VAO->ShaderProgramChanged();
cellBO->VAO->Bind();
if (!cellBO->VAO->AddAttributeArray(
cellBO->Program, cellBO->IBO, "vertexMC", 0, sizeof(float) * stride, VTK_FLOAT, 2, false))
{
vtkErrorMacro(<< "Error setting vertexMC in shader VAO.");
}
if (colors)
{
if (!cellBO->VAO->AddAttributeArray(cellBO->Program, cellBO->IBO, "vertexScalar",
sizeof(float) * cOffset, sizeof(float) * stride, VTK_UNSIGNED_CHAR, 4, true))
{
vtkErrorMacro(<< "Error setting vertexScalar in shader VAO.");
}
}
if (tcoords)
{
if (!cellBO->VAO->AddAttributeArray(cellBO->Program, cellBO->IBO, "tcoordMC",
sizeof(float) * tOffset, sizeof(float) * stride, VTK_FLOAT, 2, false))
{
vtkErrorMacro(<< "Error setting tcoordMC in shader VAO.");
}
}
cellBO->VAO->Bind();
}
void vtkOpenGLContextDevice2D::ReadyVBOProgram()
{
vtkOpenGLGL2PSHelper* gl2ps = PrepProgramForGL2PS(*this->VBO);
if (!this->VBO->Program)
{
vtkTransformFeedback* tf = nullptr;
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
tf = vtkTransformFeedback::New();
tf->AddVarying(vtkTransformFeedback::Vertex_ClipCoordinate_F, "gl_Position");
}
std::string vs = "//VTK::System::Dec\n";
vs += myVertShader;
std::string fs = "//VTK::System::Dec\n";
fs += myFragShader;
this->VBO->Program =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(vs.c_str(), fs.c_str(), "", tf);
if (tf)
{
tf->Delete();
tf = nullptr;
}
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->VBO->Program);
}
}
void vtkOpenGLContextDevice2D::ReadyVCBOProgram()
{
vtkOpenGLGL2PSHelper* gl2ps = PrepProgramForGL2PS(*this->VCBO);
if (!this->VCBO->Program)
{
vtkTransformFeedback* tf = nullptr;
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
tf = vtkTransformFeedback::New();
tf->AddVarying(vtkTransformFeedback::Vertex_ClipCoordinate_F, "gl_Position");
tf->AddVarying(vtkTransformFeedback::Color_RGBA_F, "vertexColor");
}
std::string vs = "//VTK::System::Dec\n#define haveColors\n";
vs += myVertShader;
std::string fs = "//VTK::System::Dec\n#define haveColors\n";
fs += myFragShader;
this->VCBO->Program =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(vs.c_str(), fs.c_str(), "", tf);
if (tf)
{
tf->Delete();
tf = nullptr;
}
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->VCBO->Program);
}
}
void vtkOpenGLContextDevice2D::ReadyLinesBOProgram()
{
vtkOpenGLGL2PSHelper* gl2ps = PrepProgramForGL2PS(*this->LinesBO);
if (!this->LinesBO->Program)
{
vtkTransformFeedback* tf = nullptr;
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
tf = vtkTransformFeedback::New();
tf->AddVarying(vtkTransformFeedback::Vertex_ClipCoordinate_F, "gl_Position");
}
std::string vs = "//VTK::System::Dec\n#define haveLines\n";
vs += myVertShader;
std::string fs = "//VTK::System::Dec\n#define haveLines\n";
fs += myFragShader;
this->LinesBO->Program =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(vs.c_str(), fs.c_str(), "", tf);
if (tf)
{
tf->Delete();
tf = nullptr;
}
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->LinesBO->Program);
}
}
void vtkOpenGLContextDevice2D::ReadyLinesCBOProgram()
{
vtkOpenGLGL2PSHelper* gl2ps = PrepProgramForGL2PS(*this->LinesCBO);
if (!this->LinesCBO->Program)
{
vtkTransformFeedback* tf = nullptr;
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
tf = vtkTransformFeedback::New();
tf->AddVarying(vtkTransformFeedback::Vertex_ClipCoordinate_F, "gl_Position");
tf->AddVarying(vtkTransformFeedback::Color_RGBA_F, "vertexColor");
}
std::string vs = "//VTK::System::Dec\n#define haveColors\n#define haveLines\n";
vs += myVertShader;
std::string fs = "//VTK::System::Dec\n#define haveColors\n#define haveLines\n";
fs += myFragShader;
this->LinesCBO->Program =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(vs.c_str(), fs.c_str(), "", tf);
if (tf)
{
tf->Delete();
tf = nullptr;
}
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->LinesCBO->Program);
}
}
void vtkOpenGLContextDevice2D::ReadyVTBOProgram()
{
if (!this->VTBO->Program)
{
std::string vs = "//VTK::System::Dec\n#define haveTCoords\n";
vs += myVertShader;
std::string fs = "//VTK::System::Dec\n#define haveTCoords\n";
fs += myFragShader;
this->VTBO->Program =
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(vs.c_str(), fs.c_str(), "");
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->VTBO->Program);
}
}
void vtkOpenGLContextDevice2D::ReadySBOProgram()
{
if (!this->SBO->Program)
{
this->SBO->Program = this->RenderWindow->GetShaderCache()->ReadyShaderProgram(
// vertex shader
"//VTK::System::Dec\n"
"in vec2 vertexMC;\n"
"uniform mat4 WCDCMatrix;\n"
"uniform mat4 MCWCMatrix;\n"
"void main() {\n"
"vec4 vertex = vec4(vertexMC.xy, 0.0, 1.0);\n"
"gl_Position = vertex*MCWCMatrix*WCDCMatrix; }\n",
// fragment shader
"//VTK::System::Dec\n"
"//VTK::Output::Dec\n"
"uniform vec4 vertexColor;\n"
"uniform sampler2D texture1;\n"
"void main() { gl_FragData[0] = vertexColor*texture2D(texture1, gl_PointCoord); }",
// geometry shader
"");
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->SBO->Program);
}
}
void vtkOpenGLContextDevice2D::ReadySCBOProgram()
{
if (!this->SCBO->Program)
{
this->SCBO->Program = this->RenderWindow->GetShaderCache()->ReadyShaderProgram(
// vertex shader
"//VTK::System::Dec\n"
"in vec2 vertexMC;\n"
"in vec4 vertexScalar;\n"
"uniform mat4 WCDCMatrix;\n"
"uniform mat4 MCWCMatrix;\n"
"out vec4 vertexColor;\n"
"void main() {\n"
"vec4 vertex = vec4(vertexMC.xy, 0.0, 1.0);\n"
"vertexColor = vertexScalar;\n"
"gl_Position = vertex*MCWCMatrix*WCDCMatrix; }\n",
// fragment shader
"//VTK::System::Dec\n"
"//VTK::Output::Dec\n"
"in vec4 vertexColor;\n"
"uniform sampler2D texture1;\n"
"void main() { gl_FragData[0] = vertexColor*texture2D(texture1, gl_PointCoord); }",
// geometry shader
"");
}
else
{
this->RenderWindow->GetShaderCache()->ReadyShaderProgram(this->SCBO->Program);
}
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawPoly(float* f, int n, unsigned char* colors, int nc)
{
assert("f must be non-null" && f != nullptr);
assert("n must be greater than 0" && n > 0);
if (SkipDraw())
{
return;
}
if (this->Pen->GetLineType() == vtkPen::NO_PEN)
{
return;
}
// Skip transparent elements.
if (!colors && this->Pen->GetColorObject().GetAlpha() == 0)
{
return;
}
vtkOpenGLClearErrorMacro();
this->SetLineType(this->Pen->GetLineType());
vtkOpenGLHelper* cbo = nullptr;
if (colors)
{
this->ReadyLinesCBOProgram();
cbo = this->LinesCBO;
}
else
{
this->ReadyLinesBOProgram();
cbo = this->LinesBO;
if (cbo->Program)
{
cbo->Program->SetUniform4uc("vertexColor", this->Pen->GetColor());
}
}
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniformi("stipple", this->LinePattern);
this->SetMatrices(cbo->Program);
// for line stipple we need to compute the scaled
// cumulative linear distance
double* scale = this->ModelMatrix->GetScale();
std::vector<float> distances;
distances.resize(n * 2);
float totDist = 0.0;
distances[0] = 0.0;
for (int i = 1; i < n; i++)
{
float xDel = scale[0] * (f[i * 2] - f[i * 2 - 2]);
float yDel = scale[1] * (f[i * 2 + 1] - f[i * 2 - 1]);
// discarding infinite coordinates
totDist += (std::abs(yDel) != std::numeric_limits<float>::infinity() &&
std::abs(xDel) != std::numeric_limits<float>::infinity())
? sqrt(xDel * xDel + yDel * yDel)
: 0;
distances[i * 2] = totDist;
}
// For GL2PS captures, use the path that draws lines instead of triangles --
// GL2PS can handle stipples and linewidths just fine.
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (this->Pen->GetWidth() > 1.0 &&
!(gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture))
{
// convert to triangles and draw, this is because
// OpenGL no longer supports wide lines directly
float hwidth = this->Pen->GetWidth() / 2.0;
std::vector<float> newVerts;
std::vector<unsigned char> newColors;
std::vector<float> newDistances;
newDistances.resize((n - 1) * 12);
for (int i = 0; i < n - 1; i++)
{
// for each line segment draw two triangles
// start by computing the direction
vtkVector2f dir(
(f[i * 2 + 2] - f[i * 2]) * scale[0], (f[i * 2 + 3] - f[i * 2 + 1]) * scale[1]);
vtkVector2f norm(-dir.GetY(), dir.GetX());
norm.Normalize();
norm.SetX(hwidth * norm.GetX() / scale[0]);
norm.SetY(hwidth * norm.GetY() / scale[1]);
newVerts.push_back(f[i * 2] + norm.GetX());
newVerts.push_back(f[i * 2 + 1] + norm.GetY());
newVerts.push_back(f[i * 2] - norm.GetX());
newVerts.push_back(f[i * 2 + 1] - norm.GetY());
newVerts.push_back(f[i * 2 + 2] - norm.GetX());
newVerts.push_back(f[i * 2 + 3] - norm.GetY());
newVerts.push_back(f[i * 2] + norm.GetX());
newVerts.push_back(f[i * 2 + 1] + norm.GetY());
newVerts.push_back(f[i * 2 + 2] - norm.GetX());
newVerts.push_back(f[i * 2 + 3] - norm.GetY());
newVerts.push_back(f[i * 2 + 2] + norm.GetX());
newVerts.push_back(f[i * 2 + 3] + norm.GetY());
if (colors)
{
copyColors(newColors, colors + i * nc, nc);
copyColors(newColors, colors + i * nc, nc);
copyColors(newColors, colors + (i + 1) * nc, nc);
copyColors(newColors, colors + i * nc, nc);
copyColors(newColors, colors + (i + 1) * nc, nc);
copyColors(newColors, colors + (i + 1) * nc, nc);
}
newDistances[i * 12] = distances[i * 2];
newDistances[i * 12 + 2] = distances[i * 2];
newDistances[i * 12 + 4] = distances[i * 2 + 2];
newDistances[i * 12 + 6] = distances[i * 2];
newDistances[i * 12 + 8] = distances[i * 2 + 2];
newDistances[i * 12 + 10] = distances[i * 2 + 2];
}
this->BuildVBO(cbo, &(newVerts[0]), static_cast<int>(newVerts.size() / 2),
colors ? &(newColors[0]) : nullptr, nc, &(newDistances[0]));
PreDraw(*cbo, GL_TRIANGLES, newVerts.size() / 2);
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(newVerts.size() / 2));
PostDraw(*cbo, this->Renderer, this->Pen->GetColor());
}
else
{
this->SetLineWidth(this->Pen->GetWidth());
this->BuildVBO(cbo, f, n, colors, nc, &(distances[0]));
PreDraw(*cbo, GL_LINE_STRIP, n);
glDrawArrays(GL_LINE_STRIP, 0, n);
PostDraw(*cbo, this->Renderer, this->Pen->GetColor());
this->SetLineWidth(1.0);
}
vtkOpenGLCheckErrorMacro("failed after DrawPoly");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawLines(float* f, int n, unsigned char* colors, int nc)
{
assert("f must be non-null" && f != nullptr);
assert("n must be greater than 0" && n > 0);
if (SkipDraw())
{
return;
}
if (this->Pen->GetLineType() == vtkPen::NO_PEN)
{
return;
}
// Skip transparent elements.
if (!colors && this->Pen->GetColorObject().GetAlpha() == 0)
{
return;
}
vtkOpenGLClearErrorMacro();
this->SetLineType(this->Pen->GetLineType());
vtkOpenGLHelper* cbo = nullptr;
if (colors)
{
this->ReadyLinesCBOProgram();
cbo = this->LinesCBO;
}
else
{
this->ReadyLinesBOProgram();
cbo = this->LinesBO;
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniform4uc("vertexColor", this->Pen->GetColor());
}
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniformi("stipple", this->LinePattern);
this->SetMatrices(cbo->Program);
// for line stipple we need to compute the scaled
// cumulative linear distance
double* scale = this->ModelMatrix->GetScale();
std::vector<float> distances;
distances.resize(n * 2);
float totDist = 0.0;
distances[0] = 0.0;
for (int i = 1; i < n; i++)
{
float xDel = scale[0] * (f[i * 2] - f[i * 2 - 2]);
float yDel = scale[1] * (f[i * 2 + 1] - f[i * 2 - 1]);
totDist += sqrt(xDel * xDel + yDel * yDel);
distances[i * 2] = totDist;
}
if (this->Pen->GetWidth() > 1.0)
{
// convert to triangles and draw, this is because
// OpenGL no longer supports wide lines directly
float hwidth = this->Pen->GetWidth() / 2.0;
std::vector<float> newVerts;
std::vector<unsigned char> newColors;
std::vector<float> newDistances;
newDistances.resize((n / 2) * 12);
for (int i = 0; i < n - 1; i += 2)
{
// for each line segment draw two triangles
// start by computing the direction
vtkVector2f dir(
(f[i * 2 + 2] - f[i * 2]) * scale[0], (f[i * 2 + 3] - f[i * 2 + 1]) * scale[1]);
vtkVector2f norm(-dir.GetY(), dir.GetX());
norm.Normalize();
norm.SetX(hwidth * norm.GetX() / scale[0]);
norm.SetY(hwidth * norm.GetY() / scale[1]);
newVerts.push_back(f[i * 2] + norm.GetX());
newVerts.push_back(f[i * 2 + 1] + norm.GetY());
newVerts.push_back(f[i * 2] - norm.GetX());
newVerts.push_back(f[i * 2 + 1] - norm.GetY());
newVerts.push_back(f[i * 2 + 2] - norm.GetX());
newVerts.push_back(f[i * 2 + 3] - norm.GetY());
newVerts.push_back(f[i * 2] + norm.GetX());
newVerts.push_back(f[i * 2 + 1] + norm.GetY());
newVerts.push_back(f[i * 2 + 2] - norm.GetX());
newVerts.push_back(f[i * 2 + 3] - norm.GetY());
newVerts.push_back(f[i * 2 + 2] + norm.GetX());
newVerts.push_back(f[i * 2 + 3] + norm.GetY());
if (colors)
{
copyColors(newColors, colors + i * nc, nc);
copyColors(newColors, colors + i * nc, nc);
copyColors(newColors, colors + (i + 1) * nc, nc);
copyColors(newColors, colors + i * nc, nc);
copyColors(newColors, colors + (i + 1) * nc, nc);
copyColors(newColors, colors + (i + 1) * nc, nc);
}
newDistances[i * 6] = distances[i * 2];
newDistances[i * 6 + 2] = distances[i * 2];
newDistances[i * 6 + 4] = distances[i * 2 + 2];
newDistances[i * 6 + 6] = distances[i * 2];
newDistances[i * 6 + 8] = distances[i * 2 + 2];
newDistances[i * 6 + 10] = distances[i * 2 + 2];
}
this->BuildVBO(cbo, &(newVerts[0]), static_cast<int>(newVerts.size() / 2),
colors ? &(newColors[0]) : nullptr, nc, &(newDistances[0]));
PreDraw(*cbo, GL_TRIANGLES, newVerts.size() / 2);
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(newVerts.size() / 2));
PostDraw(*cbo, this->Renderer, this->Pen->GetColor());
}
else
{
this->SetLineWidth(this->Pen->GetWidth());
this->BuildVBO(cbo, f, n, colors, nc, &(distances[0]));
PreDraw(*cbo, GL_LINES, n);
glDrawArrays(GL_LINES, 0, n);
PostDraw(*cbo, this->Renderer, this->Pen->GetColor());
this->SetLineWidth(1.0);
}
vtkOpenGLCheckErrorMacro("failed after DrawLines");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawPoints(float* f, int n, unsigned char* c, int nc)
{
if (SkipDraw())
{
return;
}
// Skip transparent elements.
if (!c && this->Pen->GetColorObject().GetAlpha() == 0)
{
return;
}
vtkOpenGLClearErrorMacro();
vtkOpenGLHelper* cbo = nullptr;
if (c)
{
this->ReadyVCBOProgram();
cbo = this->VCBO;
if (!cbo->Program)
{
return;
}
}
else
{
this->ReadyVBOProgram();
cbo = this->VBO;
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniform4uc("vertexColor", this->Pen->GetColor());
}
this->SetPointSize(this->Pen->GetWidth());
this->BuildVBO(cbo, f, n, c, nc, nullptr);
this->SetMatrices(cbo->Program);
PreDraw(*cbo, GL_POINTS, n);
glDrawArrays(GL_POINTS, 0, n);
PostDraw(*cbo, this->Renderer, this->Pen->GetColor());
vtkOpenGLCheckErrorMacro("failed after DrawPoints");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawPointSprites(
vtkImageData* sprite, float* points, int n, unsigned char* colors, int nc_comps)
{
// // Draw these to the background -- we don't currently export them to GL2PS.
// if (SkipDraw())
// {
// return;
// }
vtkOpenGLClearErrorMacro();
if (points && n > 0)
{
this->SetPointSize(this->Pen->GetWidth());
vtkOpenGLHelper* cbo = nullptr;
if (colors)
{
this->ReadySCBOProgram();
cbo = this->SCBO;
if (!cbo->Program)
{
return;
}
}
else
{
this->ReadySBOProgram();
cbo = this->SBO;
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniform4uc("vertexColor", this->Pen->GetColor());
}
this->BuildVBO(cbo, points, n, colors, nc_comps, nullptr);
this->SetMatrices(cbo->Program);
if (sprite)
{
if (!this->Storage->SpriteTexture)
{
this->Storage->SpriteTexture = vtkTexture::New();
}
int properties = this->Brush->GetTextureProperties();
this->Storage->SpriteTexture->SetInputData(sprite);
this->Storage->SpriteTexture->SetRepeat(properties & vtkContextDevice2D::Repeat);
this->Storage->SpriteTexture->SetInterpolate(properties & vtkContextDevice2D::Linear);
this->Storage->SpriteTexture->Render(this->Renderer);
int tunit = vtkOpenGLTexture::SafeDownCast(this->Storage->SpriteTexture)->GetTextureUnit();
cbo->Program->SetUniformi("texture1", tunit);
}
// We can actually use point sprites here
if (this->RenderWindow->IsPointSpriteBugPresent())
{
glEnable(GL_POINT_SPRITE);
glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);
}
glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT);
glDrawArrays(GL_POINTS, 0, n);
if (this->RenderWindow->IsPointSpriteBugPresent())
{
glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE);
glDisable(GL_POINT_SPRITE);
}
if (sprite)
{
this->Storage->SpriteTexture->PostRender(this->Renderer);
}
}
else
{
vtkWarningMacro(<< "Points supplied without a valid image or pointer.");
}
vtkOpenGLCheckErrorMacro("failed after DrawPointSprites");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawMarkers(
int shape, bool highlight, float* points, int n, unsigned char* colors, int nc_comps)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps)
{
switch (gl2ps->GetActiveState())
{
case vtkOpenGLGL2PSHelper::Capture:
this->DrawMarkersGL2PS(shape, highlight, points, n, colors, nc_comps);
return;
case vtkOpenGLGL2PSHelper::Background:
return; // Do nothing.
case vtkOpenGLGL2PSHelper::Inactive:
break; // Render as normal.
}
}
// Get a point sprite for the shape
vtkImageData* sprite = this->GetMarker(shape, this->Pen->GetWidth(), highlight);
this->DrawPointSprites(sprite, points, n, colors, nc_comps);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawQuad(float* f, int n)
{
if (SkipDraw())
{
return;
}
if (!f || n <= 0)
{
vtkWarningMacro(<< "Points supplied that were not of type float.");
return;
}
// convert quads to triangles
std::vector<float> tverts;
int numTVerts = 6 * n / 4;
tverts.resize(numTVerts * 2);
int offset[6] = { 0, 1, 2, 0, 2, 3 };
for (int i = 0; i < numTVerts; i++)
{
int index = 2 * (4 * (i / 6) + offset[i % 6]);
tverts[i * 2] = f[index];
tverts[i * 2 + 1] = f[index + 1];
}
this->CoreDrawTriangles(tverts);
}
void vtkOpenGLContextDevice2D::CoreDrawTriangles(
std::vector<float>& tverts, unsigned char* colors, int numComp)
{
if (SkipDraw())
{
return;
}
vtkOpenGLClearErrorMacro();
float* texCoord = nullptr;
vtkOpenGLHelper* cbo = nullptr;
if (this->Brush->GetTexture())
{
this->ReadyVTBOProgram();
cbo = this->VTBO;
if (!cbo->Program)
{
return;
}
this->SetTexture(this->Brush->GetTexture(), this->Brush->GetTextureProperties());
this->Storage->Texture->Render(this->Renderer);
texCoord = this->Storage->TexCoords(&(tverts[0]), static_cast<int>(tverts.size() / 2));
int tunit = vtkOpenGLTexture::SafeDownCast(this->Storage->Texture)->GetTextureUnit();
cbo->Program->SetUniformi("texture1", tunit);
}
else if (colors && numComp > 0)
{
this->ReadyVCBOProgram();
cbo = this->VCBO;
}
else
{
// Skip transparent elements.
if (this->Brush->GetColorObject().GetAlpha() == 0)
{
return;
}
this->ReadyVBOProgram();
cbo = this->VBO;
}
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniform4uc("vertexColor", this->Brush->GetColor());
this->BuildVBO(cbo, &(tverts[0]), static_cast<int>(tverts.size() / 2), colors, numComp, texCoord);
this->SetMatrices(cbo->Program);
PreDraw(*cbo, GL_TRIANGLES, tverts.size() / 2);
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(tverts.size() / 2));
PostDraw(*cbo, this->Renderer, this->Brush->GetColor());
if (this->Storage->Texture)
{
this->Storage->Texture->PostRender(this->Renderer);
delete[] texCoord;
}
vtkOpenGLCheckErrorMacro("failed after DrawQuad");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawQuadStrip(float* f, int n)
{
if (SkipDraw())
{
return;
}
if (!f || n <= 0)
{
vtkWarningMacro(<< "Points supplied that were not of type float.");
return;
}
// convert quad strips to triangles
std::vector<float> tverts;
int numTVerts = 3 * (n - 2);
tverts.resize(numTVerts * 2);
int offset[6] = { 0, 1, 3, 0, 3, 2 };
for (int i = 0; i < numTVerts; i++)
{
int index = 2 * (2 * (i / 6) + offset[i % 6]);
tverts[i * 2] = f[index];
tverts[i * 2 + 1] = f[index + 1];
}
this->CoreDrawTriangles(tverts);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawPolygon(float* f, int n)
{
if (SkipDraw())
{
return;
}
if (!f || n <= 0)
{
vtkWarningMacro(<< "Points supplied that were not of type float.");
return;
}
// convert polygon to triangles
std::vector<float> tverts;
int numTVerts = 3 * (n - 2);
tverts.reserve(numTVerts * 2);
for (int i = 0; i < n - 2; i++)
{
tverts.push_back(f[0]);
tverts.push_back(f[1]);
tverts.push_back(f[i * 2 + 2]);
tverts.push_back(f[i * 2 + 3]);
tverts.push_back(f[i * 2 + 4]);
tverts.push_back(f[i * 2 + 5]);
}
this->CoreDrawTriangles(tverts);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawColoredPolygon(
float* f, int n, unsigned char* colors, int nc_comps)
{
if (SkipDraw())
{
return;
}
if (!f || n <= 0)
{
vtkWarningMacro(<< "Points supplied that were not of type float.");
return;
}
// convert polygon to triangles
int numTVerts = 3 * (n - 2);
std::vector<float> tverts;
tverts.reserve(numTVerts * 2);
std::vector<unsigned char> tcolors;
if (colors)
{
tcolors.resize(numTVerts * nc_comps);
}
std::vector<unsigned char>::iterator colIt = tcolors.begin();
for (int i = 0; i < n - 2; i++)
{
tverts.push_back(f[0]);
tverts.push_back(f[1]);
tverts.push_back(f[i * 2 + 2]);
tverts.push_back(f[i * 2 + 3]);
tverts.push_back(f[i * 2 + 4]);
tverts.push_back(f[i * 2 + 5]);
if (colors)
{
std::copy(colors, colors + nc_comps, colIt);
colIt += nc_comps;
std::copy(colors + ((i + 1) * nc_comps), colors + ((i + 3) * nc_comps), colIt);
colIt += 2 * nc_comps;
}
}
this->CoreDrawTriangles(tverts, colors ? tcolors.data() : nullptr, nc_comps);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawEllipseWedge(float x, float y, float outRx, float outRy,
float inRx, float inRy, float startAngle, float stopAngle)
{
assert("pre: positive_outRx" && outRx >= 0.0f);
assert("pre: positive_outRy" && outRy >= 0.0f);
assert("pre: positive_inRx" && inRx >= 0.0f);
assert("pre: positive_inRy" && inRy >= 0.0f);
assert("pre: ordered_rx" && inRx <= outRx);
assert("pre: ordered_ry" && inRy <= outRy);
if (SkipDraw())
{
return;
}
if (outRy == 0.0f && outRx == 0.0f)
{
// we make sure maxRadius will never be null.
return;
}
// If the 'wedge' is actually a full circle, gl2ps can just insert a circle
// instead of using a polygonal approximation.
if (IsFullCircle(startAngle, stopAngle))
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
this->DrawWedgeGL2PS(x, y, outRx, outRy, inRx, inRy);
return;
}
}
int iterations = this->GetNumberOfArcIterations(outRx, outRy, startAngle, stopAngle);
// step in radians.
double step = vtkMath::RadiansFromDegrees(stopAngle - startAngle) / (iterations);
// step have to be lesser or equal to maxStep computed inside
// GetNumberOfIterations()
double rstart = vtkMath::RadiansFromDegrees(startAngle);
// the A vertices (0,2,4,..) are on the inner side
// the B vertices (1,3,5,..) are on the outer side
// (A and B vertices terms come from triangle strip definition in
// OpenGL spec)
// we are iterating counterclockwise
// convert polygon to triangles
std::vector<float> tverts;
int numTVerts = 6 * iterations;
tverts.resize(numTVerts * 2);
int offset[6] = { 0, 1, 3, 0, 3, 2 };
for (int i = 0; i < numTVerts; i++)
{
int index = i / 6 + offset[i % 6] / 2;
double radiusX = (offset[i % 6] % 2) ? outRx : inRx;
double radiusY = (offset[i % 6] % 2) ? outRy : inRy;
double a = rstart + index * step;
tverts.push_back(radiusX * cos(a) + x);
tverts.push_back(radiusY * sin(a) + y);
}
this->CoreDrawTriangles(tverts);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawEllipticArc(
float x, float y, float rX, float rY, float startAngle, float stopAngle)
{
assert("pre: positive_rX" && rX >= 0);
assert("pre: positive_rY" && rY >= 0);
if (SkipDraw())
{
return;
}
if (rX == 0.0f && rY == 0.0f)
{
// we make sure maxRadius will never be null.
return;
}
// If the 'arc' is actually a full circle, gl2ps can just insert a circle
// instead of using a polygonal approximation.
if (IsFullCircle(startAngle, stopAngle))
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
this->DrawCircleGL2PS(x, y, rX, rY);
return;
}
}
vtkOpenGLClearErrorMacro();
int iterations = this->GetNumberOfArcIterations(rX, rY, startAngle, stopAngle);
float* p = new float[2 * (iterations + 1)];
// step in radians.
double step = vtkMath::RadiansFromDegrees(stopAngle - startAngle) / (iterations);
// step have to be lesser or equal to maxStep computed inside
// GetNumberOfIterations()
double rstart = vtkMath::RadiansFromDegrees(startAngle);
// we are iterating counterclockwise
for (int i = 0; i <= iterations; ++i)
{
double a = rstart + i * step;
p[2 * i] = rX * cos(a) + x;
p[2 * i + 1] = rY * sin(a) + y;
}
this->DrawPolygon(p, iterations + 1);
this->DrawPoly(p, iterations + 1);
delete[] p;
vtkOpenGLCheckErrorMacro("failed after DrawEllipseArc");
}
//------------------------------------------------------------------------------
int vtkOpenGLContextDevice2D::GetNumberOfArcIterations(
float rX, float rY, float startAngle, float stopAngle)
{
assert("pre: positive_rX" && rX >= 0.0f);
assert("pre: positive_rY" && rY >= 0.0f);
assert("pre: not_both_null" && (rX > 0.0 || rY > 0.0));
// 1.0: pixel precision. 0.5 (subpixel precision, useful with multisampling)
double error = 4.0; // experience shows 4.0 is visually enough.
// The tessellation is the most visible on the biggest radius.
double maxRadius;
if (rX >= rY)
{
maxRadius = rX;
}
else
{
maxRadius = rY;
}
if (error > maxRadius)
{
// to make sure the argument of asin() is in a valid range.
error = maxRadius;
}
// Angle of a sector so that its chord is `error' pixels.
// This is will be our maximum angle step.
double maxStep = 2.0 * asin(error / (2.0 * maxRadius));
// ceil because we want to make sure we don't underestimate the number of
// iterations by 1.
return static_cast<int>(ceil(vtkMath::RadiansFromDegrees(stopAngle - startAngle) / maxStep));
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawString(float* point, const vtkStdString& string)
{
// FIXME(#18327): Migrate to processing here rather than working on
// `vtkUnicodeString`.
this->DrawString(point, vtkUnicodeString::from_utf8(string));
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeStringBounds(const vtkStdString& string, float bounds[4])
{
// FIXME(#18327): Migrate to processing here rather than working on
// `vtkUnicodeString`.
this->ComputeStringBoundsInternal(vtkUnicodeString::from_utf8(string), bounds);
bounds[0] = 0.f;
bounds[1] = 0.f;
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeJustifiedStringBounds(const char* string, float bounds[4])
{
// FIXME(#18327): Migrate to processing here rather than working on
// `vtkUnicodeString`.
this->ComputeStringBoundsInternal(vtkUnicodeString::from_utf8(string), bounds);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawString(float* point, const vtkUnicodeString& string)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps)
{
switch (gl2ps->GetActiveState())
{
case vtkOpenGLGL2PSHelper::Capture:
{
float tx = point[0];
float ty = point[1];
this->TransformPoint(tx, ty);
double x[3] = { tx, ty, 0. };
gl2ps->DrawString(string.utf8_str(), this->TextProp, x, 0., this->Renderer);
return;
}
case vtkOpenGLGL2PSHelper::Background:
return; // Do nothing.
case vtkOpenGLGL2PSHelper::Inactive:
break; // Render as normal.
}
}
vtkTextRenderer* tren = vtkTextRenderer::GetInstance();
if (!tren)
{
vtkErrorMacro("No text renderer available. Link to vtkRenderingFreeType "
"to get the default implementation.");
return;
}
vtkOpenGLClearErrorMacro();
double* mv = this->ModelMatrix->GetMatrix()->Element[0];
float xScale = mv[0];
float yScale = mv[5];
float p[] = { std::floor(point[0] * xScale) / xScale, std::floor(point[1] * yScale) / yScale };
// TODO this currently ignores vtkContextScene::ScaleTiles. Not sure how to
// get at that from here, but this is better than ignoring scaling altogether.
// TODO Also, FreeType supports anisotropic DPI. Might be needed if the
// tileScale isn't homogeneous, but we'll need to update the textrenderer API
// and see if MPL/mathtext can support it.
int tileScale[2];
this->RenderWindow->GetTileScale(tileScale);
int dpi = this->RenderWindow->GetDPI() * std::max(tileScale[0], tileScale[1]);
// Cache rendered text strings
vtkTextureImageCache<UTF16TextPropertyKey>::CacheData& cache =
this->Storage->TextTextureCache.GetCacheData(UTF16TextPropertyKey(this->TextProp, string, dpi));
vtkImageData* image = cache.ImageData;
if (image->GetNumberOfPoints() == 0 && image->GetNumberOfCells() == 0)
{
int textDims[2];
if (!tren->RenderString(this->TextProp, string, image, textDims, dpi))
{
vtkErrorMacro("Error rendering string: " << string);
return;
}
if (!tren->GetMetrics(this->TextProp, string, cache.Metrics, dpi))
{
vtkErrorMacro("Error computing bounding box for string: " << string);
return;
}
}
vtkTexture* texture = cache.Texture;
texture->Render(this->Renderer);
int imgDims[3];
image->GetDimensions(imgDims);
float textWidth =
static_cast<float>(cache.Metrics.BoundingBox[1] - cache.Metrics.BoundingBox[0] + 1);
float textHeight =
static_cast<float>(cache.Metrics.BoundingBox[3] - cache.Metrics.BoundingBox[2] + 1);
float width = textWidth / xScale;
float height = textHeight / yScale;
float xw = textWidth / static_cast<float>(imgDims[0]);
float xh = textHeight / static_cast<float>(imgDims[1]);
// Align the text (the 0 point of the bounding box is aligned to the
// rotated and justified anchor point, so just translate by the bbox origin):
p[0] += cache.Metrics.BoundingBox[0] / xScale;
p[1] += cache.Metrics.BoundingBox[2] / yScale;
float points[] = { p[0], p[1], p[0] + width, p[1], p[0] + width, p[1] + height, p[0], p[1],
p[0] + width, p[1] + height, p[0], p[1] + height };
float texCoord[] = { 0.0f, 0.0f, xw, 0.0f, xw, xh, 0.0f, 0.0f, xw, xh, 0.0f, xh };
vtkOpenGLClearErrorMacro();
this->ReadyVTBOProgram();
vtkOpenGLHelper* cbo = this->VTBO;
if (!cbo->Program)
{
return;
}
int tunit = vtkOpenGLTexture::SafeDownCast(texture)->GetTextureUnit();
cbo->Program->SetUniformi("texture1", tunit);
this->BuildVBO(cbo, points, 6, nullptr, 0, texCoord);
this->SetMatrices(cbo->Program);
glDrawArrays(GL_TRIANGLES, 0, 6);
texture->PostRender(this->Renderer);
vtkOpenGLCheckErrorMacro("failed after DrawString");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeStringBounds(const vtkUnicodeString& string, float bounds[4])
{
this->ComputeStringBoundsInternal(string, bounds);
bounds[0] = 0.f;
bounds[1] = 0.f;
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawMathTextString(float point[2], const vtkStdString& string)
{
// The default text renderer detects and handles mathtext now. Just use the
// regular implementation.
this->DrawString(point, string);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawImage(float p[2], float scale, vtkImageData* image)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps)
{
switch (gl2ps->GetActiveState())
{
case vtkOpenGLGL2PSHelper::Capture:
this->DrawImageGL2PS(p, scale, image);
return;
case vtkOpenGLGL2PSHelper::Background:
return; // Do nothing.
case vtkOpenGLGL2PSHelper::Inactive:
break; // Draw as normal.
}
}
vtkOpenGLClearErrorMacro();
this->SetTexture(image);
this->Storage->Texture->Render(this->Renderer);
int* extent = image->GetExtent();
float points[] = { p[0], p[1], p[0] + scale * extent[1] + 1.0f, p[1],
p[0] + scale * extent[1] + 1.0f, p[1] + scale * extent[3] + 1.0f, p[0], p[1],
p[0] + scale * extent[1] + 1.0f, p[1] + scale * extent[3] + 1.0f, p[0],
p[1] + scale * extent[3] + 1.0f };
float texCoord[] = { 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f };
vtkOpenGLClearErrorMacro();
this->ReadyVTBOProgram();
vtkOpenGLHelper* cbo = this->VTBO;
if (!cbo->Program)
{
return;
}
int tunit = vtkOpenGLTexture::SafeDownCast(this->Storage->Texture)->GetTextureUnit();
cbo->Program->SetUniformi("texture1", tunit);
this->BuildVBO(cbo, points, 6, nullptr, 0, texCoord);
this->SetMatrices(cbo->Program);
glDrawArrays(GL_TRIANGLES, 0, 6);
this->Storage->Texture->PostRender(this->Renderer);
vtkOpenGLCheckErrorMacro("failed after DrawImage");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawPolyData(
float p[2], float scale, vtkPolyData* polyData, vtkUnsignedCharArray* colors, int scalarMode)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps)
{
switch (gl2ps->GetActiveState())
{
case vtkOpenGLGL2PSHelper::Capture:
// TODO Implement PolyDataGL2PS
// this->DrawPolyDataGL2PS(pos, image);
return;
case vtkOpenGLGL2PSHelper::Background:
return; // Do nothing.
case vtkOpenGLGL2PSHelper::Inactive:
break; // Draw as normal.
}
}
if (SkipDraw())
{
return;
}
if (polyData->GetLines()->GetNumberOfCells() > 0)
{
this->PolyDataImpl->Draw(CellArrayHelper::LINE, polyData, polyData->GetPoints(), p[0], p[1],
scale, scalarMode, colors);
}
if (polyData->GetPolys()->GetNumberOfCells() > 0)
{
this->PolyDataImpl->Draw(CellArrayHelper::POLYGON, polyData, polyData->GetPoints(), p[0], p[1],
scale, scalarMode, colors);
}
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawImage(const vtkRectf& pos, vtkImageData* image)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps)
{
switch (gl2ps->GetActiveState())
{
case vtkOpenGLGL2PSHelper::Capture:
this->DrawImageGL2PS(pos, image);
return;
case vtkOpenGLGL2PSHelper::Background:
return; // Do nothing.
case vtkOpenGLGL2PSHelper::Inactive:
break; // Draw as normal.
}
}
int tunit = this->RenderWindow->GetTextureUnitManager()->Allocate();
if (tunit < 0)
{
vtkErrorMacro("Hardware does not support the number of textures defined.");
return;
}
this->RenderWindow->GetState()->vtkglActiveTexture(GL_TEXTURE0 + tunit);
vtkVector2f tex(1.0, 1.0);
// Call this *after* calling vtkglActiveTexture() to ensure the texture
// is bound to the correct texture unit.
GLuint index = this->Storage->TextureFromImage(image, tex);
float points[] = { pos.GetX(), pos.GetY(), pos.GetX() + pos.GetWidth(), pos.GetY(),
pos.GetX() + pos.GetWidth(), pos.GetY() + pos.GetHeight(), pos.GetX(), pos.GetY(),
pos.GetX() + pos.GetWidth(), pos.GetY() + pos.GetHeight(), pos.GetX(),
pos.GetY() + pos.GetHeight() };
float texCoord[] = { 0.0f, 0.0f, tex[0], 0.0f, tex[0], tex[1], 0.0f, 0.0f, tex[0], tex[1], 0.0f,
tex[1] };
this->ReadyVTBOProgram();
vtkOpenGLHelper* cbo = this->VTBO;
if (!cbo->Program)
{
return;
}
cbo->Program->SetUniformi("texture1", tunit);
this->BuildVBO(cbo, points, 6, nullptr, 0, texCoord);
this->SetMatrices(cbo->Program);
glDrawArrays(GL_TRIANGLES, 0, 6);
this->RenderWindow->GetTextureUnitManager()->Free(tunit);
glDeleteTextures(1, &index);
vtkOpenGLCheckErrorMacro("failed after DrawImage");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetColor4(unsigned char*)
{
vtkErrorMacro("color cannot be set this way\n");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetColor(unsigned char*)
{
vtkErrorMacro("color cannot be set this way\n");
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetTexture(vtkImageData* image, int properties)
{
if (image == nullptr)
{
if (this->Storage->Texture)
{
this->Storage->Texture->Delete();
this->Storage->Texture = nullptr;
}
return;
}
if (this->Storage->Texture == nullptr)
{
this->Storage->Texture = vtkTexture::New();
}
this->Storage->Texture->SetInputData(image);
this->Storage->TextureProperties = properties;
this->Storage->Texture->SetRepeat(properties & vtkContextDevice2D::Repeat);
this->Storage->Texture->SetInterpolate(properties & vtkContextDevice2D::Linear);
this->Storage->Texture->EdgeClampOn();
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetPointSize(float size)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
gl2ps->SetPointSize(size);
}
this->RenderWindow->GetState()->vtkglPointSize(size);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetLineWidth(float width)
{
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
gl2ps->SetLineWidth(width);
}
this->RenderWindow->GetState()->vtkglLineWidth(width);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetLineType(int type)
{
this->LinePattern = 0x0000;
switch (type)
{
case vtkPen::NO_PEN:
this->LinePattern = 0x0000;
break;
case vtkPen::DASH_LINE:
this->LinePattern = 0x00FF;
break;
case vtkPen::DOT_LINE:
this->LinePattern = 0x0101;
break;
case vtkPen::DASH_DOT_LINE:
this->LinePattern = 0x0C0F;
break;
case vtkPen::DASH_DOT_DOT_LINE:
this->LinePattern = 0x1C47;
break;
case vtkPen::DENSE_DOT_LINE:
this->LinePattern = 0x1111;
break;
default:
this->LinePattern = 0xFFFF;
}
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
if (gl2ps && gl2ps->GetActiveState() == vtkOpenGLGL2PSHelper::Capture)
{
gl2ps->SetLineStipple(this->LinePattern);
}
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::MultiplyMatrix(vtkMatrix3x3* m)
{
// We must construct a 4x4 matrix from the 3x3 matrix for OpenGL
double* M = m->GetData();
double matrix[16];
matrix[0] = M[0];
matrix[1] = M[1];
matrix[2] = 0.0;
matrix[3] = M[2];
matrix[4] = M[3];
matrix[5] = M[4];
matrix[6] = 0.0;
matrix[7] = M[5];
matrix[8] = 0.0;
matrix[9] = 0.0;
matrix[10] = 1.0;
matrix[11] = 0.0;
matrix[12] = M[6];
matrix[13] = M[7];
matrix[14] = 0.0;
matrix[15] = M[8];
this->ModelMatrix->Concatenate(matrix);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetMatrix(vtkMatrix3x3* m)
{
// We must construct a 4x4 matrix from the 3x3 matrix for OpenGL
double* M = m->GetData();
double matrix[16];
matrix[0] = M[0];
matrix[1] = M[1];
matrix[2] = 0.0;
matrix[3] = M[2];
matrix[4] = M[3];
matrix[5] = M[4];
matrix[6] = 0.0;
matrix[7] = M[5];
matrix[8] = 0.0;
matrix[9] = 0.0;
matrix[10] = 1.0;
matrix[11] = 0.0;
matrix[12] = M[6];
matrix[13] = M[7];
matrix[14] = 0.0;
matrix[15] = M[8];
this->ModelMatrix->SetMatrix(matrix);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::GetMatrix(vtkMatrix3x3* m)
{
assert("pre: non_null" && m != nullptr);
// We must construct a 4x4 matrix from the 3x3 matrix for OpenGL
double* M = m->GetData();
double* matrix = this->ModelMatrix->GetMatrix()->Element[0];
M[0] = matrix[0];
M[1] = matrix[1];
M[2] = matrix[3];
M[3] = matrix[4];
M[4] = matrix[5];
M[5] = matrix[7];
M[6] = matrix[12];
M[7] = matrix[13];
M[8] = matrix[15];
m->Modified();
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::PushMatrix()
{
this->ModelMatrix->Push();
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::PopMatrix()
{
this->ModelMatrix->Pop();
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::SetClipping(int* dim)
{
// If the window is using tile scaling, we need to update the clip coordinates
// relative to the tile being rendered.
// (see paraview/paraview#17308)
double tileViewPort[4];
this->Renderer->GetVTKWindow()->GetTileViewport(tileViewPort);
this->Renderer->NormalizedDisplayToDisplay(tileViewPort[0], tileViewPort[1]);
this->Renderer->NormalizedDisplayToDisplay(tileViewPort[2], tileViewPort[3]);
vtkRecti tileRect{ vtkContext2D::FloatToInt(tileViewPort[0]),
vtkContext2D::FloatToInt(tileViewPort[1]), 0, 0 };
tileRect.AddPoint(
vtkContext2D::FloatToInt(tileViewPort[2]), vtkContext2D::FloatToInt(tileViewPort[3]));
// tileRect is the tile being rendered in the current RenderWindow in pixels.
double viewport[4];
this->Renderer->GetViewport(viewport);
this->Renderer->NormalizedDisplayToDisplay(viewport[0], viewport[1]);
this->Renderer->NormalizedDisplayToDisplay(viewport[2], viewport[3]);
vtkRecti rendererRect{ vtkContext2D::FloatToInt(viewport[0]),
vtkContext2D::FloatToInt(viewport[1]), 0, 0 };
rendererRect.AddPoint(
vtkContext2D::FloatToInt(viewport[2]), vtkContext2D::FloatToInt(viewport[3]));
// rendererRect is the viewport in pixels.
// `dim` is specified as (x,y,width,height) relative to the viewport that this
// prop is rendering in. So let's fit it in the viewport rect i.e.
// rendererRect
vtkRecti clipRect{ dim[0], dim[1], dim[2], dim[3] };
clipRect.MoveTo(clipRect.GetX() + rendererRect.GetX(), clipRect.GetY() + rendererRect.GetY());
clipRect.Intersect(rendererRect);
// Now, clamp the clipRect to the region being shown on the current tile. This
// generally has no effect since clipRect is wholly contained in tileRect
// unless tile scaling was being used. In either case, this method will return
// true as long as the rectangle intersection will produce a valid rectangle.
if (clipRect.Intersect(tileRect))
{
// offset clipRect relative to current tile i.e. window.
clipRect.MoveTo(clipRect.GetX() - tileRect.GetX(), clipRect.GetY() - tileRect.GetY());
}
else
{
// clipping region results in empty region, just set to empty.
clipRect = vtkRecti{ 0, 0, 0, 0 };
}
assert(clipRect.GetWidth() >= 0 && clipRect.GetHeight() >= 0);
this->RenderWindow->GetState()->vtkglScissor(
clipRect.GetX(), clipRect.GetY(), clipRect.GetWidth(), clipRect.GetHeight());
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::EnableClipping(bool enable)
{
this->RenderWindow->GetState()->SetEnumState(GL_SCISSOR_TEST, enable);
}
//------------------------------------------------------------------------------
bool vtkOpenGLContextDevice2D::SetStringRendererToFreeType()
{
// FreeType is the only choice - nothing to do here
return true;
}
//------------------------------------------------------------------------------
bool vtkOpenGLContextDevice2D::SetStringRendererToQt()
{
// The Qt based strategy is not available
return false;
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ReleaseGraphicsResources(vtkWindow* window)
{
this->VBO->ReleaseGraphicsResources(window);
this->VCBO->ReleaseGraphicsResources(window);
this->LinesBO->ReleaseGraphicsResources(window);
this->LinesCBO->ReleaseGraphicsResources(window);
this->SBO->ReleaseGraphicsResources(window);
this->SCBO->ReleaseGraphicsResources(window);
this->VTBO->ReleaseGraphicsResources(window);
if (this->Storage->Texture)
{
this->Storage->Texture->ReleaseGraphicsResources(window);
}
if (this->Storage->SpriteTexture)
{
this->Storage->SpriteTexture->ReleaseGraphicsResources(window);
}
this->Storage->TextTextureCache.ReleaseGraphicsResources(window);
this->Storage->MathTextTextureCache.ReleaseGraphicsResources(window);
}
//------------------------------------------------------------------------------
bool vtkOpenGLContextDevice2D::HasGLSL()
{
return true;
}
//------------------------------------------------------------------------------
vtkImageData* vtkOpenGLContextDevice2D::GetMarker(int shape, int size, bool highlight)
{
// Generate the cache key for this marker
vtkTypeUInt64 key = highlight ? (1U << 31) : 0U;
key |= static_cast<vtkTypeUInt16>(shape);
key <<= 32;
key |= static_cast<vtkTypeUInt32>(size);
// Try to find the marker in the cache
std::list<vtkMarkerCacheObject>::iterator match =
std::find(this->MarkerCache.begin(), this->MarkerCache.end(), key);
// Was it in the cache?
if (match != this->MarkerCache.end())
{
// Yep -- move it to the front and return the data.
if (match == this->MarkerCache.begin())
{
return match->Value;
}
else
{
vtkMarkerCacheObject result = *match;
this->MarkerCache.erase(match);
this->MarkerCache.push_front(result);
return result.Value;
}
}
// Nope -- we'll need to generate it. Create the image data:
vtkMarkerCacheObject result;
result.Key = key;
result.Value = this->GenerateMarker(shape, size, highlight);
// If there was an issue generating the marker, just return nullptr.
if (!result.Value)
{
vtkErrorMacro(<< "Error generating marker: shape,size: " << shape << "," << size);
return nullptr;
}
// Check the current cache size.
while (this->MarkerCache.size() > static_cast<size_t>(this->MaximumMarkerCacheSize - 1) &&
!this->MarkerCache.empty())
{
this->MarkerCache.back().Value->Delete();
this->MarkerCache.pop_back();
}
// Add to the cache
this->MarkerCache.push_front(result);
return result.Value;
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::ComputeStringBoundsInternal(
const vtkUnicodeString& string, float bounds[4])
{
vtkTextRenderer* tren = vtkTextRenderer::GetInstance();
if (!tren)
{
vtkErrorMacro("No text renderer available. Link to vtkRenderingFreeType "
"to get the default implementation.");
return;
}
// TODO this currently ignores vtkContextScene::ScaleTiles. Not sure how to
// get at that from here, but this is better than ignoring scaling altogether.
// TODO Also, FreeType supports anisotropic DPI. Might be needed if the
// tileScale isn't homogeneous, but we'll need to update the textrenderer API
// and see if MPL/mathtext can support it.
int tileScale[2];
this->RenderWindow->GetTileScale(tileScale);
int dpi = this->RenderWindow->GetDPI() * std::max(tileScale[0], tileScale[1]);
int bbox[4];
if (!tren->GetBoundingBox(this->TextProp, string, bbox, dpi))
{
vtkErrorMacro("Error computing bounding box for string: " << string);
return;
}
// Check for invalid bounding box
if (bbox[0] >= bbox[1] || bbox[2] >= bbox[3])
{
bounds[0] = 0.f;
bounds[1] = 0.f;
bounds[2] = 0.f;
bounds[3] = 0.f;
return;
}
double* mv = this->ModelMatrix->GetMatrix()->Element[0];
float xScale = mv[0];
float yScale = mv[5];
bounds[0] = static_cast<float>(bbox[0]) / xScale;
bounds[1] = static_cast<float>(bbox[2]) / yScale;
bounds[2] = static_cast<float>((bbox[1] - bbox[0] + 1) / xScale);
bounds[3] = static_cast<float>((bbox[3] - bbox[2] + 1) / yScale);
}
//------------------------------------------------------------------------------
vtkImageData* vtkOpenGLContextDevice2D::GenerateMarker(int shape, int width, bool highlight)
{
// Set up the image data, if highlight then the mark shape is different
vtkImageData* result = vtkImageData::New();
result->SetExtent(0, width - 1, 0, width - 1, 0, 0);
result->AllocateScalars(VTK_UNSIGNED_CHAR, 4);
unsigned char* image = static_cast<unsigned char*>(result->GetScalarPointer());
memset(image, 0, width * width * 4);
// Generate the marker image at the required size
switch (shape)
{
case VTK_MARKER_CROSS:
{
int center = (width + 1) / 2;
for (int i = 0; i < center; ++i)
{
int j = width - i - 1;
memset(image + (4 * (width * i + i)), 255, 4);
memset(image + (4 * (width * i + j)), 255, 4);
memset(image + (4 * (width * j + i)), 255, 4);
memset(image + (4 * (width * j + j)), 255, 4);
if (highlight)
{
memset(image + (4 * (width * (j - 1) + (i))), 255, 4);
memset(image + (4 * (width * (i + 1) + (i))), 255, 4);
memset(image + (4 * (width * (i) + (i + 1))), 255, 4);
memset(image + (4 * (width * (i) + (j - 1))), 255, 4);
memset(image + (4 * (width * (i + 1) + (j))), 255, 4);
memset(image + (4 * (width * (j - 1) + (j))), 255, 4);
memset(image + (4 * (width * (j) + (j - 1))), 255, 4);
memset(image + (4 * (width * (j) + (i + 1))), 255, 4);
}
}
break;
}
default: // Maintaining old behavior, which produces plus for unknown shape
vtkWarningMacro(<< "Invalid marker shape: " << shape);
VTK_FALLTHROUGH;
case VTK_MARKER_PLUS:
{
int center = (width + 1) / 2;
for (int i = 0; i < center; ++i)
{
int j = width - i - 1;
int c = center - 1;
memset(image + (4 * (width * c + i)), 255, 4);
memset(image + (4 * (width * c + j)), 255, 4);
memset(image + (4 * (width * i + c)), 255, 4);
memset(image + (4 * (width * j + c)), 255, 4);
if (highlight)
{
memset(image + (4 * (width * (c - 1) + i)), 255, 4);
memset(image + (4 * (width * (c + 1) + i)), 255, 4);
memset(image + (4 * (width * (c - 1) + j)), 255, 4);
memset(image + (4 * (width * (c + 1) + j)), 255, 4);
memset(image + (4 * (width * i + (c - 1))), 255, 4);
memset(image + (4 * (width * i + (c + 1))), 255, 4);
memset(image + (4 * (width * j + (c - 1))), 255, 4);
memset(image + (4 * (width * j + (c + 1))), 255, 4);
}
}
break;
}
case VTK_MARKER_SQUARE:
{
memset(image, 255, width * width * 4);
break;
}
case VTK_MARKER_CIRCLE:
{
double r = width / 2.0;
double r2 = r * r;
for (int i = 0; i < width; ++i)
{
double dx2 = (i - r) * (i - r);
for (int j = 0; j < width; ++j)
{
double dy2 = (j - r) * (j - r);
if ((dx2 + dy2) < r2)
{
memset(image + (4 * width * i) + (4 * j), 255, 4);
}
}
}
break;
}
case VTK_MARKER_DIAMOND:
{
int r = width / 2;
for (int i = 0; i < width; ++i)
{
int dx = abs(i - r);
for (int j = 0; j < width; ++j)
{
int dy = abs(j - r);
if (r - dx >= dy)
{
memset(image + (4 * width * i) + (4 * j), 255, 4);
}
}
}
break;
}
}
return result;
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "Renderer: ";
if (this->Renderer)
{
os << endl;
this->Renderer->PrintSelf(os, indent.GetNextIndent());
}
else
{
os << "(none)" << endl;
}
os << indent << "MaximumMarkerCacheSize: " << this->MaximumMarkerCacheSize << endl;
os << indent << "MarkerCache: " << this->MarkerCache.size() << " entries." << endl;
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawMarkersGL2PS(
int shape, bool highlight, float* points, int n, unsigned char* colors, int nc_comps)
{
switch (shape)
{
case VTK_MARKER_CROSS:
this->DrawCrossMarkersGL2PS(highlight, points, n, colors, nc_comps);
break;
default:
// default is here for consistency with old impl -- defaults to plus for
// unrecognized shapes.
case VTK_MARKER_PLUS:
this->DrawPlusMarkersGL2PS(highlight, points, n, colors, nc_comps);
break;
case VTK_MARKER_SQUARE:
this->DrawSquareMarkersGL2PS(highlight, points, n, colors, nc_comps);
break;
case VTK_MARKER_CIRCLE:
this->DrawCircleMarkersGL2PS(highlight, points, n, colors, nc_comps);
break;
case VTK_MARKER_DIAMOND:
this->DrawDiamondMarkersGL2PS(highlight, points, n, colors, nc_comps);
break;
}
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawCrossMarkersGL2PS(
bool highlight, float* points, int n, unsigned char* colors, int nc_comps)
{
float oldWidth = this->Pen->GetWidth();
unsigned char oldColor[4];
this->Pen->GetColor(oldColor);
int oldLineType = this->Pen->GetLineType();
float halfWidth = oldWidth * 0.5f;
float deltaX = halfWidth;
float deltaY = halfWidth;
this->TransformSize(deltaX, deltaY);
if (highlight)
{
this->Pen->SetWidth(1.5);
}
else
{
this->Pen->SetWidth(0.5);
}
this->Pen->SetLineType(vtkPen::SOLID_LINE);
float curLine[4];
unsigned char color[4];
for (int i = 0; i < n; ++i)
{
float* point = points + (i * 2);
if (colors)
{
color[3] = 255;
switch (nc_comps)
{
case 4:
case 3:
memcpy(color, colors + (i * nc_comps), nc_comps);
break;
case 2:
color[3] = colors[i * nc_comps + 1];
VTK_FALLTHROUGH;
case 1:
memset(color, colors[i * nc_comps], 3);
break;
default:
vtkErrorMacro(<< "Invalid number of color components: " << nc_comps);
break;
}
this->Pen->SetColor(color);
}
// The first line of the cross:
curLine[0] = point[0] + deltaX;
curLine[1] = point[1] + deltaY;
curLine[2] = point[0] - deltaX;
curLine[3] = point[1] - deltaY;
this->DrawPoly(curLine, 2);
// And the second:
curLine[0] = point[0] + deltaX;
curLine[1] = point[1] - deltaY;
curLine[2] = point[0] - deltaX;
curLine[3] = point[1] + deltaY;
this->DrawPoly(curLine, 2);
}
this->Pen->SetWidth(oldWidth);
this->Pen->SetColor(oldColor);
this->Pen->SetLineType(oldLineType);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawPlusMarkersGL2PS(
bool highlight, float* points, int n, unsigned char* colors, int nc_comps)
{
float oldWidth = this->Pen->GetWidth();
unsigned char oldColor[4];
this->Pen->GetColor(oldColor);
int oldLineType = this->Pen->GetLineType();
float halfWidth = oldWidth * 0.5f;
float deltaX = halfWidth;
float deltaY = halfWidth;
this->TransformSize(deltaX, deltaY);
if (highlight)
{
this->Pen->SetWidth(1.5);
}
else
{
this->Pen->SetWidth(0.5);
}
this->Pen->SetLineType(vtkPen::SOLID_LINE);
float curLine[4];
unsigned char color[4];
for (int i = 0; i < n; ++i)
{
float* point = points + (i * 2);
if (colors)
{
color[3] = 255;
switch (nc_comps)
{
case 4:
case 3:
memcpy(color, colors + (i * nc_comps), nc_comps);
break;
case 2:
color[3] = colors[i * nc_comps + 1];
VTK_FALLTHROUGH;
case 1:
memset(color, colors[i * nc_comps], 3);
break;
default:
vtkErrorMacro(<< "Invalid number of color components: " << nc_comps);
break;
}
this->Pen->SetColor(color);
}
// The first line of the plus:
curLine[0] = point[0] - deltaX;
curLine[1] = point[1];
curLine[2] = point[0] + deltaX;
curLine[3] = point[1];
this->DrawPoly(curLine, 2);
// And the second:
curLine[0] = point[0];
curLine[1] = point[1] - deltaY;
curLine[2] = point[0];
curLine[3] = point[1] + deltaY;
this->DrawPoly(curLine, 2);
}
this->Pen->SetWidth(oldWidth);
this->Pen->SetColor(oldColor);
this->Pen->SetLineType(oldLineType);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawSquareMarkersGL2PS(
bool /*highlight*/, float* points, int n, unsigned char* colors, int nc_comps)
{
unsigned char oldColor[4];
this->Brush->GetColor(oldColor);
this->Brush->SetColor(this->Pen->GetColor());
float halfWidth = this->GetPen()->GetWidth() * 0.5f;
float deltaX = halfWidth;
float deltaY = halfWidth;
this->TransformSize(deltaX, deltaY);
float quad[8];
unsigned char color[4];
for (int i = 0; i < n; ++i)
{
float* point = points + (i * 2);
if (colors)
{
color[3] = 255;
switch (nc_comps)
{
case 4:
case 3:
memcpy(color, colors + (i * nc_comps), nc_comps);
break;
case 2:
color[3] = colors[i * nc_comps + 1];
VTK_FALLTHROUGH;
case 1:
memset(color, colors[i * nc_comps], 3);
break;
default:
vtkErrorMacro(<< "Invalid number of color components: " << nc_comps);
break;
}
this->Brush->SetColor(color);
}
quad[0] = point[0] - deltaX;
quad[1] = point[1] - deltaY;
quad[2] = point[0] + deltaX;
quad[3] = quad[1];
quad[4] = quad[2];
quad[5] = point[1] + deltaY;
quad[6] = quad[0];
quad[7] = quad[5];
this->DrawQuad(quad, 4);
}
this->Brush->SetColor(oldColor);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawCircleMarkersGL2PS(
bool /*highlight*/, float* points, int n, unsigned char* colors, int nc_comps)
{
float radius = this->GetPen()->GetWidth() * 0.475;
unsigned char oldColor[4];
this->Brush->GetColor(oldColor);
this->Brush->SetColor(this->Pen->GetColor());
unsigned char color[4];
for (int i = 0; i < n; ++i)
{
float* point = points + (i * 2);
if (colors)
{
color[3] = 255;
switch (nc_comps)
{
case 4:
case 3:
memcpy(color, colors + (i * nc_comps), nc_comps);
break;
case 2:
color[3] = colors[i * nc_comps + 1];
VTK_FALLTHROUGH;
case 1:
memset(color, colors[i * nc_comps], 3);
break;
default:
vtkErrorMacro(<< "Invalid number of color components: " << nc_comps);
break;
}
this->Brush->SetColor(color);
}
this->DrawEllipseWedge(point[0], point[1], radius, radius, 0, 0, 0, 360);
}
this->Brush->SetColor(oldColor);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawDiamondMarkersGL2PS(
bool /*highlight*/, float* points, int n, unsigned char* colors, int nc_comps)
{
unsigned char oldColor[4];
this->Brush->GetColor(oldColor);
this->Brush->SetColor(this->Pen->GetColor());
float halfWidth = this->GetPen()->GetWidth() * 0.5f;
float deltaX = halfWidth;
float deltaY = halfWidth;
this->TransformSize(deltaX, deltaY);
float quad[8];
unsigned char color[4];
for (int i = 0; i < n; ++i)
{
float* point = points + (i * 2);
if (colors)
{
color[3] = 255;
switch (nc_comps)
{
case 4:
case 3:
memcpy(color, colors + (i * nc_comps), nc_comps);
break;
case 2:
color[3] = colors[i * nc_comps + 1];
VTK_FALLTHROUGH;
case 1:
memset(color, colors[i * nc_comps], 3);
break;
default:
vtkErrorMacro(<< "Invalid number of color components: " << nc_comps);
break;
}
this->Brush->SetColor(color);
}
quad[0] = point[0] - deltaX;
quad[1] = point[1];
quad[2] = point[0];
quad[3] = point[1] - deltaY;
quad[4] = point[0] + deltaX;
quad[5] = point[1];
quad[6] = point[0];
quad[7] = point[1] + deltaY;
this->DrawQuad(quad, 4);
}
this->Brush->SetColor(oldColor);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawImageGL2PS(float p[2], vtkImageData* input)
{
// Must be unsigned char -- otherwise OpenGL rendering behaves badly anyway.
if (!vtkDataTypesCompare(input->GetScalarType(), VTK_UNSIGNED_CHAR))
{
vtkErrorMacro("Invalid image format: Expected unsigned char scalars.");
return;
}
// Convert to float for GL2PS
vtkNew<vtkImageData> image;
image->ShallowCopy(input);
vtkDataArray* s = image->GetPointData()->GetScalars();
size_t numVals = (s->GetNumberOfComponents() * s->GetNumberOfTuples());
unsigned char* vals = static_cast<unsigned char*>(s->GetVoidPointer(0));
vtkNew<vtkFloatArray> scalars;
scalars->SetNumberOfComponents(s->GetNumberOfComponents());
scalars->SetNumberOfTuples(s->GetNumberOfTuples());
for (size_t i = 0; i < numVals; ++i)
{
scalars->SetValue(static_cast<vtkIdType>(i), vals[i] / 255.f);
}
image->GetPointData()->SetScalars(scalars);
// Instance always exists when this method is called:
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
float tp[2] = { p[0], p[1] };
this->TransformPoint(tp[0], tp[1]);
double pos[3] = { static_cast<double>(tp[0]), static_cast<double>(tp[1]), 0. };
gl2ps->DrawImage(image, pos);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawImageGL2PS(float p[2], float scale, vtkImageData* image)
{
if (std::fabs(scale - 1.f) < 1e-5f)
{
this->DrawImageGL2PS(p, image);
return;
}
int dims[3];
image->GetDimensions(dims);
vtkRectf rect(p[0], p[1], dims[0] * scale, dims[1] * scale);
this->DrawImageGL2PS(rect, image);
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawImageGL2PS(const vtkRectf& rect, vtkImageData* image)
{
int dims[3];
image->GetDimensions(dims);
int width = static_cast<int>(std::round(rect.GetWidth()));
int height = static_cast<int>(std::round(rect.GetHeight()));
if (width == dims[0] && height == dims[1])
{
this->DrawImageGL2PS(rect.GetBottomLeft().GetData(), image);
return;
}
vtkNew<vtkImageResize> resize;
resize->SetInputData(image);
resize->SetResizeMethod(vtkImageResize::OUTPUT_DIMENSIONS);
resize->SetOutputDimensions(width, height, -1);
resize->Update();
this->DrawImageGL2PS(rect.GetBottomLeft().GetData(), resize->GetOutput());
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawCircleGL2PS(float x, float y, float rX, float rY)
{
if (this->Brush->GetColorObject().GetAlpha() == 0)
{
return;
}
// We know this is valid if this method has been called:
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
vtkNew<vtkPath> path;
this->AddEllipseToPath(path, 0.f, 0.f, rX, rY, false);
this->TransformPath(path);
double origin[3] = { x, y, 0.f };
// Fill
unsigned char fillColor[4];
this->Brush->GetColor(fillColor);
std::stringstream label;
label << "vtkOpenGLContextDevice2D::DrawCircleGL2PS(" << x << ", " << y << ", " << rX << ", "
<< rY << ") fill:";
gl2ps->DrawPath(path, origin, origin, fillColor, nullptr, 0.0, -1.f, label.str().c_str());
// and stroke
unsigned char strokeColor[4];
this->Pen->GetColor(strokeColor);
float strokeWidth = this->Pen->GetWidth();
label.str("");
label.clear();
label << "vtkOpenGLContextDevice2D::DrawCircleGL2PS(" << x << ", " << y << ", " << rX << ", "
<< rY << ") stroke:";
gl2ps->DrawPath(
path, origin, origin, strokeColor, nullptr, 0.0, strokeWidth, label.str().c_str());
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::DrawWedgeGL2PS(
float x, float y, float outRx, float outRy, float inRx, float inRy)
{
if (this->Brush->GetColorObject().GetAlpha() == 0)
{
return;
}
vtkNew<vtkPath> path;
this->AddEllipseToPath(path, 0.f, 0.f, outRx, outRy, false);
this->AddEllipseToPath(path, 0.f, 0.f, inRx, inRy, true);
std::stringstream label;
label << "vtkOpenGLGL2PSContextDevice2D::DrawWedgeGL2PS(" << x << ", " << y << ", " << outRx
<< ", " << outRy << ", " << inRx << ", " << inRy << ") path:";
unsigned char color[4];
this->Brush->GetColor(color);
double rasterPos[3] = { static_cast<double>(x), static_cast<double>(y), 0. };
this->TransformPoint(x, y);
double windowPos[3] = { static_cast<double>(x), static_cast<double>(y), 0. };
// We know the helper exists and that we are capturing if this function has
// been called.
vtkOpenGLGL2PSHelper* gl2ps = vtkOpenGLGL2PSHelper::GetInstance();
gl2ps->DrawPath(path, rasterPos, windowPos, color, nullptr, 0.0, -1.f, label.str().c_str());
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::AddEllipseToPath(
vtkPath* path, float x, float y, float rx, float ry, bool reverse)
{
if (rx < 1e-5 || ry < 1e-5)
{
return;
}
// method based on http://www.tinaja.com/glib/ellipse4.pdf
static const float MAGIC = (4.f / 3.f) * (sqrt(2.f) - 1.f);
if (!reverse)
{
path->InsertNextPoint(x - rx, y, 0, vtkPath::MOVE_TO);
path->InsertNextPoint(x - rx, ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(-rx * MAGIC, y + ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x, y + ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(rx * MAGIC, y + ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x + rx, ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x + rx, y, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x + rx, -ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(rx * MAGIC, y - ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x, y - ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(-rx * MAGIC, y - ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x - rx, -ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x - rx, y, 0, vtkPath::CUBIC_CURVE);
}
else
{
path->InsertNextPoint(x - rx, y, 0, vtkPath::MOVE_TO);
path->InsertNextPoint(x - rx, -ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(-rx * MAGIC, y - ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x, y - ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(rx * MAGIC, y - ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x + rx, -ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x + rx, y, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x + rx, ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(rx * MAGIC, y + ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x, y + ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(-rx * MAGIC, y + ry, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x - rx, ry * MAGIC, 0, vtkPath::CUBIC_CURVE);
path->InsertNextPoint(x - rx, y, 0, vtkPath::CUBIC_CURVE);
}
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::TransformPath(vtkPath* path) const
{
// Transform the path with the modelview matrix:
double modelview[16];
vtkMatrix4x4::DeepCopy(modelview, this->ModelMatrix->GetMatrix());
// Transform the 2D path.
float newPoint[3] = { 0, 0, 0 };
vtkPoints* points = path->GetPoints();
for (vtkIdType i = 0; i < path->GetNumberOfPoints(); ++i)
{
double* point = points->GetPoint(i);
newPoint[0] = modelview[0] * point[0] + modelview[1] * point[1] + modelview[3];
newPoint[1] = modelview[4] * point[0] + modelview[5] * point[1] + modelview[7];
points->SetPoint(i, newPoint);
}
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::TransformPoint(float& x, float& y) const
{
double modelview[16];
vtkMatrix4x4::DeepCopy(modelview, this->ModelMatrix->GetMatrix());
float inX = x;
float inY = y;
x = modelview[0] * inX + modelview[1] * inY + modelview[3];
y = modelview[4] * inX + modelview[5] * inY + modelview[7];
}
//------------------------------------------------------------------------------
void vtkOpenGLContextDevice2D::TransformSize(float& dx, float& dy) const
{
double modelview[16];
vtkMatrix4x4::DeepCopy(modelview, this->ModelMatrix->GetMatrix());
dx /= modelview[0];
dy /= modelview[5];
}