vtkPNGWriter.cxx 8.39 KB
Newer Older
Ken Martin's avatar
Ken Martin committed
1
2
3
4
5
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPNGWriter.cxx

6
  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7
8
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
Ken Martin's avatar
Ken Martin committed
9

10
11
     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12
     PURPOSE.  See the above copyright notice for more information.
Ken Martin's avatar
Ken Martin committed
13
14
15

=========================================================================*/
#include "vtkPNGWriter.h"
16

17
#include "vtkErrorCode.h"
18
#include "vtkImageData.h"
Ken Martin's avatar
Ken Martin committed
19
#include "vtkObjectFactory.h"
20
#include "vtkUnsignedCharArray.h"
Ken Martin's avatar
Ken Martin committed
21
22
23

#include <png.h>

Ken Martin's avatar
Ken Martin committed
24
vtkCxxRevisionMacro(vtkPNGWriter, "1.22");
Brad King's avatar
Brad King committed
25
vtkStandardNewMacro(vtkPNGWriter);
Ken Martin's avatar
Ken Martin committed
26

27
28
vtkCxxSetObjectMacro(vtkPNGWriter,Result,vtkUnsignedCharArray);

Ken Martin's avatar
Ken Martin committed
29
30
31
32
vtkPNGWriter::vtkPNGWriter()
{
  this->FileLowerLeft = 1;
  this->FileDimensionality = 2;
33
34
35
36
37
38
39
40
41
42
43
  this->WriteToMemory = 0;
  this->Result = 0;
}

vtkPNGWriter::~vtkPNGWriter()
{
  if (this->Result)
    {
    this->Result->Delete();
    this->Result = 0;
    }
Ken Martin's avatar
Ken Martin committed
44
45
46
47
48
49
}

//----------------------------------------------------------------------------
// Writes all the data from the input.
void vtkPNGWriter::Write()
{
50
51
  this->SetErrorCode(vtkErrorCode::NoError);
  
Ken Martin's avatar
Ken Martin committed
52
53
54
55
56
57
  // Error checking
  if ( this->GetInput() == NULL )
    {
    vtkErrorMacro(<<"Write:Please specify an input!");
    return;
    }
58
  if (!this->WriteToMemory && !this->FileName && !this->FilePattern)
Ken Martin's avatar
Ken Martin committed
59
60
    {
    vtkErrorMacro(<<"Write:Please specify either a FileName or a file prefix and pattern");
61
    this->SetErrorCode(vtkErrorCode::NoFileNameError);
Ken Martin's avatar
Ken Martin committed
62
63
64
65
66
67
    return;
    }
  
  // Make sure the file name is allocated
  this->InternalFileName = 
    new char[(this->FileName ? strlen(this->FileName) : 1) +
68
69
            (this->FilePrefix ? strlen(this->FilePrefix) : 1) +
            (this->FilePattern ? strlen(this->FilePattern) : 1) + 10];
Ken Martin's avatar
Ken Martin committed
70
71
72
73
74
75
  
  // Fill in image information.
  this->GetInput()->UpdateInformation();
  int *wExtent;
  wExtent = this->GetInput()->GetWholeExtent();
  this->FileNumber = this->GetInput()->GetWholeExtent()[4];
76
77
  this->MinimumFileNumber = this->MaximumFileNumber = this->FileNumber;
  this->FilesDeleted = 0;
Ken Martin's avatar
Ken Martin committed
78
79
80
81
82
  this->UpdateProgress(0.0);
  // loop over the z axis and write the slices
  for (this->FileNumber = wExtent[4]; this->FileNumber <= wExtent[5]; 
       ++this->FileNumber)
    {
83
    this->MaximumFileNumber = this->FileNumber;
Ken Martin's avatar
Ken Martin committed
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    this->GetInput()->SetUpdateExtent(wExtent[0], wExtent[1],
                                      wExtent[2], wExtent[3],
                                      this->FileNumber, 
                                      this->FileNumber);
    // determine the name
    if (this->FileName)
      {
      sprintf(this->InternalFileName,"%s",this->FileName);
      }
    else 
      {
      if (this->FilePrefix)
        {
        sprintf(this->InternalFileName, this->FilePattern, 
                this->FilePrefix, this->FileNumber);
        }
      else
        {
        sprintf(this->InternalFileName, this->FilePattern,this->FileNumber);
        }
      }
    this->GetInput()->UpdateData();
    this->WriteSlice(this->GetInput());
107
108
109
110
111
    if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
      {
      this->DeleteFiles();
      break;
      }
Ken Martin's avatar
Ken Martin committed
112
113
114
115
116
117
118
    this->UpdateProgress((this->FileNumber - wExtent[4])/
                         (wExtent[5] - wExtent[4] + 1.0));
    }
  delete [] this->InternalFileName;
  this->InternalFileName = NULL;
}

Berk Geveci's avatar
Berk Geveci committed
119
extern "C"
120
{
Berk Geveci's avatar
Berk Geveci committed
121
122
123
124
125
126
127
128
  void vtkPNGWriteInit(png_structp png_ptr, png_bytep data, 
                       png_size_t sizeToWrite)
  {
    vtkPNGWriter *self = 
      vtkPNGWriter::SafeDownCast(static_cast<vtkObject *>
                                 (png_get_io_ptr(png_ptr)));
    if (self)
      {
129
130
      vtkUnsignedCharArray *uc = self->GetResult();
      // write to the uc array
131
132
      unsigned char *ptr = uc->WritePointer(uc->GetMaxId()+1,sizeToWrite);
      memcpy(ptr, data, sizeToWrite);
Berk Geveci's avatar
Berk Geveci committed
133
134
      }
  }
135
136
}

Berk Geveci's avatar
Berk Geveci committed
137
extern "C"
138
{
Berk Geveci's avatar
Berk Geveci committed
139
140
141
  void vtkPNGWriteFlush(png_structp vtkNotUsed(png_ptr))
  {
  }
142
143
}

144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
extern "C"
{
  void vtkPNGWriteErrorFunction(png_structp png_ptr,
                                png_const_charp vtkNotUsed(error_msg))
  {
    longjmp(png_ptr->jmpbuf, 1);
  }
}

extern "C"
{
  void vtkPNGWriteWarningFunction(png_structp vtkNotUsed(png_ptr),
                                  png_const_charp vtkNotUsed(warning_msg))
  {
  }
}
Ken Martin's avatar
Ken Martin committed
160

Ken Martin's avatar
Ken Martin committed
161
162
163
// we disable this warning because even though this is a C++ file, between
// the setjmp and resulting longjmp there should not be any C++ constructors
// or destructors.
Ken Martin's avatar
Ken Martin committed
164
#if defined(_MSC_VER) && !defined(VTK_DISPLAY_WIN32_WARNINGS)
Ken Martin's avatar
Ken Martin committed
165
#pragma warning ( disable : 4611 )
Ken Martin's avatar
Ken Martin committed
166
#endif
Ken Martin's avatar
Ken Martin committed
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
void vtkPNGWriter::WriteSlice(vtkImageData *data)
{
  // Call the correct templated function for the output
  unsigned int ui;

  // Call the correct templated function for the input
  if (data->GetScalarType() != VTK_UNSIGNED_SHORT &&
      data->GetScalarType() != VTK_UNSIGNED_CHAR)
    {
    vtkWarningMacro("PNGWriter only supports unsigned char and unsigned short inputs");
    return;
    }   

  png_structp png_ptr = png_create_write_struct
    (PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL);
  if (!png_ptr)
    {
    vtkErrorMacro(<<"Unable to write PNG file!");
    return;
    }
  
  png_infop info_ptr = png_create_info_struct(png_ptr);
  if (!info_ptr)
    {
    png_destroy_write_struct(&png_ptr,
                             (png_infopp)NULL);
    vtkErrorMacro(<<"Unable to write PNG file!");
    return;
    }

197
198
199
200
  
  FILE *fp = 0;
  if (this->WriteToMemory)
    {
201
202
203
204
205
206
207
208
209
210
211
    vtkUnsignedCharArray *uc = this->GetResult();
    if (!uc || uc->GetReferenceCount() > 1)
      {
      uc = vtkUnsignedCharArray::New();
      this->SetResult(uc);
      uc->Delete();
      }
    // start out with 10K as a guess for the image size
    uc->Allocate(10000);
    png_set_write_fn(png_ptr, static_cast<png_voidp>(this), 
                     vtkPNGWriteInit, vtkPNGWriteFlush);
212
213
214
215
216
217
218
    }
  else
    {
      fp = fopen(this->InternalFileName, "wb");
      if (!fp)
        {
        vtkErrorMacro("Unable to open file " << this->InternalFileName);
219
        this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
220
221
222
        return;
        }
      png_init_io(png_ptr, fp);
223
224
225
226
227
228
229
230
      png_set_error_fn(png_ptr, png_ptr,
                       vtkPNGWriteErrorFunction, vtkPNGWriteWarningFunction);
      if (setjmp(png_ptr->jmpbuf))
        {
        fclose(fp);
        this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
        return;
        }
231
232
    }

Ken Martin's avatar
Ken Martin committed
233
234
  
  int *uExtent = data->GetUpdateExtent();
Ken Martin's avatar
Ken Martin committed
235
236
  void *outPtr;
  outPtr = data->GetScalarPointer(uExtent[0], uExtent[2], uExtent[4]);
Ken Martin's avatar
Ken Martin committed
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
  png_uint_32 width, height;
  width = uExtent[1] - uExtent[0] + 1;
  height = uExtent[3] - uExtent[2] + 1;  
  int bit_depth = 8;
  if (data->GetScalarType() == VTK_UNSIGNED_SHORT)
    {
    bit_depth = 16;
    }
  int color_type;
  switch (data->GetNumberOfScalarComponents())
    {
    case 1: color_type = PNG_COLOR_TYPE_GRAY;
      break;
    case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
      break;
    case 3: color_type = PNG_COLOR_TYPE_RGB;
      break;
    default: color_type = PNG_COLOR_TYPE_RGB_ALPHA;
      break;
    }
  
  png_set_IHDR(png_ptr, info_ptr, width, height,
               bit_depth, color_type, PNG_INTERLACE_NONE,
               PNG_COMPRESSION_TYPE_DEFAULT, 
               PNG_FILTER_TYPE_DEFAULT);
  // interlace_type - PNG_INTERLACE_NONE or
  //                 PNG_INTERLACE_ADAM7
    
  png_write_info(png_ptr, info_ptr);
  // default is big endian
  if (bit_depth > 8)
    {
#ifndef VTK_WORDS_BIGENDIAN
    png_set_swap(png_ptr);
#endif
    }
  png_byte **row_pointers = new png_byte *[height];
  int *outInc = data->GetIncrements();
Ken Martin's avatar
Ken Martin committed
275
  int rowInc = outInc[1]*bit_depth/8;
Ken Martin's avatar
Ken Martin committed
276
277
278
  for (ui = 0; ui < height; ui++)
    {
    row_pointers[height - ui - 1] = (png_byte *)outPtr;
Ken Martin's avatar
Ken Martin committed
279
    outPtr = (unsigned char *)outPtr + rowInc;
Ken Martin's avatar
Ken Martin committed
280
281
282
    }
  png_write_image(png_ptr, row_pointers);
  png_write_end(png_ptr, info_ptr);
Ken Martin's avatar
Ken Martin committed
283
284

  delete [] row_pointers;
Ken Martin's avatar
Ken Martin committed
285
  png_destroy_write_struct(&png_ptr, &info_ptr);
286

Jeff Lee's avatar
Jeff Lee committed
287
  if (fp)
288
    {
Jeff Lee's avatar
Jeff Lee committed
289
290
291
292
293
    fflush(fp);
    if (ferror(fp))
      {
      this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
      }
294
    }
295
  
296
297
298
299
300
301
302
303
304
305
306
307
  if (fp)
    {
    fclose(fp);
    }
}

void vtkPNGWriter::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);

  os << indent << "Result: " << this->Result << "\n";
  os << indent << "WriteToMemory: " << (this->WriteToMemory ? "On" : "Off") << "\n";
Ken Martin's avatar
Ken Martin committed
308
}