//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt 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. // // Copyright 2016 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2016 UT-Battelle, LLC. // Copyright 2016 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #include #include #include #include #include #include #include #include #include #include #include #include namespace vtkm { namespace rendering { namespace internal { struct ClearBuffers : public vtkm::worklet::WorkletMapField { typedef void ControlSignature(FieldOut<>, FieldOut<>); typedef void ExecutionSignature(_1, _2); VTKM_CONT ClearBuffers() {} VTKM_EXEC void operator()(vtkm::Vec& color, vtkm::Float32& depth) const { color[0] = 0.f; color[1] = 0.f; color[2] = 0.f; color[3] = 0.f; // The depth is set to slightly larger than 1.0f, ensuring this color value always fails a // depth check depth = 1.001f; } }; // struct ClearBuffers struct ClearBuffersExecutor { typedef vtkm::rendering::Canvas::ColorBufferType ColorBufferType; typedef vtkm::rendering::Canvas::DepthBufferType DepthBufferType; ColorBufferType ColorBuffer; DepthBufferType DepthBuffer; VTKM_CONT ClearBuffersExecutor(const ColorBufferType& colorBuffer, const DepthBufferType& depthBuffer) : ColorBuffer(colorBuffer) , DepthBuffer(depthBuffer) { } template VTKM_CONT bool operator()(Device) const { VTKM_IS_DEVICE_ADAPTER_TAG(Device); ClearBuffers worklet; vtkm::worklet::DispatcherMapField dispatcher(worklet); dispatcher.Invoke(this->ColorBuffer, this->DepthBuffer); return true; } }; // struct ClearBuffersExecutor struct BlendBackground : public vtkm::worklet::WorkletMapField { vtkm::Vec BackgroundColor; VTKM_CONT BlendBackground(const vtkm::Vec& backgroundColor) : BackgroundColor(backgroundColor) { } typedef void ControlSignature(FieldInOut<>); typedef void ExecutionSignature(_1); VTKM_EXEC void operator()(vtkm::Vec& color) const { if (color[3] >= 1.f) return; vtkm::Float32 alpha = BackgroundColor[3] * (1.f - color[3]); color[0] = color[0] + BackgroundColor[0] * alpha; color[1] = color[1] + BackgroundColor[1] * alpha; color[2] = color[2] + BackgroundColor[2] * alpha; color[3] = alpha + color[3]; } }; // struct BlendBackground struct BlendBackgroundExecutor { typedef vtkm::rendering::Canvas::ColorBufferType ColorBufferType; ColorBufferType ColorBuffer; BlendBackground Worklet; VTKM_CONT BlendBackgroundExecutor(const ColorBufferType& colorBuffer, const vtkm::Vec& backgroundColor) : ColorBuffer(colorBuffer) , Worklet(backgroundColor) { } template VTKM_CONT bool operator()(Device) const { VTKM_IS_DEVICE_ADAPTER_TAG(Device); vtkm::worklet::DispatcherMapField dispatcher(this->Worklet); dispatcher.Invoke(this->ColorBuffer); return true; } }; // struct BlendBackgroundExecutor struct DrawColorSwatch : public vtkm::worklet::WorkletMapField { typedef void ControlSignature(FieldIn<>, WholeArrayInOut<>); typedef void ExecutionSignature(_1, _2); VTKM_CONT DrawColorSwatch(vtkm::Id2 dims, vtkm::Id2 xBounds, vtkm::Id2 yBounds, const vtkm::Vec color) : Color(color) { ImageWidth = dims[0]; ImageHeight = dims[1]; SwatchBottomLeft[0] = xBounds[0]; SwatchBottomLeft[1] = yBounds[0]; SwatchWidth = xBounds[1] - xBounds[0]; SwatchHeight = yBounds[1] - yBounds[0]; } template VTKM_EXEC void operator()(const vtkm::Id& index, FrameBuffer& frameBuffer) const { // local bar coord vtkm::Id x = index % SwatchWidth; vtkm::Id y = index / SwatchWidth; // offset to global image coord x += SwatchBottomLeft[0]; y += SwatchBottomLeft[1]; vtkm::Id offset = y * ImageWidth + x; frameBuffer.Set(offset, Color); } vtkm::Id ImageWidth; vtkm::Id ImageHeight; vtkm::Id2 SwatchBottomLeft; vtkm::Id SwatchWidth; vtkm::Id SwatchHeight; const vtkm::Vec Color; }; // struct DrawColorSwatch struct DrawColorBar : public vtkm::worklet::WorkletMapField { typedef void ControlSignature(FieldIn<>, WholeArrayInOut<>, WholeArrayIn<>); typedef void ExecutionSignature(_1, _2, _3); VTKM_CONT DrawColorBar(vtkm::Id2 dims, vtkm::Id2 xBounds, vtkm::Id2 yBounds, bool horizontal) : Horizontal(horizontal) { ImageWidth = dims[0]; ImageHeight = dims[1]; BarBottomLeft[0] = xBounds[0]; BarBottomLeft[1] = yBounds[0]; BarWidth = xBounds[1] - xBounds[0]; BarHeight = yBounds[1] - yBounds[0]; } template VTKM_EXEC void operator()(const vtkm::Id& index, FrameBuffer& frameBuffer, const ColorMap& colorMap) const { // local bar coord vtkm::Id x = index % BarWidth; vtkm::Id y = index / BarWidth, yLocal = y; vtkm::Id sample = Horizontal ? x : y; vtkm::Vec color = colorMap.Get(sample); // offset to global image coord x += BarBottomLeft[0]; y += BarBottomLeft[1]; vtkm::Id offset = y * ImageWidth + x; // If the colortable has alpha values, we blend each color sample with translucent white. // The height of the resultant translucent bar indicates the opacity. vtkm::Float32 normalizedHeight = static_cast(yLocal) / BarHeight; if (color[3] < 1.0f && normalizedHeight <= color[3]) { vtkm::Float32 intensity = 0.4f; vtkm::Vec blendedColor; vtkm::Vec srcColor = color; vtkm::Float32 inverseIntensity = (1.0f - intensity); vtkm::Float32 alpha = srcColor[3] * inverseIntensity; blendedColor[0] = 1.0f * intensity + srcColor[0] * alpha; blendedColor[1] = 1.0f * intensity + srcColor[1] * alpha; blendedColor[2] = 1.0f * intensity + srcColor[2] * alpha; blendedColor[3] = 1.0f; frameBuffer.Set(offset, blendedColor); } else { frameBuffer.Set(offset, color); } } vtkm::Id ImageWidth; vtkm::Id ImageHeight; vtkm::Id2 BarBottomLeft; vtkm::Id BarWidth; vtkm::Id BarHeight; bool Horizontal; }; // struct DrawColorBar struct ColorSwatchExecutor { VTKM_CONT ColorSwatchExecutor(vtkm::Id2 dims, vtkm::Id2 xBounds, vtkm::Id2 yBounds, const vtkm::Vec& color, const vtkm::cont::ArrayHandle>& colorBuffer) : Dims(dims) , XBounds(xBounds) , YBounds(yBounds) , Color(color) , ColorBuffer(colorBuffer) { } template VTKM_CONT bool operator()(Device) const { VTKM_IS_DEVICE_ADAPTER_TAG(Device); vtkm::Id totalPixels = (XBounds[1] - XBounds[0]) * (YBounds[1] - YBounds[0]); vtkm::cont::ArrayHandleCounting iterator(0, 1, totalPixels); vtkm::worklet::DispatcherMapField dispatcher( DrawColorSwatch(this->Dims, this->XBounds, this->YBounds, Color)); dispatcher.Invoke(iterator, this->ColorBuffer); return true; } vtkm::Id2 Dims; vtkm::Id2 XBounds; vtkm::Id2 YBounds; const vtkm::Vec& Color; const vtkm::cont::ArrayHandle>& ColorBuffer; }; // struct ColorSwatchExecutor struct ColorBarExecutor { VTKM_CONT ColorBarExecutor(vtkm::Id2 dims, vtkm::Id2 xBounds, vtkm::Id2 yBounds, bool horizontal, vtkm::cont::ArrayHandle>& colorMap, const vtkm::cont::ArrayHandle>& colorBuffer) : Dims(dims) , XBounds(xBounds) , YBounds(yBounds) , Horizontal(horizontal) , ColorMap(colorMap) , ColorBuffer(colorBuffer) { } template VTKM_CONT bool operator()(Device) const { VTKM_IS_DEVICE_ADAPTER_TAG(Device); vtkm::Id totalPixels = (XBounds[1] - XBounds[0]) * (YBounds[1] - YBounds[0]); vtkm::cont::ArrayHandleCounting iterator(0, 1, totalPixels); vtkm::worklet::DispatcherMapField dispatcher( DrawColorBar(this->Dims, this->XBounds, this->YBounds, this->Horizontal)); dispatcher.Invoke(iterator, this->ColorBuffer, this->ColorMap); return true; } vtkm::Id2 Dims; vtkm::Id2 XBounds; vtkm::Id2 YBounds; bool Horizontal; vtkm::cont::ArrayHandle>& ColorMap; const vtkm::cont::ArrayHandle>& ColorBuffer; }; // struct ColorSwatchExecutor } // namespace internal struct Canvas::CanvasInternals { CanvasInternals(vtkm::Id width, vtkm::Id height) : Width(width) , Height(height) { BackgroundColor.Components[0] = 0.f; BackgroundColor.Components[1] = 0.f; BackgroundColor.Components[2] = 0.f; BackgroundColor.Components[3] = 1.f; ForegroundColor.Components[0] = 1.f; ForegroundColor.Components[1] = 1.f; ForegroundColor.Components[2] = 1.f; ForegroundColor.Components[3] = 1.f; } vtkm::Id Width; vtkm::Id Height; vtkm::rendering::Color BackgroundColor; vtkm::rendering::Color ForegroundColor; ColorBufferType ColorBuffer; DepthBufferType DepthBuffer; vtkm::rendering::BitmapFont Font; FontTextureType FontTexture; vtkm::Matrix ModelView; vtkm::Matrix Projection; }; Canvas::Canvas(vtkm::Id width, vtkm::Id height) : Internals(new CanvasInternals(0, 0)) { vtkm::MatrixIdentity(Internals->ModelView); vtkm::MatrixIdentity(Internals->Projection); this->ResizeBuffers(width, height); } Canvas::~Canvas() { } vtkm::rendering::Canvas* Canvas::NewCopy() const { return new vtkm::rendering::Canvas(*this); } vtkm::Id Canvas::GetWidth() const { return Internals->Width; } vtkm::Id Canvas::GetHeight() const { return Internals->Height; } const Canvas::ColorBufferType& Canvas::GetColorBuffer() const { return Internals->ColorBuffer; } Canvas::ColorBufferType& Canvas::GetColorBuffer() { return Internals->ColorBuffer; } const Canvas::DepthBufferType& Canvas::GetDepthBuffer() const { return Internals->DepthBuffer; } Canvas::DepthBufferType& Canvas::GetDepthBuffer() { return Internals->DepthBuffer; } const vtkm::rendering::Color& Canvas::GetBackgroundColor() const { return Internals->BackgroundColor; } void Canvas::SetBackgroundColor(const vtkm::rendering::Color& color) { Internals->BackgroundColor = color; } const vtkm::rendering::Color& Canvas::GetForegroundColor() const { return Internals->ForegroundColor; } void Canvas::SetForegroundColor(const vtkm::rendering::Color& color) { Internals->ForegroundColor = color; } void Canvas::Initialize() { } void Canvas::Activate() { } void Canvas::Clear() { // TODO: Should the rendering library support policies or some other way to // configure with custom devices? vtkm::cont::TryExecute(internal::ClearBuffersExecutor(GetColorBuffer(), GetDepthBuffer())); } void Canvas::Finish() { } void Canvas::BlendBackground() { vtkm::cont::TryExecute( internal::BlendBackgroundExecutor(GetColorBuffer(), GetBackgroundColor().Components)); } void Canvas::ResizeBuffers(vtkm::Id width, vtkm::Id height) { VTKM_ASSERT(width >= 0); VTKM_ASSERT(height >= 0); vtkm::Id numPixels = width * height; if (Internals->ColorBuffer.GetNumberOfValues() != numPixels) { Internals->ColorBuffer.Allocate(numPixels); } if (Internals->DepthBuffer.GetNumberOfValues() != numPixels) { Internals->DepthBuffer.Allocate(numPixels); } Internals->Width = width; Internals->Height = height; } void Canvas::AddColorSwatch(const vtkm::Vec& point0, const vtkm::Vec& vtkmNotUsed(point1), const vtkm::Vec& point2, const vtkm::Vec& vtkmNotUsed(point3), const vtkm::rendering::Color& color) const { vtkm::Float64 width = static_cast(this->GetWidth()); vtkm::Float64 height = static_cast(this->GetHeight()); vtkm::Id2 x, y; x[0] = static_cast(((point0[0] + 1.) / 2.) * width + .5); x[1] = static_cast(((point2[0] + 1.) / 2.) * width + .5); y[0] = static_cast(((point0[1] + 1.) / 2.) * height + .5); y[1] = static_cast(((point2[1] + 1.) / 2.) * height + .5); vtkm::Id2 dims(this->GetWidth(), this->GetHeight()); vtkm::cont::TryExecute( internal::ColorSwatchExecutor(dims, x, y, color.Components, this->GetColorBuffer())); } void Canvas::AddColorSwatch(const vtkm::Float64 x0, const vtkm::Float64 y0, const vtkm::Float64 x1, const vtkm::Float64 y1, const vtkm::Float64 x2, const vtkm::Float64 y2, const vtkm::Float64 x3, const vtkm::Float64 y3, const vtkm::rendering::Color& color) const { this->AddColorSwatch(vtkm::make_Vec(x0, y0), vtkm::make_Vec(x1, y1), vtkm::make_Vec(x2, y2), vtkm::make_Vec(x3, y3), color); } void Canvas::AddLine(const vtkm::Vec& point0, const vtkm::Vec& point1, vtkm::Float32 linewidth, const vtkm::rendering::Color& color) const { vtkm::rendering::Canvas* self = const_cast(this); LineRenderer renderer(self, vtkm::MatrixMultiply(Internals->Projection, Internals->ModelView)); renderer.RenderLine(point0, point1, linewidth, color); } void Canvas::AddLine(vtkm::Float64 x0, vtkm::Float64 y0, vtkm::Float64 x1, vtkm::Float64 y1, vtkm::Float32 linewidth, const vtkm::rendering::Color& color) const { this->AddLine(vtkm::make_Vec(x0, y0), vtkm::make_Vec(x1, y1), linewidth, color); } void Canvas::AddColorBar(const vtkm::Bounds& bounds, const vtkm::rendering::ColorTable& colorTable, bool horizontal) const { vtkm::Float64 width = static_cast(this->GetWidth()); vtkm::Float64 height = static_cast(this->GetHeight()); vtkm::Id2 x, y; x[0] = static_cast(((bounds.X.Min + 1.) / 2.) * width + .5); x[1] = static_cast(((bounds.X.Max + 1.) / 2.) * width + .5); y[0] = static_cast(((bounds.Y.Min + 1.) / 2.) * height + .5); y[1] = static_cast(((bounds.Y.Max + 1.) / 2.) * height + .5); vtkm::Id barWidth = x[1] - x[0]; vtkm::Id barHeight = y[1] - y[0]; vtkm::cont::ArrayHandle> colorMap; vtkm::Id numSamples = horizontal ? barWidth : barHeight; colorTable.Sample(static_cast(numSamples), colorMap); vtkm::Id2 dims(this->GetWidth(), this->GetHeight()); vtkm::cont::TryExecute( internal::ColorBarExecutor(dims, x, y, horizontal, colorMap, this->GetColorBuffer())); } void Canvas::AddColorBar(vtkm::Float32 x, vtkm::Float32 y, vtkm::Float32 width, vtkm::Float32 height, const vtkm::rendering::ColorTable& colorTable, bool horizontal) const { this->AddColorBar( vtkm::Bounds(vtkm::Range(x, x + width), vtkm::Range(y, y + height), vtkm::Range(0, 0)), colorTable, horizontal); } vtkm::Id2 Canvas::GetScreenPoint(vtkm::Float32 x, vtkm::Float32 y, vtkm::Float32 z, const vtkm::Matrix& transform) const { vtkm::Vec point(x, y, z, 1.0f); point = vtkm::MatrixMultiply(transform, point); vtkm::Id2 pixelPos; vtkm::Float32 width = static_cast(Internals->Width); vtkm::Float32 height = static_cast(Internals->Height); pixelPos[0] = static_cast(vtkm::Round((1.0f + point[0]) * width * 0.5f + 0.5f)); pixelPos[1] = static_cast(vtkm::Round((1.0f + point[1]) * height * 0.5f + 0.5f)); return pixelPos; } void Canvas::AddText(const vtkm::Matrix& transform, vtkm::Float32 scale, const vtkm::Vec& anchor, const vtkm::rendering::Color& color, const std::string& text) const { if (!Internals->FontTexture.IsValid()) { if (!LoadFont()) { return; } } vtkm::rendering::Canvas* self = const_cast(this); TextRenderer fontRenderer(self, Internals->Font, Internals->FontTexture); fontRenderer.RenderText(transform, scale, anchor, color, text); } void Canvas::AddText(const vtkm::Vec& position, vtkm::Float32 scale, vtkm::Float32 angle, vtkm::Float32 windowAspect, const vtkm::Vec& anchor, const vtkm::rendering::Color& color, const std::string& text) const { vtkm::Matrix translationMatrix = Transform3DTranslate(position[0], position[1], 0.f); vtkm::Matrix scaleMatrix = Transform3DScale(1.0f / windowAspect, 1.0f, 1.0f); vtkm::Vec rotationAxis(0.0f, 0.0f, 1.0f); vtkm::Matrix rotationMatrix = Transform3DRotate(angle, rotationAxis); vtkm::Matrix transform = vtkm::MatrixMultiply(translationMatrix, vtkm::MatrixMultiply(scaleMatrix, rotationMatrix)); this->AddText(transform, scale, anchor, color, text); } void Canvas::AddText(vtkm::Float32 x, vtkm::Float32 y, vtkm::Float32 scale, vtkm::Float32 angle, vtkm::Float32 windowAspect, vtkm::Float32 anchorX, vtkm::Float32 anchorY, const vtkm::rendering::Color& color, const std::string& text) const { this->AddText(vtkm::make_Vec(x, y), scale, angle, windowAspect, vtkm::make_Vec(anchorX, anchorY), color, text); } bool Canvas::LoadFont() const { Internals->Font = BitmapFontFactory::CreateLiberation2Sans(); const std::vector& rawPNG = Internals->Font.GetRawImageData(); std::vector rgba; unsigned long textureWidth, textureHeight; int error = DecodePNG(rgba, textureWidth, textureHeight, &rawPNG[0], rawPNG.size()); if (error != 0) { return false; } std::size_t numValues = textureWidth * textureHeight; std::vector alpha(numValues); for (std::size_t i = 0; i < numValues; ++i) { alpha[i] = rgba[i * 4 + 3]; } vtkm::cont::ArrayHandle textureHandle = vtkm::cont::make_ArrayHandle(alpha); Internals->FontTexture = FontTextureType(vtkm::Id(textureWidth), vtkm::Id(textureHeight), textureHandle); Internals->FontTexture.SetFilterMode(TextureFilterMode::Linear); Internals->FontTexture.SetWrapMode(TextureWrapMode::Clamp); return true; } const vtkm::Matrix& Canvas::GetModelView() const { return Internals->ModelView; } const vtkm::Matrix& Canvas::GetProjection() const { return Internals->Projection; } void Canvas::SetViewToWorldSpace(const vtkm::rendering::Camera& camera, bool vtkmNotUsed(clip)) { Internals->ModelView = camera.CreateViewMatrix(); Internals->Projection = camera.CreateProjectionMatrix(GetWidth(), GetHeight()); } void Canvas::SetViewToScreenSpace(const vtkm::rendering::Camera& vtkmNotUsed(camera), bool vtkmNotUsed(clip)) { vtkm::MatrixIdentity(Internals->ModelView); vtkm::MatrixIdentity(Internals->Projection); Internals->Projection[2][2] = -1.0f; } void Canvas::SaveAs(const std::string& fileName) const { this->RefreshColorBuffer(); std::ofstream of(fileName.c_str(), std::ios_base::binary | std::ios_base::out); vtkm::Id width = GetWidth(); vtkm::Id height = GetHeight(); of << "P6" << std::endl << width << " " << height << std::endl << 255 << std::endl; ColorBufferType::PortalConstControl colorPortal = GetColorBuffer().GetPortalConstControl(); for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--) { for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) { vtkm::Vec tuple = colorPortal.Get(yIndex * width + xIndex); of << (unsigned char)(tuple[0] * 255); of << (unsigned char)(tuple[1] * 255); of << (unsigned char)(tuple[2] * 255); } } of.close(); } vtkm::rendering::WorldAnnotator* Canvas::CreateWorldAnnotator() const { return new vtkm::rendering::WorldAnnotator(this); } } } // vtkm::rendering