Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

475 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "jpegloader.h"
  7. #include "tier0/dbg.h"
  8. #include "tier1/utlvector.h"
  9. #include "tier0/vprof.h"
  10. #include "jpeglib/jpeglib.h"
  11. #include <setjmp.h>
  12. //#include "fileio.h"
  13. class CFileWriter
  14. {
  15. };
  16. class CJpegDestMgr : public jpeg_destination_mgr
  17. {
  18. public:
  19. CJpegDestMgr( CFileWriter &refOutputFileWriter )
  20. : m_pOutputFileWriter( &refOutputFileWriter ),
  21. m_pOutputBuffer( NULL )
  22. {
  23. Init();
  24. }
  25. CJpegDestMgr( CUtlBuffer &refOutputBuffer )
  26. : m_pOutputFileWriter( NULL ),
  27. m_pOutputBuffer( &refOutputBuffer )
  28. {
  29. Init();
  30. }
  31. void Init()
  32. {
  33. this->init_destination = &CJpegDestMgr::imp_init_dest;
  34. this->empty_output_buffer = &CJpegDestMgr::imp_empty_output_buffer;
  35. this->term_destination = &CJpegDestMgr::imp_term_destination;
  36. this->next_output_byte = 0;
  37. this->free_in_buffer = 0;
  38. }
  39. static void imp_init_dest( j_compress_ptr cinfo )
  40. {
  41. CJpegDestMgr *pInstance = (CJpegDestMgr*)cinfo->dest;
  42. if ( pInstance->m_pOutputBuffer )
  43. pInstance->m_pOutputBuffer->EnsureCapacity( cinfo->image_width*cinfo->image_height*3 );
  44. const int k_cubMaxTempJpegBuffer = 1024*10;
  45. int cubBufferSize = MIN( cinfo->image_width*cinfo->image_height*3, k_cubMaxTempJpegBuffer );
  46. pInstance->m_Buffer.EnsureCapacity( cubBufferSize );
  47. pInstance->free_in_buffer = pInstance->m_Buffer.Size();
  48. pInstance->next_output_byte = (JOCTET*)pInstance->m_Buffer.Base();
  49. }
  50. static boolean imp_empty_output_buffer( j_compress_ptr cinfo )
  51. {
  52. /*
  53. CJpegDestMgr *pInstance = (CJpegDestMgr*)cinfo->dest;
  54. // Write the entire buffer, ignoring next_output_byte and free_in_buffer as they lie (see docs)
  55. if ( pInstance->m_pOutputFileWriter )
  56. pInstance->m_pOutputFileWriter->Write( pInstance->m_Buffer.Base(), pInstance->m_Buffer.Size() );
  57. else
  58. pInstance->m_pOutputBuffer->Put( pInstance->m_Buffer.Base(), pInstance->m_Buffer.Size() );
  59. pInstance->free_in_buffer = pInstance->m_Buffer.Size();
  60. pInstance->next_output_byte = (JOCTET*)pInstance->m_Buffer.Base();
  61. */
  62. return true;
  63. }
  64. static void imp_term_destination(j_compress_ptr cinfo)
  65. {
  66. /*
  67. CJpegDestMgr *pInstance = (CJpegDestMgr*)cinfo->dest;
  68. // next_output_byte and free_in_buffer don't lie here like in empty_output_buffer
  69. int cubToWrite = (byte*)pInstance->next_output_byte - (byte*)pInstance->m_Buffer.Base();
  70. if ( cubToWrite > 0 )
  71. {
  72. if ( pInstance->m_pOutputFileWriter )
  73. pInstance->m_pOutputFileWriter->Write( pInstance->m_Buffer.Base(), cubToWrite );
  74. else
  75. pInstance->m_pOutputBuffer->Put( pInstance->m_Buffer.Base(), cubToWrite );
  76. }
  77. if ( pInstance->m_pOutputFileWriter )
  78. pInstance->m_pOutputFileWriter->Flush();
  79. */
  80. }
  81. static void error_exit( j_common_ptr cptr )
  82. {
  83. longjmp( m_JmpBuf, 1 );
  84. }
  85. static jmp_buf m_JmpBuf;
  86. private:
  87. CFileWriter *m_pOutputFileWriter;
  88. CUtlBuffer *m_pOutputBuffer;
  89. CUtlBuffer m_Buffer;
  90. };
  91. jmp_buf CJpegDestMgr::m_JmpBuf;
  92. class CJpegSourceMgr : public jpeg_source_mgr
  93. {
  94. public:
  95. CJpegSourceMgr()
  96. {
  97. this->init_source = &CJpegSourceMgr::imp_init_source;
  98. this->fill_input_buffer = &CJpegSourceMgr::imp_fill_input_buffer;
  99. this->skip_input_data = &CJpegSourceMgr::imp_skip_input_data;
  100. this->resync_to_restart = &CJpegSourceMgr::imp_resync_to_restart;
  101. this->term_source = &CJpegSourceMgr::imp_term_source;
  102. this->next_input_byte = 0;
  103. this->bytes_in_buffer = 0;
  104. }
  105. bool Init( const byte *pubData, int cubData )
  106. {
  107. m_Data.AddMultipleToTail( cubData, (char *)pubData );
  108. bytes_in_buffer = m_Data.Count();
  109. next_input_byte = (unsigned char*)m_Data.Base();
  110. return true;
  111. }
  112. static void imp_init_source(j_decompress_ptr cinfo)
  113. {
  114. // We handled everything needed in our own Init() call.
  115. }
  116. static boolean imp_fill_input_buffer(j_decompress_ptr cinfo)
  117. {
  118. // If this is hit it means we are reading a corrupt file. We can't provide more data
  119. // as we provided all of it upfront already.
  120. return 0;
  121. }
  122. static void imp_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
  123. {
  124. // The library is asking us to skip a chunk of data, usually exif header data or such. Need
  125. // to actually do that. The library tries to be robust and skip obviously bad data on its own
  126. // one byte at a time as well, but faster here, and safer as the library can't always do it
  127. // correctly if we fail these calls.
  128. CJpegSourceMgr *pInstance = (CJpegSourceMgr*)cinfo->src;
  129. pInstance->bytes_in_buffer -= num_bytes;
  130. pInstance->next_input_byte += num_bytes;
  131. }
  132. static boolean imp_resync_to_restart(j_decompress_ptr cinfo, int desired)
  133. {
  134. // This happens if the library fails to find a resync marker where expected, we'll then
  135. // use it's default handler. This handler will skip ahead trying to find the next point,
  136. // we could maybe do better going backwards as we have the full buffer, but that's lots more
  137. // work and this will only get hit on a partially corrupt image anyway.
  138. return jpeg_resync_to_restart( cinfo, desired );
  139. }
  140. static void imp_term_source(j_decompress_ptr cinfo)
  141. {
  142. }
  143. static void error_exit( j_common_ptr cptr )
  144. {
  145. longjmp( m_JmpBuf, 1 );
  146. }
  147. static jmp_buf m_JmpBuf;
  148. public:
  149. CUtlVector<char> m_Data;
  150. };
  151. jmp_buf CJpegSourceMgr::m_JmpBuf;
  152. //-----------------------------------------------------------------------------
  153. // Purpose: returns the dimensions of a jpg file from it's contents
  154. //-----------------------------------------------------------------------------
  155. bool GetJpegDimensions( const byte *pubData, int cubData, uint32 &width, uint32 &height )
  156. {
  157. CJpegSourceMgr sourceMgr;
  158. bool bRet = sourceMgr.Init( pubData, cubData );
  159. if ( !bRet )
  160. return false;
  161. // Load the jpeg.
  162. struct jpeg_decompress_struct jpegInfo;
  163. struct jpeg_error_mgr jerr;
  164. memset( &jpegInfo, 0, sizeof( jpegInfo ) );
  165. jpegInfo.err = jpeg_std_error(&jerr);
  166. jerr.error_exit = &CJpegSourceMgr::error_exit;
  167. jpeg_create_decompress(&jpegInfo);
  168. jpegInfo.src = &sourceMgr;
  169. if ( setjmp( sourceMgr.m_JmpBuf ) == 1 )
  170. {
  171. jpeg_destroy_decompress(&jpegInfo);
  172. return false;
  173. }
  174. if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK)
  175. {
  176. jpeg_destroy_decompress(&jpegInfo);
  177. return false;
  178. }
  179. width = jpegInfo.image_width;
  180. height = jpegInfo.image_height;
  181. jpeg_destroy_decompress(&jpegInfo);
  182. return true;
  183. }
  184. //-----------------------------------------------------------------------------
  185. // 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
  186. //-----------------------------------------------------------------------------
  187. bool ConvertJpegToRGB( const byte *pubData, int cubData, CUtlVector<byte> &buf, int &width, int &height, int *pcubUsed )
  188. {
  189. CJpegSourceMgr sourceMgr;
  190. bool bRet = sourceMgr.Init( pubData, cubData );
  191. if ( !bRet )
  192. return false;
  193. if ( pcubUsed )
  194. *pcubUsed = 0;
  195. sourceMgr.bytes_in_buffer = sourceMgr.m_Data.Count();
  196. sourceMgr.next_input_byte = (unsigned char*)sourceMgr.m_Data.Base();
  197. // Load the jpeg.
  198. struct jpeg_decompress_struct jpegInfo;
  199. struct jpeg_error_mgr jerr;
  200. memset( &jpegInfo, 0, sizeof( jpegInfo ) );
  201. jpegInfo.err = jpeg_std_error(&jerr);
  202. jerr.error_exit = &CJpegSourceMgr::error_exit;
  203. jpeg_create_decompress(&jpegInfo);
  204. jpegInfo.src = &sourceMgr;
  205. if ( setjmp( sourceMgr.m_JmpBuf ) == 1 )
  206. {
  207. jpeg_destroy_decompress(&jpegInfo);
  208. return false;
  209. }
  210. if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK)
  211. {
  212. jpeg_destroy_decompress(&jpegInfo);
  213. return false;
  214. }
  215. // start the decompress with the jpeg engine.
  216. if ( !jpeg_start_decompress(&jpegInfo) || jpegInfo.output_components != 3)
  217. {
  218. jpeg_destroy_decompress(&jpegInfo);
  219. return false;
  220. }
  221. // now that we've started the decompress with the jpeg lib, we have the attributes of the
  222. // cdnfile ready to be read out of the decompress struct.
  223. int row_stride = jpegInfo.output_width * jpegInfo.output_components;
  224. int mem_required = jpegInfo.image_height * jpegInfo.image_width * jpegInfo.output_components;
  225. JSAMPROW row_pointer[1];
  226. int cur_row = 0;
  227. width = jpegInfo.output_width;
  228. height = jpegInfo.output_height;
  229. // allocate the memory to read the cdnfile data into.
  230. buf.SetSize( mem_required );
  231. // read in all the scan lines of the cdnfile into our cdnfile data buffer.
  232. bool working = true;
  233. while (working && (jpegInfo.output_scanline < jpegInfo.output_height))
  234. {
  235. row_pointer[0] = &(buf[cur_row * row_stride]);
  236. if ( !jpeg_read_scanlines(&jpegInfo, row_pointer, 1) )
  237. {
  238. working = false;
  239. }
  240. ++cur_row;
  241. }
  242. if (!working)
  243. {
  244. jpeg_destroy_decompress(&jpegInfo);
  245. return false;
  246. }
  247. if ( pcubUsed )
  248. *pcubUsed = (int)((byte*)sourceMgr.next_input_byte - (byte*)sourceMgr.m_Data.Base());
  249. jpeg_finish_decompress(&jpegInfo);
  250. jpeg_destroy_decompress(&jpegInfo);
  251. return true;
  252. }
  253. //-----------------------------------------------------------------------------
  254. // Purpose: Converts an RGB image to RGBA opacity will be 100%
  255. //-----------------------------------------------------------------------------
  256. void ConvertRGBToRGBAImage( CUtlVector<unsigned char> &srcData,
  257. int srcWidth,
  258. int srcHeight,
  259. byte *destData,
  260. int destWidth,
  261. int destHeight )
  262. {
  263. int destPixelSize = 4;
  264. int cub = destWidth * destHeight * destPixelSize;
  265. byte *pSrc = srcData.Base();
  266. for ( int i = 0; i < cub; i += 4 )
  267. {
  268. destData[i] = *pSrc++;
  269. destData[i+1] = *pSrc++;
  270. destData[i+2] = *pSrc++;
  271. destData[i+3] = 255;
  272. }
  273. }
  274. //-----------------------------------------------------------------------------
  275. // Purpose: Converts raw Jpeg file data and converts to RGBA image buffer
  276. //-----------------------------------------------------------------------------
  277. bool ConvertJpegToRawInternal( const byte *pubJpegData, int cubJpegData, CUtlBuffer &bufOutput, int &width, int &height, int *pcubUsed, bool bMakeRGBA )
  278. {
  279. // Temporary buffer to decompress Jpeg into as RGB, since libjpeg doesn't do RGBA
  280. CUtlVector<byte> vecRGB;
  281. bool bConverted = false;
  282. {
  283. VPROF_BUDGET( "ConvertJpegToRGB", VPROF_BUDGETGROUP_OTHER_VGUI );
  284. bConverted = ConvertJpegToRGB( pubJpegData, cubJpegData, vecRGB, width, height, pcubUsed );
  285. }
  286. if ( bConverted )
  287. {
  288. bufOutput.Clear();
  289. int bytesPerPixel = 3;
  290. if ( bMakeRGBA )
  291. bytesPerPixel = 4;
  292. // make sure the buffer is big enough to hold the cdnfile data
  293. bufOutput.EnsureCapacity( width * height * bytesPerPixel );
  294. // If the buffer was externally allocated the EnsureCapacity can fail
  295. if ( bufOutput.Size() < width * height * bytesPerPixel )
  296. return false;
  297. bufOutput.SeekPut( CUtlBuffer::SEEK_HEAD, width*height*bytesPerPixel );
  298. // convert RGB->RGBA, into the final buffer
  299. if ( bMakeRGBA )
  300. {
  301. VPROF_BUDGET( "ConvertRGBToRGBAImage", VPROF_BUDGETGROUP_OTHER_VGUI );
  302. ConvertRGBToRGBAImage( vecRGB, width, height, (byte *)bufOutput.Base(), width, height );
  303. }
  304. else
  305. {
  306. Q_memcpy( bufOutput.Base(), vecRGB.Base(), width*height*bytesPerPixel );
  307. }
  308. // done
  309. return true;
  310. }
  311. return false;
  312. }
  313. //-----------------------------------------------------------------------------
  314. // Purpose: Converts raw Jpeg file data and converts to RGBA image buffer
  315. //-----------------------------------------------------------------------------
  316. bool ConvertJpegToRGBA( const byte *pubJpegData, int cubJpegData, CUtlBuffer &bufOutput, int &width, int &height, int *pcubUsed )
  317. {
  318. return ConvertJpegToRawInternal( pubJpegData, cubJpegData, bufOutput, width, height, pcubUsed, true );
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Converts raw Jpeg file data and converts to RGB image buffer
  322. //-----------------------------------------------------------------------------
  323. bool ConvertJpegToRGB( const byte *pubJpegData, int cubJpegData, CUtlBuffer &bufOutput, int &width, int &height, int *pcubUsed )
  324. {
  325. return ConvertJpegToRawInternal( pubJpegData, cubJpegData, bufOutput, width, height, pcubUsed, false );
  326. }
  327. //-----------------------------------------------------------------------------
  328. // Purpose: Internal method for converting RGB to Jpeg and writing either
  329. // to disk or to an output buffer
  330. //-----------------------------------------------------------------------------
  331. bool ConvertRGBToJpegInternal( CJpegDestMgr &destMgr, int quality, int width, int height, CUtlBuffer &bufRGB )
  332. {
  333. struct jpeg_compress_struct cinfo = {0};
  334. JSAMPROW row_ptr[1];
  335. struct jpeg_error_mgr jerr;
  336. cinfo.err = jpeg_std_error(&jerr);
  337. jerr.error_exit = &CJpegDestMgr::error_exit;
  338. jpeg_create_compress(&cinfo);
  339. cinfo.dest = &destMgr;
  340. if ( setjmp( destMgr.m_JmpBuf ) == 1 )
  341. {
  342. jpeg_destroy_compress(&cinfo);
  343. return false;
  344. }
  345. cinfo.image_width = width;
  346. cinfo.image_height = height;
  347. cinfo.input_components = 3;
  348. cinfo.in_color_space = JCS_RGB;
  349. jpeg_set_defaults( &cinfo );
  350. cinfo.dct_method = JDCT_FLOAT;
  351. jpeg_set_quality(&cinfo, quality, TRUE);
  352. jpeg_start_compress( &cinfo, TRUE );
  353. byte *rawBytes = (byte*)bufRGB.Base();
  354. while( cinfo.next_scanline < cinfo.image_height )
  355. {
  356. row_ptr[0] = (unsigned char *)rawBytes+(width*3*cinfo.next_scanline);
  357. jpeg_write_scanlines( &cinfo, row_ptr, 1 );
  358. }
  359. jpeg_finish_compress( &cinfo );
  360. jpeg_destroy_compress( &cinfo );
  361. return true;
  362. }
  363. //-----------------------------------------------------------------------------
  364. // Purpose: Takes a RGB buffer and writes it to disk as a Jpeg
  365. // Params: qualtity should be 0-100, where 100 is best quality. 80 is generally
  366. // a good value.
  367. //-----------------------------------------------------------------------------
  368. bool ConvertRGBToJpeg( const char *pchFileOut, int quality, int width, int height, CUtlBuffer &bufRGB )
  369. {
  370. /*
  371. CFileWriter fileWriter;
  372. if ( !fileWriter.BSetFile( pchFileOut ) )
  373. return false;
  374. CJpegDestMgr destMgr( fileWriter );
  375. return ConvertRGBToJpegInternal( destMgr, quality, width, height, bufRGB );
  376. */
  377. return false;
  378. }
  379. //-----------------------------------------------------------------------------
  380. // Purpose: Takes a RGB buffer and writes it to a buffer as a jpeg
  381. // Params: qualtity should be 0-100, where 100 is best quality. 80 is generally
  382. // a good value.
  383. //-----------------------------------------------------------------------------
  384. bool ConvertRGBToJpeg( CUtlBuffer &bufOutput, int quality, int width, int height, CUtlBuffer &bufRGB )
  385. {
  386. CJpegDestMgr destMgr( bufOutput );
  387. return ConvertRGBToJpegInternal( destMgr, quality, width, height, bufRGB );
  388. }