/*========================================================================= Program: Visualization Toolkit Module: vtkPImageWriter.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. =========================================================================*/ #include "vtkPImageWriter.h" #include "vtkAlgorithmOutput.h" #include "vtkImageData.h" #include "vtkInformation.h" #include "vtkObjectFactory.h" #include "vtkPipelineSize.h" #include "vtkStreamingDemandDrivenPipeline.h" #include "vtksys/FStream.hxx" #define vtkPIWCloseFile \ if (file && fileOpenedHere) \ { \ this->WriteFileTrailer(file, cache); \ vtksys::ofstream* ofile = dynamic_cast(file); \ if (ofile) \ { \ ofile->close(); \ } \ delete file; \ } vtkStandardNewMacro(vtkPImageWriter); //------------------------------------------------------------------------------ vtkPImageWriter::vtkPImageWriter() { // Set a default memory limit of a gibibyte this->MemoryLimit = 1 * 1024 * 1024; this->SizeEstimator = vtkPipelineSize::New(); } //------------------------------------------------------------------------------ vtkPImageWriter::~vtkPImageWriter() { if (this->SizeEstimator) { this->SizeEstimator->Delete(); } } //------------------------------------------------------------------------------ void vtkPImageWriter::PrintSelf(ostream& os, vtkIndent indent) { this->Superclass::PrintSelf(os, indent); os << indent << "MemoryLimit (in kibibytes): " << this->MemoryLimit << "\n"; } //------------------------------------------------------------------------------ // Breaks region into pieces with correct dimensionality. void vtkPImageWriter::RecursiveWrite( int axis, vtkImageData* cache, vtkInformation* inInfo, ostream* file) { int min, max, mid; vtkImageData* data; int fileOpenedHere = 0; unsigned long inputMemorySize; // if we need to open another slice, do it if (!file && (axis + 1) == this->FileDimensionality) { // determine the name if (this->FileName) { snprintf(this->InternalFileName, this->InternalFileNameSize, "%s", this->FileName); } else { if (this->FilePrefix) { snprintf(this->InternalFileName, this->InternalFileNameSize, this->FilePattern, this->FilePrefix, this->FileNumber); } else { snprintf( this->InternalFileName, this->InternalFileNameSize, this->FilePattern, this->FileNumber); } } // Open the file #ifdef _WIN32 file = new vtksys::ofstream(this->InternalFileName, ios::out | ios::binary); #else file = new vtksys::ofstream(this->InternalFileName, ios::out); #endif fileOpenedHere = 1; if (file->fail()) { vtkErrorMacro("RecursiveWrite: Could not open file " << this->InternalFileName); delete file; return; } // Subclasses can write a header with this method call. this->WriteFileHeader( file, cache, inInfo->Get(vtkStreamingDemandDrivenPipeline::WHOLE_EXTENT())); ++this->FileNumber; } // Get the pipeline information for the input vtkAlgorithm* inAlg = this->GetInputAlgorithm(); // Set a hint not to combine with previous requests inInfo->Set( vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT_INITIALIZED(), VTK_UPDATE_EXTENT_REPLACE); // Propagate the update extent so we can determine pipeline size inAlg->PropagateUpdateExtent(); // Go back to the previous behaviour inInfo->Set( vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT_INITIALIZED(), VTK_UPDATE_EXTENT_COMBINE); // Now we can ask how big the pipeline will be inputMemorySize = this->SizeEstimator->GetEstimatedSize(this, 0, 0); // will the current request fit into memory // if so the just get the data and write it out if (inputMemorySize < this->MemoryLimit) { #ifndef NDEBUG int* ext = inInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()); #endif vtkDebugMacro("Getting input extent: " << ext[0] << ", " << ext[1] << ", " << ext[2] << ", " << ext[3] << ", " << ext[4] << ", " << ext[5] << endl); this->GetInputAlgorithm()->Update(); data = cache; this->RecursiveWrite(axis, cache, data, inInfo, file); vtkPIWCloseFile; return; } // if the current request did not fit into memory // the we will split the current axis int* updateExtent = inInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT()); this->GetInput()->GetAxisUpdateExtent(axis, min, max, updateExtent); vtkDebugMacro("Axes: " << axis << "(" << min << ", " << max << "), UpdateMemory: " << inputMemorySize << ", Limit: " << this->MemoryLimit << endl); if (min == max) { if (axis > 0) { this->RecursiveWrite(axis - 1, cache, inInfo, file); } else { vtkWarningMacro("MemoryLimit too small for one pixel of information!!"); } vtkPIWCloseFile; return; } mid = (min + max) / 2; int axisUpdateExtent[6]; // if it is the y axis then flip by default if (axis == 1 && !this->FileLowerLeft) { // first half cache->SetAxisUpdateExtent(axis, mid + 1, max, updateExtent, axisUpdateExtent); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), axisUpdateExtent, 6); this->RecursiveWrite(axis, cache, inInfo, file); // second half cache->SetAxisUpdateExtent(axis, min, mid, updateExtent, axisUpdateExtent); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), axisUpdateExtent, 6); this->RecursiveWrite(axis, cache, inInfo, file); } else { // first half cache->SetAxisUpdateExtent(axis, min, mid, updateExtent, axisUpdateExtent); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), axisUpdateExtent, 6); this->RecursiveWrite(axis, cache, inInfo, file); // second half cache->SetAxisUpdateExtent(axis, mid + 1, max, updateExtent, axisUpdateExtent); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), axisUpdateExtent, 6); this->RecursiveWrite(axis, cache, inInfo, file); } // restore original extent cache->SetAxisUpdateExtent(axis, min, max, updateExtent, axisUpdateExtent); inInfo->Set(vtkStreamingDemandDrivenPipeline::UPDATE_EXTENT(), axisUpdateExtent, 6); // if we opened the file here, then we need to close it up vtkPIWCloseFile; }