summaryrefslogtreecommitdiffstats
path: root/TextureLoader/src/Image.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'TextureLoader/src/Image.cpp')
-rw-r--r--TextureLoader/src/Image.cpp409
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 );
+ }
+ }
+}