From c09f8c098af04e90875b13e4c5566b326a0bbbba Mon Sep 17 00:00:00 2001
From: Utkarsh Ayachit <utkarsh.ayachit@kitware.com>
Date: Fri, 27 May 2016 12:35:10 -0400
Subject: [PATCH] Add ability to read/save alpha from/in PLY files.

Adds support to read and save alpha channel in PLY files. Default is same as
before i.e. alpha values are not saved.

Added test to test reading and saving ply files with alpha.
---
 IO/PLY/Testing/Cxx/CMakeLists.txt             |   1 +
 IO/PLY/Testing/Cxx/TestPLYWriterAlpha.cxx     | 102 ++++++
 .../Data/Baseline/TestPLYWriterAlpha.png.md5  |   1 +
 IO/PLY/vtkPLYReader.cxx                       | 120 ++++---
 IO/PLY/vtkPLYReader.h                         |   4 +-
 IO/PLY/vtkPLYWriter.cxx                       | 293 ++++++++++--------
 IO/PLY/vtkPLYWriter.h                         |  55 +++-
 7 files changed, 396 insertions(+), 180 deletions(-)
 create mode 100644 IO/PLY/Testing/Cxx/TestPLYWriterAlpha.cxx
 create mode 100644 IO/PLY/Testing/Data/Baseline/TestPLYWriterAlpha.png.md5

diff --git a/IO/PLY/Testing/Cxx/CMakeLists.txt b/IO/PLY/Testing/Cxx/CMakeLists.txt
index c2962aaa40..045c8d6fdc 100644
--- a/IO/PLY/Testing/Cxx/CMakeLists.txt
+++ b/IO/PLY/Testing/Cxx/CMakeLists.txt
@@ -3,6 +3,7 @@ vtk_add_test_cxx(${vtk-module}CxxTests tests
   TestPLYReaderIntensity.cxx
   TestPLYReaderPointCloud.cxx
   TestPLYReaderTextureUV.cxx
+  TestPLYWriterAlpha.cxx
   TestPLYWriter.cxx,NO_VALID
   )
 vtk_test_cxx_executable(${vtk-module}CxxTests tests)
diff --git a/IO/PLY/Testing/Cxx/TestPLYWriterAlpha.cxx b/IO/PLY/Testing/Cxx/TestPLYWriterAlpha.cxx
new file mode 100644
index 0000000000..227571cf8f
--- /dev/null
+++ b/IO/PLY/Testing/Cxx/TestPLYWriterAlpha.cxx
@@ -0,0 +1,102 @@
+/*=========================================================================
+
+  Program:   Visualization Toolkit
+  Module:    TestPLYWriterAlpha.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.
+
+=========================================================================*/
+// .NAME Test of vtkPLYWriter with alpha.
+
+#include "vtkElevationFilter.h"
+#include "vtkLookupTable.h"
+#include "vtkNew.h"
+#include "vtkPLYReader.h"
+#include "vtkPLYWriter.h"
+#include "vtkPolyDataMapper.h"
+#include "vtkRegressionTestImage.h"
+#include "vtkRenderWindow.h"
+#include "vtkRenderWindowInteractor.h"
+#include "vtkRenderer.h"
+#include "vtkSphereSource.h"
+#include "vtkTestUtilities.h"
+
+int TestPLYWriterAlpha(int argc, char* argv[])
+{
+  // Test temporary directory
+  char* tempDir =
+    vtkTestUtilities::GetArgOrEnvOrDefault("-T", argc, argv, "VTK_TEMP_DIR", "Testing/Temporary");
+  if (!tempDir)
+  {
+    std::cout << "Could not determine temporary directory.\n";
+    return EXIT_FAILURE;
+  }
+
+  std::string testDirectory = tempDir;
+  delete[] tempDir;
+
+  const std::string outputfile = testDirectory + std::string("/") + std::string("plyAlpha.ply");
+
+  vtkNew<vtkSphereSource> sphere;
+  sphere->SetPhiResolution(20);
+  sphere->SetThetaResolution(20);
+
+  vtkNew<vtkElevationFilter> elevation;
+  elevation->SetInputConnection(sphere->GetOutputPort());
+  elevation->SetLowPoint(-0.5, -0.5, -0.5);
+  elevation->SetHighPoint(0.5, 0.5, 0.5);
+
+  vtkNew<vtkLookupTable> lut;
+  lut->SetTableRange(0, 1);
+  lut->SetAlphaRange(0, 1.0);
+  lut->Build();
+
+  vtkNew<vtkPLYWriter> writer;
+  writer->SetFileName(outputfile.c_str());
+  writer->SetFileTypeToBinary();
+  writer->EnableAlphaOn();
+  writer->SetColorModeToDefault();
+  writer->SetArrayName("Elevation");
+  writer->SetLookupTable(lut.Get());
+  writer->SetInputConnection(elevation->GetOutputPort());
+  writer->Write();
+
+  vtkNew<vtkPLYReader> reader;
+  reader->SetFileName(outputfile.c_str());
+
+  // Create a mapper.
+  vtkNew<vtkPolyDataMapper> mapper;
+  mapper->SetInputConnection(reader->GetOutputPort());
+  mapper->ScalarVisibilityOn();
+
+  // Create the actor.
+  vtkNew<vtkActor> actor;
+  actor->SetMapper(mapper.Get());
+
+  // Basic visualisation.
+  vtkNew<vtkRenderWindow> renWin;
+  vtkNew<vtkRenderer> ren;
+  renWin->AddRenderer(ren.Get());
+  vtkNew<vtkRenderWindowInteractor> iren;
+  iren->SetRenderWindow(renWin.Get());
+
+  ren->AddActor(actor.Get());
+  ren->SetBackground(0, 0, 0);
+  renWin->SetSize(300, 300);
+
+  // interact with data
+  renWin->Render();
+
+  int retVal = vtkRegressionTestImage(renWin.Get());
+  if (retVal == vtkRegressionTester::DO_INTERACTOR)
+  {
+    iren->Start();
+  }
+  return !retVal;
+}
diff --git a/IO/PLY/Testing/Data/Baseline/TestPLYWriterAlpha.png.md5 b/IO/PLY/Testing/Data/Baseline/TestPLYWriterAlpha.png.md5
new file mode 100644
index 0000000000..9b736ae100
--- /dev/null
+++ b/IO/PLY/Testing/Data/Baseline/TestPLYWriterAlpha.png.md5
@@ -0,0 +1 @@
+b262a58b90dba7b243ace92229abf7e6
diff --git a/IO/PLY/vtkPLYReader.cxx b/IO/PLY/vtkPLYReader.cxx
index 8bb45b0411..016d2cf7eb 100644
--- a/IO/PLY/vtkPLYReader.cxx
+++ b/IO/PLY/vtkPLYReader.cxx
@@ -52,6 +52,7 @@ typedef struct _plyVertex {
   unsigned char red;
   unsigned char green;
   unsigned char blue;
+  unsigned char alpha;
 } plyVertex;
 
 typedef struct _plyFace {
@@ -59,6 +60,7 @@ typedef struct _plyFace {
   unsigned char red;
   unsigned char green;
   unsigned char blue;
+  unsigned char alpha;
   unsigned char nverts;   // number of vertex indices in list
   int *verts;             // vertex index list
 } plyFace;
@@ -77,35 +79,33 @@ int vtkPLYReader::RequestData(
     outInfo->Get(vtkDataObject::DATA_OBJECT()));
 
   PlyProperty vertProps[] = {
-    {"x", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x)),
-     0, 0, 0, 0},
-    {"y", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x)+sizeof(float)),
-     0, 0, 0, 0},
-    {"z", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x)+sizeof(float)+sizeof(float)),
-     0, 0, 0, 0},
-    {"u", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,tex)),
-     0, 0, 0, 0},
-    {"v", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,tex)+sizeof(float)),
-     0, 0, 0, 0},
-    {"nx", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,normal)),
-     0, 0, 0, 0},
-    {"ny", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,normal)+sizeof(float)),
-     0, 0, 0, 0},
-    {"nz", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,normal)+2*sizeof(float)),
-     0, 0, 0, 0},
-    {"red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,red)), 0, 0, 0, 0},
-    {"green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,green)), 0, 0, 0, 0},
-    {"blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,blue)), 0, 0, 0, 0},
+    { "x", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, x)), 0, 0, 0, 0 },
+    { "y", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, x) + sizeof(float)), 0, 0, 0,
+      0 },
+    { "z", PLY_FLOAT, PLY_FLOAT,
+      static_cast<int>(offsetof(plyVertex, x) + sizeof(float) + sizeof(float)), 0, 0, 0, 0 },
+    { "u", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, tex)), 0, 0, 0, 0 },
+    { "v", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, tex) + sizeof(float)), 0, 0,
+      0, 0 },
+    { "nx", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, normal)), 0, 0, 0, 0 },
+    { "ny", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, normal) + sizeof(float)), 0,
+      0, 0, 0 },
+    { "nz", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, normal) + 2 * sizeof(float)),
+      0, 0, 0, 0 },
+    { "red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, red)), 0, 0, 0, 0 },
+    { "green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, green)), 0, 0, 0, 0 },
+    { "blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, blue)), 0, 0, 0, 0 },
+    { "alpha", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, alpha)), 0, 0, 0, 0 },
   };
   PlyProperty faceProps[] = {
-    {"vertex_indices", PLY_INT, PLY_INT,
-     static_cast<int>(offsetof(plyFace,verts)),
-     1, PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,nverts))},
-    {"intensity", PLY_UCHAR, PLY_UCHAR,
-     static_cast<int>(offsetof(plyFace,intensity)), 0, 0, 0, 0},
-    {"red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,red)), 0, 0, 0, 0},
-    {"green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,green)), 0, 0, 0, 0},
-    {"blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,blue)), 0, 0, 0, 0},
+    { "vertex_indices", PLY_INT, PLY_INT, static_cast<int>(offsetof(plyFace, verts)), 1, PLY_UCHAR,
+      PLY_UCHAR, static_cast<int>(offsetof(plyFace, nverts)) },
+    { "intensity", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, intensity)), 0, 0, 0,
+      0 },
+    { "red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, red)), 0, 0, 0, 0 },
+    { "green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, green)), 0, 0, 0, 0 },
+    { "blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, blue)), 0, 0, 0, 0 },
+    { "alpha", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, alpha)), 0, 0, 0, 0 },
   };
 
   if (!this->FileName)
@@ -154,30 +154,51 @@ int vtkPLYReader::RequestData(
   }
 
   bool RGBCellsAvailable = false;
+  bool RGBCellsHaveAlpha = false;
   vtkSmartPointer<vtkUnsignedCharArray> RGBCells = NULL;
   if ( (elem = vtkPLY::find_element (ply, "face")) != NULL &&
        vtkPLY::find_property (elem, "red", &index) != NULL &&
        vtkPLY::find_property (elem, "green", &index) != NULL &&
        vtkPLY::find_property (elem, "blue", &index) != NULL )
   {
-    RGBCells = vtkSmartPointer<vtkUnsignedCharArray>::New();
-    RGBCells->SetName("RGB");
     RGBCellsAvailable = true;
+    RGBCells = vtkSmartPointer<vtkUnsignedCharArray>::New();
+    if (vtkPLY::find_property(elem, "alpha", &index) != NULL)
+    {
+      RGBCells->SetName("RGBA");
+      RGBCells->SetNumberOfComponents(4);
+      RGBCellsHaveAlpha = true;
+    }
+    else
+    {
+      RGBCells->SetName("RGB");
+      RGBCells->SetNumberOfComponents(3);
+    }
     output->GetCellData()->AddArray(RGBCells);
     output->GetCellData()->SetActiveScalars("RGB");
   }
 
   bool RGBPointsAvailable = false;
+  bool RGBPointsHaveAlpha = false;
   vtkSmartPointer<vtkUnsignedCharArray> RGBPoints = NULL;
   if ( (elem = vtkPLY::find_element (ply, "vertex")) != NULL &&
        vtkPLY::find_property (elem, "red", &index) != NULL &&
        vtkPLY::find_property (elem, "green", &index) != NULL &&
        vtkPLY::find_property (elem, "blue", &index) != NULL )
   {
-    RGBPoints = vtkSmartPointer<vtkUnsignedCharArray>::New();
     RGBPointsAvailable = true;
-    RGBPoints->SetName("RGB");
-    RGBPoints->SetNumberOfComponents(3);
+    RGBPoints = vtkSmartPointer<vtkUnsignedCharArray>::New();
+    if (vtkPLY::find_property(elem, "alpha", &index) != NULL)
+    {
+      RGBPoints->SetName("RGBA");
+      RGBPoints->SetNumberOfComponents(4);
+      RGBPointsHaveAlpha = true;
+    }
+    else
+    {
+      RGBPoints->SetName("RGB");
+      RGBPoints->SetNumberOfComponents(3);
+    }
     output->GetPointData()->SetScalars(RGBPoints);
   }
 
@@ -263,6 +284,10 @@ int vtkPLYReader::RequestData(
         vtkPLY::ply_get_property (ply, elemName, &vertProps[8]);
         vtkPLY::ply_get_property (ply, elemName, &vertProps[9]);
         vtkPLY::ply_get_property (ply, elemName, &vertProps[10]);
+        if (RGBPointsHaveAlpha)
+        {
+          vtkPLY::ply_get_property(ply, elemName, &vertProps[11]);
+        }
         RGBPoints->SetNumberOfTuples(numPts);
       }
 
@@ -281,7 +306,14 @@ int vtkPLYReader::RequestData(
         }
         if ( RGBPointsAvailable )
         {
-          RGBPoints->SetTuple3(j, vertex.red, vertex.green, vertex.blue);
+          if (RGBPointsHaveAlpha)
+          {
+            RGBPoints->SetTuple4(j, vertex.red, vertex.green, vertex.blue, vertex.alpha);
+          }
+          else
+          {
+            RGBPoints->SetTuple3(j, vertex.red, vertex.green, vertex.blue);
+          }
         }
       }
       output->SetPoints(pts);
@@ -310,7 +342,11 @@ int vtkPLYReader::RequestData(
         vtkPLY::ply_get_property (ply, elemName, &faceProps[2]);
         vtkPLY::ply_get_property (ply, elemName, &faceProps[3]);
         vtkPLY::ply_get_property (ply, elemName, &faceProps[4]);
-        RGBCells->SetNumberOfComponents(3);
+
+        if (RGBCellsHaveAlpha)
+        {
+          vtkPLY::ply_get_property(ply, elemName, &faceProps[5]);
+        }
         RGBCells->SetNumberOfTuples(numPolys);
       }
 
@@ -332,9 +368,19 @@ int vtkPLYReader::RequestData(
         }
         if ( RGBCellsAvailable )
         {
-          RGBCells->SetValue(3*j,face.red);
-          RGBCells->SetValue(3*j+1,face.green);
-          RGBCells->SetValue(3*j+2,face.blue);
+          if (RGBCellsHaveAlpha)
+          {
+            RGBCells->SetValue(4 * j, face.red);
+            RGBCells->SetValue(4 * j + 1, face.green);
+            RGBCells->SetValue(4 * j + 2, face.blue);
+            RGBCells->SetValue(4 * j + 3, face.alpha);
+          }
+          else
+          {
+            RGBCells->SetValue(3 * j, face.red);
+            RGBCells->SetValue(3 * j + 1, face.green);
+            RGBCells->SetValue(3 * j + 2, face.blue);
+          }
         }
       }
       output->SetPolys(polys);
diff --git a/IO/PLY/vtkPLYReader.h b/IO/PLY/vtkPLYReader.h
index 20928cf02b..507e515db5 100644
--- a/IO/PLY/vtkPLYReader.h
+++ b/IO/PLY/vtkPLYReader.h
@@ -23,8 +23,8 @@
  * must have the properties "x", "y", and "z". The "face" element must
  * have the property "vertex_indices" defined. Optionally, if the "face"
  * element has the properties "intensity" and/or the triplet "red",
- * "green", and "blue"; these are read and added as scalars to the
- * output data.
+ * "green", "blue", and optionally "alpha"; these are read and added as scalars
+ * to the output data.
  *
  * @sa
  * vtkPLYWriter
diff --git a/IO/PLY/vtkPLYWriter.cxx b/IO/PLY/vtkPLYWriter.cxx
index 3492356125..e1a652f928 100644
--- a/IO/PLY/vtkPLYWriter.cxx
+++ b/IO/PLY/vtkPLYWriter.cxx
@@ -41,6 +41,8 @@ vtkPLYWriter::vtkPLYWriter()
   this->ColorMode = VTK_COLOR_MODE_DEFAULT;
   this->LookupTable = NULL;
   this->Color[0] = this->Color[1] = this->Color[2] = 255;
+  this->EnableAlpha = false;
+  this->Alpha = 255;
   this->TextureCoordinatesName = VTK_TEXTURECOORDS_UV;
 
   this->HeaderComments = vtkSmartPointer<vtkStringArray>::New();
@@ -49,20 +51,17 @@ vtkPLYWriter::vtkPLYWriter()
 
 vtkPLYWriter::~vtkPLYWriter()
 {
-  if ( this->LookupTable )
-  {
-    this->LookupTable->Delete();
-  }
+  this->SetLookupTable(nullptr);
   delete [] this->ArrayName;
   delete [] this->FileName;
 }
 
-namespace { //required so we don't violate ODR
 typedef struct _plyVertex {
   float x[3];             // the usual 3-space position of a vertex
   unsigned char red;
   unsigned char green;
   unsigned char blue;
+  unsigned char alpha;
   float tex[2];
 } plyVertex;
 
@@ -72,8 +71,8 @@ typedef struct _plyFace {
   unsigned char red;
   unsigned char green;
   unsigned char blue;
+  unsigned char alpha;
 } plyFace;
-}
 
 void vtkPLYWriter::WriteData()
 {
@@ -82,38 +81,34 @@ void vtkPLYWriter::WriteData()
   vtkCellArray *polys;
   vtkPolyData *input = this->GetInput();
 
-  unsigned char *cellColors, *pointColors;
+  vtkSmartPointer<vtkUnsignedCharArray> cellColors, pointColors;
   PlyFile *ply;
   float version;
   static const char *elemNames[] = { "vertex", "face" };
-  static PlyProperty vertProps[] = { // property information for a vertex
-    {"x", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x)),
-     0, 0, 0, 0},
-    {"y", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x)+sizeof(float)),
-     0, 0, 0, 0},
-    {"z", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex,x)+sizeof(float)+sizeof(float)),
-     0, 0, 0, 0},
-    {"red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,red)),
-     0, 0, 0, 0},
-    {"green", PLY_UCHAR, PLY_UCHAR,
-     static_cast<int>(offsetof(plyVertex,green)), 0, 0, 0, 0},
-    {"blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex,blue)),
-     0, 0, 0, 0},
-    {(TextureCoordinatesName==1)?"texture_u":"u", PLY_FLOAT, PLY_FLOAT,
-     static_cast<int>(offsetof(plyVertex,tex)), 0, 0, 0, 0},
-    {(TextureCoordinatesName==1)?"texture_v":"v", PLY_FLOAT, PLY_FLOAT,
-     static_cast<int>(offsetof(plyVertex,tex)+sizeof(float)), 0, 0, 0, 0},
+  static PlyProperty vertProps[] = {
+    // property information for a vertex
+    { "x", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, x)), 0, 0, 0, 0 },
+    { "y", PLY_FLOAT, PLY_FLOAT, static_cast<int>(offsetof(plyVertex, x) + sizeof(float)), 0, 0, 0,
+      0 },
+    { "z", PLY_FLOAT, PLY_FLOAT,
+      static_cast<int>(offsetof(plyVertex, x) + sizeof(float) + sizeof(float)), 0, 0, 0, 0 },
+    { "red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, red)), 0, 0, 0, 0 },
+    { "green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, green)), 0, 0, 0, 0 },
+    { "blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, blue)), 0, 0, 0, 0 },
+    { "alpha", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyVertex, alpha)), 0, 0, 0, 0 },
+    { (TextureCoordinatesName == 1) ? "texture_u" : "u", PLY_FLOAT, PLY_FLOAT,
+      static_cast<int>(offsetof(plyVertex, tex)), 0, 0, 0, 0 },
+    { (TextureCoordinatesName == 1) ? "texture_v" : "v", PLY_FLOAT, PLY_FLOAT,
+      static_cast<int>(offsetof(plyVertex, tex) + sizeof(float)), 0, 0, 0, 0 },
   };
-  static PlyProperty faceProps[] = { // property information for a face
-    {"vertex_indices", PLY_INT, PLY_INT,
-     static_cast<int>(offsetof(plyFace,verts)),
-     1, PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,nverts))},
-    {"red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,red)),
-     0, 0, 0, 0},
-    {"green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,green)),
-     0, 0, 0, 0},
-    {"blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace,blue)),
-     0, 0, 0, 0},
+  static PlyProperty faceProps[] = {
+    // property information for a face
+    { "vertex_indices", PLY_INT, PLY_INT, static_cast<int>(offsetof(plyFace, verts)), 1, PLY_UCHAR,
+      PLY_UCHAR, static_cast<int>(offsetof(plyFace, nverts)) },
+    { "red", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, red)), 0, 0, 0, 0 },
+    { "green", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, green)), 0, 0, 0, 0 },
+    { "blue", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, blue)), 0, 0, 0, 0 },
+    { "alpha", PLY_UCHAR, PLY_UCHAR, static_cast<int>(offsetof(plyFace, alpha)), 0, 0, 0, 0 },
   };
 
   // Get input and check data
@@ -133,18 +128,16 @@ void vtkPLYWriter::WriteData()
 
   // Open the file in appropriate way
   if ( this->FileType == VTK_BINARY )
-  {
-    if ( this->DataByteOrder == VTK_LITTLE_ENDIAN )
     {
-      ply = vtkPLY::ply_open_for_writing(this->FileName, 2, elemNames,
-                                 PLY_BINARY_LE, &version);
-    }
-    else
-    {
-      ply = vtkPLY::ply_open_for_writing(this->FileName, 2, elemNames,
-                                 PLY_BINARY_BE, &version);
+      if (this->DataByteOrder == VTK_LITTLE_ENDIAN)
+      {
+        ply = vtkPLY::ply_open_for_writing(this->FileName, 2, elemNames, PLY_BINARY_LE, &version);
+      }
+      else
+      {
+        ply = vtkPLY::ply_open_for_writing(this->FileName, 2, elemNames, PLY_BINARY_BE, &version);
+      }
     }
-  }
   else
   {
     ply = vtkPLY::ply_open_for_writing(this->FileName, 2, elemNames,
@@ -163,6 +156,9 @@ void vtkPLYWriter::WriteData()
   pointColors = this->GetColors(numPts,input->GetPointData());
   cellColors = this->GetColors(numPolys,input->GetCellData());
 
+  bool pointAlpha = pointColors && pointColors->GetNumberOfComponents() == 4;
+  bool cellAlpha = cellColors && cellColors->GetNumberOfComponents() == 4;
+
   // get texture coordinates, if any
   const float *textureCoords = this->GetTextureCoordinates(numPts,input->GetPointData());
 
@@ -176,11 +172,15 @@ void vtkPLYWriter::WriteData()
     vtkPLY::ply_describe_property (ply, "vertex", &vertProps[3]);
     vtkPLY::ply_describe_property (ply, "vertex", &vertProps[4]);
     vtkPLY::ply_describe_property (ply, "vertex", &vertProps[5]);
+    if (pointAlpha)
+    {
+      vtkPLY::ply_describe_property(ply, "vertex", &vertProps[6]);
+    }
   }
   if ( textureCoords )
   {
-    vtkPLY::ply_describe_property(ply, "vertex", &vertProps[6]);
     vtkPLY::ply_describe_property(ply, "vertex", &vertProps[7]);
+    vtkPLY::ply_describe_property(ply, "vertex", &vertProps[8]);
   }
 
   vtkPLY::ply_element_count (ply, "face", numPolys);
@@ -190,6 +190,10 @@ void vtkPLYWriter::WriteData()
     vtkPLY::ply_describe_property (ply, "face", &faceProps[1]);
     vtkPLY::ply_describe_property (ply, "face", &faceProps[2]);
     vtkPLY::ply_describe_property (ply, "face", &faceProps[3]);
+    if (cellAlpha)
+    {
+      vtkPLY::ply_describe_property(ply, "face", &faceProps[4]);
+    }
   }
 
   // write comments and an object information field
@@ -214,10 +218,14 @@ void vtkPLYWriter::WriteData()
     vert.x[2] = static_cast<float>(dpoint[2]);
     if ( pointColors )
     {
-      idx = 3*i;
-      vert.red = *(pointColors + idx);
-      vert.green = *(pointColors + idx + 1);
-      vert.blue = *(pointColors + idx + 2);
+      idx = pointAlpha ? 4 * i : 3 * i;
+      vert.red = pointColors->GetValue(idx);
+      vert.green = pointColors->GetValue(idx + 1);
+      vert.blue = pointColors->GetValue(idx + 2);
+      if (pointAlpha)
+      {
+        vert.alpha = pointColors->GetValue(idx + 3);
+      }
     }
     if ( textureCoords )
     {
@@ -243,34 +251,35 @@ void vtkPLYWriter::WriteData()
       vtkErrorMacro(<<"Ply file only supports polygons with <256 points");
     }
     else
-    {
-      for (j=0; j<npts; j++)
-      {
-        face.nverts = npts;
-        verts[j] = (int)pts[j];
-      }
-      if ( cellColors )
       {
-        idx = 3*i;
-        face.red = *(cellColors + idx);
-        face.green = *(cellColors + idx + 1);
-        face.blue = *(cellColors + idx + 2);
+        for (j = 0; j < npts; j++)
+        {
+          face.nverts = npts;
+          verts[j] = (int)pts[j];
+        }
+        if (cellColors)
+        {
+          idx = cellAlpha ? 4 * i : 3 * i;
+          face.red = cellColors->GetValue(idx);
+          face.green = cellColors->GetValue(idx + 1);
+          face.blue = cellColors->GetValue(idx + 2);
+          if (cellAlpha)
+          {
+            face.alpha = cellColors->GetValue(idx + 3);
+          }
+        }
+        vtkPLY::ply_put_element(ply, (void*)&face);
       }
-      vtkPLY::ply_put_element (ply, (void *) &face);
-    }
-  }//for all polygons
-
-  delete [] pointColors;
-  delete [] cellColors;
+  } // for all polygons
 
   // close the PLY file
   vtkPLY::ply_close (ply);
 }
 
-unsigned char *vtkPLYWriter::GetColors(vtkIdType num,
-                                       vtkDataSetAttributes *dsa)
+vtkSmartPointer<vtkUnsignedCharArray> vtkPLYWriter::GetColors(
+  vtkIdType num, vtkDataSetAttributes* dsa)
 {
-  unsigned char *colors, *c;
+  unsigned char* c;
   vtkIdType i;
   int numComp;
 
@@ -285,74 +294,104 @@ unsigned char *vtkPLYWriter::GetColors(vtkIdType num,
   else if ( this->ColorMode == VTK_COLOR_MODE_UNIFORM_COLOR ||
     this->ColorMode == VTK_COLOR_MODE_UNIFORM_POINT_COLOR ||
     this->ColorMode == VTK_COLOR_MODE_UNIFORM_CELL_COLOR )
-  {
-    colors = c = new unsigned char[3*num];
-    for (i=0; i<num; i++)
     {
-      *c++ = this->Color[0];
-      *c++ = this->Color[1];
-      *c++ = this->Color[2];
-    }
-    return colors;
-  }
-  else //we will color based on data
-  {
-    double *tuple;
-    vtkDataArray *da;
-    const unsigned char *rgb;
-    vtkUnsignedCharArray *rgbArray;
-
-    if ( !this->ArrayName || (da=dsa->GetArray(this->ArrayName)) == NULL ||
-         this->Component >= (numComp=da->GetNumberOfComponents()) )
-    {
-      return NULL;
-    }
-    else if ( (rgbArray=vtkArrayDownCast<vtkUnsignedCharArray>(da)) != NULL &&
-              numComp == 3 )
-    {//have unsigned char array of three components, copy it
-      colors = c = new unsigned char[3*num];
-      rgb = rgbArray->GetPointer(0);
-      for (i=0; i<num; i++)
+      vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
+      colors->SetNumberOfComponents(this->EnableAlpha ? 4 : 3);
+      colors->SetNumberOfTuples(num);
+      c = colors->WritePointer(0, this->EnableAlpha ? 4 * num : 3 * num);
+      if (this->EnableAlpha)
       {
-        *c++ = *rgb++;
-        *c++ = *rgb++;
-        *c++ = *rgb++;
+        for (i = 0; i < num; i++)
+        {
+          *c++ = this->Color[0];
+          *c++ = this->Color[1];
+          *c++ = this->Color[2];
+          *c++ = this->Alpha;
+        }
       }
-      return colors;
-    }
-    else if ( (rgbArray=vtkArrayDownCast<vtkUnsignedCharArray>(da)) != NULL &&
-              numComp == 4 )
-    {//have unsigned char array of four components (RGBA), copy it without the `A`.
-      colors = c = new unsigned char[3*num];
-      const unsigned char *rgba = rgbArray->GetPointer(0);
-      for (i=0; i<num; i++)
+      else
       {
-        *c++ = *rgba++;
-        *c++ = *rgba++;
-        *c++ = *rgba++;
-        rgba++;
+        for (i = 0; i < num; i++)
+        {
+          *c++ = this->Color[0];
+          *c++ = this->Color[1];
+          *c++ = this->Color[2];
+        }
       }
       return colors;
     }
+    else // we will color based on data
+    {
+      vtkDataArray* da;
+      vtkUnsignedCharArray* rgbArray;
 
-    else if ( this->LookupTable != NULL )
-    {//use the data array mapped through lookup table
-      colors = c = new unsigned char[3*num];
-      for (i=0; i<num; i++)
+      if (!this->ArrayName || (da = dsa->GetArray(this->ArrayName)) == NULL ||
+        this->Component >= (numComp = da->GetNumberOfComponents()))
       {
-        tuple = da->GetTuple(i);
-        rgb = this->LookupTable->MapValue(tuple[this->Component]);
-        *c++ = rgb[0];
-        *c++ = rgb[1];
-        *c++ = rgb[2];
+        return NULL;
+      }
+      else if ((rgbArray = vtkArrayDownCast<vtkUnsignedCharArray>(da)) != NULL && numComp == 3)
+      { // have unsigned char array of three components, copy it
+        return rgbArray;
+      }
+      else if ((rgbArray = vtkArrayDownCast<vtkUnsignedCharArray>(da)) != NULL && numComp == 4)
+      {
+        if (this->EnableAlpha)
+        {
+          return rgbArray;
+        }
+
+        // have unsigned char array of four components (RGBA), copy it without the `A`.
+        vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
+        colors->SetNumberOfComponents(3);
+        colors->SetNumberOfTuples(num);
+        c = colors->WritePointer(0, 3 * num);
+        const unsigned char* rgba = rgbArray->GetPointer(0);
+        for (i = 0; i < num; i++)
+        {
+          *c++ = *rgba++;
+          *c++ = *rgba++;
+          *c++ = *rgba++;
+          rgba++;
+        }
+        return colors;
+      }
+      else if (this->LookupTable != NULL)
+      { // use the data array mapped through lookup table
+        vtkSmartPointer<vtkUnsignedCharArray> colors = vtkSmartPointer<vtkUnsignedCharArray>::New();
+        colors->SetNumberOfComponents(this->EnableAlpha ? 4 : 3);
+        colors->SetNumberOfTuples(num);
+        c = colors->WritePointer(0, this->EnableAlpha ? 4 * num : 3 * num);
+        if (this->EnableAlpha)
+        {
+          for (i = 0; i < num; i++)
+          {
+            double* tuple = da->GetTuple(i);
+            const unsigned char* rgba = this->LookupTable->MapValue(tuple[this->Component]);
+            *c++ = rgba[0];
+            *c++ = rgba[1];
+            *c++ = rgba[2];
+            *c++ = rgba[3];
+          }
+        }
+        else
+        {
+          for (i = 0; i < num; i++)
+          {
+            double* tuple = da->GetTuple(i);
+            const unsigned char* rgb = this->LookupTable->MapValue(tuple[this->Component]);
+            *c++ = rgb[0];
+            *c++ = rgb[1];
+            *c++ = rgb[2];
+          }
+        }
+        return colors;
+      }
+      else // no lookup table
+      {
+        return NULL;
       }
-      return colors;
-    }
-    else //no lookup table
-    {
-      return NULL;
     }
-  }
 }
 
 const float *vtkPLYWriter::GetTextureCoordinates(vtkIdType num, vtkDataSetAttributes *dsa)
@@ -415,6 +454,8 @@ void vtkPLYWriter::PrintSelf(ostream& os, vtkIndent indent)
   os << indent << "Color: (" << this->Color[0] << ","
      << this->Color[1] << "," << this->Color[2] << ")\n";
 
+  os << indent << "EnableAlpha: " << this->EnableAlpha << "\n";
+  os << indent << "Alpha: " << static_cast<int>(this->Alpha) << "\n";
 }
 
 void vtkPLYWriter::AddComment(const std::string &comment)
diff --git a/IO/PLY/vtkPLYWriter.h b/IO/PLY/vtkPLYWriter.h
index 4d92edf07e..2a33d15c5b 100644
--- a/IO/PLY/vtkPLYWriter.h
+++ b/IO/PLY/vtkPLYWriter.h
@@ -25,6 +25,9 @@
  * writer). If the array is not a vtkUnsignedCharArray with 3 or 4 components,
  * you need to specify a vtkLookupTable to map the scalars to RGB.
  *
+ * To enable saving out alpha (opacity) values, you must enable alpha using
+ * `vtkPLYWriter::SetEnableAlpha()`.
+ *
  * @warning
  * PLY does not handle big endian versus little endian correctly.
  *
@@ -45,6 +48,7 @@ class vtkDataSetAttributes;
 class vtkPolyData;
 class vtkScalarsToColors;
 class vtkStringArray;
+class vtkUnsignedCharArray;
 
 #define VTK_LITTLE_ENDIAN 0
 #define VTK_BIG_ENDIAN    1
@@ -110,6 +114,16 @@ public:
     {this->SetColorMode(VTK_COLOR_MODE_OFF);}
   //@}
 
+  //@{
+  /**
+   * Enable alpha output. Default is off, i.e. only color values will be saved
+   * based on ColorMode.
+   */
+  vtkSetMacro(EnableAlpha, bool);
+  vtkGetMacro(EnableAlpha, bool);
+  vtkBooleanMacro(EnableAlpha, bool);
+  //@}
+
   //@{
   /**
    * Specify the array name to use to color the data.
@@ -122,8 +136,8 @@ public:
   /**
    * Specify the array component to use to color the data.
    */
-  vtkSetClampMacro(Component,int,0,VTK_INT_MAX);
-  vtkGetMacro(Component,int);
+  vtkSetClampMacro(Component, int, 0, VTK_INT_MAX);
+  vtkGetMacro(Component, int);
   //@}
 
   //@{
@@ -132,7 +146,7 @@ public:
    * RGBA colors.
    */
   virtual void SetLookupTable(vtkScalarsToColors*);
-  vtkGetObjectMacro(LookupTable,vtkScalarsToColors);
+  vtkGetObjectMacro(LookupTable, vtkScalarsToColors);
   //@}
 
   //@{
@@ -142,8 +156,16 @@ public:
    * between (0,255). This only takes effect when the ColorMode is set to
    * uniform point, uniform cell, or uniform color.
    */
-  vtkSetVector3Macro(Color,unsigned char);
-  vtkGetVector3Macro(Color,unsigned char);
+  vtkSetVector3Macro(Color, unsigned char);
+  vtkGetVector3Macro(Color, unsigned char);
+  //@}
+
+  //@{
+  /** Set the alpha to use when using a uniform color (effect point or cells, or
+   *  both) and EnableAlpha is ON.
+   */
+  vtkSetMacro(Alpha, unsigned char);
+  vtkGetMacro(Alpha, unsigned char);
   //@}
 
   //@{
@@ -166,10 +188,10 @@ public:
   /**
    * Specify file type (ASCII or BINARY) for vtk data file.
    */
-  vtkSetClampMacro(FileType,int,VTK_ASCII,VTK_BINARY);
-  vtkGetMacro(FileType,int);
-  void SetFileTypeToASCII() {this->SetFileType(VTK_ASCII);};
-  void SetFileTypeToBinary() {this->SetFileType(VTK_BINARY);};
+  vtkSetClampMacro(FileType, int, VTK_ASCII, VTK_BINARY);
+  vtkGetMacro(FileType, int);
+  void SetFileTypeToASCII() { this->SetFileType(VTK_ASCII); };
+  void SetFileTypeToBinary() { this->SetFileType(VTK_BINARY); };
   //@}
 
   //@{
@@ -177,12 +199,12 @@ public:
    * Choose the name used for the texture coordinates.
    * (u, v) or (texture_u, texture_v)
    */
-  vtkSetClampMacro(TextureCoordinatesName,int,VTK_TEXTURECOORDS_UV, VTK_TEXTURECOORDS_TEXTUREUV);
-  vtkGetMacro(TextureCoordinatesName,int);
-  void SetTextureCoordinatesNameToUV()
-    {this->SetTextureCoordinatesName(VTK_TEXTURECOORDS_UV);}
+  vtkSetClampMacro(
+    TextureCoordinatesName, int, VTK_TEXTURECOORDS_UV, VTK_TEXTURECOORDS_TEXTUREUV);
+  vtkGetMacro(TextureCoordinatesName, int);
+  void SetTextureCoordinatesNameToUV() { this->SetTextureCoordinatesName(VTK_TEXTURECOORDS_UV); }
   void SetTextureCoordinatesNameToTextureUV()
-    {this->SetTextureCoordinatesName(VTK_TEXTURECOORDS_TEXTUREUV);}
+  {this->SetTextureCoordinatesName(VTK_TEXTURECOORDS_TEXTUREUV);}
   //@}
 
   /**
@@ -195,7 +217,7 @@ protected:
   ~vtkPLYWriter() VTK_OVERRIDE;
 
   void WriteData() VTK_OVERRIDE;
-  unsigned char *GetColors(vtkIdType num, vtkDataSetAttributes *dsa);
+  vtkSmartPointer<vtkUnsignedCharArray> GetColors(vtkIdType num, vtkDataSetAttributes* dsa);
   const float *GetTextureCoordinates(vtkIdType num, vtkDataSetAttributes *dsa);
 
   int DataByteOrder;
@@ -205,6 +227,9 @@ protected:
   vtkScalarsToColors *LookupTable;
   unsigned char Color[3];
 
+  bool EnableAlpha;
+  unsigned char Alpha;
+
   char* FileName;
 
   int FileType;
-- 
GitLab