text: #include <algorithm>
  #include <eepp/graphics/texturepacker.hpp>
  #include <eepp/graphics/texturepackernode.hpp>
  #include <eepp/graphics/texturepackertex.hpp>
  #include <eepp/system/filesystem.hpp>
  #include <eepp/system/iostreamfile.hpp>
  #include <eepp/system/log.hpp>
  #include <eepp/system/md5.hpp>
  #include <eepp/system/sys.hpp>
  
  namespace EE { namespace Graphics {
  
  TexturePacker* TexturePacker::New() {
  	return eeNew( TexturePacker, () );
  }
  
  TexturePacker* TexturePacker::New( const Uint32& maxWidth, const Uint32& maxHeight,
  								   const Float& pixelDensity, const bool& forcePowOfTwo,
  								   const bool& scalableSVG, const Uint32& pixelBorder,
  								   const Texture::Filter& textureFilter, const bool& allowChilds,
  								   const bool& allowFlipping ) {
  	return eeNew( TexturePacker, ( maxWidth, maxHeight, pixelDensity, forcePowOfTwo, scalableSVG,
  								   pixelBorder, textureFilter, allowChilds, allowFlipping ) );
  }
  
  TexturePacker::TexturePacker( const Uint32& maxWidth, const Uint32& maxHeight,
  							  const Float& pixelDensity, const bool& forcePowOfTwo,
  							  const bool& scalableSVG, const Uint32& pixelBorder,
  							  const Texture::Filter& textureFilter, const bool& allowChilds,
  							  const bool& allowFlipping ) :
  	mTotalArea( 0 ),
  	mFreeList( NULL ),
  	mWidth( 128 ),
  	mHeight( 128 ),
  	mPacked( false ),
  	mAllowFlipping( allowFlipping ),
  	mAllowChilds( allowChilds ),
  	mChild( NULL ),
  	mStrategy( PackBig ),
  	mCount( 0 ),
  	mParent( NULL ),
  	mPlacedCount( 0 ),
  	mForcePowOfTwo( true ),
  	mPixelBorder( 0 ),
  	mPixelDensity( eefloor( pixelDensity * 100 ) ),
  	mTextureFilter( textureFilter ),
  	mKeepExtensions( false ),
  	mScalableSVG( scalableSVG ),
  	mFormat( Image::SaveType::SAVE_TYPE_PNG ) {
  	setOptions( maxWidth, maxHeight, pixelDensity, forcePowOfTwo, scalableSVG, pixelBorder,
  				textureFilter, allowChilds, allowFlipping );
  }
  
  TexturePacker::TexturePacker() :
  	mTotalArea( 0 ),
  	mFreeList( NULL ),
  	mWidth( 128 ),
  	mHeight( 128 ),
  	mMaxSize( mWidth, mHeight ),
  	mPacked( false ),
  	mAllowFlipping( false ),
  	mChild( NULL ),
  	mStrategy( PackBig ),
  	mCount( 0 ),
  	mParent( NULL ),
  	mPlacedCount( 0 ),
  	mForcePowOfTwo( true ),
  	mPixelBorder( 0 ),
  	mPixelDensity( 1 ),
  	mTextureFilter( Texture::Filter::Linear ),
  	mKeepExtensions( false ),
  	mScalableSVG( false ),
  	mFormat( Image::SaveType::SAVE_TYPE_PNG ) {}
  
  TexturePacker::~TexturePacker() {
  	close();
  }
  
  void TexturePacker::close() {
  	reset();
  
  	std::list<TexturePackerTex*>::iterator it;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		eeSAFE_DELETE( ( *it ) );
  	}
  
  	mTextures.clear();
  }
  
  void TexturePacker::reset() {
  	mStrategy = PackBig;
  	mCount = mTextures.size();
  
  	TexturePackerTex* t = NULL;
  	std::list<TexturePackerTex*>::iterator it;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		t = ( *it );
  		t->placed( false );
  	}
  
  	if ( NULL != mFreeList ) {
  		TexturePackerNode* next = mFreeList;
  		TexturePackerNode* kill = NULL;
  
  		while ( NULL != next ) {
  			kill = next;
  
  			next = next->getNext();
  
  			eeSAFE_DELETE( kill );
  		}
  
  		mFreeList = NULL;
  	}
  
  	eeSAFE_DELETE( mChild );
  }
  
  Uint32 TexturePacker::getAtlasNumChannels() {
  	Uint32 maxChannels = 0;
  	TexturePackerTex* t = NULL;
  	std::list<TexturePackerTex*>::iterator it;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		t = ( *it );
  
  		if ( t->placed() ) {
  			maxChannels = eemax( maxChannels, (Uint32)t->channels() );
  		}
  	}
  
  	return maxChannels;
  }
  
  void TexturePacker::setOptions( const Uint32& maxWidth, const Uint32& maxHeight,
  								const Float& pixelDensity, const bool& forcePowOfTwo,
  								const bool& scalableSVG, const Uint32& pixelBorder,
  								const Texture::Filter& textureFilter, const bool& allowChilds,
  								const bool& allowFlipping ) {
  	if ( !mTextures.size() ) { // only can change the dimensions before adding any texture
  		mMaxSize.x = maxWidth;
  		mMaxSize.y = maxHeight;
  
  		if ( forcePowOfTwo && !Math::isPow2( mMaxSize.x ) )
  			mMaxSize.x = Math::nextPowOfTwo( mMaxSize.x );
  
  		if ( forcePowOfTwo && !Math::isPow2( mMaxSize.y ) )
  			mMaxSize.y = Math::nextPowOfTwo( mMaxSize.y );
  
  		mForcePowOfTwo = forcePowOfTwo;
  		mAllowFlipping = allowFlipping;
  		mAllowChilds = allowChilds;
  		mPixelBorder = pixelBorder;
  		mPixelDensity = eefloor( pixelDensity * 100 );
  		mTextureFilter = textureFilter;
  		mScalableSVG = scalableSVG;
  	}
  }
  
  void TexturePacker::newFree( Int32 x, Int32 y, Int32 width, Int32 height ) {
  	TexturePackerNode* node = eeNew( TexturePackerNode, ( x, y, width, height ) );
  	node->setNext( mFreeList );
  	mFreeList = node;
  }
  
  bool TexturePacker::mergeNodes() {
  	TexturePackerNode* f = mFreeList;
  
  	while ( f ) {
  		TexturePackerNode* prev = NULL;
  		TexturePackerNode* c = mFreeList;
  
  		while ( c ) {
  			if ( f != c ) {
  				if ( f->merge( *c ) ) {
  					eeASSERT( prev );
  
  					if ( NULL != prev ) {
  						prev->setNext( c->getNext() );
  					}
  
  					eeSAFE_DELETE( c );
  
  					return true;
  				}
  			}
  
  			prev = c;
  			c = c->getNext();
  		}
  
  		f = f->getNext();
  	}
  
  	return false;
  }
  
  void TexturePacker::validate() {
  #ifdef EE_DEBUG
  	TexturePackerNode* f = mFreeList;
  	while ( f ) {
  		TexturePackerNode* c = mFreeList;
  
  		while ( c ) {
  			if ( f != c )
  				f->validate( c );
  
  			c = c->getNext();
  		}
  
  		f = f->getNext();
  	}
  #endif
  }
  
  TexturePackerTex* TexturePacker::getLonguestEdge() {
  	TexturePackerTex* t = NULL;
  	std::list<TexturePackerTex*>::iterator it;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		if ( !( *it )->placed() ) {
  			t = ( *it );
  			break;
  		}
  	}
  
  	return t;
  }
  
  TexturePackerTex* TexturePacker::getShortestEdge() {
  	TexturePackerTex* t = NULL;
  	std::list<TexturePackerTex*>::reverse_iterator it;
  
  	for ( it = mTextures.rbegin(); it != mTextures.rend(); ++it ) {
  		if ( !( *it )->placed() ) {
  			t = ( *it );
  			break;
  		}
  	}
  
  	return t;
  }
  
  void TexturePacker::addBorderToTextures( const Int32& BorderSize ) {
  	TexturePackerTex* t;
  
  	if ( 0 != BorderSize ) {
  		std::list<TexturePackerTex*>::iterator it;
  
  		for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  			t = ( *it );
  
  			t->width( t->width() + BorderSize );
  			t->height( t->height() + BorderSize );
  		}
  	}
  }
  
  TexturePackerNode* TexturePacker::getBestFit( TexturePackerTex* t, TexturePackerNode** prevBestFit,
  											  Int32* EdgeCount ) {
  	Int32 leastY = 0x7FFFFFFF;
  	Int32 leastX = 0x7FFFFFFF;
  	TexturePackerNode* previousBestFit = NULL;
  	TexturePackerNode* bestFit = NULL;
  	TexturePackerNode* previous = NULL;
  	TexturePackerNode* search = mFreeList;
  	Int32 edgeCount = 0;
  
  	// Walk the singly linked list of free nodes
  	// see if it will fit into any currently free space
  	while ( search ) {
  		Int32 ec;
  
  		// see if the texture will fit into this slot, and if so how many edges does it share.
  		if ( search->fits( t->width(), t->height(), ec, mAllowFlipping ) ) {
  
  			if ( ec == 2 ) {
  				previousBestFit = previous; // record the pointer previous to this one (used to
  											// patch the linked list)
  				bestFit = search;			// record the best fit.
  				edgeCount = ec;
  
  				break;
  			}
  
  			if ( search->y() < leastY ) {
  				leastY = search->y();
  				leastX = search->x();
  				previousBestFit = previous;
  				bestFit = search;
  				edgeCount = ec;
  			} else if ( search->y() == leastY && search->x() < leastX ) {
  				leastX = search->x();
  				previousBestFit = previous;
  				bestFit = search;
  				edgeCount = ec;
  			}
  		}
  
  		previous = search;
  		search = search->getNext();
  	}
  
  	*EdgeCount = edgeCount;
  	*prevBestFit = previousBestFit;
  
  	return bestFit;
  }
  
  void TexturePacker::insertTexture( TexturePackerTex* t, TexturePackerNode* bestFit, Int32 edgeCount,
  								   TexturePackerNode* previousBestFit ) {
  	if ( NULL != bestFit ) {
  		validate();
  
  		switch ( edgeCount ) {
  			case 0: {
  				bool flipped = false;
  				int w = t->width();
  				int h = t->height();
  
  				if ( mAllowFlipping ) {
  					if ( t->longestEdge() <= bestFit->width() ) {
  						if ( h > w ) {
  							w = t->height();
  							h = t->width();
  							flipped = true;
  						}
  					} else {
  						eeASSERT( t->longestEdge() <= bestFit->height() );
  
  						if ( h < w ) {
  							w = t->height();
  							h = t->width();
  							flipped = true;
  						}
  					}
  				}
  
  				t->place( bestFit->x(), bestFit->y(), flipped ); // place it.
  
  				newFree( bestFit->x(), bestFit->y() + h, bestFit->width(), bestFit->height() - h );
  
  				bestFit->x( bestFit->x() + w );
  				bestFit->width( bestFit->width() - w );
  				bestFit->height( h );
  
  				validate();
  			} break;
  			case 1: {
  				if ( t->width() == bestFit->width() ) {
  					t->place( bestFit->x(), bestFit->y(), false );
  
  					bestFit->y( bestFit->y() + t->height() );
  					bestFit->height( bestFit->height() - t->height() );
  
  					validate();
  				} else if ( t->height() == bestFit->height() ) {
  					t->place( bestFit->x(), bestFit->y(), false );
  
  					bestFit->x( bestFit->x() + t->width() );
  					bestFit->width( bestFit->width() - t->width() );
  
  					validate();
  				} else if ( mAllowFlipping && t->width() == bestFit->height() ) {
  					t->place( bestFit->x(), bestFit->y(), true );
  
  					bestFit->x( bestFit->x() + t->height() );
  					bestFit->width( bestFit->width() - t->height() );
  
  					validate();
  				} else if ( mAllowFlipping && t->height() == bestFit->width() ) {
  					t->place( bestFit->x(), bestFit->y(), true );
  
  					bestFit->y( bestFit->y() + t->width() );
  					bestFit->height( bestFit->height() - t->width() );
  
  					validate();
  				}
  			} break;
  			case 2: {
  				bool flipped = t->width() != bestFit->width() || t->height() != bestFit->height();
  
  				t->place( bestFit->x(), bestFit->y(), flipped );
  
  				if ( previousBestFit )
  					previousBestFit->setNext( bestFit->getNext() );
  				else
  					mFreeList = bestFit->getNext();
  
  				eeSAFE_DELETE( bestFit );
  
  				validate();
  			} break;
  		}
  
  		while ( mergeNodes() )
  			; // keep merging nodes as much as we can...
  	}
  }
  
  void TexturePacker::createChild() {
  	mChild = TexturePacker::New( mWidth, mHeight, mPixelDensity / 100.f, mForcePowOfTwo,
  								 mScalableSVG, mPixelBorder, mTextureFilter, mAllowFlipping );
  
  	std::list<TexturePackerTex*>::iterator it;
  	std::list<std::list<TexturePackerTex*>::iterator> remove;
  
  	TexturePackerTex* t = NULL;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		t = ( *it );
  
  		if ( !t->placed() ) {
  			mChild->addTexture( t->name() );
  			mChild->mParent = this;
  
  			t->disabled( true );
  
  			remove.push_back( it );
  
  			mCount--;
  		}
  	}
  
  	// Removes the non-placed textures from the pack
  	std::list<std::list<TexturePackerTex*>::iterator>::iterator itit;
  
  	for ( itit = remove.begin(); itit != remove.end(); ++itit ) {
  		mTextures.erase( *itit );
  	}
  
  	mChild->packTextures();
  }
  
  bool TexturePacker::addTexturesPath( std::string TexturesPath ) {
  	if ( FileSystem::isDirectory( TexturesPath ) ) {
  		FileSystem::dirAddSlashAtEnd( TexturesPath );
  
  		std::vector<std::string> files = FileSystem::filesGetInPath( TexturesPath );
  		std::sort( files.begin(), files.end() );
  
  		for ( Uint32 i = 0; i < files.size(); i++ ) {
  			std::string path( TexturesPath + files[i] );
  			if ( !FileSystem::isDirectory( path ) && Image::isImageExtension( path ) )
  				addTexture( path );
  		}
  
  		return true;
  	}
  
  	return false;
  }
  
  bool TexturePacker::addPackerTex( TexturePackerTex* TPack ) {
  	if ( TPack->loadedInfo() ) {
  		// Only add the texture if can fit inside the atlas, otherwise it will ignore it
  		if ( ( TPack->width() + mPixelBorder <= mMaxSize.getWidth() &&
  			   TPack->height() + mPixelBorder <= mMaxSize.getHeight() ) ||
  			 ( mAllowFlipping && ( TPack->width() + mPixelBorder <= mMaxSize.getHeight() &&
  								   TPack->height() + mPixelBorder <= mMaxSize.getWidth() ) ) ) {
  			mTotalArea += TPack->area();
  
  			// Insert ordered
  			std::list<TexturePackerTex*>::iterator it;
  
  			bool Added = false;
  
  			for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  				if ( ( *it )->area() < TPack->area() ) {
  					mTextures.insert( it, TPack );
  					Added = true;
  					break;
  				}
  			}
  
  			if ( !Added ) {
  				mTextures.push_back( TPack );
  
  				return true;
  			}
  		}
  	}
  
  	return false;
  }
  
  bool TexturePacker::addImage( Image* Img, const std::string& Name ) {
  	TexturePackerTex* TPack = eeNew( TexturePackerTex, ( Img, Name ) );
  
  	return addPackerTex( TPack );
  }
  
  bool TexturePacker::addTexture( const std::string& TexturePath ) {
  	if ( FileSystem::fileExists( TexturePath ) ) {
  		Image::FormatConfiguration imageFormatConfiguration;
  
  		imageFormatConfiguration.svgScale( mScalableSVG ? mPixelDensity / 100.f : 1.f );
  
  		TexturePackerTex* TPack =
  			eeNew( TexturePackerTex, ( TexturePath, imageFormatConfiguration ) );
  
  		return addPackerTex( TPack );
  	}
  
  	return false;
  }
  
  Int32 TexturePacker::packTextures() {
  	TexturePackerTex* t = NULL;
  
  	addBorderToTextures( (Int32)mPixelBorder );
  
  	newFree( 0, 0, mWidth, mHeight );
  
  	mCount = (Int32)mTextures.size();
  
  	// We must place each texture
  	std::list<TexturePackerTex*>::iterator it;
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		// For the texture with the longest edge we place it according to this criteria.
  		//   (1) If it is a perfect match, we always accept it as it causes the least amount of
  		//   fragmentation. (2) A match of one edge with the minimum area left over after the split.
  		//   (3) No edges match, so look for the node which leaves the least amount of area left
  		//   over after the split.
  
  		if ( PackBig == mStrategy ) {
  			t = getLonguestEdge();
  		} else if ( PackTiny == mStrategy ) {
  			t = getShortestEdge();
  		}
  
  		TexturePackerNode* previousBestFit = NULL;
  		Int32 edgeCount = 0;
  		TexturePackerNode* bestFit = getBestFit( t, &previousBestFit, &edgeCount );
  
  		if ( NULL == bestFit ) {
  			if ( PackBig == mStrategy ) {
  				Log::debug( "TexturePacker: Chaging Strategy to Tiny. %s faults.",
  							t->name().c_str() );
  				reset();
  				addBorderToTextures( -( (Int32)mPixelBorder ) );
  				mStrategy = PackTiny;
  				return packTextures();
  			} else if ( PackTiny == mStrategy ) {
  				mStrategy = PackFail;
  				Log::warning( "TexturePacker: Strategy fail, must expand image or create a new "
  							  "one. %s faults.",
  							  t->name().c_str() );
  			}
  		} else {
  			insertTexture( t, bestFit, edgeCount, previousBestFit );
  			mCount--;
  		}
  
  		if ( PackFail == mStrategy ) {
  			if ( mWidth < mMaxSize.getWidth() || mHeight < mMaxSize.getHeight() ) {
  				reset();
  				addBorderToTextures( -( (Int32)mPixelBorder ) );
  
  				if ( mWidth <= mHeight ) {
  					mWidth *= 2;
  
  					if ( mWidth > mMaxSize.getWidth() )
  						mWidth = mMaxSize.getWidth();
  				} else {
  					mHeight *= 2;
  
  					if ( mHeight > mMaxSize.getHeight() )
  						mHeight = mMaxSize.getHeight();
  				}
  
  				return packTextures();
  			} else {
  				if ( !mAllowChilds ) {
  					return 0;
  				}
  			}
  
  			break;
  		}
  	}
  
  	if ( mCount > 0 ) {
  		if ( mAllowChilds ) {
  			Log::debug( "Creating a new image as a child. Some textures couldn't get it: %d",
  						mCount );
  			createChild();
  		} else {
  			return 0;
  		}
  	}
  
  	addBorderToTextures( -( (Int32)mPixelBorder ) );
  
  	mPacked = true;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		if ( !( *it )->placed() )
  			mTotalArea -= ( *it )->area();
  	}
  
  	Log::debug( "Total Area Used: %d. This represents the %4.3f percent", mTotalArea,
  				( (double)mTotalArea / (double)( mWidth * mHeight ) ) * 100.0 );
  
  	return mTotalArea;
  }
  
  void TexturePacker::save( const std::string& Filepath, const Image::SaveType& Format,
  						  const bool& KeepExtensions ) {
  	if ( !mPacked )
  		packTextures();
  
  	if ( !mTextures.size() )
  		return;
  
  	mFilepath = Filepath;
  	mKeepExtensions = KeepExtensions;
  
  	Image Img( (Uint32)mWidth, (Uint32)mHeight, getAtlasNumChannels() );
  
  	Img.fillWithColor( Color( 0, 0, 0, 0 ) );
  
  	TexturePackerTex* t = NULL;
  	std::list<TexturePackerTex*>::iterator it;
  
  	for ( it = mTextures.begin(); it != mTextures.end(); ++it ) {
  		t = ( *it );
  
  		if ( t->placed() ) {
  			Uint8* data;
  
  			if ( NULL == t->getImage() ) {
  				Image imageLoaded( t->name() );
  
  				if ( NULL != imageLoaded.getPixelsPtr() &&
  					 t->width() == (int)imageLoaded.getWidth() &&
  					 t->height() == (int)imageLoaded.getHeight() ) {
  					if ( t->flipped() )
  						imageLoaded.flip();
  
  					Img.copyImage( &imageLoaded, t->x(), t->y() );
  
  					mPlacedCount++;
  				}
  			} else {
  				data = t->getImage()->getPixels();
  
  				if ( NULL != data ) {
  					if ( t->flipped() )
  						t->getImage()->flip();
  
  					Img.copyImage( t->getImage(), t->x(), t->y() );
  
  					mPlacedCount++;
  				}
  			}
  		}
  	}
  
  	mFormat = Format;
  
  	Img.saveToFile( Filepath, Format );
  
  	childSave( Format );
  
  	saveTextureRegions();
  }
  
  Int32 TexturePacker::getChildCount() {
  	TexturePacker* Child = mChild;
  	Int32 ChildCount = 0;
  
  	while ( NULL != Child ) {
  		ChildCount++;
  		Child = Child->getChild();
  	}
  
  	return ChildCount;
  }
  
  void TexturePacker::saveTextureRegions() {
  	sTextureAtlasHdr TexGrHdr;
  
  	TexGrHdr.Magic = EE_TEXTURE_ATLAS_MAGIC;
  	TexGrHdr.Version = HDR_TEXTURE_ATLAS_VERSION;
  	TexGrHdr.Date = static_cast<Uint64>( Sys::getSystemTime() );
  	TexGrHdr.TextureCount = 1;
  	TexGrHdr.Format = mFormat;
  	TexGrHdr.Width = mWidth;
  	TexGrHdr.Height = mHeight;
  	TexGrHdr.PixelBorder = mPixelBorder;
  	TexGrHdr.Flags = 0;
  	TexGrHdr.TextureFilter = (Uint32)mTextureFilter;
  
  	int reservedSize = eeARRAY_SIZE( TexGrHdr.Reserved );
  	memset( TexGrHdr.Reserved, 0, reservedSize );
  
  	if ( mAllowFlipping )
  		TexGrHdr.Flags |= HDR_TEXTURE_ATLAS_ALLOW_FLIPPING;
  
  	if ( !mKeepExtensions )
  		TexGrHdr.Flags |= HDR_TEXTURE_ATLAS_REMOVE_EXTENSION;
  
  	if ( mForcePowOfTwo )
  		TexGrHdr.Flags |= HDR_TEXTURE_ATLAS_POW_OF_TWO;
  
  	if ( mScalableSVG )
  		TexGrHdr.Flags |= HDR_TEXTURE_ATLAS_SCALABLE_SVG;
  
  	std::vector<sTextureHdr> TexHdr( TexGrHdr.TextureCount );
  
  	TexHdr[0] = createTextureHdr( this );
  
  	std::vector<sTextureRegionHdr> tTextureRegionsHdr;
  
  	std::string path = FileSystem::fileRemoveExtension( mFilepath ) + EE_TEXTURE_ATLAS_EXTENSION;
  	IOStreamFile fs( path, "wb" );
  
  	if ( fs.isOpen() ) {
  		fs.write( reinterpret_cast<const char*>( &TexGrHdr ), sizeof( sTextureAtlasHdr ) );
  
  		fs.write( reinterpret_cast<const char*>( &TexHdr[0] ), sizeof( sTextureHdr ) );
  
  		createTextureRegionsHdr( this, tTextureRegionsHdr );
  
  		if ( tTextureRegionsHdr.size() )
  			fs.write( reinterpret_cast<const char*>( &tTextureRegionsHdr[0] ),
  					  sizeof( sTextureRegionHdr ) * (std::streamsize)tTextureRegionsHdr.size() );
  	}
  }
  
  void TexturePacker::createTextureRegionsHdr( TexturePacker* Packer,
  											 std::vector<sTextureRegionHdr>& TextureRegions ) {
  	TextureRegions.clear();
  
  	sTextureRegionHdr tTextureRegionHdr;
  	Uint32 c = 0;
  
  	std::list<TexturePackerTex*> tTextures = *( Packer->getTexturePackPtr() );
  	std::list<TexturePackerTex*>::iterator it;
  	TexturePackerTex* tTex;
  
  	TextureRegions.resize( tTextures.size() );
  
  	for ( it = tTextures.begin(); it != tTextures.end(); ++it ) {
  		tTex = ( *it );
  
  		if ( tTex->placed() ) {
  			std::string name = FileSystem::fileNameFromPath( tTex->name() );
  
  			if ( name.size() > HDR_NAME_SIZE )
  				name.resize( HDR_NAME_SIZE );
  
  			memset( tTextureRegionHdr.Name, 0, HDR_NAME_SIZE );
  
  			String::strCopy( tTextureRegionHdr.Name, name.c_str(), HDR_NAME_SIZE );
  
  			if ( !mKeepExtensions )
  				name = FileSystem::fileRemoveExtension( name );
  
  			tTextureRegionHdr.ResourceID = String::hash( name );
  			tTextureRegionHdr.Width = tTex->width();
  			tTextureRegionHdr.Height = tTex->height();
  			tTextureRegionHdr.Channels = tTex->channels();
  			tTextureRegionHdr.DestWidth = tTex->width();
  			tTextureRegionHdr.DestHeight = tTex->height();
  			tTextureRegionHdr.OffsetX = 0;
  			tTextureRegionHdr.OffsetY = 0;
  			tTextureRegionHdr.X = tTex->x();
  			tTextureRegionHdr.Y = tTex->y();
  			tTextureRegionHdr.Date = FileSystem::fileGetModificationDate( tTex->name() );
  			tTextureRegionHdr.Flags = 0;
  			tTextureRegionHdr.PixelDensity = mPixelDensity;
  			MD5::Result md5Result = MD5::fromFile( tTex->name() );
  			memcpy( tTextureRegionHdr.Hash, &md5Result.digest[0], HDR_HASH_SIZE );
  
  			if ( tTex->flipped() )
  				tTextureRegionHdr.Flags |= HDR_TEXTUREREGION_FLAG_FLIPED;
  
  			TextureRegions[c] = tTextureRegionHdr;
  
  			c++;
  		}
  	}
  }
  
  sTextureHdr TexturePacker::createTextureHdr( TexturePacker* Packer ) {
  	sTextureHdr TexHdr;
  
  	std::string name( FileSystem::fileNameFromPath( Packer->getFilepath() ) );
  
  	memset( TexHdr.Name, 0, HDR_NAME_SIZE );
  
  	String::strCopy( TexHdr.Name, name.c_str(), HDR_NAME_SIZE );
  
  	TexHdr.ResourceID = String::hash( name );
  	TexHdr.Size = FileSystem::fileSize( Packer->getFilepath() );
  	TexHdr.TextureRegionCount = Packer->getPlacedCount();
  
  	return TexHdr;
  }
  
  void TexturePacker::childSave( const Image::SaveType& Format ) {
  	if ( NULL != mChild ) {
  		TexturePacker* Parent = mChild->getParent();
  		TexturePacker* LastParent = NULL;
  		Int32 ParentCount = 0;
  
  		// Find the grand parent
  		while ( NULL != Parent ) {
  			ParentCount++;
  			LastParent = Parent;
  			Parent = Parent->getParent();
  		}
  
  		if ( NULL != LastParent ) {
  			std::string fFpath = FileSystem::fileRemoveExtension( LastParent->getFilepath() );
  			std::string fExt = FileSystem::fileExtension( LastParent->getFilepath() );
  			std::string fName = fFpath + "-ch" + String::toString( ParentCount ) + "." + fExt;
  
  			mChild->save( fName, Format, mKeepExtensions );
  		}
  	}
  }
  
  TexturePacker* TexturePacker::getChild() const {
  	return mChild;
  }
  
  TexturePacker* TexturePacker::getParent() const {
  	return mParent;
  }
  
  std::list<TexturePackerTex*>* TexturePacker::getTexturePackPtr() {
  	return &mTextures;
  }
  
  const std::string& TexturePacker::getFilepath() const {
  	return mFilepath;
  }
  
  const Int32& TexturePacker::getWidth() const {
  	return mWidth;
  }
  
  const Int32& TexturePacker::getHeight() const {
  	return mHeight;
  }
  
  const Int32& TexturePacker::getPlacedCount() const {
  	return mPlacedCount;
  }
  
  }} // namespace EE::Graphics
  