Counter Strike : Global Offensive Source Code
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.

778 lines
19 KiB

  1. //======= Copyright 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "bitmap/bitmap.h"
  7. #include "dbg.h"
  8. #include "utlbuffer.h"
  9. #include "bitmap/psd.h"
  10. #include "bitmap/tgaloader.h"
  11. // Should be last include
  12. #include "tier0/memdbgon.h"
  13. bool Bitmap_t::IsValid() const
  14. {
  15. if ( m_nWidth <= 0 || m_nHeight <= 0 || m_pBits == NULL )
  16. {
  17. Assert( m_nWidth == 0 );
  18. Assert( m_nHeight == 0 );
  19. Assert( m_pBits == NULL );
  20. return false;
  21. }
  22. return true;
  23. }
  24. void Bitmap_t::Clear()
  25. {
  26. if ( m_pBits && m_bOwnsBuffer )
  27. {
  28. free( m_pBits );
  29. }
  30. Reset();
  31. }
  32. void Bitmap_t::Init( int xs, int ys, ImageFormat imageFormat, int nStride )
  33. {
  34. // Check for bogus allocation sizes
  35. if (xs <= 0 || ys <= 0 )
  36. {
  37. Assert( xs == 0 );
  38. Assert( ys == 0 );
  39. Clear();
  40. return;
  41. }
  42. int nPixSize = ImageLoader::SizeInBytes( imageFormat );
  43. // Auto detect stride
  44. if ( nStride == 0 )
  45. {
  46. nStride = nPixSize * xs;
  47. }
  48. // Check for NOP
  49. if (
  50. m_pBits
  51. && m_bOwnsBuffer
  52. && m_nWidth == xs
  53. && m_nHeight == ys
  54. && nStride == m_nStride
  55. && nPixSize == m_nPixelSize )
  56. {
  57. // We're already got a buffer of the right size.
  58. // The only thing that might be wrong is the pixel format.
  59. m_ImageFormat = imageFormat;
  60. return;
  61. }
  62. // Free up anything already allocated
  63. Clear();
  64. // Remember dimensions and pixel format
  65. m_nWidth = xs;
  66. m_nHeight = ys;
  67. m_ImageFormat = imageFormat;
  68. m_nPixelSize = nPixSize;
  69. m_nStride = nStride;
  70. // Allocate buffer. Because this is a PC game,
  71. // failure is impossible....right?
  72. m_pBits = (byte *)malloc( ys * m_nStride );
  73. // Assume ownership
  74. m_bOwnsBuffer = true;
  75. }
  76. void Bitmap_t::SetBuffer( int nWidth, int nHeight, ImageFormat imageFormat, unsigned char *pBits, bool bAssumeOwnership, int nStride )
  77. {
  78. Assert( pBits );
  79. Assert( nWidth > 0 );
  80. Assert( nHeight > 0 );
  81. // Free up anything already allocated
  82. Clear();
  83. // Remember dimensions and pixel format
  84. m_nWidth = nWidth;
  85. m_nHeight = nHeight;
  86. m_ImageFormat = imageFormat;
  87. m_nPixelSize = ImageLoader::SizeInBytes( imageFormat );
  88. if ( nStride == 0 )
  89. {
  90. m_nStride = m_nPixelSize * nWidth;
  91. }
  92. else
  93. {
  94. m_nStride = nStride;
  95. }
  96. // Set our buffer pointer
  97. m_pBits = pBits;
  98. // Assume ownership of the buffer, if requested
  99. m_bOwnsBuffer = bAssumeOwnership;
  100. // We should be good to go
  101. Assert( IsValid() );
  102. }
  103. Color Bitmap_t::GetColor( int x, int y ) const
  104. {
  105. Assert( x >= 0 && x < m_nWidth );
  106. Assert( y >= 0 && y < m_nHeight );
  107. Assert( m_pBits );
  108. // Get pointer to pixel data
  109. byte *ptr = m_pBits + (y*m_nStride) + x* m_nPixelSize;
  110. // Check supported image formats
  111. switch ( m_ImageFormat )
  112. {
  113. case IMAGE_FORMAT_RGBA8888:
  114. return Color( ptr[0], ptr[1], ptr[2], ptr[3] );
  115. case IMAGE_FORMAT_ABGR8888:
  116. return Color( ptr[3], ptr[2], ptr[1], ptr[0] );
  117. default:
  118. Assert( !"Unsupport image format!");
  119. return Color( 255,0,255,255 );
  120. }
  121. }
  122. void Bitmap_t::SetColor( int x, int y, Color c )
  123. {
  124. Assert( x >= 0 && x < m_nWidth );
  125. Assert( y >= 0 && y < m_nHeight );
  126. Assert( m_pBits );
  127. // Get pointer to pixel data
  128. byte *ptr = m_pBits + (y*m_nStride) + x* m_nPixelSize;
  129. // Check supported image formats
  130. switch ( m_ImageFormat )
  131. {
  132. case IMAGE_FORMAT_RGBA8888:
  133. ptr[0] = c.r();
  134. ptr[1] = c.g();
  135. ptr[2] = c.b();
  136. ptr[3] = c.a();
  137. break;
  138. case IMAGE_FORMAT_ABGR8888:
  139. ptr[0] = c.a();
  140. ptr[1] = c.b();
  141. ptr[2] = c.g();
  142. ptr[3] = c.r();
  143. break;
  144. default:
  145. Assert( !"Unsupport image format!");
  146. break;
  147. }
  148. }
  149. //bool LoadVTF( const char *pszFilename )
  150. //{
  151. //
  152. // // Load the raw file data
  153. // CUtlBuffer fileData;
  154. // if ( !filesystem->ReadFile( pszFilename, "game", fileData ) )
  155. // {
  156. // Warning( "Failed to load %s\n", pszFilename);
  157. // return false;
  158. // }
  159. //
  160. // return LoadVTFFromBuffer( fileData, pszFilename );
  161. //}
  162. //
  163. //bool LoadVTFFromBuffer( CUtlBuffer fileData, const char *pszDebugName = "buffer" )
  164. //{
  165. //
  166. // // Parse it into VTF object
  167. // IVTFTexture *pVTFTexture( CreateVTFTexture() );
  168. // if ( !pVTFTexture->Unserialize( fileData ) )
  169. // {
  170. // DestroyVTFTexture( pVTFTexture );
  171. // Warning( "Failed to deserialize VTF %s\n", pszDebugName);
  172. // return false;
  173. // }
  174. //
  175. // // We are re-reading our own files, so they should be 8888's
  176. // if ( pVTFTexture->Format() != IMAGE_FORMAT_RGBA8888 )
  177. // {
  178. // DestroyVTFTexture( pVTFTexture );
  179. // Warning( "%s isn't RGBA8888\n", pszDebugName);
  180. // return false;
  181. // }
  182. //
  183. // // Copy the image data
  184. // Allocate( pVTFTexture->Width(), pVTFTexture->Height() );
  185. // for ( int y = 0 ; y < m_nHeight ; ++y )
  186. // {
  187. // memcpy( PixPtr(0, y), pVTFTexture->ImageData(0, 0, 0, 0, y), m_nWidth*4 );
  188. // }
  189. //
  190. // // Clean up
  191. // DestroyVTFTexture( pVTFTexture );
  192. // return true;
  193. //}
  194. //
  195. //bool SaveVTF( CUtlBuffer &outBuffer )
  196. //{
  197. // // Create the VTF to write into
  198. // IVTFTexture *pVTFTexture( CreateVTFTexture() );
  199. // const int nFlags = TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_SRGB;
  200. // if ( !pVTFTexture->Init( m_nWidth, m_nHeight, 1, IMAGE_FORMAT_RGBA8888, nFlags, 1, 1 ) )
  201. // {
  202. // DestroyVTFTexture( pVTFTexture );
  203. // return false;
  204. // }
  205. //
  206. // // write the rgba image to the vtf texture using the pixel writer
  207. // CPixelWriter pixelWriter;
  208. // pixelWriter.SetPixelMemory( pVTFTexture->Format(), pVTFTexture->ImageData(), pVTFTexture->RowSizeInBytes( 0 ) );
  209. //
  210. // for (int y = 0; y < m_nHeight; ++y)
  211. // {
  212. // pixelWriter.Seek( 0, y );
  213. // for (int x = 0; x < m_nWidth; ++x)
  214. // {
  215. // Color c = GetPix( x, y );
  216. // pixelWriter.WritePixel( c.r(), c.g(), c.b(), c.a() );
  217. // }
  218. // }
  219. //
  220. // // Serialize to the buffer
  221. // if ( !pVTFTexture->Serialize( outBuffer ) )
  222. // {
  223. // DestroyVTFTexture( pVTFTexture );
  224. // return false;
  225. // }
  226. // DestroyVTFTexture( pVTFTexture );
  227. // return true;
  228. //}
  229. //void Resize( int nNewSizeX, int nNewSizeY, const Image *pImgSrc = NULL )
  230. //{
  231. // if ( pImgSrc == NULL )
  232. // {
  233. // pImgSrc = this;
  234. // }
  235. //
  236. // if ( nNewSizeX == m_nWidth && nNewSizeY == m_nHeight && pImgSrc == this )
  237. // {
  238. // return;
  239. // }
  240. //
  241. // byte *pNewData = (byte *)malloc( nNewSizeX * nNewSizeY * 4 );
  242. // ImgUtl_StretchRGBAImage( pImgSrc->m_pBits, pImgSrc->m_nWidth, pImgSrc->m_nHeight, pNewData, nNewSizeX, nNewSizeY );
  243. // Clear();
  244. // m_pBits = pNewData;
  245. // m_nWidth = nNewSizeX;
  246. // m_nHeight = nNewSizeY;
  247. //}
  248. //
  249. //void Crop( int x0, int y0, int nNewSizeX, int nNewSizeY, const Image *pImgSrc )
  250. //{
  251. // if ( pImgSrc == NULL )
  252. // {
  253. // pImgSrc = this;
  254. // }
  255. //
  256. // if ( nNewSizeX == m_nWidth && nNewSizeY == m_nHeight && pImgSrc == this )
  257. // {
  258. // return;
  259. // }
  260. //
  261. //
  262. // Assert( x0 >= 0 );
  263. // Assert( y0 >= 0 );
  264. // Assert( x0 + nNewSizeX <= pImgSrc->m_nWidth );
  265. // Assert( y0 + nNewSizeY <= pImgSrc->m_nHeight );
  266. //
  267. // // Allocate new buffer
  268. // int nRowSize = nNewSizeX * 4;
  269. // byte *pNewData = (byte *)malloc( nNewSizeY * nRowSize );
  270. //
  271. // // Copy data, one row at a time
  272. // for ( int y = 0 ; y < nNewSizeY ; ++y )
  273. // {
  274. // memcpy( pNewData + y*nRowSize, pImgSrc->PixPtr(x0, y0+y), nRowSize );
  275. // }
  276. //
  277. // // Replace current buffer with the new one
  278. // Clear();
  279. // m_pBits = pNewData;
  280. // m_nWidth = nNewSizeX;
  281. // m_nHeight = nNewSizeY;
  282. //}
  283. void Bitmap_t::MakeLogicalCopyOf( Bitmap_t &src, bool bTransferBufferOwnership )
  284. {
  285. // What does it mean to make a logical copy of an
  286. // invalid bitmap? I'll tell you what it means: you have a bug.
  287. Assert( src.IsValid() );
  288. // Free up anything we already own
  289. Clear();
  290. // Copy all of the member variables so we are
  291. // a logical copy of the source bitmap
  292. m_nWidth = src.m_nWidth;
  293. m_nHeight = src.m_nHeight;
  294. m_nPixelSize = src.m_nPixelSize;
  295. m_nStride = src.m_nStride;
  296. m_ImageFormat = src.m_ImageFormat;
  297. m_pBits = src.m_pBits;
  298. Assert( !m_bOwnsBuffer );
  299. // Check for assuming ownership of the buffer
  300. if ( bTransferBufferOwnership )
  301. {
  302. if ( src.m_bOwnsBuffer )
  303. {
  304. m_bOwnsBuffer = true;
  305. src.m_bOwnsBuffer = false;
  306. }
  307. else
  308. {
  309. // They don't own the buffer? Then who does?
  310. // Maybe nobody, and it would safe to assume
  311. // ownership. But more than likely, this is a
  312. // bug.
  313. Assert( src.m_bOwnsBuffer );
  314. // And a leak is better than a double-free.
  315. // Don't assume ownership of the buffer.
  316. }
  317. }
  318. }
  319. void Bitmap_t::Crop( int x0, int y0, int nWidth, int nHeight, const Bitmap_t *pImgSource )
  320. {
  321. // Check for cropping in place, then save off our data to a temp
  322. Bitmap_t temp;
  323. if ( pImgSource == this || !pImgSource )
  324. {
  325. temp.MakeLogicalCopyOf( *this, m_bOwnsBuffer );
  326. pImgSource = &temp;
  327. }
  328. // No source image?
  329. if ( !pImgSource->IsValid() )
  330. {
  331. Assert( pImgSource->IsValid() );
  332. return;
  333. }
  334. // Sanity check crop rectangle
  335. Assert( x0 >= 0 );
  336. Assert( y0 >= 0 );
  337. Assert( x0 + nWidth <= pImgSource->Width() );
  338. Assert( y0 + nHeight <= pImgSource->Height() );
  339. // Allocate buffer
  340. Init( nWidth, nHeight, pImgSource->Format() );
  341. // Something wrong?
  342. if ( !IsValid() )
  343. {
  344. Assert( IsValid() );
  345. return;
  346. }
  347. // Copy the data a row at a time
  348. int nRowSize = m_nWidth * m_nPixelSize;
  349. for ( int y = 0 ; y < m_nHeight ; ++y )
  350. {
  351. memcpy( GetPixel(y,0), pImgSource->GetPixel( x0, y + y0 ), nRowSize );
  352. }
  353. }
  354. void Bitmap_t::SetPixelData( const Bitmap_t &src, int nSrcX1, int nSrcY1, int nCopySizeX, int nCopySizeY, int nDestX1, int nDestY1 )
  355. {
  356. // Safety
  357. if ( !src.IsValid() )
  358. {
  359. Assert( src.IsValid() );
  360. return;
  361. }
  362. if ( !IsValid() )
  363. {
  364. Assert( IsValid() );
  365. return;
  366. }
  367. // You need to specify a valid source rectangle, we cannot clip that for you
  368. if ( nSrcX1 < 0 || nSrcY1 < 0 || nSrcX1 + nCopySizeX > src.Width() || nSrcY1 + nCopySizeY > src.Height() )
  369. {
  370. Assert( nSrcX1 >= 0 );
  371. Assert( nSrcY1 >= 0 );
  372. Assert( nSrcX1 + nCopySizeX <= src.Width() );
  373. Assert( nSrcY1 + nCopySizeY <= src.Height() );
  374. return;
  375. }
  376. // But we can clip the rectangle if it extends outside the destination image in a perfectly
  377. // reasonable way
  378. if ( nDestX1 < 0 )
  379. {
  380. nCopySizeX += nDestX1;
  381. nDestX1 = 0;
  382. }
  383. if ( nDestX1 + nCopySizeX > Width() )
  384. {
  385. nCopySizeX = Width() - nDestX1;
  386. }
  387. if ( nDestY1 < 0 )
  388. {
  389. nCopySizeY += nDestY1;
  390. nDestY1 = 0;
  391. }
  392. if ( nDestY1 + nCopySizeY > Height() )
  393. {
  394. nCopySizeY = Height() - nDestY1;
  395. }
  396. if ( nCopySizeX <= 0 || nCopySizeY <= 0 )
  397. {
  398. return;
  399. }
  400. // Copy the pixel data
  401. for ( int y = 0 ; y < nCopySizeY ; ++y )
  402. {
  403. // Wow, this could be a lot faster in the common case
  404. // that the pixe formats are the same. But...this code
  405. // is simple and works, and is NOT the root of all evil.
  406. for ( int x = 0 ; x < nCopySizeX ; ++x )
  407. {
  408. Color c = src.GetColor( nSrcX1 + x, nSrcY1 + y );
  409. SetColor( nDestX1 + x, nDestY1 + y, c );
  410. }
  411. }
  412. }
  413. void Bitmap_t::SetPixelData( const Bitmap_t &src, int nDestX1, int nDestY1 )
  414. {
  415. SetPixelData( src, 0, 0, src.Width(), src.Height(), nDestX1, nDestY1 );
  416. }
  417. //-----------------------------------------------------------------------------
  418. // Returns true if it's a PFM file
  419. //-----------------------------------------------------------------------------
  420. bool IsPFMFile( CUtlBuffer &buf )
  421. {
  422. int nGet = buf.TellGet();
  423. char c0 = buf.GetChar();
  424. char c1 = buf.GetChar();
  425. char c2 = buf.GetChar();
  426. buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
  427. return ( c0 == 'P' && ( c1 == 'F' || c1 == 'f' ) && c2 == 0xA );
  428. }
  429. //-----------------------------------------------------------------------------
  430. // This reads an integer from a binary CUtlBuffer.
  431. //-----------------------------------------------------------------------------
  432. static int ReadIntFromUtlBuffer( CUtlBuffer &buf )
  433. {
  434. int val = 0;
  435. int c;
  436. while( buf.IsValid() )
  437. {
  438. c = buf.GetChar();
  439. if( c >= '0' && c <= '9' )
  440. {
  441. val = val * 10 + ( c - '0' );
  442. }
  443. else
  444. {
  445. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, -1 );
  446. break;
  447. }
  448. }
  449. return val;
  450. }
  451. static inline bool IsWhitespace( char c )
  452. {
  453. return c == ' ' || c == '\t' || c == 10;
  454. }
  455. static void EatWhiteSpace( CUtlBuffer &buf )
  456. {
  457. while( buf.IsValid() )
  458. {
  459. int c = buf.GetChar();
  460. if( !IsWhitespace( c ) )
  461. {
  462. buf.SeekGet( CUtlBuffer::SEEK_CURRENT, -1 );
  463. return;
  464. }
  465. }
  466. return;
  467. }
  468. //-----------------------------------------------------------------------------
  469. // Reads PFM info + advances to the texture bits
  470. //-----------------------------------------------------------------------------
  471. bool PFMGetInfo_AndAdvanceToTextureBits( CUtlBuffer &pfmBuffer, int &nWidth, int &nHeight, ImageFormat &imageFormat )
  472. {
  473. pfmBuffer.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
  474. if( pfmBuffer.GetChar() != 'P' )
  475. {
  476. Assert( 0 );
  477. return false;
  478. }
  479. char c = pfmBuffer.GetChar();
  480. if ( c == 'F' )
  481. {
  482. imageFormat = IMAGE_FORMAT_RGB323232F;
  483. }
  484. else if ( c == 'f' )
  485. {
  486. imageFormat = IMAGE_FORMAT_R32F;
  487. }
  488. else
  489. {
  490. Assert( 0 );
  491. return false;
  492. }
  493. if( pfmBuffer.GetChar() != 0xa )
  494. {
  495. Assert( 0 );
  496. return false;
  497. }
  498. nWidth = ReadIntFromUtlBuffer( pfmBuffer );
  499. EatWhiteSpace( pfmBuffer );
  500. nHeight = ReadIntFromUtlBuffer( pfmBuffer );
  501. // eat crap until the next newline
  502. while( pfmBuffer.IsValid() && pfmBuffer.GetChar() != 0xa )
  503. {
  504. }
  505. int nScale = ReadIntFromUtlBuffer( pfmBuffer );
  506. // eat crap until the next newline
  507. while( pfmBuffer.IsValid() && pfmBuffer.GetChar() != 0xa )
  508. {
  509. }
  510. if ( nScale > 0 )
  511. {
  512. pfmBuffer.SetBigEndian( true );
  513. }
  514. // Here, the buffer should be at the start of the texture data
  515. return true;
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Loads a PFM file into a Bitmap_t as RGBA32323232F data
  519. // PFM source data doesn't have alpha, so we put 1.0f into alpha
  520. //-----------------------------------------------------------------------------
  521. bool PFMReadFileRGBA32323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
  522. {
  523. int nWidth = 0, nHeight = 0;
  524. ImageFormat imageFormat = IMAGE_FORMAT_UNKNOWN;
  525. PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat ); // Read the header (advances us to the texture bits)
  526. Assert( imageFormat == IMAGE_FORMAT_RGB323232F );
  527. bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGBA32323232F ); // Init the bitmap
  528. // NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
  529. // Reading rows in reverse order here is the correct thing to do:
  530. for( int y = ( nHeight - 1 ); y >= 0; y-- )
  531. {
  532. Assert( fileBuffer.IsValid() );
  533. if ( !fileBuffer.IsValid() )
  534. return false;
  535. float *pOut = ( (float *)bitmap.GetBits() ) + y*nWidth*4; // Point to output row
  536. for ( int x = 0; x < nWidth; x++ ) // For every RGB input color
  537. {
  538. fileBuffer.Get( pOut, sizeof( float ) ); // Get red
  539. *pOut *= pfmScale; // Scale into output
  540. pOut++;
  541. fileBuffer.Get( pOut, sizeof( float ) ); // Get green
  542. *pOut *= pfmScale; // Scale into output
  543. pOut++;
  544. fileBuffer.Get( pOut, sizeof( float ) ); // Get blue
  545. *pOut *= pfmScale; // Scale into output
  546. pOut++;
  547. *pOut = 1.0f; // 1.0f into alpha
  548. pOut++;
  549. }
  550. }
  551. return true;
  552. }
  553. //-----------------------------------------------------------------------------
  554. // Loads a PFM file into a Bitmap_t as RGB323232F data
  555. //-----------------------------------------------------------------------------
  556. bool PFMReadFileRGB323232F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
  557. {
  558. // Read the header (advances us to the texture bits)
  559. int nWidth = 0, nHeight = 0;
  560. ImageFormat imageFormat = IMAGE_FORMAT_UNKNOWN;
  561. PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, imageFormat );
  562. Assert( imageFormat == IMAGE_FORMAT_RGB323232F );
  563. // Init the bitmap
  564. bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_RGB323232F );
  565. // Read the texels
  566. for( int y = ( nHeight - 1 ); y >= 0; y-- )
  567. {
  568. Assert( fileBuffer.IsValid() );
  569. if ( !fileBuffer.IsValid() )
  570. return false;
  571. // NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
  572. // Reading rows in reverse order here is the correct thing to do:
  573. float *pRow = ( (float *)bitmap.GetBits() ) + y*nWidth*3;
  574. fileBuffer.Get( pRow, nWidth*3*sizeof( float ) );
  575. for ( int x = 0; x < nWidth*3; x++ )
  576. {
  577. pRow[ x ] *= pfmScale;
  578. }
  579. }
  580. return true;
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Loads the x channel of a PFM file into a Bitmap_t as R32F data
  584. //-----------------------------------------------------------------------------
  585. bool PFMReadFileR32F( CUtlBuffer &fileBuffer, Bitmap_t &bitmap, float pfmScale )
  586. {
  587. // Read the header (advances us to the texture bits)
  588. int nWidth, nHeight;
  589. ImageFormat fileImageFormat; // Format of data in file
  590. PFMGetInfo_AndAdvanceToTextureBits( fileBuffer, nWidth, nHeight, fileImageFormat );
  591. Assert( fileImageFormat == IMAGE_FORMAT_RGB323232F );
  592. // Init the bitmap
  593. bitmap.Init( nWidth, nHeight, IMAGE_FORMAT_R32F );
  594. float flMin = FLOAT32_MAX;
  595. float flMax = FLOAT32_MIN;
  596. // Read the texels, snarfing out just the x component
  597. for( int y = ( nHeight - 1 ); y >= 0; y-- )
  598. {
  599. Assert( fileBuffer.IsValid() );
  600. if ( !fileBuffer.IsValid() )
  601. return false;
  602. // NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
  603. // Reading rows in reverse order here is the correct thing to do:
  604. float *pRow = ( (float *)bitmap.GetBits() ) + y*nWidth;
  605. for ( int x = 0; x < nWidth; x++ )
  606. {
  607. fileBuffer.Get( pRow+x, sizeof( float ) ); // Grab x component and scale
  608. pRow[x] *= pfmScale;
  609. flMin = MIN( pRow[x], flMin );
  610. flMax = MAX( pRow[x], flMax );
  611. float flDummy[2];
  612. fileBuffer.Get( &flDummy, 2*sizeof( float ) ); // Jump past y and z components in file
  613. }
  614. }
  615. printf( "Displacement Range: (%g, %g)\n", flMin, flMax );
  616. return true;
  617. }
  618. //-----------------------------------------------------------------------------
  619. // Loads a PFM file into a Bitmap_t
  620. //-----------------------------------------------------------------------------
  621. bool PFMReadFile( CUtlBuffer &buf, Bitmap_t *pBitmap )
  622. {
  623. // Read the header (advances us to the texture bits)
  624. int nWidth = 0, nHeight = 0;
  625. ImageFormat fmt = IMAGE_FORMAT_UNKNOWN;
  626. PFMGetInfo_AndAdvanceToTextureBits( buf, nWidth, nHeight, fmt );
  627. // Init the bitmap
  628. pBitmap->Init( nWidth, nHeight, fmt );
  629. int nRowBytes = ImageLoader::GetMemRequired( nWidth, 1, 1, 1, fmt );
  630. // Read the texels
  631. for( int y = ( nHeight - 1 ); y >= 0; y-- )
  632. {
  633. Assert( buf.IsValid() );
  634. if ( !buf.IsValid() )
  635. return false;
  636. // NOTE: PFMs are displayed *UPSIDE-DOWN* in Photoshop! (as of CS2... HDRShop gets it right)
  637. // Reading rows in reverse order here is the correct thing to do:
  638. void *pDest = pBitmap->GetBits() + y * nRowBytes;
  639. buf.Get( pDest, nRowBytes );
  640. }
  641. return true;
  642. }
  643. //-----------------------------------------------------------------------------
  644. // Loads a bitmap from an arbitrary file: could be a TGA, PSD, or PFM.
  645. // LoadBitmap autodetects which type
  646. //-----------------------------------------------------------------------------
  647. BitmapFileType_t LoadBitmapFile( CUtlBuffer &buf, Bitmap_t *pBitmap )
  648. {
  649. if ( IsPSDFile( buf ) )
  650. {
  651. // FIXME: Make it actually load the true format
  652. if ( !PSDReadFileRGBA8888( buf, *pBitmap ) )
  653. return BITMAP_FILE_TYPE_UNKNOWN;
  654. return BITMAP_FILE_TYPE_PSD;
  655. }
  656. if ( IsPFMFile( buf ) )
  657. {
  658. if ( !PFMReadFile( buf, pBitmap ) )
  659. return BITMAP_FILE_TYPE_UNKNOWN;
  660. return BITMAP_FILE_TYPE_PFM;
  661. }
  662. // It's not really possible to detect TGAness.. there's no identifier
  663. // Assume TGA file here
  664. int nWidth, nHeight;
  665. ImageFormat fmt;
  666. float flGamma;
  667. int nGet = buf.TellGet();
  668. bool bOk = TGALoader::GetInfo( buf, &nWidth, &nHeight, &fmt, &flGamma );
  669. buf.SeekGet( CUtlBuffer::SEEK_HEAD, nGet );
  670. if ( !bOk )
  671. return BITMAP_FILE_TYPE_UNKNOWN;
  672. // FIXME: TGALoader is incredibly inefficient when trying to just get
  673. // the bits as-is as it loads into RGBA8888 and then color converts out
  674. pBitmap->Init( nWidth, nHeight, fmt );
  675. if ( !TGALoader::Load( pBitmap->GetBits(), buf, nWidth, nHeight, fmt, flGamma, false ) )
  676. return BITMAP_FILE_TYPE_UNKNOWN;
  677. return BITMAP_FILE_TYPE_TGA;
  678. }