diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2015-10-21 04:05:29 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2015-10-21 04:05:29 +0000 |
| commit | f2e065dee10fb110e9d51175ee6381333319ef56 (patch) | |
| tree | da85ca78fe050280d7592bf6bf1071410846031f /TextureLoader/src/Image.cpp | |
| download | DiligentTools-f2e065dee10fb110e9d51175ee6381333319ef56.tar.gz DiligentTools-f2e065dee10fb110e9d51175ee6381333319ef56.zip | |
Release v1.0.0
Diffstat (limited to 'TextureLoader/src/Image.cpp')
| -rw-r--r-- | TextureLoader/src/Image.cpp | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/TextureLoader/src/Image.cpp b/TextureLoader/src/Image.cpp new file mode 100644 index 0000000..d78f2da --- /dev/null +++ b/TextureLoader/src/Image.cpp @@ -0,0 +1,409 @@ +/* Copyright 2015 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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 OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "Image.h" +#include "Errors.h" + +#include "tiffio.h" +#include "png.h" +#include "jpeglib.h" + +#include "DataBlobImpl.h" +#include "DebugUtilities.h" +#include "RefCntAutoPtr.h" + +using namespace Diligent; +using namespace Diligent; + +namespace Diligent +{ + class TIFFClientOpenWrapper + { + public: + TIFFClientOpenWrapper( IDataBlob *pData ) : + m_pData( pData ), + m_Offset( 0 ), + m_Size( pData->GetSize() ) + { + } + + static tmsize_t TIFFReadProc( thandle_t pClientData, void* pBuffer, tmsize_t Size ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + auto *pSrcPtr = reinterpret_cast<Uint8*>(pThis->m_pData->GetDataPtr()) + pThis->m_Offset; + memcpy( pBuffer, pSrcPtr, Size ); + pThis->m_Offset += Size; + return Size; + } + + static tmsize_t TIFFWriteProc( thandle_t pClientData, void* pBuffer, tmsize_t Size ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + if( pThis->m_Offset + Size > pThis->m_Size ) + { + pThis->m_Size = pThis->m_Offset + Size; + pThis->m_pData->Resize( pThis->m_Size ); + } + auto *pDstPtr = reinterpret_cast<Uint8*>(pThis->m_pData->GetDataPtr()) + pThis->m_Offset; + memcpy( pDstPtr, pBuffer, Size ); + pThis->m_Offset += Size; + return Size; + } + + static toff_t TIFFSeekProc( thandle_t pClientData, toff_t Offset, int Whence ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + switch( Whence ) + { + case SEEK_SET: pThis->m_Offset = static_cast<size_t>(Offset); break; + case SEEK_CUR: pThis->m_Offset += static_cast<size_t>(Offset); break; + case SEEK_END: pThis->m_Offset = pThis->m_Size + static_cast<size_t>(Offset); break; + default: UNEXPECTED( "Unexpected whence" ); + } + + return pThis->m_Offset; + } + + static int TIFFCloseProc( thandle_t pClientData ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + pThis->m_pData.Release(); + pThis->m_Size = 0; + pThis->m_Offset = 0; + return 0; + } + + static toff_t TIFFSizeProc( thandle_t pClientData ) + { + auto *pThis = reinterpret_cast<TIFFClientOpenWrapper*>(pClientData); + return pThis->m_Size; + } + + static int TIFFMapFileProc( thandle_t pClientData, void** base, toff_t* size ) + { + UNEXPECTED( "Client file mapping is not implemented. Use \'m\' when opening TIFF file to disable file mapping." ) + return 0; + } + + static void TIFFUnmapFileProc( thandle_t pClientData, void* base, toff_t size ) + { + UNEXPECTED( "Client file mapping is not implemented. Use \'m\' when opening TIFF file to disable file mapping." ) + } + + private: + size_t m_Offset; + size_t m_Size; + RefCntAutoPtr<IDataBlob> m_pData; + }; + + void Image::LoadTiffFile( IDataBlob *pFileData, const ImageLoadInfo& LoadInfo ) + { + TIFFClientOpenWrapper TiffClientOpenWrpr(pFileData); + + auto TiffFile = TIFFClientOpen("", "rm", &TiffClientOpenWrpr, + TIFFClientOpenWrapper::TIFFReadProc, + TIFFClientOpenWrapper::TIFFWriteProc, + TIFFClientOpenWrapper::TIFFSeekProc, + TIFFClientOpenWrapper::TIFFCloseProc, + TIFFClientOpenWrapper::TIFFSizeProc, + TIFFClientOpenWrapper::TIFFMapFileProc, + TIFFClientOpenWrapper::TIFFUnmapFileProc); + + TIFFGetField(TiffFile, TIFFTAG_IMAGEWIDTH, &m_Desc.Width); + TIFFGetField(TiffFile, TIFFTAG_IMAGELENGTH, &m_Desc.Height); + + Uint16 SamplesPerPixel = 0; + // SamplesPerPixel is usually 1 for bilevel, grayscale, and palette-color images. + // SamplesPerPixel is usually 3 for RGB images. If this value is higher, ExtraSamples + // should give an indication of the meaning of the additional channels. + TIFFGetField(TiffFile, TIFFTAG_SAMPLESPERPIXEL, &SamplesPerPixel); + m_Desc.NumComponents = SamplesPerPixel; + + Uint16 BitsPerSample = 0; + TIFFGetField(TiffFile, TIFFTAG_BITSPERSAMPLE, &BitsPerSample); + m_Desc.BitsPerPixel = m_Desc.NumComponents * BitsPerSample; + + auto ScanlineSize = TIFFScanlineSize(TiffFile); + m_Desc.RowStride = static_cast<Uint32>( ScanlineSize ); + m_pData->Resize(m_Desc.Height * m_Desc.RowStride ); + auto *pDataPtr = reinterpret_cast<Uint8*>( m_pData->GetDataPtr() ); + for (Uint32 row = 0; row < m_Desc.Height; row++, pDataPtr += ScanlineSize) + { + TIFFReadScanline(TiffFile, pDataPtr, row); + } + TIFFClose(TiffFile); + } + + + class PNGReadFnHelper + { + public: + PNGReadFnHelper( IDataBlob *pData ) : + m_pData( pData ), + m_Offset( 0 ) + { + } + + static void ReadData( png_structp pngPtr, png_bytep data, png_size_t length ) + { + auto pThis = reinterpret_cast<PNGReadFnHelper*>( png_get_io_ptr(pngPtr) ); + memcpy( data, reinterpret_cast<Uint8*>(pThis->m_pData->GetDataPtr()) + pThis->m_Offset, length ); + pThis->m_Offset += length; + } + + private: + RefCntAutoPtr<IDataBlob> m_pData; + size_t m_Offset; + }; + + void Image::LoadPngFile( IDataBlob *pFileData, const ImageLoadInfo& LoadInfo ) + { + // http://www.piko3d.net/tutorials/libpng-tutorial-loading-png-files-from-streams/ + // http://www.libpng.org/pub/png/book/chapter13.html#png.ch13.div.10 + // https://gist.github.com/niw/5963798\ + + PNGReadFnHelper ReadFnHelper(pFileData); + + const size_t PngSigSize = 8; + png_const_bytep pngsig = reinterpret_cast<png_const_bytep>(pFileData->GetDataPtr()); + //Let LibPNG check the signature. If this function returns 0, everything is OK. + if( png_sig_cmp( pngsig, 0, PngSigSize ) != 0 ) + { + LOG_ERROR_AND_THROW( "Invalid png signature" ); + } + + png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + VERIFY(png, "png_create_read_struct() failed") + + png_infop info = png_create_info_struct(png); + VERIFY(info, "png_create_info_struct() failed") + + if( setjmp( png_jmpbuf( png ) ) ) + { + // When an error occurs during parsing, libPNG will jump to here + png_destroy_read_struct(&png, &info, (png_infopp)0); + LOG_ERROR_AND_THROW( "Failed to read png file" ); + } + + png_set_read_fn(png, (png_voidp)&ReadFnHelper, PNGReadFnHelper::ReadData); + + png_read_info(png, info); + + m_Desc.Width = png_get_image_width(png, info); + m_Desc.Height = png_get_image_height(png, info); + m_Desc.NumComponents = png_get_channels(png, info); + auto bit_depth = png_get_bit_depth(png, info); + m_Desc.BitsPerPixel = bit_depth * m_Desc.NumComponents; + auto color_type = png_get_color_type(png, info); + + // PNG files store 16-bit pixels in network byte order (big-endian, ie + // most significant bytes first). png_set_swap() shall switch the byte-order + // to little-endian (ie, least significant bits first). + if( bit_depth == 16 ) + png_set_swap(png); + +#if 0 + // Read any color_type into 8bit depth, RGBA format. + // See http://www.libpng.org/pub/png/libpng-manual.txt + + if( bit_depth == 16 ) + png_set_strip_16( png ); + + if( color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_palette_to_rgb( png ); + + // PNG_COLOR_TYPE_GRAY_ALPHA is always 8 or 16bit depth. + if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 ) + png_set_expand_gray_1_2_4_to_8( png ); + + if( png_get_valid( png, info, PNG_INFO_tRNS ) ) + png_set_tRNS_to_alpha( png ); + + // These color_type don't have an alpha channel then fill it with 0xff. + if( color_type == PNG_COLOR_TYPE_RGB || + color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_filler( png, 0xFF, PNG_FILLER_AFTER ); + + if( color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA ) + png_set_gray_to_rgb( png ); + + png_read_update_info( png, info ); +#endif + + //Array of row pointers. One for every row. + std::vector<png_bytep> rowPtrs(m_Desc.Height); + + //Alocate a buffer with enough space. + m_Desc.RowStride = m_Desc.Width * bit_depth * m_Desc.NumComponents / 8; + m_pData->Resize( m_Desc.Height * m_Desc.RowStride ); + for( size_t i = 0; i < m_Desc.Height; i++ ) + rowPtrs[i] = reinterpret_cast<png_bytep>(m_pData->GetDataPtr()) + i * m_Desc.RowStride; + + //Read the imagedata and write it to the adresses pointed to + //by rowptrs (in other words: our image databuffer) + png_read_image( png, rowPtrs.data() ); + + png_destroy_read_struct(&png, &info, (png_infopp)0); + } + + + + struct my_jpeg_error_mgr { + jpeg_error_mgr pub; + jmp_buf setjmp_buffer;// for return to caller + }; + + // Here's the routine that will replace the standard error_exit method: + METHODDEF(void) + my_error_exit (j_common_ptr cinfo) + { + // cinfo->err really points to a my_jpeg_error_mgr struct, so coerce pointer + my_jpeg_error_mgr* myerr = (my_jpeg_error_mgr*) cinfo->err; + + /* Always display the message. */ + /* We could postpone this until after returning, if we chose. */ + (*cinfo->err->output_message) (cinfo); + + // Return control to the setjmp point + longjmp(myerr->setjmp_buffer, 1); + } + + void Image::LoadJpegFile( IDataBlob *pFileData, const ImageLoadInfo& LoadInfo ) + { + // https://github.com/LuaDist/libjpeg/blob/master/example.c + + // This struct contains the JPEG decompression parameters and pointers to + // working space (which is allocated as needed by the JPEG library). + jpeg_decompress_struct cinfo; + + // We use our private extension JPEG error handler. + // Note that this struct must live as long as the main JPEG parameter + // struct, to avoid dangling-pointer problems. + my_jpeg_error_mgr jerr; + + // Step 1: allocate and initialize JPEG decompression object + + // We set up the normal JPEG error routines, then override error_exit. + cinfo.err = jpeg_std_error( &jerr.pub ); + jerr.pub.error_exit = my_error_exit; + // Establish the setjmp return context for my_error_exit to use. + if( setjmp( jerr.setjmp_buffer ) ) + { + // If we get here, the JPEG code has signaled an error. + // We need to clean up the JPEG object, close the input file, and return. + jpeg_destroy_decompress( &cinfo ); + LOG_ERROR_AND_THROW( "Failed to decompress JPEG image" ) + } + // Now we can initialize the JPEG decompression object. + jpeg_create_decompress( &cinfo ); + + // Step 2: specify data source + jpeg_mem_src( &cinfo, reinterpret_cast<unsigned char*>(pFileData->GetDataPtr()), static_cast<unsigned long>(pFileData->GetSize()) ); + + // Step 3: read file parameters with jpeg_read_header() + jpeg_read_header( &cinfo, TRUE ); + // We can ignore the return value from jpeg_read_header since + // (a) suspension is not possible with the stdio data source, and + // (b) we passed TRUE to reject a tables-only JPEG file as an error. + // See libjpeg.txt for more info. + + + // Step 4: set parameters for decompression + + // In this example, we don't need to change any of the defaults set by + // jpeg_read_header(), so we do nothing here. + + + // Step 5: Start decompressor + + jpeg_start_decompress( &cinfo ); + // We can ignore the return value since suspension is not possible + // with the stdio data source. + + // We may need to do some setup of our own at this point before reading + // the data. After jpeg_start_decompress() we have the correct scaled + // output image dimensions available, as well as the output colormap + // if we asked for color quantization. + + m_Desc.Width = cinfo.output_width; + m_Desc.Height = cinfo.output_height; + m_Desc.NumComponents = cinfo.output_components; + m_Desc.RowStride = m_Desc.Width * m_Desc.NumComponents; + m_Desc.BitsPerPixel = 8 * m_Desc.NumComponents; + + m_pData->Resize(m_Desc.RowStride * m_Desc.Height); + // Step 6: while (scan lines remain to be read) + // jpeg_read_scanlines(...); + + // Here we use the library's state variable cinfo.output_scanline as the + // loop counter, so that we don't have to keep track ourselves. + while( cinfo.output_scanline < cinfo.output_height ) { + // jpeg_read_scanlines expects an array of pointers to scanlines. + // Here the array is only one element long, but you could ask for + // more than one scanline at a time if that's more convenient. + + + auto *pDstScanline = reinterpret_cast<Uint8*>( m_pData->GetDataPtr() ) + cinfo.output_scanline * m_Desc.RowStride; + JSAMPROW RowPtrs[] = { reinterpret_cast<JSAMPROW>(pDstScanline) }; + jpeg_read_scanlines( &cinfo, RowPtrs, 1 ); + } + + // Step 7: Finish decompression + + jpeg_finish_decompress( &cinfo ); + // We can ignore the return value since suspension is not possible + // with the stdio data source. + + // Step 8: Release JPEG decompression object + + // This is an important step since it will release a good deal of memory. + jpeg_destroy_decompress( &cinfo ); + + // At this point you may want to check to see whether any corrupt-data + // warnings occurred (test whether jerr.pub.num_warnings is nonzero). + } + + Image::Image( Diligent::IFileStream *pSrcFile, + const ImageLoadInfo& LoadInfo ) : + m_pData( new Diligent::DataBlobImpl ) + { + RefCntAutoPtr<IDataBlob> pFileData( new Diligent::DataBlobImpl ); + pSrcFile->Read(pFileData); + + if( LoadInfo.Format == EImageFileFormat::tiff ) + { + LoadTiffFile(pFileData, LoadInfo ); + } + else if( LoadInfo.Format == EImageFileFormat::png ) + { + LoadPngFile(pFileData, LoadInfo ); + } + else if( LoadInfo.Format == EImageFileFormat::jpeg ) + { + LoadJpegFile(pFileData, LoadInfo ); + } + } +} |
