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.

236 lines
6.8 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "pngloader.h"
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. //#include "vstdlib/strtools.h"
  10. #include <setjmp.h>
  11. // clang3 on OSX folks the attribute into the prototype, causing a compile failure
  12. // filed radar bug 10397783
  13. #if ( __clang_major__ == 3 )
  14. extern void longjmp( jmp_buf, int ) __attribute__((noreturn));
  15. #endif
  16. #include "libpng/png.h"
  17. #include "libpng/pngstruct.h"
  18. #include "tier0/dbg.h"
  19. //
  20. // struct to encapsulate png data in memory that we are walking through
  21. //
  22. struct PngDataStream_t
  23. {
  24. PngDataStream_t( const byte *pchData, int cbData )
  25. {
  26. m_pPNGData = pchData;
  27. m_cbPNGData = cbData;
  28. m_iPNGData = 0;
  29. }
  30. const byte *m_pPNGData; // the png data
  31. uint32 m_cbPNGData; // length of the data
  32. uint32 m_iPNGData; // offset for next read
  33. };
  34. // helper function to nibble some more png data from our memory buffer
  35. void ReadPNGData( png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead )
  36. {
  37. if(png_ptr->io_ptr == NULL)
  38. return; // we didn't have our mem buffer set
  39. PngDataStream_t *pData = (PngDataStream_t *)png_ptr->io_ptr;
  40. if ( byteCountToRead + pData->m_iPNGData > pData->m_cbPNGData )
  41. return;
  42. Q_memcpy( outBytes, pData->m_pPNGData + pData->m_iPNGData, byteCountToRead );
  43. pData->m_iPNGData += byteCountToRead;
  44. }
  45. // called when we failed to load a png file, we just bail to the previously set exit
  46. static void PNGErrorFunction(png_structp png_ptr, png_const_charp msg)
  47. {
  48. Warning( "PNG load error %s\n", msg );
  49. longjmp( png_jmpbuf(png_ptr), 1 );
  50. }
  51. // we had a warning lets log it
  52. static void PNGWarningFunction(png_structp png_ptr, png_const_charp msg)
  53. {
  54. Warning( "PNG load error %s\n", msg );
  55. }
  56. // Get dimensions out of PNG header
  57. bool GetPNGDimensions( const byte *pubPNGData, int cubPNGData, uint32 &width, uint32 &height )
  58. {
  59. png_const_bytep pngData = (png_const_bytep)pubPNGData;
  60. if (png_sig_cmp( pngData, 0, 8))
  61. return false; /* bad signature */
  62. PngDataStream_t pngDataStream( pubPNGData, cubPNGData ); // keeps a local copy of the data we a reading
  63. png_structp png_ptr = NULL;
  64. png_infop info_ptr = NULL;
  65. png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, &PNGErrorFunction, &PNGWarningFunction );
  66. if (!png_ptr)
  67. return false; /* out of memory */
  68. info_ptr = png_create_info_struct( png_ptr );
  69. if ( !info_ptr )
  70. {
  71. png_destroy_read_struct(&png_ptr, NULL, NULL);
  72. return false; /* out of memory */
  73. }
  74. /* setjmp() must be called in every function that calls a PNG-reading
  75. * libpng function */
  76. if ( setjmp( png_jmpbuf(png_ptr) ) )
  77. {
  78. png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
  79. return false;
  80. }
  81. png_set_read_fn( png_ptr, (void *)&pngDataStream, ReadPNGData );
  82. png_read_info( png_ptr, info_ptr ); /* read all PNG info up to image data */
  83. width = png_get_image_width( png_ptr, info_ptr );
  84. height = png_get_image_height( png_ptr, info_ptr );
  85. png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
  86. return true;
  87. }
  88. // given a png formatted memory buffer return a raw rgba buffer
  89. bool ConvertPNGToRGBA( const byte *pubPNGData, int cubPNGData, CUtlBuffer &bufOutput, int &width, int &height )
  90. {
  91. png_const_bytep pngData = (png_const_bytep)pubPNGData;
  92. if (png_sig_cmp( pngData, 0, 8))
  93. return false; /* bad signature */
  94. PngDataStream_t pngDataStream( pubPNGData, cubPNGData ); // keeps a local copy of the data we a reading
  95. png_structp png_ptr = NULL;
  96. png_infop info_ptr = NULL;
  97. /* could pass pointers to user-defined error handlers instead of NULLs: */
  98. png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, &PNGErrorFunction, &PNGWarningFunction );
  99. if (!png_ptr)
  100. return false; /* out of memory */
  101. info_ptr = png_create_info_struct( png_ptr );
  102. if ( !info_ptr )
  103. {
  104. png_destroy_read_struct(&png_ptr, NULL, NULL);
  105. return false; /* out of memory */
  106. }
  107. /* setjmp() must be called in every function that calls a PNG-reading
  108. * libpng function */
  109. if ( setjmp( png_jmpbuf(png_ptr) ) )
  110. {
  111. png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
  112. return false;
  113. }
  114. png_set_read_fn( png_ptr, (void *)&pngDataStream, ReadPNGData );
  115. png_read_info( png_ptr, info_ptr ); /* read all PNG info up to image data */
  116. /* alternatively, could make separate calls to png_get_image_width(),
  117. * etc., but want bit_depth and color_type for later [don't care about
  118. * compression_type and filter_type => NULLs] */
  119. int bit_depth;
  120. int color_type;
  121. uint32 png_width;
  122. uint32 png_height;
  123. png_get_IHDR( png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, NULL, NULL, NULL );
  124. width = png_width;
  125. height = png_height;
  126. png_uint_32 rowbytes;
  127. /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
  128. * transparency chunks to full alpha channel; strip 16-bit-per-sample
  129. * images to 8 bits per sample; and convert grayscale to RGB[A] */
  130. if (color_type == PNG_COLOR_TYPE_PALETTE)
  131. png_set_expand( png_ptr );
  132. if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
  133. png_set_expand( png_ptr );
  134. if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) )
  135. png_set_expand( png_ptr );
  136. if (bit_depth == 16)
  137. png_set_strip_16( png_ptr );
  138. if (color_type == PNG_COLOR_TYPE_GRAY ||
  139. color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
  140. png_set_gray_to_rgb( png_ptr );
  141. /*
  142. double gamma;
  143. if (png_get_gAMA(png_ptr, info_ptr, &gamma))
  144. png_set_gamma(png_ptr, display_exponent, gamma);
  145. */
  146. /* all transformations have been registered; now update info_ptr data,
  147. * get rowbytes and channels, and allocate image memory */
  148. png_read_update_info( png_ptr, info_ptr );
  149. rowbytes = png_get_rowbytes( png_ptr, info_ptr );
  150. png_byte channels = (int)png_get_channels( png_ptr, info_ptr );
  151. Assert( channels == 4 );
  152. if ( channels != 4 )
  153. return false;
  154. png_bytepp row_pointers = NULL;
  155. if ( ( row_pointers = (png_bytepp)malloc(height*sizeof(png_bytep) ) ) == NULL )
  156. {
  157. png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
  158. return false;
  159. }
  160. bufOutput.EnsureCapacity( rowbytes*height );
  161. bufOutput.SeekPut( CUtlBuffer::SEEK_HEAD, rowbytes*height );
  162. /* set the individual row_pointers to point at the correct offsets */
  163. for ( int i = 0; i < height; ++i)
  164. row_pointers[i] = (png_byte *)bufOutput.Base() + i*rowbytes;
  165. /* now we can go ahead and just read the whole image */
  166. png_read_image( png_ptr, row_pointers );
  167. free( row_pointers );
  168. row_pointers = NULL;
  169. png_read_end(png_ptr, NULL);
  170. if ( png_ptr && info_ptr )
  171. {
  172. png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
  173. png_ptr = NULL;
  174. info_ptr = NULL;
  175. }
  176. return true;
  177. }