1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkJPEGWriter.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 #include "vtkJPEGWriter.h"
16 
17 #include "vtkErrorCode.h"
18 #include "vtkImageData.h"
19 #include "vtkInformation.h"
20 #include "vtkObjectFactory.h"
21 #include "vtkStreamingDemandDrivenPipeline.h"
22 #include "vtkToolkits.h"
23 #include "vtkUnsignedCharArray.h"
24 
25 extern "C" {
26 #include "vtk_jpeg.h"
27 #if defined(__sgi) && !defined(__GNUC__)
28 #  if   (_COMPILER_VERSION >= 730)
29 #  pragma set woff 3505
30 #  endif
31 #endif
32 #include <setjmp.h>
33 }
34 
35 #if _MSC_VER
36 #define snprintf _snprintf
37 #endif
38 
39 vtkStandardNewMacro(vtkJPEGWriter);
40 
41 vtkCxxSetObjectMacro(vtkJPEGWriter,Result,vtkUnsignedCharArray);
42 
vtkJPEGWriter()43 vtkJPEGWriter::vtkJPEGWriter()
44 {
45   this->FileLowerLeft = 1;
46   this->FileDimensionality = 2;
47 
48   this->Quality = 95;
49   this->Progressive = 1;
50   this->WriteToMemory = 0;
51   this->Result = 0;
52   this->TempFP = 0;
53 }
54 
~vtkJPEGWriter()55 vtkJPEGWriter::~vtkJPEGWriter()
56 {
57   if (this->Result)
58     {
59     this->Result->Delete();
60     this->Result = 0;
61     }
62 }
63 
64 //----------------------------------------------------------------------------
65 // Writes all the data from the input.
Write()66 void vtkJPEGWriter::Write()
67 {
68   this->SetErrorCode(vtkErrorCode::NoError);
69 
70   // Error checking
71   if ( this->GetInput() == NULL )
72     {
73     vtkErrorMacro(<<"Write:Please specify an input!");
74     return;
75     }
76   if (!this->WriteToMemory && ! this->FileName && !this->FilePattern)
77     {
78     vtkErrorMacro(<<"Write:Please specify either a FileName or a file prefix and pattern");
79     this->SetErrorCode(vtkErrorCode::NoFileNameError);
80     return;
81     }
82 
83   // Make sure the file name is allocated
84   size_t InternalFileNameSize = (this->FileName ? strlen(this->FileName) : 1) +
85     (this->FilePrefix ? strlen(this->FilePrefix) : 1) +
86     (this->FilePattern ? strlen(this->FilePattern) : 1) + 10;
87   this->InternalFileName = new char[InternalFileNameSize];
88 
89   // Fill in image information.
90   vtkDemandDrivenPipeline::SafeDownCast(this->GetInputExecutive(0, 0))->UpdateInformation();
91   int *wExtent;
92   wExtent = this->GetInputInformation(0, 0)->Get(
93     vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT());
94   this->FileNumber = wExtent[4];
95   this->MinimumFileNumber = this->MaximumFileNumber = this->FileNumber;
96   this->FilesDeleted = 0;
97   this->UpdateProgress(0.0);
98   // loop over the z axis and write the slices
99   for (this->FileNumber = wExtent[4]; this->FileNumber <= wExtent[5];
100        ++this->FileNumber)
101     {
102     this->MaximumFileNumber = this->FileNumber;
103     int uExtent[6];
104     memcpy(uExtent, wExtent, 4*sizeof(int));
105     uExtent[4] = this->FileNumber;
106     uExtent[5] = this->FileNumber;
107     vtkStreamingDemandDrivenPipeline::SetUpdateExtent(
108       this->GetInputInformation(0, 0),
109       uExtent);
110     // determine the name
111     if (this->FileName)
112       {
113       sprintf(this->InternalFileName,"%s",this->FileName);
114       }
115     else
116       {
117       if (this->FilePrefix)
118         {
119         sprintf(this->InternalFileName, this->FilePattern,
120                 this->FilePrefix, this->FileNumber);
121         }
122       else
123         {
124         snprintf(this->InternalFileName, InternalFileNameSize,
125           this->FilePattern, this->FileNumber);
126         }
127       }
128     this->GetInputExecutive(0, 0)->Update();
129     this->WriteSlice(this->GetInput(), uExtent);
130     if (this->ErrorCode == vtkErrorCode::OutOfDiskSpaceError)
131       {
132       vtkErrorMacro("Ran out of disk space; deleting file(s) already written");
133       this->DeleteFiles();
134       return;
135       }
136     this->UpdateProgress((this->FileNumber - wExtent[4])/
137                          (wExtent[5] - wExtent[4] + 1.0));
138     }
139   delete [] this->InternalFileName;
140   this->InternalFileName = NULL;
141 }
142 
143 // these three routines are for writing into memory
144 extern "C"
145 {
vtkJPEGWriteToMemoryInit(j_compress_ptr cinfo)146   static void vtkJPEGWriteToMemoryInit(j_compress_ptr cinfo)
147   {
148     vtkJPEGWriter *self = vtkJPEGWriter::SafeDownCast(
149       static_cast<vtkObject *>(cinfo->client_data));
150     if (self)
151       {
152       vtkUnsignedCharArray *uc = self->GetResult();
153       if (!uc || uc->GetReferenceCount() > 1)
154         {
155         uc = vtkUnsignedCharArray::New();
156         self->SetResult(uc);
157         uc->Delete();
158         // start out with 10K as a guess for the image size
159         uc->Allocate(10000);
160         }
161       cinfo->dest->next_output_byte = uc->GetPointer(0);
162       cinfo->dest->free_in_buffer = uc->GetSize();
163       }
164   }
165 }
166 
167 extern "C"
168 {
vtkJPEGWriteToMemoryEmpty(j_compress_ptr cinfo)169   static boolean vtkJPEGWriteToMemoryEmpty(j_compress_ptr cinfo)
170   {
171     // Even if (cinfo->dest->free_in_buffer != 0) we still need to write on the
172     // new array and not at (arraySize - nbFree)
173     vtkJPEGWriter *self = vtkJPEGWriter::SafeDownCast(
174       static_cast<vtkObject *>(cinfo->client_data));
175     if (self)
176       {
177       vtkUnsignedCharArray *uc = self->GetResult();
178       // we must grow the array
179       vtkIdType oldSize = uc->GetSize();
180       uc->Resize(static_cast<vtkIdType>(oldSize + oldSize/2));
181       // Resize do grow the array but it is not the size we expect
182       vtkIdType newSize = uc->GetSize();
183       cinfo->dest->next_output_byte = uc->GetPointer(oldSize);
184       cinfo->dest->free_in_buffer = static_cast<size_t>(newSize - oldSize);
185       }
186     return TRUE;
187   }
188 }
189 
190 extern "C"
191 {
vtkJPEGWriteToMemoryTerm(j_compress_ptr cinfo)192   static void vtkJPEGWriteToMemoryTerm(j_compress_ptr cinfo)
193   {
194     vtkJPEGWriter *self = vtkJPEGWriter::SafeDownCast(
195       static_cast<vtkObject *>(cinfo->client_data));
196     if (self)
197       {
198       vtkUnsignedCharArray *uc = self->GetResult();
199       // we must close the array
200       vtkIdType realSize = uc->GetSize() - static_cast<vtkIdType>(cinfo->dest->free_in_buffer);
201       uc->SetNumberOfTuples(realSize);
202       }
203   }
204 }
205 
206 #if defined ( _MSC_VER )
207 #if defined ( _WIN64 )
208 #pragma warning ( disable : 4324 ) // structure was padded at end...
209 #endif
210 #endif
211 
212 struct VTK_JPEG_ERROR_MANAGER
213 {
214   struct jpeg_error_mgr pub;
215   jmp_buf setjmp_buffer;
216 };
217 
218 typedef struct VTK_JPEG_ERROR_MANAGER* VTK_JPEG_ERROR_PTR;
219 
220 extern "C"
221 {
222   /* The JPEG library does not expect the error function to return.
223      Therefore we must use this ugly longjmp call.  */
224   void
VTK_JPEG_ERROR_EXIT(j_common_ptr cinfo)225   VTK_JPEG_ERROR_EXIT (j_common_ptr cinfo)
226 {
227   VTK_JPEG_ERROR_PTR jpegErr = reinterpret_cast<VTK_JPEG_ERROR_PTR>(cinfo->err);
228   longjmp(jpegErr->setjmp_buffer, 1);
229 }
230 }
231 
232 
233 // we disable this warning because even though this is a C++ file, between
234 // the setjmp and resulting longjmp there should not be any C++ constructors
235 // or destructors.
236 #if defined(_MSC_VER) && !defined(VTK_DISPLAY_WIN32_WARNINGS)
237 #pragma warning ( disable : 4611 )
238 #endif
WriteSlice(vtkImageData * data,int * uExtent)239 void vtkJPEGWriter::WriteSlice(vtkImageData *data, int* uExtent)
240 {
241   // Call the correct templated function for the output
242   unsigned int ui;
243 
244   // Call the correct templated function for the input
245   if (data->GetScalarType() != VTK_UNSIGNED_CHAR)
246     {
247     vtkWarningMacro("JPEGWriter only supports unsigned char input");
248     return;
249     }
250 
251   if (data->GetNumberOfScalarComponents() > MAX_COMPONENTS)
252     {
253     vtkErrorMacro("Exceed JPEG limits for number of components (" << data->GetNumberOfScalarComponents() << " > " << MAX_COMPONENTS << ")" );
254     return;
255     }
256 
257   // overriding jpeg_error_mgr so we don't exit when an error happens
258 
259   // Create the jpeg compression object and error handler
260   struct jpeg_compress_struct cinfo;
261   struct VTK_JPEG_ERROR_MANAGER jerr;
262   this->TempFP = 0;
263   if (!this->WriteToMemory)
264     {
265     this->TempFP = fopen(this->InternalFileName, "wb");
266     if (!this->TempFP)
267       {
268       vtkErrorMacro("Unable to open file " << this->InternalFileName);
269       this->SetErrorCode(vtkErrorCode::CannotOpenFileError);
270       return;
271       }
272     }
273 
274   cinfo.err = jpeg_std_error(&jerr.pub);
275   jerr.pub.error_exit = VTK_JPEG_ERROR_EXIT;
276   if (setjmp(jerr.setjmp_buffer))
277     {
278     jpeg_destroy_compress(&cinfo);
279     if (!this->WriteToMemory)
280       {
281       fclose(this->TempFP);
282       }
283     this->SetErrorCode(vtkErrorCode::OutOfDiskSpaceError);
284     return;
285     }
286 
287   jpeg_create_compress(&cinfo);
288 
289   // set the destination file
290   struct jpeg_destination_mgr compressionDestination;
291   if (this->WriteToMemory)
292     {
293     // setup the compress structure to write to memory
294     compressionDestination.init_destination = vtkJPEGWriteToMemoryInit;
295     compressionDestination.empty_output_buffer = vtkJPEGWriteToMemoryEmpty;
296     compressionDestination.term_destination = vtkJPEGWriteToMemoryTerm;
297     cinfo.dest = &compressionDestination;
298     cinfo.client_data = static_cast<void *>(this);
299     }
300   else
301     {
302     jpeg_stdio_dest(&cinfo, this->TempFP);
303     }
304 
305   // set the information about image
306   unsigned int width, height;
307   width = uExtent[1] - uExtent[0] + 1;
308   height = uExtent[3] - uExtent[2] + 1;
309 
310   cinfo.image_width = width;    /* image width and height, in pixels */
311   cinfo.image_height = height;
312 
313   cinfo.input_components = data->GetNumberOfScalarComponents();
314   switch (cinfo.input_components)
315     {
316     case 1: cinfo.in_color_space = JCS_GRAYSCALE;
317       break;
318     case 3: cinfo.in_color_space = JCS_RGB;
319       break;
320     default: cinfo.in_color_space = JCS_UNKNOWN;
321       break;
322     }
323 
324   // set the compression parameters
325   jpeg_set_defaults(&cinfo);         // start with reasonable defaults
326   jpeg_set_quality(&cinfo, this->Quality, TRUE);
327   if (this->Progressive)
328     {
329     jpeg_simple_progression(&cinfo);
330     }
331 
332   // start compression
333   jpeg_start_compress(&cinfo, TRUE);
334 
335   // write the data. in jpeg, the first row is the top row of the image
336   void *outPtr;
337   outPtr = data->GetScalarPointer(uExtent[0], uExtent[2], uExtent[4]);
338   JSAMPROW *row_pointers = new JSAMPROW [height];
339   vtkIdType *outInc = data->GetIncrements();
340   vtkIdType rowInc = outInc[1];
341   for (ui = 0; ui < height; ui++)
342     {
343     row_pointers[height - ui - 1] = (JSAMPROW) outPtr;
344     outPtr = (unsigned char *)outPtr + rowInc;
345     }
346   jpeg_write_scanlines(&cinfo, row_pointers, height);
347 
348   if (!this->WriteToMemory)
349     {
350     if (fflush(this->TempFP) == EOF)
351       {
352       this->ErrorCode = vtkErrorCode::OutOfDiskSpaceError;
353       fclose(this->TempFP);
354       return;
355       }
356     }
357 
358   // finish the compression
359   jpeg_finish_compress(&cinfo);
360 
361   // clean up and close the file
362   delete [] row_pointers;
363   jpeg_destroy_compress(&cinfo);
364 
365   if (!this->WriteToMemory)
366     {
367     fclose(this->TempFP);
368     }
369 }
370 
PrintSelf(ostream & os,vtkIndent indent)371 void vtkJPEGWriter::PrintSelf(ostream& os, vtkIndent indent)
372 {
373   this->Superclass::PrintSelf(os,indent);
374 
375   os << indent << "Quality: " << this->Quality << "\n";
376   os << indent << "Progressive: " << (this->Progressive ? "On" : "Off") << "\n";
377   os << indent << "Result: " << this->Result << "\n";
378   os << indent << "WriteToMemory: " << (this->WriteToMemory ? "On" : "Off") << "\n";
379 }
380