imagecore/formats/internal/tiff.cpp (173 lines of code) (raw):

/* * MIT License * * Copyright (c) 2017 Twitter * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "tiff.h" #include "imagecore/image/rgba.h" #include "imagecore/formats/writer.h" #include "imagecore/utils/mathutils.h" #include "imagecore/utils/securemath.h" namespace imagecore { REGISTER_IMAGE_READER(ImageReaderTIFF); static void TIFFSilentWarningHandler( const char*, const char*, va_list ) {} tsize_t tiffRead(thandle_t handle, tdata_t buffer, tsize_t size) { ImageReader::Storage* storage = (ImageReader::Storage*)handle; return (tsize_t)storage->read(buffer, size); } tsize_t tiffWrite(thandle_t handle, tdata_t buffer, tsize_t size) { ASSERT(0); return 0; } int tiffClose(thandle_t handle) { return 0; } toff_t tiffSeek(thandle_t handle, toff_t pos, int whence) { if( pos == 0xFFFFFFFF ) { return 0xFFFFFFFF; } ImageReader::Storage* storage = (ImageReader::Storage*)handle; if( whence == SEEK_CUR ) { storage->seek(pos, ImageReader::Storage::kSeek_Current); } else if( whence == SEEK_END ) { storage->seek(pos, ImageReader::Storage::kSeek_End); } else { storage->seek(pos, ImageReader::Storage::kSeek_Set); } return (toff_t)storage->tell(); } toff_t tiffSize(thandle_t handle) { ImageReader::Storage* storage = (ImageReader::Storage*)handle; uint64_t pos = storage->tell(); storage->seek(0, ImageReader::Storage::kSeek_End); uint64_t size = storage->tell(); storage->seek(pos, ImageReader::Storage::kSeek_Set); return (toff_t)size; } int tiffMap(thandle_t, tdata_t*, toff_t*) { return 0; } void tiffUnmap(thandle_t, tdata_t, toff_t) { return; } bool ImageReaderTIFF::Factory::matchesSignature(const uint8_t* sig, unsigned int sigLen) { if( sigLen >= 2 && ((sig[0] == 'I' && sig[1] == 'I') || (sig[0] == 'M' && sig[1] == 'M')) ) { return true; } return false; } ImageReaderTIFF::ImageReaderTIFF() : m_Source(NULL) , m_TempSource(NULL) , m_TempStorage(NULL) , m_Width(0) , m_Height(0) , m_HasAlpha(false) { } ImageReaderTIFF::~ImageReaderTIFF() { if( m_Tiff != NULL ) { TIFFClose(m_Tiff); m_Tiff = NULL; } delete m_TempSource; m_TempSource = NULL; delete m_TempStorage; m_TempStorage = NULL; } bool ImageReaderTIFF::initWithStorage(ImageReader::Storage* source) { m_Source = source; // libtiff requires random access, so if we have a non-seekable source, it needs to be fully read and copied. if( !m_Source->canSeek() ) { m_TempStorage = new ImageWriter::MemoryStorage(); uint8_t buffer[1024]; long totalBytesRead = 0; long bytesRead = 0; do { bytesRead = (long)source->read(buffer, 1024); if( bytesRead > 0 ) { m_TempStorage->write(buffer, bytesRead); } totalBytesRead += bytesRead; } while ( bytesRead > 0 ); uint8_t* storageBuffer; uint64_t storageLength; if( m_TempStorage->asBuffer(storageBuffer, storageLength) ) { m_TempSource = new MemoryStorage(storageBuffer, totalBytesRead); m_Source = m_TempSource; } } return true; } bool ImageReaderTIFF::readHeader() { TIFFSetErrorHandler(TIFFSilentWarningHandler); TIFFSetWarningHandler(TIFFSilentWarningHandler); // Don't allow memory mapping, read only. m_Tiff = TIFFClientOpen("None", "rm", m_Source, tiffRead, tiffWrite, tiffSeek, tiffClose, tiffSize, tiffMap, tiffUnmap); int width = 0; int height = 0; if( m_Tiff == NULL ) { return false; } if( TIFFGetField(m_Tiff, TIFFTAG_IMAGEWIDTH, &width) == 0 || TIFFGetField(m_Tiff, TIFFTAG_IMAGELENGTH, &height) == 0 ) { return false; } m_Width = width > 0 ? width : 0; m_Height = height > 0 ? height : 0; return true; } bool ImageReaderTIFF::readImage(Image* dest) { if( !supportsOutputColorModel(dest->getColorModel()) ) { return false; } ImageRGBA* destImage = dest->asRGBA(); char err[1024]; if( TIFFRGBAImageOK(m_Tiff, err) == 0 ) { fprintf(stderr, "error reading TIFF: '%s'\n", err); } TIFFRGBAImage tiffImage; memset(&tiffImage, 0, sizeof(TIFFRGBAImage)); if( TIFFRGBAImageBegin(&tiffImage, m_Tiff, 1, err) == 0 ) { fprintf(stderr, "error reading TIFF: '%s'\n", err); } m_HasAlpha = tiffImage.alpha > 0; bool success = false; ImageRGBA* tempImage = ImageRGBA::create(m_Width, m_Height); if( tempImage != NULL ) { unsigned int pitch = 0; uint8_t* buffer = tempImage->lockRect(m_Width, m_Height, pitch); TIFFReadRGBAImageOriented(m_Tiff, m_Width, m_Height, (uint32*)buffer, ORIENTATION_TOPLEFT, 1); tempImage->unlockRect(); tempImage->copy(destImage); delete tempImage; success = true; } TIFFRGBAImageEnd(&tiffImage); return success; } EImageFormat ImageReaderTIFF::getFormat() { return kImageFormat_TIFF; } const char* ImageReaderTIFF::getFormatName() { return "TIFF"; } unsigned int ImageReaderTIFF::getWidth() { return m_Width; } unsigned int ImageReaderTIFF::getHeight() { return m_Height; } EImageColorModel ImageReaderTIFF::getNativeColorModel() { return m_HasAlpha ? kColorModel_RGBA : kColorModel_RGBX; } }