Sfoglia il codice sorgente

Tabs to spaces and update copyright year.

Lasse Öörni 11 anni fa
parent
commit
4354b1709c

+ 1 - 1
Source/Tools/SpritePacker/CMakeLists.txt

@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2008-2014 the Urho3D project.
+# Copyright (c) 2008-2015 the Urho3D project.
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy
 # of this software and associated documentation files (the "Software"), to deal

+ 354 - 354
Source/Tools/SpritePacker/SpritePacker.cpp

@@ -1,5 +1,5 @@
 //
-// Copyright (c) 2008-2014 the Urho3D project.
+// Copyright (c) 2008-2015 the Urho3D project.
 //
 // Permission is hereby granted, free of charge, to any person obtaining a copy
 // of this software and associated documentation files (the "Software"), to deal
@@ -60,374 +60,374 @@ void Run(Vector<String>& arguments);
 class PackerInfo : public RefCounted
 {
 public:
-	String path;
-	String name;
-	int x;
-	int y;
-	int offsetX;
-	int offsetY;
-	int width;
-	int height;
-	int frameWidth;
-	int frameHeight;
-	int frameX;
-	int frameY;
-
-	PackerInfo(String path_, String name_) :
-		path(path_),
-		name(name_),
-		x(0),
-		y(0),
-		offsetX(0),
-		offsetY(0),
-		frameX(0),
-		frameY(0)
-	{
-	}
-
-	~PackerInfo() {}
+    String path;
+    String name;
+    int x;
+    int y;
+    int offsetX;
+    int offsetY;
+    int width;
+    int height;
+    int frameWidth;
+    int frameHeight;
+    int frameX;
+    int frameY;
+
+    PackerInfo(String path_, String name_) :
+        path(path_),
+        name(name_),
+        x(0),
+        y(0),
+        offsetX(0),
+        offsetY(0),
+        frameX(0),
+        frameY(0)
+    {
+    }
+
+    ~PackerInfo() {}
 };
 
 void Help()
 {
-	ErrorExit("Usage: SpritePacker -options <input file> <input file> <output png file>\n"
-		"\n"
-		"Options:\n"
-		"-h Shows this help message.\n"
-		"-px Adds x pixels of padding per image to width.\n"
-		"-py Adds y pixels of padding per image to height.\n"
-		"-ox Adds x pixels to the horizontal position per image.\n"
-		"-oy Adds y pixels to the horizontal position per image.\n"
-		"-frameHeight Sets a fixed height for image and centers within frame.\n"
-		"-frameWidth Sets a fixed width for image and centers within frame.\n"
-		"-trim Trims excess transparent space from individual images offsets by frame size.\n"
-		"-xml \'path\' Generates an SpriteSheet xml file at path.\n"
-		"-debug Draws allocation boxes on sprite.\n");
+    ErrorExit("Usage: SpritePacker -options <input file> <input file> <output png file>\n"
+        "\n"
+        "Options:\n"
+        "-h Shows this help message.\n"
+        "-px Adds x pixels of padding per image to width.\n"
+        "-py Adds y pixels of padding per image to height.\n"
+        "-ox Adds x pixels to the horizontal position per image.\n"
+        "-oy Adds y pixels to the horizontal position per image.\n"
+        "-frameHeight Sets a fixed height for image and centers within frame.\n"
+        "-frameWidth Sets a fixed width for image and centers within frame.\n"
+        "-trim Trims excess transparent space from individual images offsets by frame size.\n"
+        "-xml \'path\' Generates an SpriteSheet xml file at path.\n"
+        "-debug Draws allocation boxes on sprite.\n");
 }
 
 int main(int argc, char** argv)
 {
-	Vector<String> arguments;
+    Vector<String> arguments;
 
 #ifdef WIN32
-	arguments = ParseArguments(GetCommandLineW());
+    arguments = ParseArguments(GetCommandLineW());
 #else
-	arguments = ParseArguments(argc, argv);
+    arguments = ParseArguments(argc, argv);
 #endif
 
-	Run(arguments);
-	return 0;
+    Run(arguments);
+    return 0;
 }
 
 void Run(Vector<String>& arguments)
 {
-	if (arguments.Size() < 2)
-		Help();
-
-	SharedPtr<Context> context(new Context());
-	context->RegisterSubsystem(new FileSystem(context));
-	context->RegisterSubsystem(new Log(context));
-	FileSystem* fileSystem = context->GetSubsystem<FileSystem>();
-
-	Vector<String> inputFiles;
-	String outputFile;
-	String spriteSheetFileName;
-	bool debug = false;
-	unsigned padX = 0;
-	unsigned padY = 0;
-	unsigned offsetX = 0;
-	unsigned offsetY = 0;
-	unsigned frameWidth = 0;
-	unsigned frameHeight = 0;
-	bool help = false;
-	bool trim = false;
-
-	while (arguments.Size() > 0)
-	{
-		String arg = arguments[0];
-		arguments.Erase(0);
-
-		if (arg.Empty())
-			continue;
-
-		if (arg.StartsWith("-"))
-		{
-			if (arg == "-px")      { padX = ToUInt(arguments[0]); arguments.Erase(0); }
-			else if (arg == "-py") { padY = ToUInt(arguments[0]); arguments.Erase(0); }
-			else if (arg == "-ox") { offsetX = ToUInt(arguments[0]); arguments.Erase(0); }
-			else if (arg == "-oy") { offsetY = ToUInt(arguments[0]); arguments.Erase(0); }
-			else if (arg == "-frameWidth") { frameWidth = ToUInt(arguments[0]); arguments.Erase(0); }
-			else if (arg == "-frameHeight") { frameHeight = ToUInt(arguments[0]); arguments.Erase(0); }
-			else if (arg == "-trim") { trim = true; }
-			else if (arg == "-xml")  { spriteSheetFileName = arguments[0]; arguments.Erase(0); }
-			else if (arg == "-h")  { help = true; break; }
-			else if (arg == "-debug")  { debug = true; }
-		}
-		else
-			inputFiles.Push(arg);
-	}
-
-	if (help)
-		Help();
-
-	if (inputFiles.Size() < 2)
-		ErrorExit("An input and output file must be specified.");
-
-	if (frameWidth ^ frameHeight)
-		ErrorExit("Both frameHeight and frameWidth must be ommited or specified.");
-
-	// take last input file as output
-	if (inputFiles.Size() > 1)
-	{
-		outputFile = inputFiles[inputFiles.Size() - 1];
-		LOGINFO("Output file set to " + outputFile + ".");
-		inputFiles.Erase(inputFiles.Size() - 1);
-	}
-
-	// set spritesheet name to outputfile.xml if not specified
-	if (spriteSheetFileName.Empty())
-		spriteSheetFileName = ReplaceExtension(outputFile, ".xml");
-
-	if (GetParentPath(spriteSheetFileName) != GetParentPath(outputFile))
-		ErrorExit("Both output xml and png must be in the same folder");
-
-	// check all input files exist
-	for (unsigned i = 0; i < inputFiles.Size(); ++i)
-	{
-		LOGINFO("Checking " + inputFiles[i] + " to see if file exists.");
-		if (!fileSystem->FileExists(inputFiles[i]))
-			ErrorExit("File " + inputFiles[i] + " does not exist.");
-	}
-
-	// Set the max offset equal to padding to prevent images from going out of bounds
-	offsetX = Min((int)offsetX, (int)padX);
-	offsetY = Min((int)offsetY, (int)padY);
-
-	Vector<SharedPtr<PackerInfo > > packerInfos;
-
-	for (unsigned i = 0; i < inputFiles.Size(); ++i)
-	{
-		String path = inputFiles[i];
-		String name = ReplaceExtension(GetFileName(path), "");
-		File file(context, path);
-		Image image(context);
-
-		if (!image.Load(file))
-			ErrorExit("Could not load image " + path + ".");
-
-		if (image.IsCompressed())
-			ErrorExit(path + " is compressed. Compressed images are not allowed.");
-
-		SharedPtr<PackerInfo> packerInfo(new PackerInfo(path, name));
-		int imageWidth = image.GetWidth();
-		int imageHeight = image.GetHeight();
-		int trimOffsetX = 0;
-		int trimOffsetY = 0;
-		int adjustedWidth = imageWidth;
-		int adjustedHeight = imageHeight;
-
-		if (trim)
-		{
-			int minX = imageWidth;
-			int minY = imageHeight;
-			int maxX = 0;
-			int maxY = 0;
-
-			for (int y = 0; y < imageHeight; ++y)
-			{
-				for (int x = 0; x < imageWidth; ++x)
-				{
-					bool found = (image.GetPixelInt(x, y) & 0x000000ff) != 0;
-					if (found) {
-						minX = Min(minX, x);
-						minY = Min(minY, y);
-						maxX = Max(maxX, x);
-						maxY = Max(maxY, y);
-					}
-				}
-			}
-
-			trimOffsetX = minX;
-			trimOffsetY = minY;
-			adjustedWidth = maxX - minX + 1;
-			adjustedHeight = maxY - minY + 1;
-		}
-
-		if (trim)
-		{
-			packerInfo->frameWidth = imageWidth;
-			packerInfo->frameHeight = imageHeight;
-		}
-		else if (frameWidth || frameHeight)
-		{
-			packerInfo->frameWidth = frameWidth;
-			packerInfo->frameHeight = frameHeight;
-		}
-		packerInfo->width = adjustedWidth;
-		packerInfo->height = adjustedHeight;
-		packerInfo->offsetX -= trimOffsetX;
-		packerInfo->offsetY -= trimOffsetY;
-		packerInfos.Push(packerInfo);
-	}
-
-	int packedWidth = MAX_TEXTURE_SIZE;
-	int packedHeight = MAX_TEXTURE_SIZE;
-	{
-		// fill up an list of tries in increasing size and take the first win
-		Vector<IntVector2> tries;
-		for(unsigned x=2; x<11; ++x)
-		{
-			for(unsigned y=2; y<11; ++y)
-				tries.Push(IntVector2((1<<x), (1<<y)));
-		}
-
-		// load rectangles
-		stbrp_rect* packerRects = new stbrp_rect[packerInfos.Size()];
-		for (unsigned i = 0; i < packerInfos.Size(); ++i)
-		{
-			PackerInfo* packerInfo = packerInfos[i];
-			stbrp_rect* packerRect = &packerRects[i];
-			packerRect->id = i;
-			packerRect->h = packerInfo->height + padY;
-			packerRect->w = packerInfo->width + padX;
-		}
-
-		bool success = false;
-		while (tries.Size() > 0)
-		{
-			IntVector2 size = tries[0];
-			tries.Erase(0);
-			bool fit = true;
-			int textureHeight = size.y_;
-			int textureWidth = size.x_;
-			if (success && textureHeight * textureWidth > packedWidth * packedHeight)
-				continue;
-
-			stbrp_context packerContext;
-			stbrp_node packerMemory[PACKER_NUM_NODES];
-			stbrp_init_target(&packerContext, textureWidth, textureHeight, packerMemory, packerInfos.Size());
-			stbrp_pack_rects(&packerContext, packerRects, packerInfos.Size());
-
-			// check to see if everything fit
-			for (unsigned i = 0; i < packerInfos.Size(); ++i)
-			{
-				stbrp_rect* packerRect = &packerRects[i];
-				if (!packerRect->was_packed)
-				{
-					fit = false;
-					break;
-				}
-			}
-			if (fit)
-			{
-				success = true;
-				// distribute values to packer info
-				for (unsigned i = 0; i < packerInfos.Size(); ++i)
-				{
-					stbrp_rect* packerRect = &packerRects[i];
-					PackerInfo* packerInfo = packerInfos[packerRect->id];
-					packerInfo->x = packerRect->x;
-					packerInfo->y = packerRect->y;
-				}
-				packedWidth = size.x_;
-				packedHeight = size.y_;
-			}
-		}
-		delete packerRects;
-		if (!success)
-			ErrorExit("Could not allocate for all images.  The max sprite sheet texture size is " + String(MAX_TEXTURE_SIZE) + "x" + String(MAX_TEXTURE_SIZE) + ".");
-	}
-
-
-	// create image for spritesheet
-	Image spriteSheetImage(context);
-	spriteSheetImage.SetSize(packedWidth, packedHeight, 4);
-
-	// zero out image
-	spriteSheetImage.SetData((unsigned char*)calloc(sizeof(unsigned char), packedWidth * packedHeight * 4));
-
-	XMLFile xml(context);
-	XMLElement root = xml.CreateRoot("TextureAtlas");
-	root.SetAttribute("imagePath", GetFileNameAndExtension(outputFile));
-
-	for (unsigned i = 0; i < packerInfos.Size(); ++i)
-	{
-		SharedPtr<PackerInfo> packerInfo = packerInfos[i];
-		XMLElement subTexture = root.CreateChild("SubTexture");
-		subTexture.SetString("name", packerInfo->name);
-		subTexture.SetInt("x", packerInfo->x + offsetX);
-		subTexture.SetInt("y", packerInfo->y + offsetY);
-		subTexture.SetInt("width", packerInfo->width);
-		subTexture.SetInt("height", packerInfo->height);
-
-		if (packerInfo->frameWidth || packerInfo->frameHeight)
-		{
-			subTexture.SetInt("frameWidth", packerInfo->frameWidth);
-			subTexture.SetInt("frameHeight", packerInfo->frameHeight);
-			subTexture.SetInt("offsetX", packerInfo->offsetX);
-			subTexture.SetInt("offsetY", packerInfo->offsetY);
-		}
-
-		LOGINFO("Transfering " + packerInfo->path + " to sprite sheet.");
-
-		File file(context, packerInfo->path);
-		Image image(context);
-		if (!image.Load(file))
-			ErrorExit("Could not load image " + packerInfo->path + ".");
-
-		for (int y = 0; y < packerInfo->height; ++y)
-		{
-			for (int x = 0; x < packerInfo->width; ++x)
-			{
-				unsigned color = image.GetPixelInt(x - packerInfo->offsetX, y - packerInfo->offsetY);
-				spriteSheetImage.SetPixelInt(
-					packerInfo->x + offsetX + x,
-					packerInfo->y + offsetY + y, color);
-			}
-		}
-	}
-
-	if (debug)
-	{
-		unsigned OUTER_BOUNDS_DEBUG_COLOR = Color::BLUE.ToUInt();
-		unsigned INNER_BOUNDS_DEBUG_COLOR = Color::GREEN.ToUInt();
-
-		LOGINFO("Drawing debug information.");
-		for (unsigned i = 0; i < packerInfos.Size(); ++i)
-		{
-			SharedPtr<PackerInfo> packerInfo = packerInfos[i];
-
-			// Draw outer bounds
-			for (int x = 0; x < packerInfo->frameWidth; ++x)
-			{
-				spriteSheetImage.SetPixelInt(packerInfo->x + x, packerInfo->y, OUTER_BOUNDS_DEBUG_COLOR);
-				spriteSheetImage.SetPixelInt(packerInfo->x + x, packerInfo->y + packerInfo->frameHeight, OUTER_BOUNDS_DEBUG_COLOR);
-			}
-			for (int y = 0; y < packerInfo->frameHeight; ++y)
-			{
-				spriteSheetImage.SetPixelInt(packerInfo->x, packerInfo->y + y, OUTER_BOUNDS_DEBUG_COLOR);
-				spriteSheetImage.SetPixelInt(packerInfo->x + packerInfo->frameWidth, packerInfo->y + y, OUTER_BOUNDS_DEBUG_COLOR);
-			}
-
-			// Draw inner bounds
-			for (int x = 0; x < packerInfo->width; ++x)
-			{
-				spriteSheetImage.SetPixelInt(packerInfo->x + offsetX + x, packerInfo->y + offsetY, INNER_BOUNDS_DEBUG_COLOR);
-				spriteSheetImage.SetPixelInt(packerInfo->x + offsetX + x, packerInfo->y + offsetY + packerInfo->height, INNER_BOUNDS_DEBUG_COLOR);
-			}
-			for (int y = 0; y < packerInfo->height; ++y)
-			{
-				spriteSheetImage.SetPixelInt(packerInfo->x + offsetX, packerInfo->y + offsetY + y, INNER_BOUNDS_DEBUG_COLOR);
-				spriteSheetImage.SetPixelInt(packerInfo->x + offsetX + packerInfo->width, packerInfo->y + offsetY + y, INNER_BOUNDS_DEBUG_COLOR);
-			}
-		}
-	}
-
-	LOGINFO("Saving output image.");
-	spriteSheetImage.SavePNG(outputFile);
-
-	LOGINFO("Saving SpriteSheet xml file.");
-	File spriteSheetFile(context);
-	spriteSheetFile.Open(spriteSheetFileName, FILE_WRITE);
-	xml.Save(spriteSheetFile);
+    if (arguments.Size() < 2)
+        Help();
+
+    SharedPtr<Context> context(new Context());
+    context->RegisterSubsystem(new FileSystem(context));
+    context->RegisterSubsystem(new Log(context));
+    FileSystem* fileSystem = context->GetSubsystem<FileSystem>();
+
+    Vector<String> inputFiles;
+    String outputFile;
+    String spriteSheetFileName;
+    bool debug = false;
+    unsigned padX = 0;
+    unsigned padY = 0;
+    unsigned offsetX = 0;
+    unsigned offsetY = 0;
+    unsigned frameWidth = 0;
+    unsigned frameHeight = 0;
+    bool help = false;
+    bool trim = false;
+
+    while (arguments.Size() > 0)
+    {
+        String arg = arguments[0];
+        arguments.Erase(0);
+
+        if (arg.Empty())
+            continue;
+
+        if (arg.StartsWith("-"))
+        {
+            if (arg == "-px")      { padX = ToUInt(arguments[0]); arguments.Erase(0); }
+            else if (arg == "-py") { padY = ToUInt(arguments[0]); arguments.Erase(0); }
+            else if (arg == "-ox") { offsetX = ToUInt(arguments[0]); arguments.Erase(0); }
+            else if (arg == "-oy") { offsetY = ToUInt(arguments[0]); arguments.Erase(0); }
+            else if (arg == "-frameWidth") { frameWidth = ToUInt(arguments[0]); arguments.Erase(0); }
+            else if (arg == "-frameHeight") { frameHeight = ToUInt(arguments[0]); arguments.Erase(0); }
+            else if (arg == "-trim") { trim = true; }
+            else if (arg == "-xml")  { spriteSheetFileName = arguments[0]; arguments.Erase(0); }
+            else if (arg == "-h")  { help = true; break; }
+            else if (arg == "-debug")  { debug = true; }
+        }
+        else
+            inputFiles.Push(arg);
+    }
+
+    if (help)
+        Help();
+
+    if (inputFiles.Size() < 2)
+        ErrorExit("An input and output file must be specified.");
+
+    if (frameWidth ^ frameHeight)
+        ErrorExit("Both frameHeight and frameWidth must be ommited or specified.");
+
+    // take last input file as output
+    if (inputFiles.Size() > 1)
+    {
+        outputFile = inputFiles[inputFiles.Size() - 1];
+        LOGINFO("Output file set to " + outputFile + ".");
+        inputFiles.Erase(inputFiles.Size() - 1);
+    }
+
+    // set spritesheet name to outputfile.xml if not specified
+    if (spriteSheetFileName.Empty())
+        spriteSheetFileName = ReplaceExtension(outputFile, ".xml");
+
+    if (GetParentPath(spriteSheetFileName) != GetParentPath(outputFile))
+        ErrorExit("Both output xml and png must be in the same folder");
+
+    // check all input files exist
+    for (unsigned i = 0; i < inputFiles.Size(); ++i)
+    {
+        LOGINFO("Checking " + inputFiles[i] + " to see if file exists.");
+        if (!fileSystem->FileExists(inputFiles[i]))
+            ErrorExit("File " + inputFiles[i] + " does not exist.");
+    }
+
+    // Set the max offset equal to padding to prevent images from going out of bounds
+    offsetX = Min((int)offsetX, (int)padX);
+    offsetY = Min((int)offsetY, (int)padY);
+
+    Vector<SharedPtr<PackerInfo > > packerInfos;
+
+    for (unsigned i = 0; i < inputFiles.Size(); ++i)
+    {
+        String path = inputFiles[i];
+        String name = ReplaceExtension(GetFileName(path), "");
+        File file(context, path);
+        Image image(context);
+
+        if (!image.Load(file))
+            ErrorExit("Could not load image " + path + ".");
+
+        if (image.IsCompressed())
+            ErrorExit(path + " is compressed. Compressed images are not allowed.");
+
+        SharedPtr<PackerInfo> packerInfo(new PackerInfo(path, name));
+        int imageWidth = image.GetWidth();
+        int imageHeight = image.GetHeight();
+        int trimOffsetX = 0;
+        int trimOffsetY = 0;
+        int adjustedWidth = imageWidth;
+        int adjustedHeight = imageHeight;
+
+        if (trim)
+        {
+            int minX = imageWidth;
+            int minY = imageHeight;
+            int maxX = 0;
+            int maxY = 0;
+
+            for (int y = 0; y < imageHeight; ++y)
+            {
+                for (int x = 0; x < imageWidth; ++x)
+                {
+                    bool found = (image.GetPixelInt(x, y) & 0x000000ff) != 0;
+                    if (found) {
+                        minX = Min(minX, x);
+                        minY = Min(minY, y);
+                        maxX = Max(maxX, x);
+                        maxY = Max(maxY, y);
+                    }
+                }
+            }
+
+            trimOffsetX = minX;
+            trimOffsetY = minY;
+            adjustedWidth = maxX - minX + 1;
+            adjustedHeight = maxY - minY + 1;
+        }
+
+        if (trim)
+        {
+            packerInfo->frameWidth = imageWidth;
+            packerInfo->frameHeight = imageHeight;
+        }
+        else if (frameWidth || frameHeight)
+        {
+            packerInfo->frameWidth = frameWidth;
+            packerInfo->frameHeight = frameHeight;
+        }
+        packerInfo->width = adjustedWidth;
+        packerInfo->height = adjustedHeight;
+        packerInfo->offsetX -= trimOffsetX;
+        packerInfo->offsetY -= trimOffsetY;
+        packerInfos.Push(packerInfo);
+    }
+
+    int packedWidth = MAX_TEXTURE_SIZE;
+    int packedHeight = MAX_TEXTURE_SIZE;
+    {
+        // fill up an list of tries in increasing size and take the first win
+        Vector<IntVector2> tries;
+        for(unsigned x=2; x<11; ++x)
+        {
+            for(unsigned y=2; y<11; ++y)
+                tries.Push(IntVector2((1<<x), (1<<y)));
+        }
+
+        // load rectangles
+        stbrp_rect* packerRects = new stbrp_rect[packerInfos.Size()];
+        for (unsigned i = 0; i < packerInfos.Size(); ++i)
+        {
+            PackerInfo* packerInfo = packerInfos[i];
+            stbrp_rect* packerRect = &packerRects[i];
+            packerRect->id = i;
+            packerRect->h = packerInfo->height + padY;
+            packerRect->w = packerInfo->width + padX;
+        }
+
+        bool success = false;
+        while (tries.Size() > 0)
+        {
+            IntVector2 size = tries[0];
+            tries.Erase(0);
+            bool fit = true;
+            int textureHeight = size.y_;
+            int textureWidth = size.x_;
+            if (success && textureHeight * textureWidth > packedWidth * packedHeight)
+                continue;
+
+            stbrp_context packerContext;
+            stbrp_node packerMemory[PACKER_NUM_NODES];
+            stbrp_init_target(&packerContext, textureWidth, textureHeight, packerMemory, packerInfos.Size());
+            stbrp_pack_rects(&packerContext, packerRects, packerInfos.Size());
+
+            // check to see if everything fit
+            for (unsigned i = 0; i < packerInfos.Size(); ++i)
+            {
+                stbrp_rect* packerRect = &packerRects[i];
+                if (!packerRect->was_packed)
+                {
+                    fit = false;
+                    break;
+                }
+            }
+            if (fit)
+            {
+                success = true;
+                // distribute values to packer info
+                for (unsigned i = 0; i < packerInfos.Size(); ++i)
+                {
+                    stbrp_rect* packerRect = &packerRects[i];
+                    PackerInfo* packerInfo = packerInfos[packerRect->id];
+                    packerInfo->x = packerRect->x;
+                    packerInfo->y = packerRect->y;
+                }
+                packedWidth = size.x_;
+                packedHeight = size.y_;
+            }
+        }
+        delete packerRects;
+        if (!success)
+            ErrorExit("Could not allocate for all images.  The max sprite sheet texture size is " + String(MAX_TEXTURE_SIZE) + "x" + String(MAX_TEXTURE_SIZE) + ".");
+    }
+
+
+    // create image for spritesheet
+    Image spriteSheetImage(context);
+    spriteSheetImage.SetSize(packedWidth, packedHeight, 4);
+
+    // zero out image
+    spriteSheetImage.SetData((unsigned char*)calloc(sizeof(unsigned char), packedWidth * packedHeight * 4));
+
+    XMLFile xml(context);
+    XMLElement root = xml.CreateRoot("TextureAtlas");
+    root.SetAttribute("imagePath", GetFileNameAndExtension(outputFile));
+
+    for (unsigned i = 0; i < packerInfos.Size(); ++i)
+    {
+        SharedPtr<PackerInfo> packerInfo = packerInfos[i];
+        XMLElement subTexture = root.CreateChild("SubTexture");
+        subTexture.SetString("name", packerInfo->name);
+        subTexture.SetInt("x", packerInfo->x + offsetX);
+        subTexture.SetInt("y", packerInfo->y + offsetY);
+        subTexture.SetInt("width", packerInfo->width);
+        subTexture.SetInt("height", packerInfo->height);
+
+        if (packerInfo->frameWidth || packerInfo->frameHeight)
+        {
+            subTexture.SetInt("frameWidth", packerInfo->frameWidth);
+            subTexture.SetInt("frameHeight", packerInfo->frameHeight);
+            subTexture.SetInt("offsetX", packerInfo->offsetX);
+            subTexture.SetInt("offsetY", packerInfo->offsetY);
+        }
+
+        LOGINFO("Transfering " + packerInfo->path + " to sprite sheet.");
+
+        File file(context, packerInfo->path);
+        Image image(context);
+        if (!image.Load(file))
+            ErrorExit("Could not load image " + packerInfo->path + ".");
+
+        for (int y = 0; y < packerInfo->height; ++y)
+        {
+            for (int x = 0; x < packerInfo->width; ++x)
+            {
+                unsigned color = image.GetPixelInt(x - packerInfo->offsetX, y - packerInfo->offsetY);
+                spriteSheetImage.SetPixelInt(
+                    packerInfo->x + offsetX + x,
+                    packerInfo->y + offsetY + y, color);
+            }
+        }
+    }
+
+    if (debug)
+    {
+        unsigned OUTER_BOUNDS_DEBUG_COLOR = Color::BLUE.ToUInt();
+        unsigned INNER_BOUNDS_DEBUG_COLOR = Color::GREEN.ToUInt();
+
+        LOGINFO("Drawing debug information.");
+        for (unsigned i = 0; i < packerInfos.Size(); ++i)
+        {
+            SharedPtr<PackerInfo> packerInfo = packerInfos[i];
+
+            // Draw outer bounds
+            for (int x = 0; x < packerInfo->frameWidth; ++x)
+            {
+                spriteSheetImage.SetPixelInt(packerInfo->x + x, packerInfo->y, OUTER_BOUNDS_DEBUG_COLOR);
+                spriteSheetImage.SetPixelInt(packerInfo->x + x, packerInfo->y + packerInfo->frameHeight, OUTER_BOUNDS_DEBUG_COLOR);
+            }
+            for (int y = 0; y < packerInfo->frameHeight; ++y)
+            {
+                spriteSheetImage.SetPixelInt(packerInfo->x, packerInfo->y + y, OUTER_BOUNDS_DEBUG_COLOR);
+                spriteSheetImage.SetPixelInt(packerInfo->x + packerInfo->frameWidth, packerInfo->y + y, OUTER_BOUNDS_DEBUG_COLOR);
+            }
+
+            // Draw inner bounds
+            for (int x = 0; x < packerInfo->width; ++x)
+            {
+                spriteSheetImage.SetPixelInt(packerInfo->x + offsetX + x, packerInfo->y + offsetY, INNER_BOUNDS_DEBUG_COLOR);
+                spriteSheetImage.SetPixelInt(packerInfo->x + offsetX + x, packerInfo->y + offsetY + packerInfo->height, INNER_BOUNDS_DEBUG_COLOR);
+            }
+            for (int y = 0; y < packerInfo->height; ++y)
+            {
+                spriteSheetImage.SetPixelInt(packerInfo->x + offsetX, packerInfo->y + offsetY + y, INNER_BOUNDS_DEBUG_COLOR);
+                spriteSheetImage.SetPixelInt(packerInfo->x + offsetX + packerInfo->width, packerInfo->y + offsetY + y, INNER_BOUNDS_DEBUG_COLOR);
+            }
+        }
+    }
+
+    LOGINFO("Saving output image.");
+    spriteSheetImage.SavePNG(outputFile);
+
+    LOGINFO("Saving SpriteSheet xml file.");
+    File spriteSheetFile(context);
+    spriteSheetFile.Open(spriteSheetFileName, FILE_WRITE);
+    xml.Save(spriteSheetFile);
 }