diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /common/jpegloader.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'common/jpegloader.cpp')
| -rw-r--r-- | common/jpegloader.cpp | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/common/jpegloader.cpp b/common/jpegloader.cpp new file mode 100644 index 0000000..e7fd501 --- /dev/null +++ b/common/jpegloader.cpp @@ -0,0 +1,476 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "jpegloader.h" +#include "tier0/dbg.h" +#include "tier1/utlvector.h" +#include "tier0/vprof.h" +#include "jpeglib/jpeglib.h" +#include <setjmp.h> +//#include "fileio.h" + +class CFileWriter +{ + +}; + +class CJpegDestMgr : public jpeg_destination_mgr +{ +public: + CJpegDestMgr( CFileWriter &refOutputFileWriter ) + : m_pOutputFileWriter( &refOutputFileWriter ), + m_pOutputBuffer( NULL ) + { + Init(); + } + + CJpegDestMgr( CUtlBuffer &refOutputBuffer ) + : m_pOutputFileWriter( NULL ), + m_pOutputBuffer( &refOutputBuffer ) + { + Init(); + } + + void Init() + { + this->init_destination = &CJpegDestMgr::imp_init_dest; + this->empty_output_buffer = &CJpegDestMgr::imp_empty_output_buffer; + this->term_destination = &CJpegDestMgr::imp_term_destination; + + this->next_output_byte = 0; + this->free_in_buffer = 0; + } + + static void imp_init_dest( j_compress_ptr cinfo ) + { + CJpegDestMgr *pInstance = (CJpegDestMgr*)cinfo->dest; + + if ( pInstance->m_pOutputBuffer ) + pInstance->m_pOutputBuffer->EnsureCapacity( cinfo->image_width*cinfo->image_height*3 ); + + const int k_cubMaxTempJpegBuffer = 1024*10; + int cubBufferSize = MIN( cinfo->image_width*cinfo->image_height*3, k_cubMaxTempJpegBuffer ); + pInstance->m_Buffer.EnsureCapacity( cubBufferSize ); + pInstance->free_in_buffer = pInstance->m_Buffer.Size(); + pInstance->next_output_byte = (JOCTET*)pInstance->m_Buffer.Base(); + } + + static boolean imp_empty_output_buffer( j_compress_ptr cinfo ) + { + /* + CJpegDestMgr *pInstance = (CJpegDestMgr*)cinfo->dest; + + // Write the entire buffer, ignoring next_output_byte and free_in_buffer as they lie (see docs) + if ( pInstance->m_pOutputFileWriter ) + pInstance->m_pOutputFileWriter->Write( pInstance->m_Buffer.Base(), pInstance->m_Buffer.Size() ); + else + pInstance->m_pOutputBuffer->Put( pInstance->m_Buffer.Base(), pInstance->m_Buffer.Size() ); + + pInstance->free_in_buffer = pInstance->m_Buffer.Size(); + pInstance->next_output_byte = (JOCTET*)pInstance->m_Buffer.Base(); + */ + return true; + } + + + static void imp_term_destination(j_compress_ptr cinfo) + { + /* + CJpegDestMgr *pInstance = (CJpegDestMgr*)cinfo->dest; + + // next_output_byte and free_in_buffer don't lie here like in empty_output_buffer + int cubToWrite = (byte*)pInstance->next_output_byte - (byte*)pInstance->m_Buffer.Base(); + if ( cubToWrite > 0 ) + { + if ( pInstance->m_pOutputFileWriter ) + pInstance->m_pOutputFileWriter->Write( pInstance->m_Buffer.Base(), cubToWrite ); + else + pInstance->m_pOutputBuffer->Put( pInstance->m_Buffer.Base(), cubToWrite ); + } + + if ( pInstance->m_pOutputFileWriter ) + pInstance->m_pOutputFileWriter->Flush(); + */ + } + + static void error_exit( j_common_ptr cptr ) + { + longjmp( m_JmpBuf, 1 ); + } + + static jmp_buf m_JmpBuf; + +private: + CFileWriter *m_pOutputFileWriter; + CUtlBuffer *m_pOutputBuffer; + CUtlBuffer m_Buffer; +}; + +jmp_buf CJpegDestMgr::m_JmpBuf; + + +class CJpegSourceMgr : public jpeg_source_mgr +{ +public: + CJpegSourceMgr() + { + this->init_source = &CJpegSourceMgr::imp_init_source; + this->fill_input_buffer = &CJpegSourceMgr::imp_fill_input_buffer; + this->skip_input_data = &CJpegSourceMgr::imp_skip_input_data; + this->resync_to_restart = &CJpegSourceMgr::imp_resync_to_restart; + this->term_source = &CJpegSourceMgr::imp_term_source; + + this->next_input_byte = 0; + this->bytes_in_buffer = 0; + } + + bool Init( const byte *pubData, int cubData ) + { + m_Data.AddMultipleToTail( cubData, (char *)pubData ); + bytes_in_buffer = m_Data.Count(); + next_input_byte = (unsigned char*)m_Data.Base(); + return true; + } + + static void imp_init_source(j_decompress_ptr cinfo) + { + // We handled everything needed in our own Init() call. + } + + static boolean imp_fill_input_buffer(j_decompress_ptr cinfo) + { + // If this is hit it means we are reading a corrupt file. We can't provide more data + // as we provided all of it upfront already. + return 0; + } + + static void imp_skip_input_data(j_decompress_ptr cinfo, long num_bytes) + { + // The library is asking us to skip a chunk of data, usually exif header data or such. Need + // to actually do that. The library tries to be robust and skip obviously bad data on its own + // one byte at a time as well, but faster here, and safer as the library can't always do it + // correctly if we fail these calls. + CJpegSourceMgr *pInstance = (CJpegSourceMgr*)cinfo->src; + pInstance->bytes_in_buffer -= num_bytes; + pInstance->next_input_byte += num_bytes; + } + + static boolean imp_resync_to_restart(j_decompress_ptr cinfo, int desired) + { + // This happens if the library fails to find a resync marker where expected, we'll then + // use it's default handler. This handler will skip ahead trying to find the next point, + // we could maybe do better going backwards as we have the full buffer, but that's lots more + // work and this will only get hit on a partially corrupt image anyway. + return jpeg_resync_to_restart( cinfo, desired ); + } + + static void imp_term_source(j_decompress_ptr cinfo) + { + } + + static void error_exit( j_common_ptr cptr ) + { + longjmp( m_JmpBuf, 1 ); + } + + static jmp_buf m_JmpBuf; + +public: + CUtlVector<char> m_Data; +}; + +jmp_buf CJpegSourceMgr::m_JmpBuf; + + + +//----------------------------------------------------------------------------- +// Purpose: returns the dimensions of a jpg file from it's contents +//----------------------------------------------------------------------------- +bool GetJpegDimensions( const byte *pubData, int cubData, uint32 &width, uint32 &height ) +{ + CJpegSourceMgr sourceMgr; + bool bRet = sourceMgr.Init( pubData, cubData ); + if ( !bRet ) + return false; + + // Load the jpeg. + struct jpeg_decompress_struct jpegInfo; + struct jpeg_error_mgr jerr; + + memset( &jpegInfo, 0, sizeof( jpegInfo ) ); + jpegInfo.err = jpeg_std_error(&jerr); + jerr.error_exit = &CJpegSourceMgr::error_exit; + jpeg_create_decompress(&jpegInfo); + jpegInfo.src = &sourceMgr; + + if ( setjmp( sourceMgr.m_JmpBuf ) == 1 ) + { + jpeg_destroy_decompress(&jpegInfo); + return false; + } + + if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK) + { + jpeg_destroy_decompress(&jpegInfo); + return false; + } + + width = jpegInfo.image_width; + height = jpegInfo.image_height; + + jpeg_destroy_decompress(&jpegInfo); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: takes the contents of a .jpg file and turns it into RGB data, returning the width and height and optionally the number of bytes used +//----------------------------------------------------------------------------- +bool ConvertJpegToRGB( const byte *pubData, int cubData, CUtlVector<byte> &buf, int &width, int &height, int *pcubUsed ) +{ + CJpegSourceMgr sourceMgr; + bool bRet = sourceMgr.Init( pubData, cubData ); + if ( !bRet ) + return false; + + if ( pcubUsed ) + *pcubUsed = 0; + + sourceMgr.bytes_in_buffer = sourceMgr.m_Data.Count(); + sourceMgr.next_input_byte = (unsigned char*)sourceMgr.m_Data.Base(); + + // Load the jpeg. + struct jpeg_decompress_struct jpegInfo; + struct jpeg_error_mgr jerr; + + memset( &jpegInfo, 0, sizeof( jpegInfo ) ); + jpegInfo.err = jpeg_std_error(&jerr); + jerr.error_exit = &CJpegSourceMgr::error_exit; + jpeg_create_decompress(&jpegInfo); + jpegInfo.src = &sourceMgr; + + if ( setjmp( sourceMgr.m_JmpBuf ) == 1 ) + { + jpeg_destroy_decompress(&jpegInfo); + return false; + } + + if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK) + { + jpeg_destroy_decompress(&jpegInfo); + return false; + } + + // start the decompress with the jpeg engine. + if ( !jpeg_start_decompress(&jpegInfo) || jpegInfo.output_components != 3) + { + jpeg_destroy_decompress(&jpegInfo); + return false; + } + + // now that we've started the decompress with the jpeg lib, we have the attributes of the + // cdnfile ready to be read out of the decompress struct. + int row_stride = jpegInfo.output_width * jpegInfo.output_components; + int mem_required = jpegInfo.image_height * jpegInfo.image_width * jpegInfo.output_components; + JSAMPROW row_pointer[1]; + int cur_row = 0; + + width = jpegInfo.output_width; + height = jpegInfo.output_height; + + // allocate the memory to read the cdnfile data into. + buf.SetSize( mem_required ); + + // read in all the scan lines of the cdnfile into our cdnfile data buffer. + bool working = true; + while (working && (jpegInfo.output_scanline < jpegInfo.output_height)) + { + row_pointer[0] = &(buf[cur_row * row_stride]); + if ( !jpeg_read_scanlines(&jpegInfo, row_pointer, 1) ) + { + working = false; + } + ++cur_row; + } + + if (!working) + { + jpeg_destroy_decompress(&jpegInfo); + return false; + } + + if ( pcubUsed ) + *pcubUsed = (int)((byte*)sourceMgr.next_input_byte - (byte*)sourceMgr.m_Data.Base()); + jpeg_finish_decompress(&jpegInfo); + jpeg_destroy_decompress(&jpegInfo); + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts an RGB image to RGBA opacity will be 100% +//----------------------------------------------------------------------------- +void ConvertRGBToRGBAImage( CUtlVector<unsigned char> &srcData, + int srcWidth, + int srcHeight, + byte *destData, + int destWidth, + int destHeight ) +{ + int destPixelSize = 4; + int cub = destWidth * destHeight * destPixelSize; + byte *pSrc = srcData.Base(); + for ( int i = 0; i < cub; i += 4 ) + { + destData[i] = *pSrc++; + destData[i+1] = *pSrc++; + destData[i+2] = *pSrc++; + destData[i+3] = 255; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts raw Jpeg file data and converts to RGBA image buffer +//----------------------------------------------------------------------------- +bool ConvertJpegToRawInternal( const byte *pubJpegData, int cubJpegData, CUtlBuffer &bufOutput, int &width, int &height, int *pcubUsed, bool bMakeRGBA ) +{ + // Temporary buffer to decompress Jpeg into as RGB, since libjpeg doesn't do RGBA + CUtlVector<byte> vecRGB; + + bool bConverted = false; + { + VPROF_BUDGET( "ConvertJpegToRGB", VPROF_BUDGETGROUP_OTHER_VGUI ); + bConverted = ConvertJpegToRGB( pubJpegData, cubJpegData, vecRGB, width, height, pcubUsed ); + } + + if ( bConverted ) + { + bufOutput.Clear(); + + int bytesPerPixel = 3; + if ( bMakeRGBA ) + bytesPerPixel = 4; + + // make sure the buffer is big enough to hold the cdnfile data + bufOutput.EnsureCapacity( width * height * bytesPerPixel ); + + // If the buffer was externally allocated the EnsureCapacity can fail + if ( bufOutput.Size() < width * height * bytesPerPixel ) + return false; + + bufOutput.SeekPut( CUtlBuffer::SEEK_HEAD, width*height*bytesPerPixel ); + + // convert RGB->RGBA, into the final buffer + if ( bMakeRGBA ) + { + VPROF_BUDGET( "ConvertRGBToRGBAImage", VPROF_BUDGETGROUP_OTHER_VGUI ); + ConvertRGBToRGBAImage( vecRGB, width, height, (byte *)bufOutput.Base(), width, height ); + } + else + { + Q_memcpy( bufOutput.Base(), vecRGB.Base(), width*height*bytesPerPixel ); + } + + // done + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts raw Jpeg file data and converts to RGBA image buffer +//----------------------------------------------------------------------------- +bool ConvertJpegToRGBA( const byte *pubJpegData, int cubJpegData, CUtlBuffer &bufOutput, int &width, int &height, int *pcubUsed ) +{ + return ConvertJpegToRawInternal( pubJpegData, cubJpegData, bufOutput, width, height, pcubUsed, true ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Converts raw Jpeg file data and converts to RGB image buffer +//----------------------------------------------------------------------------- +bool ConvertJpegToRGB( const byte *pubJpegData, int cubJpegData, CUtlBuffer &bufOutput, int &width, int &height, int *pcubUsed ) +{ + return ConvertJpegToRawInternal( pubJpegData, cubJpegData, bufOutput, width, height, pcubUsed, false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Internal method for converting RGB to Jpeg and writing either +// to disk or to an output buffer +//----------------------------------------------------------------------------- +bool ConvertRGBToJpegInternal( CJpegDestMgr &destMgr, int quality, int width, int height, CUtlBuffer &bufRGB ) +{ + struct jpeg_compress_struct cinfo = {0}; + JSAMPROW row_ptr[1]; + struct jpeg_error_mgr jerr; + + cinfo.err = jpeg_std_error(&jerr); + jerr.error_exit = &CJpegDestMgr::error_exit; + jpeg_create_compress(&cinfo); + cinfo.dest = &destMgr; + + if ( setjmp( destMgr.m_JmpBuf ) == 1 ) + { + jpeg_destroy_compress(&cinfo); + return false; + } + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults( &cinfo ); + cinfo.dct_method = JDCT_FLOAT; + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_start_compress( &cinfo, TRUE ); + + byte *rawBytes = (byte*)bufRGB.Base(); + while( cinfo.next_scanline < cinfo.image_height ) + { + row_ptr[0] = (unsigned char *)rawBytes+(width*3*cinfo.next_scanline); + jpeg_write_scanlines( &cinfo, row_ptr, 1 ); + } + + jpeg_finish_compress( &cinfo ); + jpeg_destroy_compress( &cinfo ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Takes a RGB buffer and writes it to disk as a Jpeg +// Params: qualtity should be 0-100, where 100 is best quality. 80 is generally +// a good value. +//----------------------------------------------------------------------------- +bool ConvertRGBToJpeg( const char *pchFileOut, int quality, int width, int height, CUtlBuffer &bufRGB ) +{ + /* + CFileWriter fileWriter; + if ( !fileWriter.BSetFile( pchFileOut ) ) + return false; + + CJpegDestMgr destMgr( fileWriter ); + return ConvertRGBToJpegInternal( destMgr, quality, width, height, bufRGB ); + */ + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: Takes a RGB buffer and writes it to a buffer as a jpeg +// Params: qualtity should be 0-100, where 100 is best quality. 80 is generally +// a good value. +//----------------------------------------------------------------------------- +bool ConvertRGBToJpeg( CUtlBuffer &bufOutput, int quality, int width, int height, CUtlBuffer &bufRGB ) +{ + CJpegDestMgr destMgr( bufOutput ); + return ConvertRGBToJpegInternal( destMgr, quality, width, height, bufRGB ); +}
\ No newline at end of file |