| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- // FILE: ImagePacker.cpp //////////////////////////////////////////////////////
- //-----------------------------------------------------------------------------
- //
- // Westwood Studios Pacific.
- //
- // Confidential Information
- // Copyright (C) 2001 - All Rights Reserved
- //
- //-----------------------------------------------------------------------------
- //
- // Project: ImagePacker
- //
- // File name: ImagePacker.cpp
- //
- // Created: Colin Day, August 2001
- //
- // Desc: Entry point for the image packer. This program takes
- // separate image files and combines them into a single
- // image as close as possible so that we can conserve texture
- // memory
- //
- //-----------------------------------------------------------------------------
- ///////////////////////////////////////////////////////////////////////////////
- // SYSTEM INCLUDES ////////////////////////////////////////////////////////////
- #include <stdio.h>
- #include <io.h>
- #include <assert.h>
- // USER INCLUDES //////////////////////////////////////////////////////////////
- #include "Common/Debug.h"
- #include "WWLib/Targa.h"
- #include "Resource.h"
- #include "ImagePacker.h"
- #include "WinMain.h"
- #include "WindowProc.h"
- // DEFINES ////////////////////////////////////////////////////////////////////
- char *gAppPrefix = "ip_"; // So IP can have a different debug log file name if we need it.
- // PRIVATE TYPES //////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // PRIVATE DATA ///////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- ImagePacker *TheImagePacker = NULL;
- // PUBLIC DATA ////////////////////////////////////////////////////////////////
- // PRIVATE PROTOTYPES /////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // ImagePacker::createNewTexturePage ==========================================
- /** Create a new texture page and add to the list */
- //=============================================================================
- TexturePage *ImagePacker::createNewTexturePage( void )
- {
- TexturePage *page;
- // allocate new page
- page = new TexturePage( getTargetWidth(), getTargetHeight() );
- if( page == NULL )
- {
- DEBUG_ASSERTCRASH( page, ("Unable to allocate new texture page.\n") );
- return NULL;
- } // end if
- // link page to list
- page->m_prev = NULL;
- page->m_next = m_pageList;
- if( m_pageList )
- m_pageList->m_prev = page;
- m_pageList = page;
- // add the tail pointer if this is the first page
- if( m_pageTail == NULL )
- m_pageTail = page;
- // we got a new page now
- m_pageCount++;
- // set page id as the current page count
- page->setID( m_pageCount );
- return page;
- } // end createNewTexturePage
- // ImagePacker::validateImages ================================================
- /** Check all the images in the image list, if any of them cannot be
- * processed we will flag them as so. If we have some images that can't
- * be processed, we will warn the user of these images and ask them
- * whether or not to proceed.
- *
- * Returns TRUE to proceed
- * Returns FALSE to cancel build
- */
- //=============================================================================
- Bool ImagePacker::validateImages( void )
- {
- UnsignedInt i;
- ImageInfo *image;
- Bool errors = FALSE;
- Bool proceed = TRUE;
- // loop through all images
- for( i = 0; i < m_imageCount; i++ )
- {
- // get this image
- image = m_imageList[ i ];
- // sanity
- if( image == NULL )
- {
- DEBUG_ASSERTCRASH( image, ("Image in imagelist is NULL") );
- continue; // should never happen
- } // end if
- //
- // if this image is too big to fit in the target page size as a whole
- // then there is nothing we can do about it
- //
- if( image->m_size.x > getTargetWidth() ||
- image->m_size.y > getTargetHeight() )
- {
- errors = TRUE;
- BitSet( image->m_status, ImageInfo::TOOBIG );
- BitSet( image->m_status, ImageInfo::CANTPROCESS );
- } // end if
- //
- // if this image is not the right format we can't process it, at
- // present we only understand 32 and 24 bit images
- //
- if( image->m_colorDepth != 32 && image->m_colorDepth != 24 )
- {
-
- errors = TRUE;
- BitSet( image->m_status, ImageInfo::INVALIDCOLORDEPTH );
- BitSet( image->m_status, ImageInfo::CANTPROCESS );
-
- } // end if
- } // end for i
- //
- // if we have errors, build a list and show them to the user
- //
- if( errors == TRUE )
- {
- proceed = DialogBox( ApplicationHInstance,
- (LPCTSTR)IMAGE_ERRORS,
- TheImagePacker->getWindowHandle(),
- (DLGPROC)ImageErrorProc );
- } // end if
- return proceed;
- } // end validateImages
- // ImagePacker::packImages ====================================================
- /** Pack all the images in the image list, starting from the top and
- * working from there */
- //=============================================================================
- Bool ImagePacker::packImages( void )
- {
- UnsignedInt i;
- TexturePage *page = NULL;
- ImageInfo *image = NULL;
- //
- // first sanity check all images loaded, if there are images that cannot
- // be processed the user will be given a list of these and asked wether
- // or not to proceed
- //
- Bool proceed;
- proceed = validateImages();
- if( proceed == FALSE )
- {
- statusMessage( "Build Cancelled By User." );
- return FALSE;
- } // end if
- // loop through all images
- for( i = 0; i < m_imageCount; i++ )
- {
- // update status
- sprintf( m_statusBuffer, "Fitting Image %d of %d.", i, m_imageCount );
- statusMessage( m_statusBuffer );
-
- // get this image out of the list
- image = m_imageList[ i ];
- // ignore images that we cannot process
- if( BitTest( image->m_status, ImageInfo::CANTPROCESS) )
- continue;
- // try to put image on each page
- for( page = m_pageTail; page; page = page->m_prev )
- {
- if( page->addImage( image ) == TRUE )
- break; // page added, stop trying to add into pages
- } // end for page
- // if image was not able to go on any existing page create a new page for it
- if( page == NULL )
- {
- page = createNewTexturePage();
- if( page == NULL )
- return FALSE;
- // try to add the image to this page
- if( page->addImage( image ) == FALSE )
- {
- char buffer[ _MAX_PATH ];
- sprintf( buffer, "Unable to add image '%s' to a brand new page!\n", image->m_path );
- DEBUG_ASSERTCRASH( 0, (buffer) );
- MessageBox( NULL, buffer, "Internal Error", MB_OK | MB_ICONERROR );
- return FALSE;
- } // end if
- } // end if
- } // end for i
- return TRUE; // success
- } // end packImages
- // ImagePacker::writeFinalTextures ============================================
- /** Generate and write the final textures to the output directory
- * of the packed images along with a definition file for which images
- * are where on the page */
- //=============================================================================
- void ImagePacker::writeFinalTextures( void )
- {
- TexturePage *page;
- Bool errors = FALSE;
- char buffer[ 128 ];
- //
- // go through each page, let's start from the end of the list since
- // that's where we packed first, but it doesn't matter
- //
- for( page = m_pageTail; page; page = page->m_prev )
- {
- // update status message
- sprintf( buffer, "Generating texture #%d of %d.",
- page->getID(), m_pageCount );
- statusMessage( buffer );
- // generate the final texture for this page
- if( page->generateTexture() == FALSE )
- {
-
- errors = TRUE;
- continue; // could not generate this page, but try to continue
- } // end if
- //
- // write this page out to a file using the filename given by
- // the user and the texture page ID to keep it unique
- //
- if( page->writeFile( m_outputFile ) == FALSE )
- {
- errors = TRUE;
- continue; // could not write page, but try to go on
- } // end if
- } // end for page
- // check for any errors and notify the user
- if( errors == TRUE )
- {
- DialogBox( ApplicationHInstance,
- (LPCTSTR)PAGE_ERRORS,
- TheImagePacker->getWindowHandle(),
- (DLGPROC)PageErrorProc );
- } // end if
- } // end writeFinalTextures
- // sortImageCompare ===========================================================
- /** Compare function for qsort
- * -1 item1 less than item2
- * 0 item1 identical to item2
- * 1 item1 greater than item2
- */
- //=============================================================================
- static Int sortImageCompare( const void *aa, const void *bb )
- {
- const ImageInfo **a = (const ImageInfo **)aa;
- const ImageInfo **b = (const ImageInfo **)bb;
- if( (*a)->m_area < (*b)->m_area )
- return 1;
- else if( (*a)->m_area > (*b)->m_area )
- return -1;
- else
- return 0;
- } // end sortImageCompare
- // ImagePacker::sortImageList =================================================
- /** Sort the image list */
- //=============================================================================
- void ImagePacker::sortImageList( void )
- {
- // sort all images so that largest area ones are first
- qsort( (void *)m_imageList, m_imageCount, sizeof( ImageInfo *), sortImageCompare );
- } // end sortImageList
- // ImagePacker::addImagesInDirectory ==========================================
- /** Add all the images in the specified directory */
- //=============================================================================
- void ImagePacker::addImagesInDirectory( char *dir )
- {
- // sanity
- if( dir == NULL )
- return;
- char currDir[ _MAX_PATH ];
- char filePath[ _MAX_PATH ];
- WIN32_FIND_DATA item; // search item
- HANDLE hFile; // handle for search resources
- Int len;
- // save the current directory
- GetCurrentDirectory( _MAX_PATH, currDir );
- // change into the directory
- SetCurrentDirectory( dir );
- // go through each item in the output directory
- hFile = FindFirstFile( "*", &item);
- if( hFile != INVALID_HANDLE_VALUE )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- {
- len = strlen( item.cFileName );
- if( len > 4 &&
- item.cFileName[ len - 4 ] == '.' &&
- (item.cFileName[ len - 3 ] == 't' || item.cFileName[ len - 3 ] == 'T') &&
- (item.cFileName[ len - 2 ] == 'g' || item.cFileName[ len - 2 ] == 'G') &&
- (item.cFileName[ len - 1 ] == 'a' || item.cFileName[ len - 1 ] == 'A') )
- {
- sprintf( filePath, "%s%s", dir, item.cFileName );
- addImage( filePath );
- } // end if
- } // end if
- // find the rest of the files
- while( FindNextFile( hFile, &item ) != 0 )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- {
- len = strlen( item.cFileName );
- if( len > 4 &&
- item.cFileName[ len - 4 ] == '.' &&
- (item.cFileName[ len - 3 ] == 't' || item.cFileName[ len - 3 ] == 'T') &&
- (item.cFileName[ len - 2 ] == 'g' || item.cFileName[ len - 2 ] == 'G') &&
- (item.cFileName[ len - 1 ] == 'a' || item.cFileName[ len - 1 ] == 'A') )
- {
- sprintf( filePath, "%s%s", dir, item.cFileName );
- addImage( filePath );
- } // end if
- } // end if
- } // end while
- // close search
- FindClose( hFile );
- } //end if, items found
- // restore our current directory
- SetCurrentDirectory( currDir );
- } // end addImagesInDirectory
- // ImagePacker::checkOutputDirectory ==========================================
- /** Verify that there are no files in the output directory ... if there
- * are give the user the option to delete them, cancel the operation,
- * or proceed with possibly overwriting any files there
- *
- * Returns TRUE to proceed with the process, FALSE if the user wants
- * to cancel the process
- */
- //=============================================================================
- Bool ImagePacker::checkOutputDirectory( void )
- {
- WIN32_FIND_DATA item; // search item
- HANDLE hFile; // handle for search resources
- Int fileCount = 0;
- char currDir[ _MAX_PATH ];
-
- // get the working directory
- GetCurrentDirectory( _MAX_PATH, currDir );
- // create the output directory if it does not exist
- CreateDirectory( m_outputDirectory, NULL );
- // change into the output directory
- SetCurrentDirectory( m_outputDirectory );
- // go through each item in the output directory
- hFile = FindFirstFile( "*", &item);
- if( hFile != INVALID_HANDLE_VALUE )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- fileCount++;
- // find the rest of the files
- while( FindNextFile( hFile, &item ) != 0 )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- fileCount++;
- } // end while
- // close search
- FindClose( hFile );
- } //end if, items found
- // switch back to the current directory
- SetCurrentDirectory( currDir );
- if( fileCount != 0 )
- {
- char buffer[ 256 ];
- Int response;
- sprintf( buffer, "The output directory (%s) must be empty before proceeding. Delete '%d' files and continue with build process?",
- m_outputDirectory, fileCount );
- response = MessageBox( NULL, buffer,
- "Delete files to continue?",
- MB_YESNO | MB_ICONWARNING );
- // if they said no, do not delete the files and abort the pack process
- if( response == IDNO )
- return FALSE;
- //
- // they said yes, delete all the files in the output directory
- //
- // change into the output directory
- SetCurrentDirectory( m_outputDirectory );
-
- // go through each item in the output directory
- hFile = FindFirstFile( "*", &item);
- if( hFile != INVALID_HANDLE_VALUE )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- DeleteFile( item.cFileName );
- // find the rest of the files
- while( FindNextFile( hFile, &item ) != 0 )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- DeleteFile( item.cFileName );
- } // end while
- // close search
- FindClose( hFile );
- } //end if, items found
- // switch back to the current directory
- SetCurrentDirectory( currDir );
- } // end if
- return TRUE; // proceed
- } // end checkOutputDirectory
- // ImagePacker::resetPageList =================================================
- /** Clear the page list */
- //=============================================================================
- void ImagePacker::resetPageList( void )
- {
- TexturePage *next;
- while( m_pageList )
- {
- next = m_pageList->m_next;
- delete m_pageList;
- m_pageList = next;
- } // end while
- m_pageTail = NULL;
- m_pageCount = 0;
- m_targetPreviewPage = 1;
- } // end resetPageList
- // ImagePacker::resetImageDirectoryList =======================================
- /** Clear the image directory list */
- //=============================================================================
- void ImagePacker::resetImageDirectoryList( void )
- {
- ImageDirectory *next;
- while( m_dirList )
- {
- next = m_dirList->m_next;
- delete m_dirList;
- m_dirList = next;
-
- } // end while
- m_dirCount = 0;
- m_imagesInDirs = 0;
- } // end resetImageDirectoryList
- // ImagePacker::resetImageList ================================================
- /** Clear the image list */
- //=============================================================================
- void ImagePacker::resetImageList( void )
- {
- if( m_imageList )
- delete [] m_imageList;
- m_imageList = NULL;
- m_imageCount = 0;
- } // end resetImageList
- // ImagePacker::addDirectory ==================================================
- /** Add the directory to the directory list, do not add it if it is already
- * in the directory list. We want to have that sanity check so that
- * we can be assured that each image being added to the image list from
- * each directory will be unique and we therefore don't have to do
- * any further checking for duplicates */
- //=============================================================================
- void ImagePacker::addDirectory( char *path, Bool subDirs )
- {
- char currDir[ _MAX_PATH ];
- WIN32_FIND_DATA item; // search item
- HANDLE hFile; // handle for search resources
- // santiy
- if( path == NULL )
- return;
- // check to see if path is already in list
- ImageDirectory *dir;
- for( dir = m_dirList; dir; dir = dir->m_next )
- if( stricmp( dir->m_path, path ) == 0 )
- return; // already in list
- // save our current directory
- GetCurrentDirectory( _MAX_PATH, currDir );
- // set our directory to this one
- if( SetCurrentDirectory( path ) == 0 )
- return; // directory does not exist
- // image is not in list, make a new entry and link to the list
- dir = new ImageDirectory;
- if( dir == NULL )
- {
-
- MessageBox( NULL, "Unable to allocate image directory", "Error",
- MB_OK | MB_ICONERROR );
- return;
- } // end if
- // allocate space for the path
- Int len = strlen( path );
- dir->m_path = new char[ len + 1 ];
- strcpy( dir->m_path, path );
- if( dir->m_path == NULL )
- {
- MessageBox( NULL, "Unable to allocate path for directory", "Error",
- MB_OK | MB_ICONERROR );
- delete dir;
- return;
- } // end if
- // tie to list
- dir->m_prev = NULL;
- dir->m_next = m_dirList;
- if( m_dirList )
- m_dirList->m_prev = dir;
- m_dirList = dir;
-
- // increase our directory count
- m_dirCount++;
- // update status
- sprintf( m_statusBuffer, "Folder Added: %d.", m_dirCount );
- statusMessage( m_statusBuffer );
- // count how many image files are in this directory
- hFile = FindFirstFile( "*", &item);
- if( hFile != INVALID_HANDLE_VALUE )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- {
- len = strlen( item.cFileName );
- if( len > 4 &&
- item.cFileName[ len - 4 ] == '.' &&
- (item.cFileName[ len - 3 ] == 't' || item.cFileName[ len - 3 ] == 'T') &&
- (item.cFileName[ len - 2 ] == 'g' || item.cFileName[ len - 2 ] == 'G') &&
- (item.cFileName[ len - 1 ] == 'a' || item.cFileName[ len - 1 ] == 'A') )
- dir->m_imageCount++;
- } // end if
- // find the rest of the files
- while( FindNextFile( hFile, &item ) != 0 )
- {
- // if this is a file count it
- if( !(item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- {
- len = strlen( item.cFileName );
- if( len > 4 &&
- item.cFileName[ len - 4 ] == '.' &&
- (item.cFileName[ len - 3 ] == 't' || item.cFileName[ len - 3 ] == 'T') &&
- (item.cFileName[ len - 2 ] == 'g' || item.cFileName[ len - 2 ] == 'G') &&
- (item.cFileName[ len - 1 ] == 'a' || item.cFileName[ len - 1 ] == 'A') )
- dir->m_imageCount++;
- } // end if
- } // end while
- // close search
- FindClose( hFile );
- } //end if, items found
- // add the image count of this directory to the total image count
- m_imagesInDirs += dir->m_imageCount;
- // if we are adding subdirectories add them all
- if( subDirs )
- {
- char subDir[ _MAX_PATH ];
- // go through each item in the output directory
- hFile = FindFirstFile( "*", &item);
- if( hFile != INVALID_HANDLE_VALUE )
- {
- // if this is a file count it
- if( (item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- {
- sprintf( subDir, "%s%s\\", path, item.cFileName );
- addDirectory( subDir, subDirs );
- } // end if
- // find the rest of the files
- while( FindNextFile( hFile, &item ) != 0 )
- {
- // if this is a file count it
- if( (item.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
- strcmp( item.cFileName, "." ) &&
- strcmp( item.cFileName, ".." ) )
- {
- sprintf( subDir, "%s%s\\", path, item.cFileName );
- addDirectory( subDir, subDirs );
- } // end if
- } // end while
- // close search
- FindClose( hFile );
- } //end if, items found
- } // end if
- // restore our current directory
- SetCurrentDirectory( currDir );
- } // end addDirectory
- // ImagePacker::addImage ======================================================
- /** Add the image to the image list */
- //=============================================================================
- void ImagePacker::addImage( char *path )
- {
- // sanity
- if( path == NULL )
- return;
- // allocate a new entry
- ImageInfo *info = new ImageInfo;
- if( info == NULL )
- {
- MessageBox( NULL, "Unable to allocate image info", "Error",
- MB_OK | MB_ICONERROR );
- return;
- } // end if
- // allocate space for the path
- Int len = strlen( path );
- info->m_path = new char[ len + 1 ];
- strcpy( info->m_path, path );
- if( info->m_path == NULL )
- {
- MessageBox( NULL, "Unable to allcoate image path info", "Error",
- MB_OK | MB_ICONERROR );
- delete info;
- return;
- } // end if
- // load just the header information from the targa
- m_targa->Load( info->m_path, 0, TRUE );
- // get the data we need out of the targa header
- info->m_colorDepth = m_targa->Header.PixelDepth;
- info->m_size.x = m_targa->Header.Width;
- info->m_size.y = m_targa->Header.Height;
- info->m_area = info->m_size.x * info->m_size.y;
- // save the filename only without path
- Int i;
- char *c;
- for( i = len - 1; i >= 0; i-- )
- {
- if( path[ i ] == '\\' )
- {
- c = &path[ i + 1 ];
- break;
- }
- } // end for i
- Int nameLen = strlen( c );
- info->m_filenameOnly = new char[ nameLen + 1 ];
- strcpy( info->m_filenameOnly, c );
- info->m_filenameOnlyNoExt = new char[ nameLen - 4 + 1 ];
- strncpy( info->m_filenameOnlyNoExt, c, nameLen - 4 );
- info->m_filenameOnlyNoExt[ nameLen - 4 ] = '\0';
- // assign to array
- m_imageList[ m_imageCount++ ] = info;
- // update status
- sprintf( m_statusBuffer, "Loading Image %d of %d.",
- m_imageCount, m_imagesInDirs );
- statusMessage( m_statusBuffer );
- } // end addImage
- // ImagePacker::generateINIFile ===============================================
- /** Generate the INI image file definition for the final packed images */
- //=============================================================================
- Bool ImagePacker::generateINIFile( void )
- {
- FILE *fp;
- char filename[ _MAX_PATH ];
- // construct filename we'll use
- sprintf( filename, "%s%s.INI", m_outputDirectory, m_outputFile );
- // open the file
- fp = fopen( filename, "w" );
- if( fp == NULL )
- {
- char buffer[ _MAX_PATH + 64 ];
- sprintf( buffer, "Cannot open INI file '%s' for writing.", filename );
- MessageBox( NULL, buffer, "Error Opening File", MB_OK | MB_ICONERROR );
- return FALSE;
- } // end if
- // print header for file
- fprintf( fp, "; ------------------------------------------------------------\n" );
- fprintf( fp, "; Do NOT edit by hand, ImagePacker.exe auto generated INI file\n" );
- fprintf( fp, "; ------------------------------------------------------------\n\n" );
- //
- // loop through all the pages so that we write image definitions that
- // are on the same page close together in the file, note we're
- // going backwards through the page list because page 1 is at the
- // tail and I want them to print out in number order, but it
- // doesn't really matter
- //
- TexturePage *page;
- ImageInfo *image;
- for( page = m_pageTail; page; page = page->m_prev )
- {
- // ignore texture pages that generated errors
- if( BitTest( page->m_status, TexturePage::PAGE_ERROR ) )
- continue;
- // go through each image on this page
- for( image = page->getFirstImage();
- image;
- image = image->m_nextPageImage )
- {
- //
- // write the item definition, note when we output the texture coords
- // we add on to the right and bottom to include that pixel in the
- // texture calculations ... need to do this since we are using a zero
- // based region for the "filled regions" in the image packer
- //
- fprintf( fp, "MappedImage %s\n", image->m_filenameOnlyNoExt );
- fprintf( fp, " Texture = %s_%03d.tga\n", m_outputFile, page->getID() );
- fprintf( fp, " TextureWidth = %d\n", page->getWidth() );
- fprintf( fp, " TextureHeight = %d\n", page->getHeight() );
- fprintf( fp, " Coords = Left:%d Top:%d Right:%d Bottom:%d\n",
- image->m_pagePos.lo.x, image->m_pagePos.lo.y,
- image->m_pagePos.hi.x + 1, image->m_pagePos.hi.y + 1 );
- fprintf( fp, " Status = %s\n",
- BitTest( image->m_status, ImageInfo::ROTATED90C ) ?
- "ROTATED_90_CLOCKWISE" : "NONE" );
- fprintf( fp, "End\n\n" );
- } // end for image
- } // end for page
- // close the file
- fclose( fp );
- return TRUE; // success
- } // end generateINIFile
- ///////////////////////////////////////////////////////////////////////////////
- // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////
- // ImagePacker::getSettingsFromDialog =========================================
- /** Given the current state of the option dialog passed in, get all the
- * settings we need for the image packer from the GUI and validate them */
- //=============================================================================
- Bool ImagePacker::getSettingsFromDialog( HWND dialog )
- {
- Int i;
- // sanity
- if( dialog == NULL )
- return FALSE;
- // if we are using a user target image size, it must be a power of 2
- if( IsDlgButtonChecked( dialog, RADIO_TARGET_OTHER ) )
- {
- UnsignedInt size, val;
- Int bitCount = 0;
- size = GetDlgItemInt( dialog, EDIT_WIDTH, NULL, FALSE );
- for( val = size; val; val >>= 1 )
- if( BitTest( val, 0x1 ) )
- bitCount++;
- //
- // if bit count was not 1, this is not a power of 2 ... it also
- // guards us from entering a size of zero :)
- //
- if( bitCount != 1 )
- {
- MessageBox( NULL, "The target image size must be a power of 2.",
- "Must Be Power Of 2", MB_OK | MB_ICONERROR );
- return FALSE;
- } // end if
- // set the size for the image packer
- setTargetSize( size, size );
- } // end if
- else if( IsDlgButtonChecked( dialog, RADIO_128X128 ) )
- setTargetSize( 128, 128 );
- else if( IsDlgButtonChecked( dialog, RADIO_256X256 ) )
- setTargetSize( 256, 256 );
- else if( IsDlgButtonChecked( dialog, RADIO_512X512 ) )
- setTargetSize( 512, 512 );
- else
- {
- MessageBox( NULL, "Internal Error. Target Size Unknown.",
- "Error", MB_OK | MB_ICONERROR );
- return FALSE;
- } // end else
-
- // get alpha option
- Bool outputAlpha = FALSE;
- if( IsDlgButtonChecked( dialog, CHECK_ALPHA ) == BST_CHECKED )
- outputAlpha = TRUE;
- TheImagePacker->setOutputAlpha( outputAlpha );
- // get create INI option
- Bool createINI = FALSE;
- if( IsDlgButtonChecked( dialog, CHECK_INI ) == BST_CHECKED )
- createINI = TRUE;
- TheImagePacker->setINICreate( createINI );
- // get preview with image option
- Bool useBitmap = FALSE;
- if( IsDlgButtonChecked( dialog, CHECK_BITMAP_PREVIEW ) == BST_CHECKED )
- useBitmap = TRUE;
- TheImagePacker->setUseTexturePreview( useBitmap );
- // get option to compress final textures
- Bool compress = FALSE;
- if( IsDlgButtonChecked( dialog, CHECK_COMPRESS ) == BST_CHECKED )
- compress = TRUE;
- TheImagePacker->setCompressTextures( compress );
- // get options for the gap options
- TheImagePacker->clearGapMethod( ImagePacker::GAP_METHOD_EXTEND_RGB );
- if( IsDlgButtonChecked( dialog, CHECK_GAP_EXTEND_RGB ) == BST_CHECKED )
- TheImagePacker->setGapMethod( ImagePacker::GAP_METHOD_EXTEND_RGB );
- TheImagePacker->clearGapMethod( ImagePacker::GAP_METHOD_GUTTER );
- if( IsDlgButtonChecked( dialog, CHECK_GAP_GUTTER ) == BST_CHECKED )
- TheImagePacker->setGapMethod( ImagePacker::GAP_METHOD_GUTTER );
- // get gutter size whether we are using that option or not
- Int gutter = GetDlgItemInt( dialog, EDIT_GUTTER, NULL, FALSE );
- if( gutter < 0 )
- gutter = 0;
- setGutter( gutter );
-
- // save the filename to output
- GetDlgItemText( dialog, EDIT_FILENAME, m_outputFile, MAX_OUTPUT_FILE_LEN - 1 );
-
- // check for illegal characters in the output name
- Int len = strlen( m_outputFile );
- for( i = 0; i < len; i++ )
- {
- char *illegal = "/\\:*?<>|";
- Int illegalLen = strlen( illegal );
-
- for( Int j = 0; j < illegalLen; j++ )
- {
- if( m_outputFile[ i ] == illegal[ j ] )
- {
- char buffer[ 256 ];
- sprintf( buffer, "Output filename '%s' contains one or more of the following illegal characters:\n\n%s",
- m_outputFile, illegal );
- MessageBox( NULL, buffer, "Illegal Filename", MB_OK | MB_ICONERROR );
- return FALSE;
- } // end if
- } // end for j
- } // end for i
- // get the work on sub-folders option
- m_useSubFolders = IsDlgButtonChecked( dialog, CHECK_USE_SUB_FOLDERS );
- // clear our list of image directories
- resetImageDirectoryList();
- // set a status message
- statusMessage( "Gathering Directory Information, Please Wait ..." );
- // add all the image directories specified in the folder listbox
- Int count = SendDlgItemMessage( dialog, LIST_FOLDERS, LB_GETCOUNT, 0, 0 );
- char buffer[ _MAX_PATH ];
- for( i = 0; i < count; i++ )
- {
- // get text from the listbox
- SendDlgItemMessage( dialog, LIST_FOLDERS,
- LB_GETTEXT, i, (LPARAM)buffer );
- // add the directory
- addDirectory( buffer, m_useSubFolders );
- } // end for i
- // all done
- return TRUE;
- } // end getSettingsFromDialog
- // ImagePacker::ImagePacker ===================================================
- /** */
- //=============================================================================
- ImagePacker::ImagePacker( void )
- {
- m_hWnd = NULL;
- m_targetSize.x = DEFAULT_TARGET_SIZE;
- m_targetSize.y = DEFAULT_TARGET_SIZE;
- m_useSubFolders = TRUE;
- strcpy( m_outputFile, "" );
- strcpy( m_outputDirectory, "" );
- m_dirList = NULL;
- m_dirCount = 0;
- m_imagesInDirs = 0;
- m_imageList = NULL;
- m_imageCount = 0;
- strcpy( m_statusBuffer, "" );
- m_pageList = NULL;
- m_pageTail = NULL;
- m_pageCount = 0;
- m_gapMethod = GAP_METHOD_EXTEND_RGB;
- m_gutterSize = 1;
- m_outputAlpha = TRUE;
- m_createINI = TRUE;
- m_targetPreviewPage = 1;
- m_hWndPreview = NULL;
- m_showTextureInPreview = FALSE;
- m_targa = NULL;
- m_compressTextures = FALSE;
- } // end ImagePacker
- // ImagePacker::~ImagePacker ==================================================
- /** */
- //=============================================================================
- ImagePacker::~ImagePacker( void )
- {
- // delete our lists
- resetImageDirectoryList();
- resetImageList();
- resetPageList();
- // delete our targa header loader
- if( m_targa )
- delete m_targa;
- } // end ~ImagePacker
- // ImagePacker::init ==========================================================
- /** Initialize the image packer system */
- //=============================================================================
- Bool ImagePacker::init( void )
- {
- // allocate a targa to read the headers for the images
- m_targa = new Targa;
- if( m_targa == NULL )
- {
-
- DEBUG_ASSERTCRASH( m_targa, ("Unable to allocate targa header during init\n") );
- MessageBox( NULL, "ImagePacker can't init, unable to create targa",
- "Internal Error", MB_OK | MB_ICONERROR );
- return FALSE;
- } // end if
- return TRUE;
- } // end init
- // ImagePacker::statusMessage =================================================
- /** Status message for the program */
- //=============================================================================
- void ImagePacker::statusMessage( char *message )
- {
- SetDlgItemText( getWindowHandle(), STATIC_STATUS, message );
- } // end statusMessage
- // ImagePacker::process =======================================================
- /** Run the packing process */
- //=============================================================================
- Bool ImagePacker::process( void )
- {
- // build the output directory based on the base name of the output images
- char currDir[ _MAX_PATH ];
- GetCurrentDirectory( _MAX_PATH, currDir );
- sprintf( m_outputDirectory, "%s\\ImagePackerOutput\\", currDir );
- CreateDirectory( m_outputDirectory, NULL );
- // subdir of output directory based on output image name
- strcat( m_outputDirectory, m_outputFile );
- strcat( m_outputDirectory, "\\" );
- //
- // check for existing images in the output directory ... if we have
- // some then ask the user if they want to delete them or proceed with
- // a possible overwrite
- //
- if( checkOutputDirectory() == FALSE )
- {
- statusMessage( "Build Process Cancelled." );
- return FALSE;
- } // end if
- // reset the contents of our image list and existing textures
- resetImageList();
- resetPageList();
- // set a status message
- statusMessage( "Gathering Image Information, Please Wait ..." );
- // allocate an array to hold all the images
- m_imageList = new ImageInfo *[ m_imagesInDirs ];
- // load our image list with all the art files from the specified directories
- ImageDirectory *dir;
- for( dir = m_dirList; dir; dir = dir->m_next )
- addImagesInDirectory( dir->m_path );
- // sort the images with the largest biggest images at the top of the list
- sortImageList();
- // pack all images
- if( packImages() )
- {
- // generate the actual final textures and write them out to the file
- writeFinalTextures();
- // generate the INI definition file if requested
- if( createINIFile() == TRUE )
- generateINIFile();
- // update preview window
- UpdatePreviewWindow();
- // all done
- sprintf( m_statusBuffer, "Image Packing Complete: '%d' Texture Pages Generated from '%d' Images in '%d' Folder(s)",
- m_pageCount, m_imageCount, m_dirCount );
- statusMessage( m_statusBuffer );
- } // end if
- return TRUE;
- } // end process
|