diff --git a/vtkm/rendering/CMakeLists.txt b/vtkm/rendering/CMakeLists.txt index cae304028..2c80d8cda 100644 --- a/vtkm/rendering/CMakeLists.txt +++ b/vtkm/rendering/CMakeLists.txt @@ -51,6 +51,7 @@ set(headers ConnectivityProxy.h Cylinderizer.h DecodePNG.h + EncodePNG.h LineRenderer.h MatrixHelpers.h Scene.h @@ -85,6 +86,7 @@ set(sources Camera.cxx Color.cxx DecodePNG.cxx + EncodePNG.cxx raytracing/Logger.cxx ) diff --git a/vtkm/rendering/Canvas.cxx b/vtkm/rendering/Canvas.cxx index 6af889acc..5980db059 100644 --- a/vtkm/rendering/Canvas.cxx +++ b/vtkm/rendering/Canvas.cxx @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -568,11 +569,40 @@ void Canvas::SetViewToScreenSpace(const vtkm::rendering::Camera& vtkmNotUsed(cam void Canvas::SaveAs(const std::string& fileName) const { this->RefreshColorBuffer(); - std::ofstream of(fileName.c_str(), std::ios_base::binary | std::ios_base::out); + auto ends_with = [](std::string const& value, std::string const& ending) { + if (ending.size() > value.size()) + { + return false; + } + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); + }; + + ColorBufferType::ReadPortalType colorPortal = GetColorBuffer().ReadPortal(); vtkm::Id width = GetWidth(); vtkm::Id height = GetHeight(); + + if (ends_with(fileName, ".png")) + { + std::vector img(static_cast(width * height)); + for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--) + { + for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) + { + vtkm::Vec4f_32 tuple = colorPortal.Get(yIndex * width + xIndex); + size_t idx = static_cast(4 * width * yIndex + 4 * xIndex); + img[idx + 0] = (unsigned char)(tuple[0] * 255); + img[idx + 1] = (unsigned char)(tuple[1] * 255); + img[idx + 2] = (unsigned char)(tuple[2] * 255); + img[idx + 3] = (unsigned char)(tuple[3] * 255); + } + } + + SavePNG(fileName, img, static_cast(width), static_cast(height)); + return; + } + + std::ofstream of(fileName.c_str(), std::ios_base::binary | std::ios_base::out); of << "P6" << std::endl << width << " " << height << std::endl << 255 << std::endl; - ColorBufferType::ReadPortalType colorPortal = GetColorBuffer().ReadPortal(); for (vtkm::Id yIndex = height - 1; yIndex >= 0; yIndex--) { for (vtkm::Id xIndex = 0; xIndex < width; xIndex++) diff --git a/vtkm/rendering/DecodePNG.cxx b/vtkm/rendering/DecodePNG.cxx index 33df89986..359d7b4fc 100644 --- a/vtkm/rendering/DecodePNG.cxx +++ b/vtkm/rendering/DecodePNG.cxx @@ -14,8 +14,6 @@ #include VTKM_THIRDPARTY_PRE_INCLUDE -#define LODEPNG_NO_COMPILE_ENCODER -#define LODEPNG_NO_COMPILE_DISK #include VTKM_THIRDPARTY_PRE_INCLUDE diff --git a/vtkm/rendering/EncodePNG.cxx b/vtkm/rendering/EncodePNG.cxx new file mode 100644 index 000000000..fcd03f409 --- /dev/null +++ b/vtkm/rendering/EncodePNG.cxx @@ -0,0 +1,69 @@ +//============================================================================ +// 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. +//============================================================================ +#include // for std::equal +#include + +#include +#include + +VTKM_THIRDPARTY_PRE_INCLUDE +#include +VTKM_THIRDPARTY_PRE_INCLUDE + +namespace vtkm +{ +namespace rendering +{ + +vtkm::UInt32 EncodePNG(std::vector const& image, + unsigned long width, + unsigned long height, + std::vector& output_png) +{ + // The default is 8 bit RGBA; does anyone care to have more options? + vtkm::UInt32 error = vtkm::png::lodepng::encode(output_png, image, width, height); + if (error) + { + // TODO: Use logging framework instead: + std::cerr << "PNG Encoder error number " << error << ": " << png::lodepng_error_text(error) + << "\n"; + } + return error; +} + + +vtkm::UInt32 SavePNG(std::string const& filename, + std::vector const& image, + unsigned long width, + unsigned long height) +{ + auto ends_with = [](std::string const& value, std::string const& ending) { + if (ending.size() > value.size()) + { + return false; + } + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); + }; + + if (!ends_with(filename, ".png")) + { + std::cerr << "PNG filename must end with .png\n"; + } + + std::vector output_png; + vtkm::UInt32 error = EncodePNG(image, width, height, output_png); + if (!error) + { + vtkm::png::lodepng::save_file(output_png, filename); + } + return error; +} +} +} // namespace vtkm::rendering diff --git a/vtkm/rendering/EncodePNG.h b/vtkm/rendering/EncodePNG.h new file mode 100644 index 000000000..c66d02cac --- /dev/null +++ b/vtkm/rendering/EncodePNG.h @@ -0,0 +1,39 @@ +//============================================================================ +// 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. +//============================================================================ +#ifndef vtk_m_rendering_EncodePNG_h +#define vtk_m_rendering_EncodePNG_h + +#include +#include + +#include + +namespace vtkm +{ +namespace rendering +{ + +// +VTKM_RENDERING_EXPORT +vtkm::UInt32 EncodePNG(std::vector const& image, + unsigned long width, + unsigned long height, + unsigned char* out_png, + std::size_t out_size); + +VTKM_RENDERING_EXPORT +vtkm::UInt32 SavePNG(std::string filename, + std::vector const& image, + unsigned long width, + unsigned long height); +} +} // vtkm::rendering + +#endif //vtk_m_rendering_EncodePNG_h