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