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.

2301 lines
62 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // NOTE: To make use of this file, g_pFullFileSystem must be defined, or you can modify
  4. // this source to take an IFileSystem * as input.
  5. //
  6. //=======================================================================================//
  7. // @note Tom Bui: we need to use fopen below in the jpeg code, so we can't have this on...
  8. #ifdef PROTECTED_THINGS_ENABLE
  9. #if !defined( POSIX )
  10. #undef fopen
  11. #endif // POSIX
  12. #endif
  13. #if defined( WIN32 ) && !defined( _X360 )
  14. #include <windows.h> // SRC only!!
  15. #elif defined( POSIX )
  16. #include <stdio.h>
  17. #include <sys/stat.h>
  18. #ifdef OSX
  19. #include <copyfile.h>
  20. #endif
  21. #endif
  22. #include "imageutils.h"
  23. #include "filesystem.h"
  24. #include "utlbuffer.h"
  25. #include "bitmap/bitmap.h"
  26. #include "vtf/vtf.h"
  27. // clang3 on OSX folks the attribute into the prototype, causing a compile failure
  28. // filed radar bug 10397783
  29. #if ( __clang_major__ == 3 )
  30. #include <setjmp.h>
  31. extern void longjmp( jmp_buf, int ) __attribute__((noreturn));
  32. #endif
  33. #ifdef ENGINE_DLL
  34. #include "common.h"
  35. #elif CLIENT_DLL
  36. // @note Tom Bui: instead of forcing the project to include EngineInterface.h...
  37. #include "cdll_int.h"
  38. // engine interface singleton accessors
  39. extern IVEngineClient *engine;
  40. extern class IGameUIFuncs *gameuifuncs;
  41. extern class IEngineSound *enginesound;
  42. extern class IMatchmaking *matchmaking;
  43. extern class IXboxSystem *xboxsystem;
  44. extern class IAchievementMgr *achievementmgr;
  45. extern class CSteamAPIContext *steamapicontext;
  46. #elif REPLAY_DLL
  47. #include "replay/ienginereplay.h"
  48. extern IEngineReplay *g_pEngine;
  49. #elif ENGINE_DLL
  50. #include "EngineInterface.h"
  51. #else
  52. #include "cdll_int.h"
  53. extern IVEngineClient *engine;
  54. #endif
  55. // use the JPEGLIB_USE_STDIO define so that we can read in jpeg's from outside the game directory tree.
  56. #define JPEGLIB_USE_STDIO
  57. #include "jpeglib/jpeglib.h"
  58. #undef JPEGLIB_USE_STDIO
  59. #include "libpng/png.h"
  60. #include <setjmp.h>
  61. #include "bitmap/tgawriter.h"
  62. #include "ivtex.h"
  63. #ifdef WIN32
  64. #include <io.h>
  65. #endif
  66. #ifdef OSX
  67. #include <copyfile.h>
  68. #endif
  69. #ifndef WIN32
  70. #define DeleteFile(s) remove(s)
  71. #endif
  72. #if defined( _X360 )
  73. #include "xbox/xbox_win32stubs.h"
  74. #endif
  75. // memdbgon must be the last include file in a .cpp file!!!
  76. #include <tier0/memdbgon.h>
  77. //-----------------------------------------------------------------------------
  78. // Purpose:
  79. //-----------------------------------------------------------------------------
  80. struct ValveJpegErrorHandler_t
  81. {
  82. // The default manager
  83. struct jpeg_error_mgr m_Base;
  84. // For handling any errors
  85. jmp_buf m_ErrorContext;
  86. };
  87. #define JPEG_OUTPUT_BUF_SIZE 4096
  88. struct JPEGDestinationManager_t
  89. {
  90. struct jpeg_destination_mgr pub; // public fields
  91. CUtlBuffer *pBuffer; // target/final buffer
  92. byte *buffer; // start of temp buffer
  93. };
  94. //-----------------------------------------------------------------------------
  95. // Purpose: We'll override the default error handler so we can deal with errors without having to exit the engine
  96. //-----------------------------------------------------------------------------
  97. static void ValveJpegErrorHandler( j_common_ptr cinfo )
  98. {
  99. ValveJpegErrorHandler_t *pError = reinterpret_cast< ValveJpegErrorHandler_t * >( cinfo->err );
  100. char buffer[ JMSG_LENGTH_MAX ];
  101. /* Create the message */
  102. ( *cinfo->err->format_message )( cinfo, buffer );
  103. Warning( "%s\n", buffer );
  104. // Bail
  105. longjmp( pError->m_ErrorContext, 1 );
  106. }
  107. // convert the JPEG file given to a TGA file at the given output path.
  108. ConversionErrorType ImgUtl_ConvertJPEGToTGA( const char *jpegpath, const char *tgaPath, bool bRequirePowerOfTwo )
  109. {
  110. #if !defined( _X360 )
  111. //
  112. // !FIXME! This really probably should use ImgUtl_ReadJPEGAsRGBA, to avoid duplicated code.
  113. //
  114. struct jpeg_decompress_struct jpegInfo;
  115. struct ValveJpegErrorHandler_t jerr;
  116. JSAMPROW row_pointer[1];
  117. int row_stride;
  118. int cur_row = 0;
  119. // image attributes
  120. int image_height;
  121. int image_width;
  122. // open the jpeg image file.
  123. FILE *infile = fopen(jpegpath, "rb");
  124. if (infile == NULL)
  125. {
  126. return CE_CANT_OPEN_SOURCE_FILE;
  127. }
  128. // setup error to print to stderr.
  129. jpegInfo.err = jpeg_std_error(&jerr.m_Base);
  130. jpegInfo.err->error_exit = &ValveJpegErrorHandler;
  131. // create the decompress struct.
  132. jpeg_create_decompress(&jpegInfo);
  133. if ( setjmp( jerr.m_ErrorContext ) )
  134. {
  135. // Get here if there is any error
  136. jpeg_destroy_decompress( &jpegInfo );
  137. fclose(infile);
  138. return CE_ERROR_PARSING_SOURCE;
  139. }
  140. jpeg_stdio_src(&jpegInfo, infile);
  141. // read in the jpeg header and make sure that's all good.
  142. if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK)
  143. {
  144. fclose(infile);
  145. return CE_ERROR_PARSING_SOURCE;
  146. }
  147. // start the decompress with the jpeg engine.
  148. if ( !jpeg_start_decompress(&jpegInfo) )
  149. {
  150. jpeg_destroy_decompress(&jpegInfo);
  151. fclose(infile);
  152. return CE_ERROR_PARSING_SOURCE;
  153. }
  154. // Check for valid width and height (ie. power of 2 and print out an error and exit if not).
  155. if ( ( bRequirePowerOfTwo && ( !IsPowerOfTwo(jpegInfo.image_height) || !IsPowerOfTwo(jpegInfo.image_width) ) )
  156. || jpegInfo.output_components != 3 )
  157. {
  158. jpeg_destroy_decompress(&jpegInfo);
  159. fclose( infile );
  160. return CE_SOURCE_FILE_SIZE_NOT_SUPPORTED;
  161. }
  162. // now that we've started the decompress with the jpeg lib, we have the attributes of the
  163. // image ready to be read out of the decompress struct.
  164. row_stride = jpegInfo.output_width * jpegInfo.output_components;
  165. image_height = jpegInfo.image_height;
  166. image_width = jpegInfo.image_width;
  167. int mem_required = jpegInfo.image_height * jpegInfo.image_width * jpegInfo.output_components;
  168. // allocate the memory to read the image data into.
  169. unsigned char *buf = (unsigned char *)malloc(mem_required);
  170. if (buf == NULL)
  171. {
  172. jpeg_destroy_decompress(&jpegInfo);
  173. fclose(infile);
  174. return CE_MEMORY_ERROR;
  175. }
  176. // read in all the scan lines of the image into our image data buffer.
  177. bool working = true;
  178. while (working && (jpegInfo.output_scanline < jpegInfo.output_height))
  179. {
  180. row_pointer[0] = &(buf[cur_row * row_stride]);
  181. if ( !jpeg_read_scanlines(&jpegInfo, row_pointer, 1) )
  182. {
  183. working = false;
  184. }
  185. ++cur_row;
  186. }
  187. if (!working)
  188. {
  189. free(buf);
  190. jpeg_destroy_decompress(&jpegInfo);
  191. fclose(infile);
  192. return CE_ERROR_PARSING_SOURCE;
  193. }
  194. jpeg_finish_decompress(&jpegInfo);
  195. fclose(infile);
  196. // ok, at this point we have read in the JPEG image to our buffer, now we need to write it out as a TGA file.
  197. CUtlBuffer outBuf;
  198. bool bRetVal = TGAWriter::WriteToBuffer( buf, outBuf, image_width, image_height, IMAGE_FORMAT_RGB888, IMAGE_FORMAT_RGB888 );
  199. if ( bRetVal )
  200. {
  201. if ( !g_pFullFileSystem->WriteFile( tgaPath, NULL, outBuf ) )
  202. {
  203. bRetVal = false;
  204. }
  205. }
  206. free(buf);
  207. return bRetVal ? CE_SUCCESS : CE_ERROR_WRITING_OUTPUT_FILE;
  208. #else
  209. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  210. #endif
  211. }
  212. // convert the bmp file given to a TGA file at the given destination path.
  213. ConversionErrorType ImgUtl_ConvertBMPToTGA(const char *bmpPath, const char *tgaPath)
  214. {
  215. if ( !IsPC() )
  216. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  217. #ifdef WIN32
  218. int nWidth, nHeight;
  219. ConversionErrorType result;
  220. unsigned char *pBufRGBA = ImgUtl_ReadBMPAsRGBA( bmpPath, nWidth, nHeight, result );
  221. if ( result != CE_SUCCESS)
  222. {
  223. Assert( !pBufRGBA );
  224. free( pBufRGBA );
  225. return result;
  226. }
  227. Assert( pBufRGBA );
  228. // write out the TGA file using the RGB data buffer.
  229. CUtlBuffer outBuf;
  230. bool retval = TGAWriter::WriteToBuffer(pBufRGBA, outBuf, nWidth, nHeight, IMAGE_FORMAT_RGBA8888, IMAGE_FORMAT_RGB888);
  231. free( pBufRGBA );
  232. if ( retval )
  233. {
  234. if ( !g_pFullFileSystem->WriteFile( tgaPath, NULL, outBuf ) )
  235. {
  236. retval = false;
  237. }
  238. }
  239. return retval ? CE_SUCCESS : CE_ERROR_WRITING_OUTPUT_FILE;
  240. #else // WIN32
  241. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  242. #endif
  243. }
  244. unsigned char *ImgUtl_ReadVTFAsRGBA( const char *vtfPath, int &width, int &height, ConversionErrorType &errcode )
  245. {
  246. // Just load the whole file into a memory buffer
  247. CUtlBuffer bufFileContents;
  248. if ( !g_pFullFileSystem->ReadFile( vtfPath, NULL, bufFileContents ) )
  249. {
  250. errcode = CE_CANT_OPEN_SOURCE_FILE;
  251. return NULL;
  252. }
  253. IVTFTexture *pVTFTexture = CreateVTFTexture();
  254. if ( !pVTFTexture->Unserialize( bufFileContents ) )
  255. {
  256. DestroyVTFTexture( pVTFTexture );
  257. errcode = CE_ERROR_PARSING_SOURCE;
  258. return NULL;
  259. }
  260. width = pVTFTexture->Width();
  261. height = pVTFTexture->Height();
  262. pVTFTexture->ConvertImageFormat( IMAGE_FORMAT_RGBA8888, false );
  263. int nMemSize = ImageLoader::GetMemRequired( width, height, 1, IMAGE_FORMAT_RGBA8888, false );
  264. unsigned char *pMemImage = (unsigned char *)malloc(nMemSize);
  265. if ( pMemImage == NULL )
  266. {
  267. DestroyVTFTexture( pVTFTexture );
  268. errcode = CE_MEMORY_ERROR;
  269. return NULL;
  270. }
  271. Q_memcpy( pMemImage, pVTFTexture->ImageData(), nMemSize );
  272. DestroyVTFTexture( pVTFTexture );
  273. errcode = CE_SUCCESS;
  274. return pMemImage;
  275. }
  276. // read a TGA header from the current point in the file stream.
  277. static void ImgUtl_ReadTGAHeader(FILE *infile, TGAHeader &header)
  278. {
  279. if (infile == NULL)
  280. {
  281. return;
  282. }
  283. fread(&header.identsize, sizeof(header.identsize), 1, infile);
  284. fread(&header.colourmaptype, sizeof(header.colourmaptype), 1, infile);
  285. fread(&header.imagetype, sizeof(header.imagetype), 1, infile);
  286. fread(&header.colourmapstart, sizeof(header.colourmapstart), 1, infile);
  287. fread(&header.colourmaplength, sizeof(header.colourmaplength), 1, infile);
  288. fread(&header.colourmapbits, sizeof(header.colourmapbits), 1, infile);
  289. fread(&header.xstart, sizeof(header.xstart), 1, infile);
  290. fread(&header.ystart, sizeof(header.ystart), 1, infile);
  291. fread(&header.width, sizeof(header.width), 1, infile);
  292. fread(&header.height, sizeof(header.height), 1, infile);
  293. fread(&header.bits, sizeof(header.bits), 1, infile);
  294. fread(&header.descriptor, sizeof(header.descriptor), 1, infile);
  295. }
  296. // write a TGA header to the current point in the file stream.
  297. static void WriteTGAHeader(FILE *outfile, TGAHeader &header)
  298. {
  299. if (outfile == NULL)
  300. {
  301. return;
  302. }
  303. fwrite(&header.identsize, sizeof(header.identsize), 1, outfile);
  304. fwrite(&header.colourmaptype, sizeof(header.colourmaptype), 1, outfile);
  305. fwrite(&header.imagetype, sizeof(header.imagetype), 1, outfile);
  306. fwrite(&header.colourmapstart, sizeof(header.colourmapstart), 1, outfile);
  307. fwrite(&header.colourmaplength, sizeof(header.colourmaplength), 1, outfile);
  308. fwrite(&header.colourmapbits, sizeof(header.colourmapbits), 1, outfile);
  309. fwrite(&header.xstart, sizeof(header.xstart), 1, outfile);
  310. fwrite(&header.ystart, sizeof(header.ystart), 1, outfile);
  311. fwrite(&header.width, sizeof(header.width), 1, outfile);
  312. fwrite(&header.height, sizeof(header.height), 1, outfile);
  313. fwrite(&header.bits, sizeof(header.bits), 1, outfile);
  314. fwrite(&header.descriptor, sizeof(header.descriptor), 1, outfile);
  315. }
  316. // reads in a TGA file and converts it to 32 bit RGBA color values in a memory buffer.
  317. unsigned char * ImgUtl_ReadTGAAsRGBA(const char *tgaPath, int &width, int &height, ConversionErrorType &errcode, TGAHeader &tgaHeader )
  318. {
  319. FILE *tgaFile = fopen(tgaPath, "rb");
  320. if (tgaFile == NULL)
  321. {
  322. errcode = CE_CANT_OPEN_SOURCE_FILE;
  323. return NULL;
  324. }
  325. // read header for TGA file.
  326. ImgUtl_ReadTGAHeader(tgaFile, tgaHeader);
  327. if (
  328. ( tgaHeader.imagetype != 2 ) // image type 2 is uncompressed RGB, other types not supported.
  329. || ( tgaHeader.descriptor & 0x10 ) // Origin on righthand side (flipped horizontally from common sense) --- nobody ever uses this
  330. || ( tgaHeader.bits != 24 && tgaHeader.bits != 32 ) // Must be 24- ot 32-bit
  331. )
  332. {
  333. fclose(tgaFile);
  334. errcode = CE_SOURCE_FILE_TGA_FORMAT_NOT_SUPPORTED;
  335. return NULL;
  336. }
  337. int tgaDataSize = tgaHeader.width * tgaHeader.height * tgaHeader.bits / 8;
  338. unsigned char *tgaData = (unsigned char *)malloc(tgaDataSize);
  339. if (tgaData == NULL)
  340. {
  341. fclose(tgaFile);
  342. errcode = CE_MEMORY_ERROR;
  343. return NULL;
  344. }
  345. fread(tgaData, 1, tgaDataSize, tgaFile);
  346. fclose(tgaFile);
  347. width = tgaHeader.width;
  348. height = tgaHeader.height;
  349. int numPixels = tgaHeader.width * tgaHeader.height;
  350. if (tgaHeader.bits == 24)
  351. {
  352. // image needs to be converted to a 32-bit image.
  353. unsigned char *retBuf = (unsigned char *)malloc(numPixels * 4);
  354. if (retBuf == NULL)
  355. {
  356. free(tgaData);
  357. errcode = CE_MEMORY_ERROR;
  358. return NULL;
  359. }
  360. // convert from BGR to RGBA color format.
  361. for (int index = 0; index < numPixels; ++index)
  362. {
  363. retBuf[index * 4] = tgaData[index * 3 + 2];
  364. retBuf[index * 4 + 1] = tgaData[index * 3 + 1];
  365. retBuf[index * 4 + 2] = tgaData[index * 3];
  366. retBuf[index * 4 + 3] = 0xff;
  367. }
  368. free(tgaData);
  369. tgaData = retBuf;
  370. tgaHeader.bits = 32;
  371. }
  372. else if (tgaHeader.bits == 32)
  373. {
  374. // Swap blue and red to convert BGR -> RGB
  375. for (int index = 0; index < numPixels; ++index)
  376. {
  377. V_swap( tgaData[index*4], tgaData[index*4 + 2] );
  378. }
  379. }
  380. // Flip image vertically if necessary
  381. if ( !( tgaHeader.descriptor & 0x20 ) )
  382. {
  383. int y0 = 0;
  384. int y1 = height-1;
  385. int iStride = width*4;
  386. while ( y0 < y1 )
  387. {
  388. unsigned char *ptr0 = tgaData + y0*iStride;
  389. unsigned char *ptr1 = tgaData + y1*iStride;
  390. for ( int i = 0 ; i < iStride ; ++i )
  391. {
  392. V_swap( ptr0[i], ptr1[i] );
  393. }
  394. ++y0;
  395. --y1;
  396. }
  397. tgaHeader.descriptor |= 0x20;
  398. }
  399. errcode = CE_SUCCESS;
  400. return tgaData;
  401. }
  402. unsigned char *ImgUtl_ReadJPEGAsRGBA( const char *jpegPath, int &width, int &height, ConversionErrorType &errcode )
  403. {
  404. #if !defined( _X360 )
  405. struct jpeg_decompress_struct jpegInfo;
  406. struct ValveJpegErrorHandler_t jerr;
  407. JSAMPROW row_pointer[1];
  408. int row_stride;
  409. int cur_row = 0;
  410. // image attributes
  411. int image_height;
  412. int image_width;
  413. // open the jpeg image file.
  414. FILE *infile = fopen(jpegPath, "rb");
  415. if (infile == NULL)
  416. {
  417. errcode = CE_CANT_OPEN_SOURCE_FILE;
  418. return NULL;
  419. }
  420. //CJpegSourceMgr src;
  421. //FileHandle_t fileHandle = g_pFullFileSystem->Open( jpegPath, "rb" );
  422. //if ( fileHandle == FILESYSTEM_INVALID_HANDLE )
  423. //{
  424. // errcode = CE_CANT_OPEN_SOURCE_FILE;
  425. // return NULL;
  426. //}
  427. //if ( !src.Init( g_pFullFileSystem, fileHandle ) ) {
  428. // errcode = CE_CANT_OPEN_SOURCE_FILE;
  429. // g_pFullFileSystem->Close( fileHandle );
  430. // return NULL;
  431. //}
  432. // setup error to print to stderr.
  433. memset( &jpegInfo, 0, sizeof( jpegInfo ) );
  434. jpegInfo.err = jpeg_std_error(&jerr.m_Base);
  435. jpegInfo.err->error_exit = &ValveJpegErrorHandler;
  436. // create the decompress struct.
  437. jpeg_create_decompress(&jpegInfo);
  438. if ( setjmp( jerr.m_ErrorContext ) )
  439. {
  440. // Get here if there is any error
  441. jpeg_destroy_decompress( &jpegInfo );
  442. fclose( infile );
  443. //g_pFullFileSystem->Close( fileHandle );
  444. errcode = CE_ERROR_PARSING_SOURCE;
  445. return NULL;
  446. }
  447. jpeg_stdio_src(&jpegInfo, infile);
  448. //jpegInfo.src = &src;
  449. // read in the jpeg header and make sure that's all good.
  450. if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK)
  451. {
  452. fclose( infile );
  453. //g_pFullFileSystem->Close( fileHandle );
  454. errcode = CE_ERROR_PARSING_SOURCE;
  455. return NULL;
  456. }
  457. // start the decompress with the jpeg engine.
  458. if ( !jpeg_start_decompress(&jpegInfo) )
  459. {
  460. jpeg_destroy_decompress(&jpegInfo);
  461. fclose( infile );
  462. //g_pFullFileSystem->Close( fileHandle );
  463. errcode = CE_ERROR_PARSING_SOURCE;
  464. return NULL;
  465. }
  466. // We only support 24-bit JPEG's
  467. if ( jpegInfo.out_color_space != JCS_RGB || jpegInfo.output_components != 3 )
  468. {
  469. jpeg_destroy_decompress(&jpegInfo);
  470. fclose( infile );
  471. //g_pFullFileSystem->Close( fileHandle );
  472. errcode = CE_SOURCE_FILE_SIZE_NOT_SUPPORTED;
  473. return NULL;
  474. }
  475. // now that we've started the decompress with the jpeg lib, we have the attributes of the
  476. // image ready to be read out of the decompress struct.
  477. row_stride = jpegInfo.output_width * 4;
  478. image_height = jpegInfo.image_height;
  479. image_width = jpegInfo.image_width;
  480. int mem_required = jpegInfo.image_height * row_stride;
  481. // allocate the memory to read the image data into.
  482. unsigned char *buf = (unsigned char *)malloc(mem_required);
  483. if (buf == NULL)
  484. {
  485. jpeg_destroy_decompress(&jpegInfo);
  486. fclose( infile );
  487. //g_pFullFileSystem->Close( fileHandle );
  488. errcode = CE_MEMORY_ERROR;
  489. return NULL;
  490. }
  491. // read in all the scan lines of the image into our image data buffer.
  492. bool working = true;
  493. while (working && (jpegInfo.output_scanline < jpegInfo.output_height))
  494. {
  495. unsigned char *pRow = &(buf[cur_row * row_stride]);
  496. row_pointer[0] = pRow;
  497. if ( !jpeg_read_scanlines(&jpegInfo, row_pointer, 1) )
  498. {
  499. working = false;
  500. }
  501. // Expand the row RGB -> RGBA
  502. for ( int x = image_width-1 ; x >= 0 ; --x )
  503. {
  504. pRow[x*4+3] = 0xff;
  505. pRow[x*4+2] = pRow[x*3+2];
  506. pRow[x*4+1] = pRow[x*3+1];
  507. pRow[x*4] = pRow[x*3];
  508. }
  509. ++cur_row;
  510. }
  511. // Clean up
  512. fclose( infile );
  513. //g_pFullFileSystem->Close( fileHandle );
  514. jpeg_destroy_decompress(&jpegInfo);
  515. // Check success status
  516. if (!working)
  517. {
  518. free(buf);
  519. errcode = CE_ERROR_PARSING_SOURCE;
  520. return NULL;
  521. }
  522. // OK!
  523. width = image_width;
  524. height = image_height;
  525. errcode = CE_SUCCESS;
  526. return buf;
  527. #else
  528. errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  529. return NULL;
  530. #endif
  531. }
  532. static void ReadPNGData( png_structp png_ptr, png_bytep outBytes, png_size_t byteCountToRead )
  533. {
  534. // Cast pointer
  535. CUtlBuffer *pBuf = (CUtlBuffer *)png_get_io_ptr( png_ptr );
  536. Assert( pBuf );
  537. // Check for IO error
  538. if ( pBuf->TellGet() + (int)byteCountToRead > pBuf->TellPut() )
  539. {
  540. // Attempt to read past the end of the buffer.
  541. // Use longjmp to report the error
  542. png_longjmp( png_ptr, 1 );
  543. }
  544. // Read the bytes
  545. pBuf->Get( outBytes, byteCountToRead );
  546. }
  547. unsigned char *ImgUtl_ReadPNGAsRGBA( const char *pngPath, int &width, int &height, ConversionErrorType &errcode )
  548. {
  549. #if !defined( _X360 )
  550. // Just load the whole file into a memory buffer
  551. CUtlBuffer bufFileContents;
  552. if ( !g_pFullFileSystem->ReadFile( pngPath, NULL, bufFileContents ) )
  553. {
  554. errcode = CE_CANT_OPEN_SOURCE_FILE;
  555. return NULL;
  556. }
  557. // Load it
  558. return ImgUtl_ReadPNGAsRGBAFromBuffer( bufFileContents, width, height, errcode );
  559. #else
  560. errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  561. return NULL;
  562. #endif
  563. }
  564. unsigned char *ImgUtl_ReadPNGAsRGBAFromBuffer( CUtlBuffer &buffer, int &width, int &height, ConversionErrorType &errcode )
  565. {
  566. #if !defined( _X360 )
  567. png_const_bytep pngData = (png_const_bytep)buffer.Base();
  568. if (png_sig_cmp( pngData, 0, 8))
  569. {
  570. errcode = CE_ERROR_PARSING_SOURCE;
  571. return NULL;
  572. }
  573. png_structp png_ptr = NULL;
  574. png_infop info_ptr = NULL;
  575. /* could pass pointers to user-defined error handlers instead of NULLs: */
  576. png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL );
  577. if (!png_ptr)
  578. {
  579. errcode = CE_MEMORY_ERROR;
  580. return NULL;
  581. }
  582. unsigned char *pResultData = NULL;
  583. png_bytepp row_pointers = NULL;
  584. info_ptr = png_create_info_struct( png_ptr );
  585. if ( !info_ptr )
  586. {
  587. errcode = CE_MEMORY_ERROR;
  588. fail:
  589. png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
  590. if ( row_pointers )
  591. {
  592. free( row_pointers );
  593. }
  594. if ( pResultData )
  595. {
  596. free( pResultData );
  597. }
  598. return NULL;
  599. }
  600. /* setjmp() must be called in every function that calls a PNG-reading
  601. * libpng function */
  602. if ( setjmp( png_jmpbuf(png_ptr) ) )
  603. {
  604. errcode = CE_ERROR_PARSING_SOURCE;
  605. goto fail;
  606. }
  607. png_set_read_fn( png_ptr, &buffer, ReadPNGData );
  608. png_read_info( png_ptr, info_ptr ); /* read all PNG info up to image data */
  609. /* alternatively, could make separate calls to png_get_image_width(),
  610. * etc., but want bit_depth and color_type for later [don't care about
  611. * compression_type and filter_type => NULLs] */
  612. int bit_depth;
  613. int color_type;
  614. uint32 png_width;
  615. uint32 png_height;
  616. png_get_IHDR( png_ptr, info_ptr, &png_width, &png_height, &bit_depth, &color_type, NULL, NULL, NULL );
  617. width = png_width;
  618. height = png_height;
  619. png_uint_32 rowbytes;
  620. /* expand palette images to RGB, low-bit-depth grayscale images to 8 bits,
  621. * transparency chunks to full alpha channel; strip 16-bit-per-sample
  622. * images to 8 bits per sample; and convert grayscale to RGB[A] */
  623. if (color_type == PNG_COLOR_TYPE_PALETTE)
  624. png_set_expand( png_ptr );
  625. if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
  626. png_set_expand( png_ptr );
  627. if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS ) )
  628. png_set_expand( png_ptr );
  629. if (bit_depth == 16)
  630. png_set_strip_16( png_ptr );
  631. if (color_type == PNG_COLOR_TYPE_GRAY ||
  632. color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
  633. png_set_gray_to_rgb( png_ptr );
  634. // Force in an alpha channel
  635. if ( !( color_type & PNG_COLOR_MASK_ALPHA ) )
  636. {
  637. png_set_add_alpha(png_ptr, 255, PNG_FILLER_AFTER);
  638. }
  639. /*
  640. double gamma;
  641. if (png_get_gAMA(png_ptr, info_ptr, &gamma))
  642. png_set_gamma(png_ptr, display_exponent, gamma);
  643. */
  644. /* all transformations have been registered; now update info_ptr data,
  645. * get rowbytes and channels, and allocate image memory */
  646. png_read_update_info( png_ptr, info_ptr );
  647. rowbytes = png_get_rowbytes( png_ptr, info_ptr );
  648. png_byte channels = (int)png_get_channels( png_ptr, info_ptr );
  649. if ( channels != 4 )
  650. {
  651. Assert( channels == 4 );
  652. errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  653. goto fail;
  654. }
  655. row_pointers = (png_bytepp)malloc( height*sizeof(png_bytep) );
  656. pResultData = (unsigned char *)malloc( rowbytes*height );
  657. if ( row_pointers == NULL || pResultData == NULL )
  658. {
  659. errcode = CE_MEMORY_ERROR;
  660. goto fail;
  661. }
  662. /* set the individual row_pointers to point at the correct offsets */
  663. for ( int i = 0; i < height; ++i)
  664. row_pointers[i] = pResultData + i*rowbytes;
  665. /* now we can go ahead and just read the whole image */
  666. png_read_image( png_ptr, row_pointers );
  667. png_read_end(png_ptr, NULL);
  668. free( row_pointers );
  669. row_pointers = NULL;
  670. // Clean up
  671. png_destroy_read_struct( &png_ptr, &info_ptr, NULL );
  672. // OK!
  673. width = png_width;
  674. height = png_height;
  675. errcode = CE_SUCCESS;
  676. return pResultData;
  677. #else
  678. errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  679. return NULL;
  680. #endif
  681. }
  682. unsigned char *ImgUtl_ReadBMPAsRGBA( const char *bmpPath, int &width, int &height, ConversionErrorType &errcode )
  683. {
  684. #ifdef WIN32
  685. // Load up bitmap
  686. HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, bmpPath, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE | LR_DEFAULTSIZE);
  687. // Handle failure
  688. if ( hBitmap == NULL)
  689. {
  690. // !KLUDGE! Try to detect what went wrong
  691. FILE *fp = fopen( bmpPath, "rb" );
  692. if (fp == NULL)
  693. {
  694. errcode = CE_CANT_OPEN_SOURCE_FILE;
  695. }
  696. else
  697. {
  698. errcode = CE_ERROR_PARSING_SOURCE;
  699. }
  700. return NULL;
  701. }
  702. BITMAP bitmap;
  703. GetObject(hBitmap, sizeof(bitmap), &bitmap);
  704. BITMAPINFO *bitmapInfo;
  705. bool bUseColorTable = false;
  706. if (bitmap.bmBitsPixel == 24 || bitmap.bmBitsPixel == 32)
  707. {
  708. bitmapInfo = (BITMAPINFO *)malloc(sizeof(BITMAPINFO));
  709. }
  710. else if (bitmap.bmBitsPixel == 8 || bitmap.bmBitsPixel == 4 || bitmap.bmBitsPixel == 1)
  711. {
  712. int colorsUsed = 1 << bitmap.bmBitsPixel;
  713. bitmapInfo = (BITMAPINFO *)malloc(colorsUsed * sizeof(RGBQUAD) + sizeof(BITMAPINFO));
  714. bUseColorTable = true;
  715. }
  716. else
  717. {
  718. DeleteObject(hBitmap);
  719. errcode = CE_SOURCE_FILE_BMP_FORMAT_NOT_SUPPORTED;
  720. return NULL;
  721. }
  722. memset(bitmapInfo, 0, sizeof(BITMAPINFO));
  723. bitmapInfo->bmiHeader.biSize = sizeof(bitmapInfo->bmiHeader);
  724. if (bUseColorTable)
  725. {
  726. bitmapInfo->bmiHeader.biBitCount = bitmap.bmBitsPixel; // need to specify the bits per pixel so GDI will generate a color table for us.
  727. }
  728. HDC dc = CreateCompatibleDC(NULL);
  729. int retcode = GetDIBits(dc, hBitmap, 0, bitmap.bmHeight, NULL, bitmapInfo, DIB_RGB_COLORS);
  730. DeleteDC(dc);
  731. if (retcode == 0)
  732. {
  733. // error getting the bitmap info for some reason.
  734. free(bitmapInfo);
  735. errcode = CE_SOURCE_FILE_BMP_FORMAT_NOT_SUPPORTED;
  736. return NULL;
  737. }
  738. int nDestStride = 4 * bitmap.bmWidth;
  739. int mem_required = nDestStride * bitmap.bmHeight; // mem required for copying the data out into RGBA format.
  740. unsigned char *buf = (unsigned char *)malloc(mem_required);
  741. if (buf == NULL)
  742. {
  743. free(bitmapInfo);
  744. errcode = CE_MEMORY_ERROR;
  745. return NULL;
  746. }
  747. if (bitmapInfo->bmiHeader.biBitCount == 32)
  748. {
  749. for (int y = 0; y < bitmap.bmHeight; ++y)
  750. {
  751. unsigned char *pDest = buf + nDestStride * ( ( bitmap.bmHeight - 1 ) - y ); // BMPs are stored upside down
  752. const unsigned char *pSrc = (unsigned char *)(bitmap.bmBits) + (y * bitmap.bmWidthBytes);
  753. for (int x = 0; x < bitmap.bmWidth; ++x)
  754. {
  755. // Swap BGR -> RGB while copying data
  756. pDest[0] = pSrc[2]; // R
  757. pDest[1] = pSrc[1]; // G
  758. pDest[2] = pSrc[0]; // B
  759. pDest[3] = pSrc[3]; // A
  760. pSrc += 4;
  761. pDest += 4;
  762. }
  763. }
  764. }
  765. else if (bitmapInfo->bmiHeader.biBitCount == 24)
  766. {
  767. for (int y = 0; y < bitmap.bmHeight; ++y)
  768. {
  769. unsigned char *pDest = buf + nDestStride * ( ( bitmap.bmHeight - 1 ) - y ); // BMPs are stored upside down
  770. const unsigned char *pSrc = (unsigned char *)(bitmap.bmBits) + (y * bitmap.bmWidthBytes);
  771. for (int x = 0; x < bitmap.bmWidth; ++x)
  772. {
  773. // Swap BGR -> RGB while copying data
  774. pDest[0] = pSrc[2]; // R
  775. pDest[1] = pSrc[1]; // G
  776. pDest[2] = pSrc[0]; // B
  777. pDest[3] = 0xff; // A
  778. pSrc += 3;
  779. pDest += 4;
  780. }
  781. }
  782. }
  783. else if (bitmapInfo->bmiHeader.biBitCount == 8)
  784. {
  785. // 8-bit 256 color bitmap.
  786. for (int y = 0; y < bitmap.bmHeight; ++y)
  787. {
  788. unsigned char *pDest = buf + nDestStride * ( ( bitmap.bmHeight - 1 ) - y ); // BMPs are stored upside down
  789. const unsigned char *pSrc = (unsigned char *)(bitmap.bmBits) + (y * bitmap.bmWidthBytes);
  790. for (int x = 0; x < bitmap.bmWidth; ++x)
  791. {
  792. // compute the color map entry for this pixel
  793. int colorTableEntry = *pSrc;
  794. // get the color for this color map entry.
  795. RGBQUAD *rgbQuad = &(bitmapInfo->bmiColors[colorTableEntry]);
  796. // copy the color values for this pixel to the destination buffer.
  797. pDest[0] = rgbQuad->rgbRed;
  798. pDest[1] = rgbQuad->rgbGreen;
  799. pDest[2] = rgbQuad->rgbBlue;
  800. pDest[3] = 0xff;
  801. ++pSrc;
  802. pDest += 4;
  803. }
  804. }
  805. }
  806. else if (bitmapInfo->bmiHeader.biBitCount == 4)
  807. {
  808. // 4-bit 16 color bitmap.
  809. for (int y = 0; y < bitmap.bmHeight; ++y)
  810. {
  811. unsigned char *pDest = buf + nDestStride * ( ( bitmap.bmHeight - 1 ) - y ); // BMPs are stored upside down
  812. const unsigned char *pSrc = (unsigned char *)(bitmap.bmBits) + (y * bitmap.bmWidthBytes);
  813. // Two pixels at a time
  814. for (int x = 0; x < bitmap.bmWidth; x += 2)
  815. {
  816. // get the color table entry for this pixel
  817. int colorTableEntry = (0xf0 & *pSrc) >> 4;
  818. // get the color values for this pixel's color table entry.
  819. RGBQUAD *rgbQuad = &(bitmapInfo->bmiColors[colorTableEntry]);
  820. // copy the pixel's color values to the destination buffer.
  821. pDest[0] = pSrc[2]; // R
  822. pDest[1] = pSrc[1]; // G
  823. pDest[2] = pSrc[0]; // B
  824. pDest[3] = 0xff; // A
  825. // make sure we haven't reached the end of the row.
  826. if ((x + 1) > bitmap.bmWidth)
  827. {
  828. break;
  829. }
  830. pDest += 4;
  831. // get the color table entry for this pixel.
  832. colorTableEntry = 0x0f & *pSrc;
  833. // get the color values for this pixel's color table entry.
  834. rgbQuad = &(bitmapInfo->bmiColors[colorTableEntry]);
  835. // copy the pixel's color values to the destination buffer.
  836. pDest[0] = pSrc[2]; // R
  837. pDest[1] = pSrc[1]; // G
  838. pDest[2] = pSrc[0]; // B
  839. pDest[3] = 0xff; // A
  840. ++pSrc;
  841. pDest += 4;
  842. }
  843. }
  844. }
  845. else if (bitmapInfo->bmiHeader.biBitCount == 1)
  846. {
  847. // 1-bit monochrome bitmap.
  848. for (int y = 0; y < bitmap.bmHeight; ++y)
  849. {
  850. unsigned char *pDest = buf + nDestStride * ( ( bitmap.bmHeight - 1 ) - y ); // BMPs are stored upside down
  851. const unsigned char *pSrc = (unsigned char *)(bitmap.bmBits) + (y * bitmap.bmWidthBytes);
  852. // Eight pixels at a time
  853. int x = 0;
  854. while (x < bitmap.bmWidth)
  855. {
  856. RGBQUAD *rgbQuad = NULL;
  857. int bitMask = 0x80;
  858. // go through all 8 bits in this byte to get all 8 pixel colors.
  859. do
  860. {
  861. // get the value of the bit for this pixel.
  862. int bit = *pSrc & bitMask;
  863. // bit will either be 0 or non-zero since there are only two colors.
  864. if (bit == 0)
  865. {
  866. rgbQuad = &(bitmapInfo->bmiColors[0]);
  867. }
  868. else
  869. {
  870. rgbQuad = &(bitmapInfo->bmiColors[1]);
  871. }
  872. // copy this pixel's color values into the destination buffer.
  873. pDest[0] = pSrc[2]; // R
  874. pDest[1] = pSrc[1]; // G
  875. pDest[2] = pSrc[0]; // B
  876. pDest[3] = 0xff; // A
  877. pDest += 4;
  878. // go to the next pixel.
  879. ++x;
  880. bitMask = bitMask >> 1;
  881. } while ((x < bitmap.bmWidth) && (bitMask > 0));
  882. ++pSrc;
  883. }
  884. }
  885. }
  886. else
  887. {
  888. free(bitmapInfo);
  889. free(buf);
  890. DeleteObject(hBitmap);
  891. errcode = CE_SOURCE_FILE_BMP_FORMAT_NOT_SUPPORTED;
  892. return NULL;
  893. }
  894. free(bitmapInfo);
  895. DeleteObject(hBitmap);
  896. // OK!
  897. width = bitmap.bmWidth;
  898. height = bitmap.bmHeight;
  899. errcode = CE_SUCCESS;
  900. return buf;
  901. #else
  902. errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  903. return NULL;
  904. #endif
  905. }
  906. unsigned char *ImgUtl_ReadImageAsRGBA( const char *path, int &width, int &height, ConversionErrorType &errcode )
  907. {
  908. // Split out the file extension
  909. const char *pExt = V_GetFileExtension( path );
  910. if ( pExt )
  911. {
  912. if ( !Q_stricmp(pExt, "vtf") )
  913. {
  914. return ImgUtl_ReadVTFAsRGBA( path, width, height, errcode );
  915. }
  916. if ( !Q_stricmp(pExt, "bmp") )
  917. {
  918. return ImgUtl_ReadBMPAsRGBA( path, width, height, errcode );
  919. }
  920. if ( !Q_stricmp(pExt, "jpg") || !Q_stricmp(pExt, "jpeg") )
  921. {
  922. return ImgUtl_ReadJPEGAsRGBA( path, width, height, errcode );
  923. }
  924. if ( !Q_stricmp(pExt, "png") )
  925. {
  926. return ImgUtl_ReadPNGAsRGBA( path, width, height, errcode );
  927. }
  928. if ( !Q_stricmp(pExt, "tga") )
  929. {
  930. TGAHeader header;
  931. return ImgUtl_ReadTGAAsRGBA( path, width, height, errcode, header );
  932. }
  933. }
  934. errcode = CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  935. return NULL;
  936. }
  937. // resizes the file specified by tgaPath so that it has dimensions that are
  938. // powers-of-two and is equal to or smaller than (nMaxWidth)x(nMaxHeight).
  939. // also converts from 24-bit RGB to 32-bit RGB (with 8-bit alpha)
  940. ConversionErrorType ImgUtl_ConvertTGA(const char *tgaPath, int nMaxWidth/*=-1*/, int nMaxHeight/*=-1*/)
  941. {
  942. int tgaWidth = 0, tgaHeight = 0;
  943. ConversionErrorType errcode;
  944. TGAHeader tgaHeader;
  945. unsigned char *srcBuffer = ImgUtl_ReadTGAAsRGBA(tgaPath, tgaWidth, tgaHeight, errcode, tgaHeader);
  946. if (srcBuffer == NULL)
  947. {
  948. return errcode;
  949. }
  950. int paddedImageWidth, paddedImageHeight;
  951. if ((tgaWidth <= 0) || (tgaHeight <= 0))
  952. {
  953. free(srcBuffer);
  954. return CE_ERROR_PARSING_SOURCE;
  955. }
  956. // get the nearest power of two that is greater than the width of the image.
  957. paddedImageWidth = tgaWidth;
  958. if (!IsPowerOfTwo(paddedImageWidth))
  959. {
  960. // width is not a power of two, calculate the next highest power of two value.
  961. int i = 1;
  962. while (paddedImageWidth > 1)
  963. {
  964. paddedImageWidth = paddedImageWidth >> 1;
  965. ++i;
  966. }
  967. paddedImageWidth = paddedImageWidth << i;
  968. }
  969. // make sure the width is less than or equal to nMaxWidth
  970. if (nMaxWidth != -1 && paddedImageWidth > nMaxWidth)
  971. {
  972. paddedImageWidth = nMaxWidth;
  973. }
  974. // get the nearest power of two that is greater than the height of the image
  975. paddedImageHeight = tgaHeight;
  976. if (!IsPowerOfTwo(paddedImageHeight))
  977. {
  978. // height is not a power of two, calculate the next highest power of two value.
  979. int i = 1;
  980. while (paddedImageHeight > 1)
  981. {
  982. paddedImageHeight = paddedImageHeight >> 1;
  983. ++i;
  984. }
  985. paddedImageHeight = paddedImageHeight << i;
  986. }
  987. // make sure the height is less than or equal to nMaxHeight
  988. if (nMaxHeight != -1 && paddedImageHeight > nMaxHeight)
  989. {
  990. paddedImageHeight = nMaxHeight;
  991. }
  992. // compute the amount of stretching that needs to be done to both width and height to get the image to fit.
  993. float widthRatio = (float)paddedImageWidth / tgaWidth;
  994. float heightRatio = (float)paddedImageHeight / tgaHeight;
  995. int finalWidth;
  996. int finalHeight;
  997. // compute the final dimensions of the stretched image.
  998. if (widthRatio < heightRatio)
  999. {
  1000. finalWidth = paddedImageWidth;
  1001. finalHeight = (int)(tgaHeight * widthRatio + 0.5f);
  1002. // i.e. for 1x1 size pixels in the resized image we will take color from sourceRatio x sourceRatio sized pixels in the source image.
  1003. }
  1004. else if (heightRatio < widthRatio)
  1005. {
  1006. finalHeight = paddedImageHeight;
  1007. finalWidth = (int)(tgaWidth * heightRatio + 0.5f);
  1008. }
  1009. else
  1010. {
  1011. finalHeight = paddedImageHeight;
  1012. finalWidth = paddedImageWidth;
  1013. }
  1014. unsigned char *resizeBuffer = (unsigned char *)malloc(finalWidth * finalHeight * 4);
  1015. // do the actual stretching
  1016. ImgUtl_StretchRGBAImage(srcBuffer, tgaWidth, tgaHeight, resizeBuffer, finalWidth, finalHeight);
  1017. free(srcBuffer); // don't need this anymore.
  1018. ///////////////////////////////////////////////////////////////////////
  1019. ///// need to pad the image so both dimensions are power of two's /////
  1020. ///////////////////////////////////////////////////////////////////////
  1021. unsigned char *finalBuffer = (unsigned char *)malloc(paddedImageWidth * paddedImageHeight * 4);
  1022. ImgUtl_PadRGBAImage(resizeBuffer, finalWidth, finalHeight, finalBuffer, paddedImageWidth, paddedImageHeight);
  1023. FILE *outfile = fopen(tgaPath, "wb");
  1024. if (outfile == NULL)
  1025. {
  1026. free(resizeBuffer);
  1027. free(finalBuffer);
  1028. return CE_ERROR_WRITING_OUTPUT_FILE;
  1029. }
  1030. tgaHeader.width = paddedImageWidth;
  1031. tgaHeader.height = paddedImageHeight;
  1032. WriteTGAHeader(outfile, tgaHeader);
  1033. // Write the image data --- remember that TGA uses BGRA data
  1034. int numPixels = paddedImageWidth * paddedImageHeight;
  1035. for (int i = 0 ; i < numPixels ; ++i )
  1036. {
  1037. fputc( finalBuffer[i*4 + 2], outfile ); // B
  1038. fputc( finalBuffer[i*4 + 1], outfile ); // G
  1039. fputc( finalBuffer[i*4 ], outfile ); // R
  1040. fputc( finalBuffer[i*4 + 3], outfile ); // A
  1041. }
  1042. fclose(outfile);
  1043. free(resizeBuffer);
  1044. free(finalBuffer);
  1045. return CE_SUCCESS;
  1046. }
  1047. // resize by stretching (or compressing) an RGBA image pointed to by srcBuf into the buffer pointed to by destBuf.
  1048. // the buffers are assumed to be sized appropriately to accomidate RGBA images of the given widths and heights.
  1049. ConversionErrorType ImgUtl_StretchRGBAImage(const unsigned char *srcBuf, const int srcWidth, const int srcHeight,
  1050. unsigned char *destBuf, const int destWidth, const int destHeight)
  1051. {
  1052. if ((srcBuf == NULL) || (destBuf == NULL))
  1053. {
  1054. return CE_CANT_OPEN_SOURCE_FILE;
  1055. }
  1056. int destRow,destColumn;
  1057. float ratioX = (float)srcWidth / (float)destWidth;
  1058. float ratioY = (float)srcHeight / (float)destHeight;
  1059. // loop through all the pixels in the destination image.
  1060. for (destRow = 0; destRow < destHeight; ++destRow)
  1061. {
  1062. for (destColumn = 0; destColumn < destWidth; ++destColumn)
  1063. {
  1064. // calculate the center of the pixel in the source image.
  1065. float srcCenterX = ratioX * (destColumn + 0.5f);
  1066. float srcCenterY = ratioY * (destRow + 0.5f);
  1067. // calculate the starting and ending coords for this destination pixel in the source image.
  1068. float srcStartX = srcCenterX - (ratioX / 2.0f);
  1069. if (srcStartX < 0.0f)
  1070. {
  1071. srcStartX = 0.0f; // this should never happen, but just in case.
  1072. }
  1073. float srcStartY = srcCenterY - (ratioY / 2.0f);
  1074. if (srcStartY < 0.0f)
  1075. {
  1076. srcStartY = 0.0f; // this should never happen, but just in case.
  1077. }
  1078. float srcEndX = srcCenterX + (ratioX / 2.0f);
  1079. if (srcEndX > srcWidth)
  1080. {
  1081. srcEndX = srcWidth; // this should never happen, but just in case.
  1082. }
  1083. float srcEndY = srcCenterY + (ratioY / 2.0f);
  1084. if (srcEndY > srcHeight)
  1085. {
  1086. srcEndY = srcHeight; // this should never happen, but just in case.
  1087. }
  1088. // Calculate the percentage of each source pixels' contribution to the destination pixel color.
  1089. float srcCurrentX; // initialized at the start of the y loop.
  1090. float srcCurrentY = srcStartY;
  1091. float destRed = 0.0f;
  1092. float destGreen = 0.0f;
  1093. float destBlue = 0.0f;
  1094. float destAlpha = 0.0f;
  1095. //// loop for the parts of the source image that will contribute color to the destination pixel.
  1096. while (srcCurrentY < srcEndY)
  1097. {
  1098. float srcCurrentEndY = (float)((int)srcCurrentY + 1);
  1099. if (srcCurrentEndY > srcEndY)
  1100. {
  1101. srcCurrentEndY = srcEndY;
  1102. }
  1103. float srcCurrentHeight = srcCurrentEndY - srcCurrentY;
  1104. srcCurrentX = srcStartX;
  1105. while (srcCurrentX < srcEndX)
  1106. {
  1107. float srcCurrentEndX = (float)((int)srcCurrentX + 1);
  1108. if (srcCurrentEndX > srcEndX)
  1109. {
  1110. srcCurrentEndX = srcEndX;
  1111. }
  1112. float srcCurrentWidth = srcCurrentEndX - srcCurrentX;
  1113. // compute the percentage of the destination pixel's color this source pixel will contribute.
  1114. float srcColorPercentage = (srcCurrentWidth / ratioX) * (srcCurrentHeight / ratioY);
  1115. int srcCurrentPixelX = (int)srcCurrentX;
  1116. int srcCurrentPixelY = (int)srcCurrentY;
  1117. // get the color values for this source pixel.
  1118. unsigned char srcCurrentRed = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4)];
  1119. unsigned char srcCurrentGreen = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4) + 1];
  1120. unsigned char srcCurrentBlue = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4) + 2];
  1121. unsigned char srcCurrentAlpha = srcBuf[(srcCurrentPixelY * srcWidth * 4) + (srcCurrentPixelX * 4) + 3];
  1122. // add the color contribution from this source pixel to the destination pixel.
  1123. destRed += srcCurrentRed * srcColorPercentage;
  1124. destGreen += srcCurrentGreen * srcColorPercentage;
  1125. destBlue += srcCurrentBlue * srcColorPercentage;
  1126. destAlpha += srcCurrentAlpha * srcColorPercentage;
  1127. srcCurrentX = srcCurrentEndX;
  1128. }
  1129. srcCurrentY = srcCurrentEndY;
  1130. }
  1131. // assign the computed color to the destination pixel, round to the nearest value. Make sure the value doesn't exceed 255.
  1132. destBuf[(destRow * destWidth * 4) + (destColumn * 4)] = min((int)(destRed + 0.5f), 255);
  1133. destBuf[(destRow * destWidth * 4) + (destColumn * 4) + 1] = min((int)(destGreen + 0.5f), 255);
  1134. destBuf[(destRow * destWidth * 4) + (destColumn * 4) + 2] = min((int)(destBlue + 0.5f), 255);
  1135. destBuf[(destRow * destWidth * 4) + (destColumn * 4) + 3] = min((int)(destAlpha + 0.5f), 255);
  1136. } // column loop
  1137. } // row loop
  1138. return CE_SUCCESS;
  1139. }
  1140. ConversionErrorType ImgUtl_PadRGBAImage(const unsigned char *srcBuf, const int srcWidth, const int srcHeight,
  1141. unsigned char *destBuf, const int destWidth, const int destHeight)
  1142. {
  1143. if ((srcBuf == NULL) || (destBuf == NULL))
  1144. {
  1145. return CE_CANT_OPEN_SOURCE_FILE;
  1146. }
  1147. memset(destBuf, 0, destWidth * destHeight * 4);
  1148. if ((destWidth < srcWidth) || (destHeight < srcHeight))
  1149. {
  1150. return CE_ERROR_PARSING_SOURCE;
  1151. }
  1152. if ((srcWidth == destWidth) && (srcHeight == destHeight))
  1153. {
  1154. // no padding is needed, just copy the buffer straight over and call it done.
  1155. memcpy(destBuf, srcBuf, destWidth * destHeight * 4);
  1156. return CE_SUCCESS;
  1157. }
  1158. if (destWidth == srcWidth)
  1159. {
  1160. // only the top and bottom of the image need padding.
  1161. // do this separately since we can do this more efficiently than the other cases.
  1162. int numRowsToPad = (destHeight - srcHeight) / 2;
  1163. memcpy(destBuf + (numRowsToPad * destWidth * 4), srcBuf, srcWidth * srcHeight * 4);
  1164. }
  1165. else
  1166. {
  1167. int numColumnsToPad = (destWidth - srcWidth) / 2;
  1168. int numRowsToPad = (destHeight - srcHeight) / 2;
  1169. int lastRow = numRowsToPad + srcHeight;
  1170. int row;
  1171. for (row = numRowsToPad; row < lastRow; ++row)
  1172. {
  1173. unsigned char * destOffset = destBuf + (row * destWidth * 4) + (numColumnsToPad * 4);
  1174. const unsigned char * srcOffset = srcBuf + ((row - numRowsToPad) * srcWidth * 4);
  1175. memcpy(destOffset, srcOffset, srcWidth * 4);
  1176. }
  1177. }
  1178. return CE_SUCCESS;
  1179. }
  1180. // convert TGA file at the given location to a VTF file of the same root name at the same location.
  1181. ConversionErrorType ImgUtl_ConvertTGAToVTF(const char *tgaPath, int nMaxWidth/*=-1*/, int nMaxHeight/*=-1*/ )
  1182. {
  1183. FILE *infile = fopen(tgaPath, "rb");
  1184. if (infile == NULL)
  1185. {
  1186. Msg( "Failed to open TGA: %s\n", tgaPath);
  1187. return CE_CANT_OPEN_SOURCE_FILE;
  1188. }
  1189. // read out the header of the image.
  1190. TGAHeader header;
  1191. ImgUtl_ReadTGAHeader(infile, header);
  1192. // check to make sure that the TGA has the proper dimensions and size.
  1193. if (!IsPowerOfTwo(header.width) || !IsPowerOfTwo(header.height))
  1194. {
  1195. fclose(infile);
  1196. Msg( "Failed to open TGA - size dimensions (%d, %d) not power of 2: %s\n", header.width, header.height, tgaPath);
  1197. return CE_SOURCE_FILE_SIZE_NOT_SUPPORTED;
  1198. }
  1199. // check to make sure that the TGA isn't too big, if we care.
  1200. if ( ( nMaxWidth != -1 && header.width > nMaxWidth ) || ( nMaxHeight != -1 && header.height > nMaxHeight ) )
  1201. {
  1202. fclose(infile);
  1203. Msg( "Failed to open TGA - dimensions too large (%d, %d) (max: %d, %d): %s\n", header.width, header.height, nMaxWidth, nMaxHeight, tgaPath);
  1204. return CE_SOURCE_FILE_SIZE_NOT_SUPPORTED;
  1205. }
  1206. int imageMemoryFootprint = header.width * header.height * header.bits / 8;
  1207. CUtlBuffer inbuf(0, imageMemoryFootprint);
  1208. // read in the image
  1209. int nBytesRead = fread(inbuf.Base(), imageMemoryFootprint, 1, infile);
  1210. fclose(infile);
  1211. inbuf.SeekPut( CUtlBuffer::SEEK_HEAD, nBytesRead );
  1212. // load vtex_dll.dll and get the interface to it.
  1213. CSysModule *vtexmod = Sys_LoadModule("vtex_dll");
  1214. if (vtexmod == NULL)
  1215. {
  1216. Msg( "Failed to open TGA conversion module vtex_dll: %s\n", tgaPath);
  1217. return CE_ERROR_LOADING_DLL;
  1218. }
  1219. CreateInterfaceFn factory = Sys_GetFactory(vtexmod);
  1220. if (factory == NULL)
  1221. {
  1222. Sys_UnloadModule(vtexmod);
  1223. Msg( "Failed to open TGA conversion module vtex_dll Factory: %s\n", tgaPath);
  1224. return CE_ERROR_LOADING_DLL;
  1225. }
  1226. IVTex *vtex = (IVTex *)factory(IVTEX_VERSION_STRING, NULL);
  1227. if (vtex == NULL)
  1228. {
  1229. Sys_UnloadModule(vtexmod);
  1230. Msg( "Failed to open TGA conversion module vtex_dll Factory (is null): %s\n", tgaPath);
  1231. return CE_ERROR_LOADING_DLL;
  1232. }
  1233. char *vtfParams[4];
  1234. // the 0th entry is skipped cause normally thats the program name.
  1235. vtfParams[0] = "";
  1236. vtfParams[1] = "-quiet";
  1237. vtfParams[2] = "-dontusegamedir";
  1238. vtfParams[3] = (char *)tgaPath;
  1239. // call vtex to do the conversion.
  1240. vtex->VTex(4, vtfParams); // how do we know this works?
  1241. Sys_UnloadModule(vtexmod);
  1242. return CE_SUCCESS;
  1243. }
  1244. static void DoCopyFile( const char *source, const char *destination )
  1245. {
  1246. #if defined( WIN32 )
  1247. CopyFile( source, destination, true );
  1248. #elif defined( OSX )
  1249. copyfile( source, destination, NULL, COPYFILE_ALL );
  1250. #elif defined( ENGINE_DLL )
  1251. ::COM_CopyFile( source, destination );
  1252. #elif REPLAY_DLL
  1253. g_pEngine->CopyFile( source, destination );
  1254. #else
  1255. engine->CopyLocalFile( source, destination );
  1256. #endif
  1257. }
  1258. static void DoDeleteFile( const char *filename )
  1259. {
  1260. #ifdef WIN32
  1261. DeleteFile( filename );
  1262. #else
  1263. unlink( filename );
  1264. #endif
  1265. }
  1266. ConversionErrorType ImgUtl_ConvertToVTFAndDumpVMT( const char *pInPath, const char *pMaterialsSubDir, int nMaxWidth/*=-1*/, int nMaxHeight/*=-1*/ )
  1267. {
  1268. #ifndef _XBOX
  1269. if ((pInPath == NULL) || (pInPath[0] == 0))
  1270. {
  1271. return CE_ERROR_PARSING_SOURCE;
  1272. }
  1273. ConversionErrorType nErrorCode = CE_SUCCESS;
  1274. // get the extension of the file we're to convert
  1275. char extension[MAX_PATH];
  1276. const char *constchar = pInPath + strlen(pInPath);
  1277. while ((constchar > pInPath) && (*(constchar-1) != '.'))
  1278. {
  1279. --constchar;
  1280. }
  1281. Q_strncpy(extension, constchar, MAX_PATH);
  1282. bool deleteIntermediateTGA = false;
  1283. bool deleteIntermediateVTF = false;
  1284. bool convertTGAToVTF = true;
  1285. char tgaPath[MAX_PATH*2];
  1286. char *c;
  1287. bool failed = false;
  1288. Q_strncpy(tgaPath, pInPath, sizeof(tgaPath));
  1289. // Construct a TGA version if necessary
  1290. if (stricmp(extension, "tga"))
  1291. {
  1292. // It is not a TGA file, so create a temporary file name for the TGA you have to create
  1293. c = tgaPath + strlen(tgaPath);
  1294. while ((c > tgaPath) && (*(c-1) != '\\') && (*(c-1) != '/'))
  1295. {
  1296. --c;
  1297. }
  1298. *c = 0;
  1299. char origpath[MAX_PATH*2];
  1300. Q_strncpy(origpath, tgaPath, sizeof(origpath));
  1301. // Look for an empty temp file - find the first one that doesn't exist.
  1302. int index = 0;
  1303. do {
  1304. Q_snprintf(tgaPath, sizeof(tgaPath), "%stemp%d.tga", origpath, index);
  1305. ++index;
  1306. } while (_access(tgaPath, 0) != -1);
  1307. // Convert the other formats to TGA
  1308. // jpeg files
  1309. //
  1310. if (!stricmp(extension, "jpg") || !stricmp(extension, "jpeg"))
  1311. {
  1312. // convert from the jpeg file format to the TGA file format
  1313. nErrorCode = ImgUtl_ConvertJPEGToTGA(pInPath, tgaPath, false);
  1314. if (nErrorCode == CE_SUCCESS)
  1315. {
  1316. deleteIntermediateTGA = true;
  1317. }
  1318. else
  1319. {
  1320. failed = true;
  1321. }
  1322. }
  1323. // bmp files
  1324. //
  1325. else if (!stricmp(extension, "bmp"))
  1326. {
  1327. // convert from the bmp file format to the TGA file format
  1328. nErrorCode = ImgUtl_ConvertBMPToTGA(pInPath, tgaPath);
  1329. if (nErrorCode == CE_SUCCESS)
  1330. {
  1331. deleteIntermediateTGA = true;
  1332. }
  1333. else
  1334. {
  1335. failed = true;
  1336. }
  1337. }
  1338. // vtf files
  1339. //
  1340. else if (!stricmp(extension, "vtf"))
  1341. {
  1342. // if the file is already in the vtf format there's no need to convert it.
  1343. convertTGAToVTF = false;
  1344. }
  1345. }
  1346. // if we now have a TGA file, convert it to VTF
  1347. if (convertTGAToVTF && !failed)
  1348. {
  1349. nErrorCode = ImgUtl_ConvertTGA( tgaPath, nMaxWidth, nMaxHeight ); // resize TGA so that it has power-of-two dimensions with a max size of (nMaxWidth)x(nMaxHeight).
  1350. if (nErrorCode != CE_SUCCESS)
  1351. {
  1352. failed = true;
  1353. }
  1354. if (!failed)
  1355. {
  1356. char tempPath[MAX_PATH*2];
  1357. Q_strncpy(tempPath, tgaPath, sizeof(tempPath));
  1358. nErrorCode = ImgUtl_ConvertTGAToVTF( tempPath, nMaxWidth, nMaxHeight );
  1359. if (nErrorCode == CE_SUCCESS)
  1360. {
  1361. deleteIntermediateVTF = true;
  1362. }
  1363. else
  1364. {
  1365. Msg( "Failed to convert TGA to VTF: %s\n", tempPath);
  1366. failed = true;
  1367. }
  1368. }
  1369. }
  1370. // At this point everything should be a VTF file
  1371. char finalPath[MAX_PATH*2];
  1372. finalPath[0] = 0;
  1373. char vtfPath[MAX_PATH*2];
  1374. vtfPath[0] = 0;
  1375. // If we haven't failed so far, create a VMT to go with this VTF
  1376. if (!failed)
  1377. {
  1378. // If I had to convert from another filetype (i.e. the original was NOT a .vtf)
  1379. if ( convertTGAToVTF )
  1380. {
  1381. Q_strncpy(vtfPath, tgaPath, sizeof(vtfPath));
  1382. // rename the tga file to be a vtf file.
  1383. c = vtfPath + strlen(vtfPath);
  1384. while ((c > vtfPath) && (*(c-1) != '.'))
  1385. {
  1386. --c;
  1387. }
  1388. *c = 0;
  1389. Q_strncat(vtfPath, "vtf", sizeof(vtfPath), COPY_ALL_CHARACTERS);
  1390. }
  1391. else
  1392. {
  1393. // We were handed a vtf file originally, so use it.
  1394. Q_strncpy(vtfPath, pInPath, sizeof(vtfPath));
  1395. }
  1396. // get the vtfFilename from the path.
  1397. const char *vtfFilename = pInPath + strlen(pInPath);
  1398. while ((vtfFilename > pInPath) && (*(vtfFilename-1) != '\\') && (*(vtfFilename-1) != '/'))
  1399. {
  1400. --vtfFilename;
  1401. }
  1402. // Create a safe version of pOutDir with corrected slashes
  1403. char szOutDir[MAX_PATH*2];
  1404. V_strcpy_safe( szOutDir, IsPosix() ? "/materials/" : "\\materials\\" );
  1405. if ( pMaterialsSubDir[0] == '\\' || pMaterialsSubDir[0] == '/' )
  1406. pMaterialsSubDir = pMaterialsSubDir + 1;
  1407. V_strcat_safe(szOutDir, pMaterialsSubDir, sizeof(szOutDir) );
  1408. Q_StripTrailingSlash( szOutDir );
  1409. Q_AppendSlash( szOutDir, sizeof(szOutDir) );
  1410. Q_FixSlashes( szOutDir, CORRECT_PATH_SEPARATOR );
  1411. #ifdef ENGINE_DLL
  1412. Q_strncpy(finalPath, com_gamedir, sizeof(finalPath));
  1413. #elif REPLAY_DLL
  1414. Q_strncpy(finalPath, g_pEngine->GetGameDir(), sizeof(finalPath));
  1415. #else
  1416. Q_strncpy(finalPath, engine->GetGameDirectory(), sizeof(finalPath));
  1417. #endif
  1418. Q_strncat(finalPath, szOutDir, sizeof(finalPath), COPY_ALL_CHARACTERS);
  1419. Q_strncat(finalPath, vtfFilename, sizeof(finalPath), COPY_ALL_CHARACTERS);
  1420. c = finalPath + strlen(finalPath);
  1421. while ((c > finalPath) && (*(c-1) != '.'))
  1422. {
  1423. --c;
  1424. }
  1425. *c = 0;
  1426. Q_strncat(finalPath,"vtf", sizeof(finalPath), COPY_ALL_CHARACTERS);
  1427. // make sure the directory exists before we try to copy the file.
  1428. g_pFullFileSystem->CreateDirHierarchy(szOutDir + 1, "GAME");
  1429. //g_pFullFileSystem->CreateDirHierarchy("materials/VGUI/logos/", "GAME");
  1430. // write out the spray VMT file.
  1431. if ( strcmp(vtfPath, finalPath) ) // If they're not already the same
  1432. {
  1433. nErrorCode = ImgUtl_WriteGenericVMT(finalPath, pMaterialsSubDir);
  1434. if (nErrorCode != CE_SUCCESS)
  1435. {
  1436. failed = true;
  1437. }
  1438. if (!failed)
  1439. {
  1440. // copy vtf file to the final location, only if we're not already in vtf
  1441. DoCopyFile( vtfPath, finalPath );
  1442. }
  1443. }
  1444. }
  1445. // delete the intermediate VTF file if one was made.
  1446. if (deleteIntermediateVTF)
  1447. {
  1448. DoDeleteFile( vtfPath );
  1449. // the TGA->VTF conversion process generates a .txt file if one wasn't already there.
  1450. // in this case, delete the .txt file.
  1451. c = vtfPath + strlen(vtfPath);
  1452. while ((c > vtfPath) && (*(c-1) != '.'))
  1453. {
  1454. --c;
  1455. }
  1456. Q_strncpy(c, "txt", sizeof(vtfPath)-(c-vtfPath));
  1457. DoDeleteFile( vtfPath );
  1458. }
  1459. // delete the intermediate TGA file if one was made.
  1460. if (deleteIntermediateTGA)
  1461. {
  1462. DoDeleteFile( tgaPath );
  1463. }
  1464. return nErrorCode;
  1465. #endif
  1466. }
  1467. ConversionErrorType ImgUtl_WriteGenericVMT( const char *vtfPath, const char *pMaterialsSubDir )
  1468. {
  1469. if (vtfPath == NULL || pMaterialsSubDir == NULL )
  1470. {
  1471. return CE_ERROR_WRITING_OUTPUT_FILE;
  1472. }
  1473. // make the vmt filename
  1474. char vmtPath[MAX_PATH*4];
  1475. Q_strncpy(vmtPath, vtfPath, sizeof(vmtPath));
  1476. char *c = vmtPath + strlen(vmtPath);
  1477. while ((c > vmtPath) && (*(c-1) != '.'))
  1478. {
  1479. --c;
  1480. }
  1481. Q_strncpy(c, "vmt", sizeof(vmtPath) - (c - vmtPath));
  1482. // get the root filename for the vtf file
  1483. char filename[MAX_PATH];
  1484. while ((c > vmtPath) && (*(c-1) != '/') && (*(c-1) != '\\'))
  1485. {
  1486. --c;
  1487. }
  1488. int i = 0;
  1489. while ((*c != 0) && (*c != '.'))
  1490. {
  1491. filename[i++] = *(c++);
  1492. }
  1493. filename[i] = 0;
  1494. // create the vmt file.
  1495. FILE *vmtFile = fopen(vmtPath, "w");
  1496. if (vmtFile == NULL)
  1497. {
  1498. return CE_ERROR_WRITING_OUTPUT_FILE;
  1499. }
  1500. // make a copy of the subdir and remove any trailing slash
  1501. char szMaterialsSubDir[ MAX_PATH*2 ];
  1502. V_strcpy_safe( szMaterialsSubDir, pMaterialsSubDir );
  1503. V_StripTrailingSlash( szMaterialsSubDir );
  1504. // fix slashes
  1505. V_FixSlashes( szMaterialsSubDir );
  1506. // write the contents of the file.
  1507. fprintf(vmtFile, "\"UnlitGeneric\"\n{\n\t\"$basetexture\" \"%s%c%s\"\n\t\"$translucent\" \"1\"\n\t\"$ignorez\" \"1\"\n\t\"$vertexcolor\" \"1\"\n\t\"$vertexalpha\" \"1\"\n}\n", szMaterialsSubDir, CORRECT_PATH_SEPARATOR, filename);
  1508. fclose(vmtFile);
  1509. return CE_SUCCESS;
  1510. }
  1511. static void WritePNGData( png_structp png_ptr, png_bytep inBytes, png_size_t byteCountToWrite )
  1512. {
  1513. // Cast pointer
  1514. CUtlBuffer *pBuf = (CUtlBuffer *)png_get_io_ptr( png_ptr );
  1515. Assert( pBuf );
  1516. // Write the bytes
  1517. pBuf->Put( inBytes, byteCountToWrite );
  1518. // What? Put() returns void. No way to detect error?
  1519. }
  1520. static void FlushPNGData( png_structp png_ptr )
  1521. {
  1522. // We're writing to a memory buffer, it's a NOP
  1523. }
  1524. ConversionErrorType ImgUtl_WriteRGBAAsPNGToBuffer( const unsigned char *pRGBAData, int nWidth, int nHeight, CUtlBuffer &bufOutData, int nStride )
  1525. {
  1526. #if !defined( _X360 )
  1527. // Auto detect image stride
  1528. if ( nStride <= 0 )
  1529. {
  1530. nStride = nWidth*4;
  1531. }
  1532. /* could pass pointers to user-defined error handlers instead of NULLs: */
  1533. png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  1534. NULL, NULL, NULL);
  1535. if (png_ptr == NULL)
  1536. {
  1537. return CE_MEMORY_ERROR;
  1538. }
  1539. ConversionErrorType errcode = CE_MEMORY_ERROR;
  1540. png_bytepp row_pointers = NULL;
  1541. png_infop info_ptr = png_create_info_struct(png_ptr);
  1542. if ( !info_ptr )
  1543. {
  1544. errcode = CE_MEMORY_ERROR;
  1545. fail:
  1546. if ( row_pointers )
  1547. {
  1548. free( row_pointers );
  1549. }
  1550. png_destroy_write_struct( &png_ptr, &info_ptr );
  1551. return errcode;
  1552. }
  1553. // We'll use the default setjmp / longjmp error handling.
  1554. if ( setjmp( png_jmpbuf(png_ptr) ) )
  1555. {
  1556. // Error "writing". But since we're writing to a memory bufferm,
  1557. // that just means we must have run out of memory
  1558. errcode = CE_MEMORY_ERROR;
  1559. goto fail;
  1560. }
  1561. // Setup stream writing callbacks
  1562. png_set_write_fn(png_ptr, (void *)&bufOutData, WritePNGData, FlushPNGData);
  1563. // Setup info structure
  1564. png_set_IHDR(png_ptr, info_ptr, nWidth, nHeight, 8, PNG_COLOR_TYPE_RGB_ALPHA,
  1565. PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  1566. // !FIXME! Here we really should scan for the common case of
  1567. // an opaque image (all alpha=255) and strip the alpha channel
  1568. // in that case.
  1569. // Write the file header information.
  1570. png_write_info(png_ptr, info_ptr);
  1571. row_pointers = (png_bytepp)malloc( nHeight*sizeof(png_bytep) );
  1572. if ( row_pointers == NULL )
  1573. {
  1574. errcode = CE_MEMORY_ERROR;
  1575. goto fail;
  1576. }
  1577. /* set the individual row_pointers to point at the correct offsets */
  1578. for ( int i = 0; i < nHeight; ++i)
  1579. row_pointers[i] = const_cast<unsigned char *>(pRGBAData + i*nStride);
  1580. // Write the image
  1581. png_write_image(png_ptr, row_pointers);
  1582. /* It is REQUIRED to call this to finish writing the rest of the file */
  1583. png_write_end(png_ptr, info_ptr);
  1584. // Clean up, and we're done
  1585. free( row_pointers );
  1586. row_pointers = NULL;
  1587. png_destroy_write_struct(&png_ptr, &info_ptr);
  1588. return CE_SUCCESS;
  1589. #else
  1590. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1591. #endif
  1592. }
  1593. //-----------------------------------------------------------------------------
  1594. // Purpose: Initialize destination --- called by jpeg_start_compress
  1595. // before any data is actually written.
  1596. //-----------------------------------------------------------------------------
  1597. METHODDEF(void) init_destination (j_compress_ptr cinfo)
  1598. {
  1599. JPEGDestinationManager_t *dest = ( JPEGDestinationManager_t *) cinfo->dest;
  1600. // Allocate the output buffer --- it will be released when done with image
  1601. dest->buffer = (byte *)
  1602. (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
  1603. JPEG_OUTPUT_BUF_SIZE * sizeof(byte));
  1604. dest->pub.next_output_byte = dest->buffer;
  1605. dest->pub.free_in_buffer = JPEG_OUTPUT_BUF_SIZE;
  1606. }
  1607. //-----------------------------------------------------------------------------
  1608. // Purpose: Empty the output buffer --- called whenever buffer fills up.
  1609. // Input : boolean -
  1610. //-----------------------------------------------------------------------------
  1611. METHODDEF(boolean) empty_output_buffer (j_compress_ptr cinfo)
  1612. {
  1613. JPEGDestinationManager_t *dest = ( JPEGDestinationManager_t * ) cinfo->dest;
  1614. CUtlBuffer *buf = dest->pBuffer;
  1615. // Add some data
  1616. buf->Put( dest->buffer, JPEG_OUTPUT_BUF_SIZE );
  1617. dest->pub.next_output_byte = dest->buffer;
  1618. dest->pub.free_in_buffer = JPEG_OUTPUT_BUF_SIZE;
  1619. return TRUE;
  1620. }
  1621. //-----------------------------------------------------------------------------
  1622. // Purpose: Terminate destination --- called by jpeg_finish_compress
  1623. // after all data has been written. Usually needs to flush buffer.
  1624. //
  1625. // NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
  1626. // application must deal with any cleanup that should happen even
  1627. // for error exit.
  1628. //-----------------------------------------------------------------------------
  1629. METHODDEF(void) term_destination (j_compress_ptr cinfo)
  1630. {
  1631. JPEGDestinationManager_t *dest = (JPEGDestinationManager_t *) cinfo->dest;
  1632. size_t datacount = JPEG_OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
  1633. CUtlBuffer *buf = dest->pBuffer;
  1634. /* Write any data remaining in the buffer */
  1635. if (datacount > 0)
  1636. {
  1637. buf->Put( dest->buffer, datacount );
  1638. }
  1639. }
  1640. //-----------------------------------------------------------------------------
  1641. // Purpose: Set up functions for writing data to a CUtlBuffer instead of FILE *
  1642. //-----------------------------------------------------------------------------
  1643. GLOBAL(void) jpeg_UtlBuffer_dest (j_compress_ptr cinfo, CUtlBuffer *pBuffer )
  1644. {
  1645. JPEGDestinationManager_t *dest;
  1646. /* The destination object is made permanent so that multiple JPEG images
  1647. * can be written to the same file without re-executing jpeg_stdio_dest.
  1648. * This makes it dangerous to use this manager and a different destination
  1649. * manager serially with the same JPEG object, because their private object
  1650. * sizes may be different. Caveat programmer.
  1651. */
  1652. if (cinfo->dest == NULL) { /* first time for this JPEG object? */
  1653. cinfo->dest = (struct jpeg_destination_mgr *)
  1654. (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
  1655. sizeof(JPEGDestinationManager_t));
  1656. }
  1657. dest = ( JPEGDestinationManager_t * ) cinfo->dest;
  1658. dest->pub.init_destination = init_destination;
  1659. dest->pub.empty_output_buffer = empty_output_buffer;
  1660. dest->pub.term_destination = term_destination;
  1661. dest->pBuffer = pBuffer;
  1662. }
  1663. //-----------------------------------------------------------------------------
  1664. // Purpose: Write three channel RGB data to a JPEG file
  1665. //-----------------------------------------------------------------------------
  1666. bool ImgUtl_WriteRGBToJPEG( unsigned char *pSrcBuf, unsigned int nSrcWidth, unsigned int nSrcHeight, const char *lpszFilename )
  1667. {
  1668. CUtlBuffer dstBuf;
  1669. JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s]
  1670. int row_stride; // physical row width in image buffer
  1671. // stderr handler
  1672. struct jpeg_error_mgr jerr;
  1673. // compression data structure
  1674. struct jpeg_compress_struct cinfo;
  1675. row_stride = nSrcWidth * 3; // JSAMPLEs per row in image_buffer
  1676. // point at stderr
  1677. cinfo.err = jpeg_std_error(&jerr);
  1678. // create compressor
  1679. jpeg_create_compress(&cinfo);
  1680. // Hook CUtlBuffer to compression
  1681. jpeg_UtlBuffer_dest(&cinfo, &dstBuf );
  1682. // image width and height, in pixels
  1683. cinfo.image_width = nSrcWidth;
  1684. cinfo.image_height = nSrcHeight;
  1685. // RGB is 3 component
  1686. cinfo.input_components = 3;
  1687. // # of color components per pixel
  1688. cinfo.in_color_space = JCS_RGB;
  1689. // Apply settings
  1690. jpeg_set_defaults(&cinfo);
  1691. jpeg_set_quality(&cinfo, 100, TRUE );
  1692. // Start compressor
  1693. jpeg_start_compress(&cinfo, TRUE);
  1694. // Write scanlines
  1695. while ( cinfo.next_scanline < cinfo.image_height )
  1696. {
  1697. row_pointer[ 0 ] = &pSrcBuf[ cinfo.next_scanline * row_stride ];
  1698. jpeg_write_scanlines( &cinfo, row_pointer, 1 );
  1699. }
  1700. // Finalize image
  1701. jpeg_finish_compress(&cinfo);
  1702. // Cleanup
  1703. jpeg_destroy_compress(&cinfo);
  1704. return CE_SUCCESS;
  1705. }
  1706. ConversionErrorType ImgUtl_WriteRGBAAsJPEGToBuffer( const unsigned char *pRGBAData, int nWidth, int nHeight, CUtlBuffer &bufOutData, int nStride )
  1707. {
  1708. #if !defined( _X360 )
  1709. JSAMPROW row_pointer[1]; // pointer to JSAMPLE row[s]
  1710. int row_stride; // physical row width in image buffer
  1711. // stderr handler
  1712. struct jpeg_error_mgr jerr;
  1713. // compression data structure
  1714. struct jpeg_compress_struct cinfo;
  1715. row_stride = nWidth * 4;
  1716. // point at stderr
  1717. cinfo.err = jpeg_std_error(&jerr);
  1718. // create compressor
  1719. jpeg_create_compress(&cinfo);
  1720. // Hook CUtlBuffer to compression
  1721. jpeg_UtlBuffer_dest(&cinfo, &bufOutData );
  1722. // image width and height, in pixels
  1723. cinfo.image_width = nWidth;
  1724. cinfo.image_height = nHeight;
  1725. // RGB is 3 component
  1726. cinfo.input_components = 3;
  1727. // # of color components per pixel
  1728. cinfo.in_color_space = JCS_RGB;
  1729. // Apply settings
  1730. jpeg_set_defaults(&cinfo);
  1731. jpeg_set_quality(&cinfo, 100, TRUE );
  1732. // Start compressor
  1733. jpeg_start_compress(&cinfo, TRUE);
  1734. // Write scanlines
  1735. unsigned char *pDstRow = (unsigned char *)malloc( sizeof(unsigned char) * nWidth * 4 );
  1736. while ( cinfo.next_scanline < cinfo.image_height )
  1737. {
  1738. const unsigned char *pSrcRow = &(pRGBAData[cinfo.next_scanline * row_stride]);
  1739. // convert row from RGBA to RGB
  1740. for ( int x = nWidth-1 ; x >= 0 ; --x )
  1741. {
  1742. pDstRow[x*3+2] = pSrcRow[x*4+2];
  1743. pDstRow[x*3+1] = pSrcRow[x*4+1];
  1744. pDstRow[x*3] = pSrcRow[x*4];
  1745. }
  1746. row_pointer[ 0 ] = pDstRow;
  1747. jpeg_write_scanlines( &cinfo, row_pointer, 1 );
  1748. }
  1749. // Finalize image
  1750. jpeg_finish_compress(&cinfo);
  1751. // Cleanup
  1752. jpeg_destroy_compress(&cinfo);
  1753. free( pDstRow );
  1754. return CE_SUCCESS;
  1755. #else
  1756. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1757. #endif
  1758. }
  1759. ConversionErrorType ImgUtl_LoadBitmap( const char *pszFilename, Bitmap_t &bitmap )
  1760. {
  1761. bitmap.Clear();
  1762. ConversionErrorType nErrorCode;
  1763. int width, height;
  1764. unsigned char *buffer = ImgUtl_ReadImageAsRGBA( pszFilename, width, height, nErrorCode );
  1765. if ( nErrorCode != CE_SUCCESS )
  1766. {
  1767. return nErrorCode;
  1768. }
  1769. // Install the buffer into the bitmap, and transfer ownership
  1770. bitmap.SetBuffer( width, height, IMAGE_FORMAT_RGBA8888, buffer, true, width*4 );
  1771. return CE_SUCCESS;
  1772. }
  1773. static ConversionErrorType ImgUtl_LoadJPEGBitmapFromBuffer( CUtlBuffer &fileData, Bitmap_t &bitmap )
  1774. {
  1775. // @todo implement
  1776. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1777. }
  1778. static ConversionErrorType ImgUtl_SaveJPEGBitmapToBuffer( CUtlBuffer &fileData, const Bitmap_t &bitmap )
  1779. {
  1780. if ( !bitmap.IsValid() )
  1781. {
  1782. Assert( bitmap.IsValid() );
  1783. return CE_CANT_OPEN_SOURCE_FILE;
  1784. }
  1785. // Sorry, only RGBA8888 supported right now
  1786. if ( bitmap.Format() != IMAGE_FORMAT_RGBA8888 )
  1787. {
  1788. Assert( bitmap.Format() == IMAGE_FORMAT_RGBA8888 );
  1789. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1790. }
  1791. // Do it
  1792. ConversionErrorType result = ImgUtl_WriteRGBAAsJPEGToBuffer(
  1793. bitmap.GetBits(),
  1794. bitmap.Width(),
  1795. bitmap.Height(),
  1796. fileData,
  1797. bitmap.Stride()
  1798. );
  1799. return result;
  1800. }
  1801. ConversionErrorType ImgUtl_LoadBitmapFromBuffer( CUtlBuffer &fileData, Bitmap_t &bitmap, ImageFileFormat eImageFileFormat )
  1802. {
  1803. switch ( eImageFileFormat )
  1804. {
  1805. case kImageFileFormat_PNG:
  1806. return ImgUtl_LoadPNGBitmapFromBuffer( fileData, bitmap );
  1807. case kImageFileFormat_JPG:
  1808. return ImgUtl_LoadJPEGBitmapFromBuffer( fileData, bitmap );
  1809. }
  1810. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1811. }
  1812. ConversionErrorType ImgUtl_SaveBitmapToBuffer( CUtlBuffer &fileData, const Bitmap_t &bitmap, ImageFileFormat eImageFileFormat )
  1813. {
  1814. switch ( eImageFileFormat )
  1815. {
  1816. case kImageFileFormat_PNG:
  1817. return ImgUtl_SavePNGBitmapToBuffer( fileData, bitmap );
  1818. case kImageFileFormat_JPG:
  1819. return ImgUtl_SaveJPEGBitmapToBuffer( fileData, bitmap );
  1820. }
  1821. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1822. }
  1823. ConversionErrorType ImgUtl_LoadPNGBitmapFromBuffer( CUtlBuffer &fileData, Bitmap_t &bitmap )
  1824. {
  1825. bitmap.Clear();
  1826. ConversionErrorType nErrorCode;
  1827. int width, height;
  1828. unsigned char *buffer = ImgUtl_ReadPNGAsRGBAFromBuffer( fileData, width, height, nErrorCode );
  1829. if ( nErrorCode != CE_SUCCESS )
  1830. {
  1831. return nErrorCode;
  1832. }
  1833. // Install the buffer into the bitmap, and transfer ownership
  1834. bitmap.SetBuffer( width, height, IMAGE_FORMAT_RGBA8888, buffer, true, width*4 );
  1835. return CE_SUCCESS;
  1836. }
  1837. ConversionErrorType ImgUtl_SavePNGBitmapToBuffer( CUtlBuffer &fileData, const Bitmap_t &bitmap )
  1838. {
  1839. if ( !bitmap.IsValid() )
  1840. {
  1841. Assert( bitmap.IsValid() );
  1842. return CE_CANT_OPEN_SOURCE_FILE;
  1843. }
  1844. // Sorry, only RGBA8888 supported right now
  1845. if ( bitmap.Format() != IMAGE_FORMAT_RGBA8888 )
  1846. {
  1847. Assert( bitmap.Format() == IMAGE_FORMAT_RGBA8888 );
  1848. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1849. }
  1850. // Do it
  1851. ConversionErrorType result = ImgUtl_WriteRGBAAsPNGToBuffer(
  1852. bitmap.GetBits(),
  1853. bitmap.Width(),
  1854. bitmap.Height(),
  1855. fileData,
  1856. bitmap.Stride()
  1857. );
  1858. return result;
  1859. }
  1860. ConversionErrorType ImgUtl_ResizeBitmap( Bitmap_t &destBitmap, int nWidth, int nHeight, const Bitmap_t *pImgSource )
  1861. {
  1862. // Check for resizing in place, then save off data into a temp
  1863. Bitmap_t temp;
  1864. if ( pImgSource == NULL || pImgSource == &destBitmap )
  1865. {
  1866. temp.MakeLogicalCopyOf( destBitmap, destBitmap.GetOwnsBuffer() );
  1867. pImgSource = &temp;
  1868. }
  1869. // No source image?
  1870. if ( !pImgSource->IsValid() )
  1871. {
  1872. Assert( pImgSource->IsValid() );
  1873. return CE_CANT_OPEN_SOURCE_FILE;
  1874. }
  1875. // Sorry, we're using an existing rescaling routine that
  1876. // only withs for RGBA images with assumed stride
  1877. if (
  1878. pImgSource->Format() != IMAGE_FORMAT_RGBA8888
  1879. || pImgSource->Stride() != pImgSource->Width()*4
  1880. ) {
  1881. Assert( pImgSource->Format() == IMAGE_FORMAT_RGBA8888 );
  1882. Assert( pImgSource->Stride() == pImgSource->Width()*4 );
  1883. return CE_SOURCE_FILE_FORMAT_NOT_SUPPORTED;
  1884. }
  1885. // Allocate buffer
  1886. destBitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGBA8888 );
  1887. // Something wrong?
  1888. if ( !destBitmap.IsValid() )
  1889. {
  1890. Assert( destBitmap.IsValid() );
  1891. return CE_MEMORY_ERROR;
  1892. }
  1893. // Do it
  1894. return ImgUtl_StretchRGBAImage(
  1895. pImgSource->GetBits(), pImgSource->Width(), pImgSource->Height(),
  1896. destBitmap.GetBits(), destBitmap.Width(), destBitmap.Height()
  1897. );
  1898. }