Created
November 26, 2012 06:40
-
-
Save DigiTec/4146881 to your computer and use it in GitHub Desktop.
CSS Image Sprite Packer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Drawing; | |
using System.Drawing.Imaging; | |
public class PackImagesApplication | |
{ | |
private static string cssFormat = | |
@"#{0}_{1} {{ | |
width: {2}px; | |
height: {3}px; | |
background-position: -{4}px -{5}px; | |
background-image: url({6}); | |
}} | |
#{0}_{1}_half {{ | |
width: {7}px; | |
height: {8}px; | |
background-position: -{9}px -{10}px; | |
background-image: url({6}); | |
background-size: {11}px {12}px | |
}} | |
#{0}_{1}_double {{ | |
width: {13}px; | |
height: {14}px; | |
background-position: -{15}px -{16}px; | |
background-image: url({6}); | |
background-size: {17}px {18}px | |
}}"; | |
public class Run | |
{ | |
public List<Image> Images = new List<Image>(); | |
public long RunHeight; | |
public long RunWidth; | |
} | |
private static void Main(string[] args) | |
{ | |
uint maximumWidth = 1024; | |
uint maximumHeight = 1024; | |
long fileSize = 0; | |
List<string> importDirectories = new List<string>(); | |
string outputFile = null; | |
string cssFile = null; | |
for(int i = 0; i < args.Length; i++) | |
{ | |
string arg = args[i]; | |
if ( arg.StartsWith("/o:") ) | |
{ | |
outputFile = arg.Substring(3); | |
} | |
else if ( arg.StartsWith("/h:") ) | |
{ | |
maximumHeight = uint.Parse(arg.Substring(3)); | |
} | |
else if ( arg.StartsWith("/w:") ) | |
{ | |
maximumWidth = uint.Parse(arg.Substring(3)); | |
} | |
else if ( arg.StartsWith("/css:") ) | |
{ | |
cssFile = arg.Substring(5); | |
} | |
else | |
{ | |
importDirectories.Add(arg); | |
} | |
} | |
if ( outputFile == null ) | |
{ | |
Console.WriteLine("Usage: PackImages /o:out.png [/h:maxHeight] [/w:maxWidth] [/css:out.css] [dirs...]"); | |
return; | |
} | |
if ( cssFile == null ) | |
{ | |
cssFile = Path.ChangeExtension(outputFile, ".css"); | |
} | |
List<Image> images = new List<Image>(); | |
if ( outputFile != null ) | |
{ | |
for(int i = 0; i < importDirectories.Count; i++) | |
{ | |
DirectoryInfo di = new DirectoryInfo(importDirectories[i]); | |
if ( !di.Exists ) | |
{ | |
throw new Exception("Directory doesn't exist " + importDirectories[i]); | |
} | |
FileInfo[] fis = di.GetFiles("*.png"); | |
foreach(FileInfo fi in fis) | |
{ | |
fileSize += fi.Length; | |
Image image = Image.FromFile(fi.FullName); | |
image.Tag = fi.FullName; | |
images.Add(image); | |
} | |
} | |
} | |
List<Run> ImageRuns = new List<Run>(); | |
long totalImageHeight = 0; | |
long totalImageWidth = 0; | |
images.Sort((x,y) => { return x.Width.CompareTo(y.Width); }); | |
Run currentRun = new Run(); | |
long currentWidth = 0; | |
foreach(Image image in images) | |
{ | |
if ( currentWidth + image.Width >= maximumWidth ) | |
{ | |
if (currentRun.Images.Count == 0) | |
{ | |
throw new Exception("An image is too large for spriting"); | |
} | |
currentRun.RunWidth = currentWidth; | |
totalImageWidth = Math.Max(currentRun.RunWidth, totalImageWidth); | |
totalImageHeight += currentRun.RunHeight; | |
ImageRuns.Add(currentRun); | |
currentRun = new Run(); | |
currentWidth = 0; | |
} | |
currentRun.Images.Add(image); | |
currentRun.RunHeight = Math.Max(currentRun.RunHeight, image.Height); | |
currentWidth += image.Width; | |
} | |
if (currentRun.Images.Count > 0) | |
{ | |
currentRun.RunWidth = currentWidth; | |
totalImageWidth = Math.Max(currentRun.RunWidth, totalImageWidth); | |
totalImageHeight += currentRun.RunHeight; | |
ImageRuns.Add(currentRun); | |
} | |
Console.WriteLine("Size before pack is {0}", fileSize); | |
Console.WriteLine("Final image size is {0},{1} with {2} images in {3} runs", totalImageWidth, totalImageHeight, images.Count, ImageRuns.Count); | |
string debugHtmlFile = Path.ChangeExtension(cssFile, ".htm"); | |
using(StreamWriter swDebug = new StreamWriter(debugHtmlFile)) | |
{ | |
swDebug.WriteLine("<!DOCTYPE HTML><html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\" /></head><body>", Path.GetFileName(cssFile)); | |
using(StreamWriter sw = new StreamWriter(cssFile)) | |
{ | |
Bitmap bmpPack = new Bitmap((int) totalImageWidth, (int) totalImageHeight); | |
using(Graphics gfx = Graphics.FromImage(bmpPack)) | |
{ | |
long currentY = 0; | |
foreach(Run imageRun in ImageRuns) | |
{ | |
long currentX = 0; | |
foreach(Image image in imageRun.Images) | |
{ | |
// Render the image | |
gfx.DrawImage(image, currentX, currentY); | |
// Build the CSS for this image | |
string fileName = image.Tag as string; | |
if ( fileName != null ) | |
{ | |
string imageName = Path.GetFileNameWithoutExtension(fileName); | |
string imagePack = Path.GetFileNameWithoutExtension(outputFile); | |
string imageUrl = Path.GetFileName(outputFile); | |
sw.WriteLine(cssFormat, | |
imagePack, imageName, image.Width, image.Height, currentX, currentY, imageUrl, | |
image.Width / 2, image.Height / 2, currentX / 2, currentY / 2, totalImageWidth / 2, totalImageHeight / 2, | |
image.Width * 2, image.Height * 2, currentX * 2, currentY * 2, totalImageWidth * 2, totalImageHeight * 2); | |
swDebug.WriteLine("<div id=\"{0}_{1}\"></div>", imagePack, imageName); | |
swDebug.WriteLine("<div id=\"{0}_{1}_half\"></div>", imagePack, imageName); | |
swDebug.WriteLine("<div id=\"{0}_{1}_double\"></div>", imagePack, imageName); | |
} | |
else | |
{ | |
throw new Exception("Image has a null tag, no name, can't emit CSS"); | |
} | |
// Update our current offsets | |
currentX += image.Width; | |
} | |
currentY += imageRun.RunHeight; | |
} | |
bmpPack.Save(outputFile, ImageFormat.Png); | |
} | |
} | |
swDebug.WriteLine("</body></html>"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment