imagecore/formats/reader.cpp (308 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 "imagecore/imagecore.h" #include "imagecore/formats/reader.h" #include "imagecore/utils/securemath.h" #include "internal/register.h" #include "writer.h" #include <sys/stat.h> #include <sys/mman.h> #define SIGNATURE_MAX_SIZE 8 #define MAX_FORMATS 32 namespace imagecore { static ImageReader::Factory* s_ReaderFactories[MAX_FORMATS]; static unsigned int s_NumReaderFactories; static int s_RegisteredDefaultReaders = registerDefaultImageReaders(); int ImageReader::registerReader(ImageReader::Factory* factory) { ASSERT(s_NumReaderFactories < MAX_FORMATS); s_ReaderFactories[s_NumReaderFactories] = factory; s_NumReaderFactories++; return s_NumReaderFactories; } ImageReader* ImageReader::createFromSignature(const uint8_t* sig) { for( unsigned int i = 0; i < s_NumReaderFactories; i++ ) { if( s_ReaderFactories[i]->matchesSignature(sig, SIGNATURE_MAX_SIZE) ) { return s_ReaderFactories[i]->create(); } } return NULL; } ImageReader* ImageReader::create(Storage* source) { uint8_t signature[SIGNATURE_MAX_SIZE]; if( source->peekSignature(signature) ) { ImageReader* imageReader = ImageReader::createFromSignature(signature); if( imageReader ) { return ImageReader::initReader(source, imageReader); } } return NULL; } ImageReader* ImageReader::initReader(Storage* source, ImageReader* imageReader) { if( !imageReader ) { return NULL; } if( !imageReader->initWithStorage(source) ) { delete imageReader; return NULL; } if( !imageReader->readHeader() ) { delete imageReader; return NULL; } return imageReader; } unsigned int ImageReader::getOrientedWidth() { EImageOrientation orientation = getOrientation(); if( orientation == kImageOrientation_Left || orientation == kImageOrientation_Right ) { return getHeight(); } return getWidth(); } unsigned int ImageReader::getOrientedHeight() { EImageOrientation orientation = getOrientation(); if( orientation == kImageOrientation_Left || orientation == kImageOrientation_Right ) { return getWidth(); } return getHeight(); } EImageOrientation ImageReader::getOrientation() { return kImageOrientation_Up; } EImageColorModel ImageReader::getNativeColorModel() { return kColorModel_RGBX; } bool ImageReader::supportsOutputColorModel(EImageColorModel colorSpace) { return Image::colorModelIsRGBA(colorSpace); } bool ImageReader::beginRead(unsigned int outputWidth, unsigned int outputHeight, EImageColorModel outputColorModel) { return false; } unsigned int ImageReader::readRows(Image* destImage, unsigned int destRow, unsigned int numRows) { return false; } bool ImageReader::endRead() { return false; } void ImageReader::computeReadDimensions(unsigned int desiredWidth, unsigned int desiredHeight, unsigned int& readWidth, unsigned int& readHeight) { readWidth = getWidth(); readHeight = getHeight(); } // ImageReader::FileStorage ImageReader::FileStorage* ImageReader::FileStorage::open(const char* filePath) { if( strcmp(filePath, "-") == 0 ) { return new FileStorage(stdin, false, false); } else { FILE* f = fopen(filePath, "rb"); if( f != NULL) { return new FileStorage(f, true, true); } } return NULL; } ImageReader::FileStorage::FileStorage(FILE* file) { m_MmapStorage = NULL; m_File = file; // Test and see if we can seek with this file descriptor. size_t basePos = ftell(m_File); if( fseek(m_File, basePos, SEEK_SET) == 0 ) { m_CanSeek = true; } else { m_CanSeek = false; } m_OwnsFile = false; } ImageReader::FileStorage::FileStorage(FILE* file, bool canSeek, bool ownsFile) { m_MmapStorage = NULL; m_File = file; m_CanSeek = canSeek; m_OwnsFile = ownsFile; } ImageReader::FileStorage::~FileStorage() { if( m_MmapStorage != NULL ) { delete m_MmapStorage; m_MmapStorage = NULL; } if( m_OwnsFile ) { fclose(m_File); m_File = NULL; } } uint64_t ImageReader::FileStorage::read(void* destBuffer, uint64_t numBytes) { return (unsigned int)fread(destBuffer, 1, (size_t)numBytes, m_File); } uint64_t ImageReader::FileStorage::tell() { return ftell(m_File); } bool ImageReader::FileStorage::seek(int64_t pos, SeekMode mode) { int whence = mode == kSeek_Set ? SEEK_SET : (mode == kSeek_Current ? kSeek_Current : kSeek_End); if( m_CanSeek && fseek(m_File, (long)pos, whence) == 0 ) { return true; } return false; } bool ImageReader::FileStorage::canSeek() { return m_CanSeek; } bool ImageReader::FileStorage::asFile(FILE*& file) { file = m_File; return true; } bool ImageReader::FileStorage::asBuffer(uint8_t*& buffer, uint64_t& length) { buffer = NULL; length = 0; if( m_MmapStorage == NULL && m_CanSeek ) { m_MmapStorage = MemoryMappedStorage::map(m_File); } return m_MmapStorage != NULL ? m_MmapStorage->asBuffer(buffer, length) : false; } bool ImageReader::FileStorage::peekSignature(uint8_t* signature) { if( signature != NULL ) { if( m_CanSeek ) { // We can seek, so just read the signature and seek back. size_t basePos = ftell(m_File); if( fread(signature, 1, SIGNATURE_MAX_SIZE, m_File) != SIGNATURE_MAX_SIZE ) { return false; } if( fseek(m_File, basePos, SEEK_SET) != 0 ) { return false; } return true; } else { // Can't seek back, probably stdin. Put the signature back into the stream buffer. for( int i = 0; i < SIGNATURE_MAX_SIZE; i++ ) { int b = fgetc(m_File); if( b != EOF ) { signature[i] = b; } else { return false; } } for( int i = SIGNATURE_MAX_SIZE - 1; i >= 0; i-- ) { ungetc(signature[i], m_File); } } return true; } return false; } // ImageReader::MemoryStorage ImageReader::MemoryStorage::MemoryStorage(void* buffer, uint64_t length, bool ownsBuffer) { m_Buffer = (uint8_t*)buffer; m_UsedBytes = 0; m_TotalBytes = length; m_OwnsBuffer = ownsBuffer; } ImageReader::MemoryStorage::~MemoryStorage() { if( m_OwnsBuffer ) { free(m_Buffer); m_Buffer = NULL; } } uint64_t ImageReader::MemoryStorage::read(void* destBuffer, uint64_t numBytes) { uint64_t bytesToRead = numBytes; if( SafeUAdd(m_UsedBytes, numBytes) > m_TotalBytes ) { bytesToRead = SafeUSub(m_TotalBytes, m_UsedBytes); } if( bytesToRead > 0 ) { memcpy(destBuffer, m_Buffer + m_UsedBytes, (size_t)bytesToRead); m_UsedBytes = SafeUAdd(m_UsedBytes, bytesToRead); } return bytesToRead; } uint64_t ImageReader::MemoryStorage::tell() { return m_UsedBytes; } bool ImageReader::MemoryStorage::seek(int64_t pos, SeekMode mode) { if( mode == kSeek_Set ) { if( pos > (int64_t)m_TotalBytes || pos < 0 ) { return false; } m_UsedBytes = pos; } else if( mode == kSeek_Current ) { if( m_UsedBytes + pos > m_TotalBytes || (int64_t)m_UsedBytes + pos < 0 ) { return false; } m_UsedBytes = SafeUAdd(m_UsedBytes, pos); } else if( mode == kSeek_End ) { if( m_TotalBytes - pos > m_TotalBytes || (int64_t)m_TotalBytes - pos < 0 ) { return false; } m_UsedBytes = SafeUSub(m_TotalBytes, pos); } return true; } bool ImageReader::MemoryStorage::canSeek() { return true; } bool ImageReader::MemoryStorage::asFile(FILE*& file) { return false; } bool ImageReader::MemoryStorage::asBuffer(uint8_t*& buffer, uint64_t& length) { buffer = m_Buffer; length = m_TotalBytes; return true; } bool ImageReader::MemoryStorage::peekSignature(uint8_t* signature) { uint64_t remainingBytes = SafeUSub(m_TotalBytes, m_UsedBytes); if( remainingBytes >= SIGNATURE_MAX_SIZE ) { memcpy(signature, m_Buffer, SIGNATURE_MAX_SIZE); return true; } return false; } // ImageReader::MemoryMappedStorage ImageReader::MemoryMappedStorage* ImageReader::MemoryMappedStorage::map(FILE* f) { int fd = fileno(f); if (fd >= 0) { struct stat sb; if( fstat(fd, &sb) != -1 ) { size_t size = (size_t)sb.st_size; return MemoryMappedStorage::map(fd, size); } } return NULL; } ImageReader::MemoryMappedStorage* ImageReader::MemoryMappedStorage::map(int fd, uint64_t length) { void* mapBuffer = mmap(NULL, (size_t)length, PROT_READ, MAP_SHARED, fd, 0); if( mapBuffer != MAP_FAILED ) { return new MemoryMappedStorage(mapBuffer, length); } return NULL; } ImageReader::MemoryMappedStorage::MemoryMappedStorage(void* buffer, uint64_t length) : MemoryStorage(buffer, length) { } ImageReader::MemoryMappedStorage::~MemoryMappedStorage() { munmap(m_Buffer, (size_t)m_TotalBytes); m_Buffer = NULL; } }