TexturePage.cpp 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. // FILE: TexturePage.cpp //////////////////////////////////////////////////////
  19. //-----------------------------------------------------------------------------
  20. //
  21. // Westwood Studios Pacific.
  22. //
  23. // Confidential Information
  24. // Copyright (C) 2001 - All Rights Reserved
  25. //
  26. //-----------------------------------------------------------------------------
  27. //
  28. // Project: ImagePacker
  29. //
  30. // File name: TexturePage.cpp
  31. //
  32. // Created: Colin Day, August 2001
  33. //
  34. // Desc: This class represents a texture that contains packed
  35. // images.
  36. //
  37. //-----------------------------------------------------------------------------
  38. ///////////////////////////////////////////////////////////////////////////////
  39. // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
  40. #include <stdlib.h>
  41. #include <stdio.h>
  42. // USER INCLUDES //////////////////////////////////////////////////////////////
  43. #include "Common/Debug.h"
  44. #include "TexturePage.h"
  45. #include "ImagePacker.h"
  46. // DEFINES ////////////////////////////////////////////////////////////////////
  47. // PRIVATE TYPES //////////////////////////////////////////////////////////////
  48. // PRIVATE DATA ///////////////////////////////////////////////////////////////
  49. // PUBLIC DATA ////////////////////////////////////////////////////////////////
  50. // PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
  51. ///////////////////////////////////////////////////////////////////////////////
  52. // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
  53. ///////////////////////////////////////////////////////////////////////////////
  54. // TexturePage::extendToRowIfOpen =============================================
  55. /** If the pixel at location 'row' is "open", then extend the pixel
  56. * at 'src' to that location
  57. *
  58. * NOTE this assumes 'src' and 'row' are pointers into the same
  59. * buffer and the bits per pixel (buffBPP) are treated as the
  60. * same for both */
  61. //=============================================================================
  62. void TexturePage::extendToRowIfOpen( char *src,
  63. Int buffWidth,
  64. Int buffBPP,
  65. Bool extendAlpha,
  66. Int imageHeight,
  67. UnsignedInt fitBits,
  68. Int srcX, Int srcY )
  69. {
  70. char otherAlpha;
  71. char otherColor[ 3 ];
  72. char *row = NULL;
  73. // sanity
  74. if( src == NULL )
  75. return;
  76. //
  77. // try to extend pixel up and down a row as well, we try to extend
  78. // up if we're in the top half of the image down if we're
  79. // in the bottom half of the image. Note we're not
  80. // allowed to extend outside the image region if we don't have
  81. // a border to extend into
  82. //
  83. if( srcY < imageHeight / 2 &&
  84. (srcY != 0 || BitTest( fitBits, ImageInfo::FIT_YBORDER_TOP )) )
  85. {
  86. // try to extend pixel "up" if that pixel is "open"
  87. row = src + (buffWidth * buffBPP);
  88. } // end if
  89. else if( srcY >= imageHeight / 2 &&
  90. (srcY != imageHeight - 1 ||
  91. BitTest( fitBits, ImageInfo::FIT_YBORDER_BOTTOM )) )
  92. {
  93. // try to extend pixel "down" if that pixel is "open"
  94. row = src - (buffWidth * buffBPP);
  95. } // end else
  96. //
  97. // if a 'row' is available, try to extend the current pixel
  98. // into that location if it's open
  99. //
  100. if( row )
  101. {
  102. // read the pixel at 'row'
  103. if( buffBPP == 4 )
  104. {
  105. otherAlpha = row[ 0 ];
  106. otherColor[ 0 ] = row[ 1 ];
  107. otherColor[ 1 ] = row[ 2 ];
  108. otherColor[ 2 ] = row[ 3 ];
  109. } // end if
  110. else
  111. {
  112. otherColor[ 0 ] = row[ 0 ];
  113. otherColor[ 1 ] = row[ 1 ];
  114. otherColor[ 2 ] = row[ 2 ];
  115. } // end else
  116. //
  117. // see if this pixel is "open", again we prefer to check the
  118. // alpha channel if present, otherwise we say black is "open"
  119. //
  120. Bool otherOpen = FALSE;
  121. if( buffBPP == 4 )
  122. {
  123. if( otherAlpha == 0 )
  124. otherOpen = TRUE;
  125. } // end if
  126. else
  127. {
  128. if( otherColor[ 0 ] == 0 &&
  129. otherColor[ 1 ] == 0 &&
  130. otherColor[ 2 ] == 0 )
  131. otherOpen = TRUE;
  132. } // end else
  133. // copy pixel data from 'src' to 'row' if 'row' is "open"
  134. if( otherOpen == TRUE )
  135. {
  136. char alpha;
  137. char color[ 3 ];
  138. // read the pixel data at 'src'
  139. if( buffBPP == 4 )
  140. {
  141. alpha = src[ 0 ];
  142. color[ 0 ] = src[ 1 ];
  143. color[ 1 ] = src[ 2 ];
  144. color[ 2 ] = src[ 3 ];
  145. } // end if
  146. else
  147. {
  148. color[ 0 ] = src[ 0 ];
  149. color[ 1 ] = src[ 1 ];
  150. color[ 2 ] = src[ 2 ];
  151. } // end else
  152. // copy the pixel to 'row'
  153. if( buffBPP == 4 )
  154. {
  155. if( extendAlpha )
  156. row[ 0 ] = alpha;
  157. row[ 1 ] = color[ 0 ];
  158. row[ 2 ] = color[ 1 ];
  159. row[ 3 ] = color[ 2 ];
  160. } // end if
  161. else
  162. {
  163. row[ 0 ] = color[ 0 ];
  164. row[ 1 ] = color[ 1 ];
  165. row[ 2 ] = color[ 2 ];
  166. } // end else
  167. } // end if, other spot is open, copy it
  168. } // end if, row
  169. } // end extendToRowIfOpen
  170. // TexturePage::extendImageEdges ==============================================
  171. /** We want to extend the image data in destBuffer at the location region
  172. * of image->m_pagePos in "outward" directions, effectively bleeding
  173. * the edges outward.
  174. *
  175. * Note we will not extend outward from the image region UNLESS we
  176. * have a border present on that side (described in the image->m_fitBits
  177. * for the region image->m_pagePos).
  178. */
  179. //=============================================================================
  180. void TexturePage::extendImageEdges( Byte *destBuffer,
  181. Int destWidth,
  182. Int destHeight,
  183. Int destBPP,
  184. ImageInfo *image,
  185. Bool extendAlpha )
  186. {
  187. // sanity
  188. if( destBuffer == NULL || image == NULL )
  189. return;
  190. //
  191. // get the extents that we will loop through on the destination surface,
  192. // those extents are the size of the image, but we have to take into
  193. // account whether or not the destination image was rotated or not
  194. //
  195. Int imageWidth, imageHeight;
  196. if( BitTest( image->m_status, ImageInfo::ROTATED90C ) )
  197. {
  198. imageWidth = image->m_size.y;
  199. imageHeight = image->m_size.x;
  200. } // end if
  201. else
  202. {
  203. imageWidth = image->m_size.x;
  204. imageHeight = image->m_size.y;
  205. } // end else
  206. Int x, y;
  207. char *ptr;
  208. char color[ 3 ];
  209. char alpha;
  210. Bool prevPixel, currPixel;
  211. for( y = 0; y < imageHeight; y++ )
  212. {
  213. // compute beginning of destination row
  214. ptr = destBuffer +
  215. ((destHeight - 1 - (image->m_pagePos.lo.y + y)) * destWidth * destBPP ) +
  216. (image->m_pagePos.lo.x * destBPP);
  217. prevPixel = FALSE;
  218. for( x = 0; x < imageWidth; x++ )
  219. {
  220. // read the pixel
  221. if( destBPP == 4 )
  222. {
  223. alpha = ptr[ 0 ];
  224. color[ 0 ] = ptr[ 1 ];
  225. color[ 1 ] = ptr[ 2 ];
  226. color[ 2 ] = ptr[ 3 ];
  227. } // end if
  228. else
  229. {
  230. color[ 0 ] = ptr[ 0 ];
  231. color[ 1 ] = ptr[ 1 ];
  232. color[ 2 ] = ptr[ 2 ];
  233. } // end else
  234. //
  235. // see wheter or not we have data at this pixel, if we have alpha
  236. // we will use just the mask comparison, if not we will compare the
  237. // colors with black (0,0,0) being an "empty" pixel
  238. //
  239. currPixel = FALSE;
  240. if( destBPP == 4 )
  241. {
  242. if( alpha != 0 )
  243. currPixel = TRUE;
  244. } // end if
  245. else
  246. {
  247. if( color[ 0 ] != 0 &&
  248. color[ 1 ] != 0 &&
  249. color[ 2 ] != 0 )
  250. currPixel = TRUE;
  251. } // end else
  252. //
  253. // if we're at the right edge we will extend this pixel off the
  254. // image to the right border if present, we dont' have to worry about
  255. // the top and bottom edges because they are attempted to be
  256. // extended into when we detect an edge change moving across x
  257. //
  258. if( currPixel == TRUE && x == imageWidth - 1 &&
  259. BitTest( image->m_fitBits, ImageInfo::FIT_XBORDER_RIGHT ) )
  260. {
  261. //
  262. // we are at the right side of the image, we have a pixel here,
  263. // AND we have a border to extend that pixel into
  264. //
  265. if( destBPP == 4 )
  266. {
  267. if( extendAlpha )
  268. *(ptr + 4) = alpha;
  269. *(ptr + 5) = color[ 0 ];
  270. *(ptr + 6) = color[ 1 ];
  271. *(ptr + 7) = color[ 2 ];
  272. } // end if
  273. else
  274. {
  275. *(ptr + 3) = color[ 0 ];
  276. *(ptr + 4) = color[ 1 ];
  277. *(ptr + 5) = color[ 2 ];
  278. } // end else
  279. } // end if
  280. //
  281. // if we have a pixel here, attempt to extend it to the above
  282. // or below row if that spot is empty
  283. //
  284. if( currPixel == TRUE )
  285. extendToRowIfOpen( ptr, destWidth, destBPP, extendAlpha,
  286. imageHeight, image->m_fitBits, x, y );
  287. //
  288. // if we've crossed from empty<->filled either extend that pixel
  289. // left or right
  290. //
  291. if( prevPixel == FALSE && currPixel == TRUE )
  292. {
  293. //
  294. // we've crossed from empty to filled, copy the color of current
  295. // pixel to the position of previous pixel. Note this is not allowed
  296. // when we're at the left edge and we DON'T have a border to copy into
  297. //
  298. if( x != 0 || BitTest( image->m_fitBits, ImageInfo::FIT_XBORDER_LEFT ) )
  299. {
  300. // extend our current pixel to the previous location
  301. if( destBPP == 4 && extendAlpha )
  302. *(ptr - 4) = alpha;
  303. *(ptr - 3) = color[ 0 ];
  304. *(ptr - 2) = color[ 1 ];
  305. *(ptr - 1) = color[ 2 ];
  306. } // end if
  307. } // end if
  308. else if( prevPixel == TRUE && currPixel == FALSE )
  309. {
  310. //
  311. // we've crossed from filled to empty, copy the color of previous
  312. // pixel to the current pixel position.
  313. //
  314. //
  315. // this assert should never happen because if x were 0, we are on the
  316. // first column in this image, and the prevPixel should be FALSE since
  317. // previous would be "off" the image which is by definition "open"
  318. //
  319. DEBUG_ASSERTCRASH( x != 0, ("Coming from off image and detecting right edge!") );
  320. // extend the previous pixel to this location
  321. if( destBPP == 4 )
  322. {
  323. if( extendAlpha )
  324. ptr[ 0 ] = *(ptr - 4);
  325. ptr[ 1 ] = *(ptr - 3);
  326. ptr[ 2 ] = *(ptr - 2);
  327. ptr[ 3 ] = *(ptr - 1);
  328. } // end if
  329. else
  330. {
  331. ptr[ 0 ] = *(ptr - 3);
  332. ptr[ 1 ] = *(ptr - 2);
  333. ptr[ 2 ] = *(ptr - 1);
  334. } // end else
  335. } // end else if
  336. //
  337. // one more time now for a special case in the corners of the extended
  338. // image. since this algorithm goes across extending pixels left, right
  339. // up, and down we must check to see if we have a pixel when our
  340. // source location is in the 4 corners of the image ... if so we will
  341. // extend that pixel DIAGONALLY out to make a complete extended rectangle
  342. //
  343. if( currPixel == TRUE )
  344. {
  345. char *dst = NULL;
  346. // top left corner
  347. if( x == 0 && y == 0 &&
  348. BitTest( image->m_fitBits, ImageInfo::FIT_XBORDER_LEFT ) &&
  349. BitTest( image->m_fitBits, ImageInfo::FIT_YBORDER_TOP ) )
  350. dst = (ptr + (destWidth * destBPP)) - destBPP;
  351. // top right corner
  352. else if( x == imageWidth - 1 && y == 0 &&
  353. BitTest( image->m_fitBits, ImageInfo::FIT_XBORDER_RIGHT ) &&
  354. BitTest( image->m_fitBits, ImageInfo::FIT_YBORDER_TOP ) )
  355. dst = (ptr + (destWidth * destBPP)) + destBPP;
  356. // bottom right corner
  357. else if( x == imageWidth - 1 && y == imageHeight - 1 &&
  358. BitTest( image->m_fitBits, ImageInfo::FIT_XBORDER_RIGHT ) &&
  359. BitTest( image->m_fitBits, ImageInfo::FIT_YBORDER_BOTTOM ) )
  360. dst = (ptr - (destWidth * destBPP)) + destBPP;
  361. // bottom left corner
  362. else if( x == 0 && y == imageHeight - 1 &&
  363. BitTest( image->m_fitBits, ImageInfo::FIT_XBORDER_LEFT ) &&
  364. BitTest( image->m_fitBits, ImageInfo::FIT_YBORDER_BOTTOM ) )
  365. dst = (ptr - (destWidth * destBPP)) - destBPP;
  366. // copy the pixel at 'ptr' to 'dst' for the diagonal extend
  367. if( dst )
  368. {
  369. if( destBPP == 4 )
  370. {
  371. if( extendAlpha )
  372. dst[ 0 ] = alpha;
  373. dst[ 1 ] = color[ 0 ];
  374. dst[ 2 ] = color[ 1 ];
  375. dst[ 3 ] = color[ 2 ];
  376. } // end if
  377. else
  378. {
  379. dst[ 0 ] = color[ 0 ];
  380. dst[ 1 ] = color[ 1 ];
  381. dst[ 2 ] = color[ 2 ];
  382. } // end else
  383. } // end if dst
  384. } // end if
  385. // move to the next pixel
  386. ptr += destBPP;
  387. //
  388. // the state of our current pixel (on/off) becomes the state of the
  389. // previous pixel now
  390. //
  391. prevPixel = currPixel;
  392. } // end for x
  393. } // end for y
  394. } // end extendImageEdges
  395. // TexturePage::addImageData ==================================================
  396. /** Add the actual image data from 'image' to the destination buffer
  397. * at the coordinates specified in the 'image' ... this puts the
  398. * actual packed image data on the final texture page
  399. *
  400. * NOTE that we have created our texture page regions with the
  401. * assumption that we were packing images with an upper left
  402. * corner at (0,0), but the targa files have the origin in the
  403. * lower left corner ... thus the translation here to shift source
  404. * images into the right positions
  405. */
  406. //=============================================================================
  407. Bool TexturePage::addImageData( Byte *destBuffer,
  408. Int destWidth,
  409. Int destHeight,
  410. Int destBPP,
  411. ImageInfo *image )
  412. {
  413. // sanity
  414. if( destBuffer == NULL || image == NULL )
  415. return FALSE;
  416. // load the real image data for the source
  417. Targa source;
  418. if( source.Load( image->m_path, TGAF_IMAGE, FALSE ) != 0 )
  419. {
  420. char buffer[ _MAX_PATH + 32 ];
  421. sprintf( buffer, "Error loading source file '%s'\n", image->m_path );
  422. DEBUG_ASSERTCRASH( 0, (buffer) );
  423. MessageBox( NULL, buffer, "Cannot Load Source File", MB_OK | MB_ICONERROR );
  424. return FALSE;
  425. } // end if
  426. // get the source image buffer
  427. char *sourceBuffer = source.GetImage();
  428. DEBUG_ASSERTCRASH( sourceBuffer, ("No Source buffer for source image\n") );
  429. // get the source bytes per pixel
  430. Int sourceBPP = TGA_BytesPerPixel( source.Header.PixelDepth );
  431. //
  432. // the loaded targas are all laid out flat with no encoding, copy
  433. // all the rows in the source to the destination buffer at the coords
  434. // specified in the images' page location
  435. //
  436. char *src, *dest;
  437. Int count;
  438. Int x, y;
  439. if( BitTest( image->m_status, ImageInfo::ROTATED90C ) == FALSE )
  440. {
  441. //
  442. // normal copy, image was not rotated
  443. //
  444. // do all rows
  445. for( y = 0; y < image->m_size.y; y++ )
  446. {
  447. // compute source location
  448. src = sourceBuffer + ( (image->m_size.y - 1 - y) * image->m_size.x * sourceBPP);
  449. // compute destination location
  450. dest = destBuffer +
  451. ((destHeight - 1 - (image->m_pagePos.lo.y + y)) * destWidth * destBPP ) +
  452. (image->m_pagePos.lo.x * destBPP);
  453. // copy a row from source to destination
  454. count = image->m_pagePos.hi.x - image->m_pagePos.lo.x + 1;
  455. for( x = 0; x < count; x++ )
  456. {
  457. // check the target and source formats
  458. if( destBPP == 4 )
  459. {
  460. // copy the rgb
  461. dest[ 3 ] = src[ 0 ];
  462. dest[ 2 ] = src[ 1 ];
  463. dest[ 1 ] = src[ 2 ];
  464. // copy the alpha if present in the source
  465. if( sourceBPP == 4 )
  466. dest[ 0 ] = src[ 3 ];
  467. else
  468. dest[ 0 ] = (char)0xFF; // solid alpha
  469. } // end if
  470. else
  471. {
  472. // copy the rgb
  473. dest[ 2 ] = src[ 0 ];
  474. dest[ 1 ] = src[ 1 ];
  475. dest[ 0 ] = src[ 2 ];
  476. } // end else
  477. // skip past all these pixels
  478. dest += destBPP;
  479. src += sourceBPP;
  480. } // end for x
  481. } // end for y
  482. } // end if, not rotated
  483. else
  484. {
  485. //
  486. // image was rotated, perform a 90 degrees rotation clockwise when we
  487. // copy over the image data
  488. //
  489. for( y = 0; y < image->m_size.y; y++ )
  490. {
  491. // compute beginning of source row to copy from
  492. src = sourceBuffer + ((image->m_size.y - 1 - y) * image->m_size.x * sourceBPP);
  493. // for each pixel in source put it in dest rotated
  494. for( x = 0; x < image->m_size.x; x++ )
  495. {
  496. // compute destination location
  497. dest = destBuffer +
  498. ( ( (destHeight - 1) - (image->m_pagePos.lo.y + x) ) * destWidth * destBPP ) +
  499. ((image->m_pagePos.lo.x + (image->m_size.y - 1 - y)) * destBPP);
  500. // copy this pixel, checking target and source formats
  501. if( destBPP == 4 )
  502. {
  503. // copy the rgb
  504. dest[ 3 ] = src[ 0 ];
  505. dest[ 2 ] = src[ 1 ];
  506. dest[ 1 ] = src[ 2 ];
  507. // copy the alpha if present in the source
  508. if( sourceBPP == 4 )
  509. dest[ 0 ] = src[ 3 ];
  510. else
  511. dest[ 0 ] = (char)0xFF; // solid alpha
  512. } // end if
  513. else
  514. {
  515. // copy the rgb
  516. dest[ 2 ] = src[ 0 ];
  517. dest[ 1 ] = src[ 1 ];
  518. dest[ 0 ] = src[ 2 ];
  519. } // end else
  520. // skip past all these pixels
  521. dest += destBPP;
  522. src += sourceBPP;
  523. } // end for x
  524. } // end for y
  525. } // end else
  526. //
  527. // if we have the option to extend the RGB edges on we now need to process
  528. // the image we just copied into the texture page and "bleed" the edges outward
  529. // and if a border is present, into the border
  530. //
  531. if( BitTest( TheImagePacker->getGapMethod(),
  532. ImagePacker::GAP_METHOD_EXTEND_RGB ) )
  533. extendImageEdges( destBuffer,
  534. destWidth,
  535. destHeight,
  536. destBPP,
  537. image,
  538. FALSE );
  539. return TRUE; // all done
  540. } // end addImageData
  541. // TexturePage::spotUsed ======================================================
  542. /** Is this spot in the texture page open? */
  543. //=============================================================================
  544. Bool TexturePage::spotUsed( Int x, Int y )
  545. {
  546. return m_canvas[ y * m_size.y + x ];
  547. } // end spotUsed
  548. // TexturePage::lineUsed ======================================================
  549. /** Is there ANY spot in the line specified that is used */
  550. //=============================================================================
  551. Bool TexturePage::lineUsed( Int sx, Int sy, Int ex, Int ey )
  552. {
  553. Int x, y;
  554. UnsignedByte *ptr;
  555. for( y = sy; y <= ey; y++ )
  556. {
  557. // compute start of row
  558. ptr = m_canvas + (y * m_size.y + sx);
  559. // scan the row
  560. for( x = sx; x <= ex; x++, ptr++ )
  561. if( *ptr == USED )
  562. return USED;
  563. } // end for y
  564. return FALSE; // it's open!
  565. } // end lineUsed
  566. // TexturePage::markRegionUsed ================================================
  567. /** Mark this region as used */
  568. //=============================================================================
  569. void TexturePage::markRegionUsed( IRegion2D *region )
  570. {
  571. UnsignedByte *ptr;
  572. Int y;
  573. Int count;
  574. // loop through y
  575. for( y = region->lo.y; y <= region->hi.y; y++ )
  576. {
  577. // compute start of row
  578. ptr = m_canvas + (y * m_size.y + region->lo.x);
  579. // fill this row
  580. count = (region->hi.x - region->lo.x) + 1;
  581. memset( ptr, USED, count );
  582. } // end for
  583. } // end markRegionUsed
  584. // TexturePage::buildFitRegion ================================================
  585. /** Build an image region to try to fit into the page based on the location
  586. * given, with the image size, the gutter sizes, and the all sides border
  587. * size
  588. *
  589. * Note that x and y Gutter sizes can be changed as a result of this
  590. * method
  591. *
  592. * Returns a set of "fit bits" that describe what the components
  593. * have been included in the region constructed
  594. */
  595. //=============================================================================
  596. UnsignedInt TexturePage::buildFitRegion( IRegion2D *region,
  597. Int startX, Int startY,
  598. Int imageWidth, Int imageHeight,
  599. Int *xGutter, Int *yGutter,
  600. Bool allSidesBorder )
  601. {
  602. // sanity
  603. if( region == NULL || xGutter == NULL || yGutter == NULL )
  604. return 0;
  605. //
  606. // create border size, if we have an 'allSidesBorder' then we need to
  607. // add two pixels to width and height
  608. //
  609. Int xBorder = 0, yBorder = 0;
  610. if( allSidesBorder )
  611. {
  612. xBorder = 2;
  613. yBorder = 2;
  614. } // end if
  615. //
  616. // when the image size exactly matches the target size of the texture
  617. // page the region will not include any gutter or border sizes cause
  618. // we can fit the image exactly as it is on the page
  619. //
  620. if( imageWidth == m_size.x )
  621. {
  622. *xGutter = 0;
  623. xBorder = 0;
  624. } // end if
  625. if( imageHeight == m_size.y )
  626. {
  627. *yGutter = 0;
  628. yBorder = 0;
  629. } // end if
  630. //
  631. // when an image is 1 pixel smaller than the destination texture
  632. // page we can eliminate some borders used to stretch the RGB values
  633. // at the edges because we will be hitting one side of the texture
  634. // anyway. We will say these borders that are only on one side
  635. // will be on the right and bottom (as described in the fit bits
  636. // returned below)
  637. //
  638. if( imageWidth == m_size.x - 1 )
  639. xBorder = 1;
  640. if( imageHeight == m_size.y - 1 )
  641. yBorder = 1;
  642. // build the region
  643. region->lo.x = startX;
  644. region->lo.y = startY;
  645. region->hi.x = startX + imageWidth - 1 + *xGutter + xBorder; // -1 for zero based
  646. region->hi.y = startY + imageHeight - 1 + *yGutter + yBorder; // -1 for zero based
  647. //
  648. // build a set of region bit flags that tell what this region ACTUALLY
  649. // used in its construction. Note that when we stripped off only ONE
  650. // pixel off one dimension when talking about borders, we say that the
  651. // border that we added was either on the right side of the image,
  652. // or on the bottom of the image
  653. //
  654. UnsignedInt fitBits = 0;
  655. if( *xGutter != 0 )
  656. BitSet( fitBits, ImageInfo::FIT_XGUTTER );
  657. if( *yGutter != 0 )
  658. BitSet( fitBits, ImageInfo::FIT_YGUTTER );
  659. if( xBorder >= 1 )
  660. BitSet( fitBits, ImageInfo::FIT_XBORDER_RIGHT );
  661. if( xBorder == 2 )
  662. BitSet( fitBits, ImageInfo::FIT_XBORDER_LEFT );
  663. if( yBorder >= 1 )
  664. BitSet( fitBits, ImageInfo::FIT_YBORDER_BOTTOM );
  665. if( yBorder == 2 )
  666. BitSet( fitBits, ImageInfo::FIT_YBORDER_TOP );
  667. return fitBits;
  668. } // end buildFitRegion
  669. ///////////////////////////////////////////////////////////////////////////////
  670. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
  671. ///////////////////////////////////////////////////////////////////////////////
  672. // TexturePage::TexturePage ===================================================
  673. /** */
  674. //============================================================================
  675. TexturePage::TexturePage( Int width, Int height )
  676. {
  677. Int canvasSize;
  678. m_id = -1;
  679. m_next = NULL;
  680. m_prev = NULL;
  681. m_size.x = width;
  682. m_size.y = height;
  683. m_packedImage = NULL;
  684. m_targa = NULL;
  685. // create a "canvas" to represent used and unused areas
  686. canvasSize = m_size.x * m_size.y;
  687. m_canvas = new UnsignedByte[ canvasSize ];
  688. DEBUG_ASSERTCRASH( m_canvas, ("Cannot allocate canvas for texture page\n") );
  689. memset( m_canvas, FREE, sizeof( UnsignedByte ) * canvasSize );
  690. } // end TexturePage
  691. // TexturePage::~TexturePage ==================================================
  692. /** */
  693. //=============================================================================
  694. TexturePage::~TexturePage( void )
  695. {
  696. // delete the canvas
  697. if( m_canvas )
  698. delete [] m_canvas;
  699. // delete targa if present, this will NOT delete a user assigned image buffer
  700. if( m_targa )
  701. delete m_targa;
  702. // delete the final image buffer if present
  703. if( m_packedImage )
  704. delete [] m_packedImage;
  705. } // end ~TexturePage
  706. // TexturePage::addImage ======================================================
  707. /** If this image will fit on this page, add it */
  708. //=============================================================================
  709. Bool TexturePage::addImage( ImageInfo *image )
  710. {
  711. IRegion2D region;
  712. // santiy
  713. if( image == NULL )
  714. {
  715. DEBUG_ASSERTCRASH( image, ("TexturePage::addImage: NULL image!\n") );
  716. return TRUE; // say it was added
  717. } // end if
  718. // get our options for fitting
  719. Bool useGutter, useRGBExtend;
  720. useGutter = BitTest( TheImagePacker->getGapMethod(),
  721. ImagePacker::GAP_METHOD_GUTTER );
  722. useRGBExtend = BitTest( TheImagePacker->getGapMethod(),
  723. ImagePacker::GAP_METHOD_EXTEND_RGB );
  724. //
  725. // try to fit this image in this page ... we have two tries, once
  726. // normally, and once with the image rotated 90 degrees clockwise
  727. //
  728. Int triesLeft = 2;
  729. Bool tryRotate = FALSE;
  730. while( triesLeft )
  731. {
  732. // try the rotated image the second time around
  733. if( triesLeft == 1 )
  734. tryRotate = TRUE;
  735. // we've now 'used up' a try
  736. triesLeft--;
  737. // find a free upper left corner to put the image in
  738. Int x, y;
  739. Int xGutter = 0, yGutter = 0;
  740. Int imageWidth, imageHeight;
  741. UnsignedInt fitBits = 0;
  742. for( y = 0; y < m_size.y; y++ )
  743. {
  744. for( x = 0; x < m_size.x; x++ )
  745. {
  746. // get the gutter size
  747. if( useGutter )
  748. {
  749. xGutter = TheImagePacker->getGutter();
  750. yGutter = TheImagePacker->getGutter();
  751. } // end if
  752. else
  753. {
  754. xGutter = 0;
  755. yGutter = 0;
  756. } // end else
  757. //
  758. // compute the region of the image at this location, the region that will
  759. // be used will take up the image region AND the gutter. UNLESS the
  760. // image is as big as the texture page, in that case there is no reason
  761. // to use a gutter size. Also note that if we're trying to fit
  762. // a rotated image this time we have to swap the coords
  763. // around a little bit
  764. //
  765. if( tryRotate == FALSE )
  766. {
  767. // normal, non-rotated image
  768. imageWidth = image->m_size.x;
  769. imageHeight = image->m_size.y;
  770. } // end if
  771. else
  772. {
  773. //
  774. // build region for rotation 90 degrees clockwise
  775. //
  776. // 1------------2
  777. // | |
  778. // 3------------4
  779. //
  780. // becomes
  781. //
  782. // 3--1
  783. // | |
  784. // | |
  785. // | |
  786. // | |
  787. // | |
  788. // 4--2
  789. //
  790. imageWidth = image->m_size.y;
  791. imageHeight = image->m_size.x;
  792. } // end else
  793. // build the region
  794. fitBits = buildFitRegion( &region, x, y,
  795. imageWidth, imageHeight,
  796. &xGutter, &yGutter,
  797. useRGBExtend );
  798. //
  799. // if the image region plus the gutter goes off the image page, BUT
  800. // the image itself stays on the page, adjust the gutter to only
  801. // be as big as from the end of the image to the end of the page,
  802. // this technically doesn't fill the requirements of making a gutter
  803. // around every image, but it's OK since that space will be
  804. // designated as filled, and at that will be filled with
  805. // transparent alpha - nothingness!
  806. //
  807. if( region.hi.x >= m_size.x )
  808. {
  809. //
  810. // attempt to shrink x gutter if image can still fit with a
  811. // smaller gutter to the right
  812. //
  813. if( region.hi.x - xGutter < m_size.x )
  814. xGutter = m_size.x - imageWidth;
  815. // rebuild region with new gutter size
  816. fitBits = buildFitRegion( &region, x, y,
  817. imageWidth, imageHeight,
  818. &xGutter, &yGutter,
  819. useRGBExtend );
  820. } // end if
  821. if( region.hi.y >= m_size.y )
  822. {
  823. //
  824. // attempt to shrink y gutter if image can still fit with
  825. // a smaller gutter below
  826. //
  827. if( region.hi.y - yGutter < m_size.y )
  828. yGutter = m_size.y - imageHeight;
  829. // rebuild region with new gutter size
  830. fitBits = buildFitRegion( &region, x, y,
  831. imageWidth, imageHeight,
  832. &xGutter, &yGutter,
  833. useRGBExtend );
  834. } // end if
  835. // reject this location if the hi region goes off the texture page
  836. if( region.hi.y >= m_size.y )
  837. {
  838. y = m_size.y; // skip to end, this isn't gonna work
  839. continue;
  840. } // end if
  841. if( region.hi.x >= m_size.x )
  842. {
  843. x = m_size.x; // skip to end of row to try next row
  844. continue;
  845. } // end if
  846. //
  847. // reject this location if any of the corners are in used spots,
  848. // note since we're trying to fit things by "sliding" them
  849. // horizontally ... we cut out more checks if we see if the horizontal
  850. // bounds are full at the right side first ... if they are we
  851. // move our "anchor" point to try to fit again to after that
  852. // point (saving the checking of between the current upper
  853. // left anchor point and the right side point that is used anyway
  854. //
  855. // upper right and lower right
  856. if( spotUsed( region.hi.x, region.lo.y ) || // upper right
  857. spotUsed( region.hi.x, region.hi.y ) ) // lower right
  858. {
  859. x = region.hi.x; // next anchor spot will be to the right of here
  860. continue;
  861. } // end if
  862. // upper left and lower left
  863. if( spotUsed( region.lo.x, region.lo.y ) || // upper left
  864. spotUsed( region.lo.x, region.hi.y ) ) // lower left
  865. continue;
  866. //
  867. // reject this location if we cross any used locations between
  868. // the 4 corners
  869. //
  870. if( lineUsed( region.lo.x, region.lo.y,
  871. region.hi.x, region.lo.y ) || // top side
  872. lineUsed( region.hi.x, region.lo.y,
  873. region.hi.x, region.hi.y ) || // right side
  874. lineUsed( region.lo.x, region.hi.y,
  875. region.hi.x, region.hi.y ) || // bottom side
  876. lineUsed( region.lo.x, region.lo.y,
  877. region.lo.x, region.hi.y ) ) // left side
  878. continue;
  879. //
  880. // we passed all tests, take up this spot
  881. //
  882. markRegionUsed( &region ); // marks region AND gutter used
  883. BitClear( image->m_status, ImageInfo::TOOBIG );
  884. BitClear( image->m_status, ImageInfo::UNPACKED );
  885. BitSet( image->m_status, ImageInfo::PACKED );
  886. image->m_page = this;
  887. //
  888. // store the properties of the region that was used to fit this
  889. // image
  890. //
  891. image->m_fitBits = fitBits;
  892. // store the gutter sizes used in fitting this image
  893. image->m_gutterUsed.x = xGutter;
  894. image->m_gutterUsed.y = yGutter;
  895. //
  896. // if we packed this image rotated, set a flag telling us we
  897. // need to swap the size dimension in the image structure
  898. // when copying the image data
  899. //
  900. if( tryRotate == TRUE )
  901. BitSet( image->m_status, ImageInfo::ROTATED90C );
  902. //
  903. // save the page position of this image, but do not include
  904. // the gutter or padding borders which is incorporated into the region,
  905. // we're interested in just the bounding rectangle of the image itself
  906. // on the texture page
  907. //
  908. image->m_pagePos = region;
  909. if( BitTest( fitBits, ImageInfo::FIT_XBORDER_LEFT ) )
  910. image->m_pagePos.lo.x++;
  911. if( BitTest( fitBits, ImageInfo::FIT_YBORDER_TOP ) )
  912. image->m_pagePos.lo.y++;
  913. if( BitTest( fitBits, ImageInfo::FIT_XBORDER_RIGHT ) )
  914. image->m_pagePos.hi.x--;
  915. if( BitTest( fitBits, ImageInfo::FIT_YBORDER_BOTTOM ) )
  916. image->m_pagePos.hi.y--;
  917. if( BitTest( fitBits, ImageInfo::FIT_XGUTTER ) )
  918. image->m_pagePos.hi.x -= xGutter;
  919. if( BitTest( fitBits, ImageInfo::FIT_YGUTTER ) )
  920. image->m_pagePos.hi.y -= yGutter;
  921. // link this image to the texture page
  922. image->m_prevPageImage = NULL;
  923. image->m_nextPageImage = m_imageList;
  924. if( m_imageList )
  925. m_imageList->m_prevPageImage = image;
  926. m_imageList = image;
  927. return TRUE; // success
  928. } // end for x
  929. } // end for y
  930. } // end while, triesLeft
  931. // no space
  932. return FALSE;
  933. } // end addImage
  934. // TexturePage::generateTexture ===============================================
  935. /** Generate the final packed texture given all the images that have
  936. * already been assigned to this page */
  937. //=============================================================================
  938. Bool TexturePage::generateTexture( void )
  939. {
  940. // sanity
  941. if( m_imageList == NULL )
  942. return FALSE;
  943. // sanity
  944. DEBUG_ASSERTCRASH( m_packedImage == NULL, ("The packed image list must be NULL before generating texture\n") );
  945. DEBUG_ASSERTCRASH( m_targa == NULL, ("The targa must be NULL before generating a new texture\n") );
  946. // allocate targa to help us generate the final texture
  947. m_targa = new Targa;
  948. if( m_targa == NULL )
  949. {
  950. char buffer[ 128 ];
  951. sprintf( buffer, "Unable to allocate new targa to generate texture\n" );
  952. DEBUG_ASSERTCRASH( m_targa, (buffer) );
  953. MessageBox( NULL, buffer, "Internal Error", MB_OK | MB_ICONERROR );
  954. return FALSE;
  955. } // end if
  956. Bool outputAlpha = TheImagePacker->getOutputAlpha();
  957. Int depth, bpp;
  958. //
  959. // if we're outputting an alpha channel we will use 32 bit color depth,
  960. // if we're not we will use 24 bit
  961. //
  962. if( outputAlpha )
  963. depth = 32;
  964. else
  965. depth = 24;
  966. // how many bytes per pixel for the targa file format
  967. bpp = TGA_BytesPerPixel( depth );
  968. // allocate a buffer for our final image
  969. Int bufferSize = m_size.x * m_size.y * bpp;
  970. m_packedImage = new Byte[ bufferSize ];
  971. if( m_packedImage == NULL )
  972. {
  973. char buffer[ 128 ];
  974. sprintf( buffer, "Unable to allocate final packed image buffer\n" );
  975. DEBUG_ASSERTCRASH( m_packedImage, (buffer) );
  976. MessageBox( NULL, buffer, "Internal Error", MB_OK | MB_ICONERROR );
  977. BitSet( m_status, PAGE_ERROR );
  978. BitSet( m_status, CANT_ALLOCATE_PACKED_IMAGE );
  979. return FALSE;
  980. } // end if
  981. // zero the packed image to all zero
  982. memset( m_packedImage, 0, sizeof( Byte ) * bufferSize );
  983. // setup the targa header
  984. m_targa->Header.ImageType = TGA_TRUECOLOR;
  985. m_targa->Header.Width = m_size.x;
  986. m_targa->Header.Height = m_size.y;
  987. m_targa->Header.PixelDepth = depth;
  988. // add all the images to the final packed buffer
  989. ImageInfo *image;
  990. for( image = m_imageList; image; image = image->m_nextPageImage )
  991. {
  992. if( addImageData( m_packedImage, m_size.x, m_size.y,
  993. bpp, image ) == FALSE )
  994. {
  995. BitSet( m_status, PAGE_ERROR );
  996. BitSet( m_status, CANT_ADD_IMAGE_DATA );
  997. return FALSE;
  998. } // end if
  999. } // end for image
  1000. // set this data into the targa structure
  1001. m_targa->SetImage( m_packedImage );
  1002. return TRUE; // success
  1003. } // end generateTexture
  1004. // TexturePage::writeFile =====================================================
  1005. /** Write the texture data that has already been generated to a file
  1006. * starting with the baseName passed in with the texture id of
  1007. * this texture page appended to the end of it */
  1008. //=============================================================================
  1009. Bool TexturePage::writeFile( char *baseFilename )
  1010. {
  1011. // sanity
  1012. if( baseFilename == NULL || m_targa == NULL )
  1013. {
  1014. BitSet( m_status, PAGE_ERROR );
  1015. BitSet( m_status, NO_TEXTURE_DATA );
  1016. return FALSE;
  1017. } // end if
  1018. // construct filename
  1019. char filePath[ _MAX_PATH ];
  1020. sprintf( filePath, "%s%s_%03d.tga", TheImagePacker->getOutputDirectory(),
  1021. baseFilename, getID() );
  1022. // write the file
  1023. Bool error = FALSE;
  1024. long flags = TGAF_IMAGE;
  1025. if( TheImagePacker->getCompressTextures() == TRUE )
  1026. BitSet( flags, TGAF_COMPRESS );
  1027. error = m_targa->Save( filePath, flags , FALSE );
  1028. if( error != 0 )
  1029. {
  1030. // there was an error, set a status bit
  1031. BitSet( m_status, PAGE_ERROR );
  1032. BitSet( m_status, ERROR_DURING_SAVE );
  1033. } // end if
  1034. // return success or not
  1035. return !error;
  1036. } // end writeFile
  1037. // TexturePage::getPixel ======================================================
  1038. /** Get the RGB pixel stored at location (x,y) (where (0,0) is the upper
  1039. * left corner of the image ... even though the internal targa
  1040. * isn't stored that way */
  1041. //=============================================================================
  1042. void TexturePage::getPixel( Int x, Int y, Byte *r, Byte *g, Byte *b, Byte *a )
  1043. {
  1044. // do nothing if we have no image data
  1045. if( m_packedImage == NULL )
  1046. return;
  1047. // how many bytes per pixel for the targa file format
  1048. Int depth = m_targa->Header.PixelDepth;
  1049. Int bpp = TGA_BytesPerPixel( depth );
  1050. // compute location into buffer
  1051. Byte *buf;
  1052. buf = m_packedImage + ((m_size.y - 1 - y) * m_size.x * bpp) + (x * bpp);
  1053. // read the pixel data
  1054. if( bpp == 4 )
  1055. {
  1056. if( a )
  1057. *a = buf[ 0 ];
  1058. *r = buf[ 1 ];
  1059. *g = buf[ 2 ];
  1060. *b = buf[ 3 ];
  1061. } // end if
  1062. else
  1063. {
  1064. if( a )
  1065. *a = (char)0xff; // no data, just return solid alpha
  1066. *r = buf[ 0 ];
  1067. *g = buf[ 1 ];
  1068. *b = buf[ 2 ];
  1069. } // end else
  1070. } // end getPixel