private static void GenerateSprite(string path, ImageSettings settings, int x, int y, int spriteNumber, List<Bitmap> images, TextWriter cssHighCompatOutput, TextWriter cssLowCompatOutput) { // Create a drawing surface and add the images to it using (Bitmap sprite = new Bitmap(x, y)) using (Graphics drawingSurface = Graphics.FromImage(sprite)) { // Set the background to the specs from the settings file drawingSurface.Clear(settings.BackgroundColor); // Make the final sprite and save it int xOffset = 0; int yOffset = 0; foreach (Bitmap image in images) { drawingSurface.DrawImage(image, new Rectangle(xOffset, yOffset, image.Width, image.Height)); // Add the CSS data GenerateCss(xOffset, yOffset, spriteNumber, settings.Format, settings.Base64, image, cssHighCompatOutput); GenerateCss(xOffset, yOffset, spriteNumber, settings.Format, false, image, cssLowCompatOutput); if (settings.TileInYAxis) { yOffset += image.Height; } else { xOffset += image.Width; } } // Set the encoder parameters and make the image try { using (EncoderParameters spriteEncoderParameters = new EncoderParameters(1)) { spriteEncoderParameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, settings.Quality); // Attempt to save the image to disk with the specified encoder sprite.Save(Path.Combine(path, "sprite" + spriteNumber + "." + settings.Format), GetEncoderInfo(settings.Format), spriteEncoderParameters); } } catch (Exception) { // If errors occur, get the CLI to auto-choose an encoder. Unfortunately this means that the quality settings will be not used. try { sprite.Save(Path.Combine(path, "sprite" + spriteNumber + "." + settings.Format)); } catch (Exception) { // If errors occur again, try to save as a png sprite.Save(Path.Combine(path, "sprite" + spriteNumber + ".png")); } } } return; }
private static ImageSettings ReadSettings(string path, string spriteFolderRelativePath) { ImageSettings settings = new ImageSettings(); XmlTextReader settingsData; // Open the settings file. If it cannot be opened, or one cannot be found, use defaults try { using (settingsData = new XmlTextReader(Path.Combine(path, SettingsFileName))) { while (settingsData.Read()) { if (settingsData.NodeType == XmlNodeType.Element) { string nodeName = settingsData.Name; if (nodeName.Equals("FileFormat", StringComparison.OrdinalIgnoreCase)) { settings.Format = settingsData.ReadElementContentAsString().Trim('.'); } else if (nodeName.Equals("Quality", StringComparison.OrdinalIgnoreCase)) { settings.Quality = settingsData.ReadElementContentAsInt(); } else if (nodeName.Equals("MaxSize", StringComparison.OrdinalIgnoreCase)) { settings.MaxSize = settingsData.ReadElementContentAsInt(); } else if (nodeName.Equals("BackgroundColor", StringComparison.OrdinalIgnoreCase)) { string output = settingsData.ReadElementContentAsString(); int temp = Int32.Parse(output, System.Globalization.NumberStyles.HexNumber); settings.BackgroundColor = Color.FromArgb(temp); } else if (nodeName.Equals("Base64Encoding", StringComparison.OrdinalIgnoreCase)) { settings.Base64 = settingsData.ReadElementContentAsBoolean(); } else if (nodeName.Equals("TileInYAxis", StringComparison.OrdinalIgnoreCase)) { settings.TileInYAxis = settingsData.ReadElementContentAsBoolean(); } } } } return settings; } catch (FileNotFoundException) { // If no file is found, recursively check parent directories up until the root image folder for a settings file if (path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).TrimEnd(Path.AltDirectorySeparatorChar).EndsWith(spriteFolderRelativePath.TrimStart('~').Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Trim(Path.AltDirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)) { // If the current directory is the root image folder, use the default values return settings; } try { return ReadSettings(Directory.GetParent(path).FullName, spriteFolderRelativePath); } catch (Exception) { return settings; } } // If any other exceptions occur, use the default values catch (Exception) { return settings; } }
private static void PerformOptimizations(string path, string spriteFolderRelativePath, ImageSettings settings, TextWriter cssHighCompatOutput, TextWriter cssLowCompatOutput, List<string> imageLocations) { // Create a list containing each image (in Bitmap format), and calculate the total size (in pixels) of final image int x = 0; int y = 0; int imageIndex = 0; long size = 0; int spriteNumber = 0; List<Bitmap> images = new List<Bitmap>(); try { foreach (string imagePath in imageLocations) { // If the image is growing above the specified max file size, make the sprite with the existing images // and add the new image to the next sprite list if ((imageIndex > 0) && IsSpriteOversized(settings.MaxSize, size, imagePath)) { GenerateSprite(path, settings, x, y, spriteNumber, images, cssHighCompatOutput, cssLowCompatOutput); // Clear the existing images foreach (Bitmap image in images) { image.Dispose(); } // Reset variables to initial values, and increment the spriteNumber images.Clear(); x = 0; y = 0; imageIndex = 0; size = 0; spriteNumber++; } // Add the current image to the list of images that are to be processed images.Add(new Bitmap(imagePath)); // Use the image tag to store its name images[imageIndex].Tag = MakeCssClassName(imagePath, spriteFolderRelativePath); // Find the total pixel size of the sprite based on the tiling direction if (settings.TileInYAxis) { y += images[imageIndex].Height; if (x < images[imageIndex].Width) { x = images[imageIndex].Width; } } else { x += images[imageIndex].Width; if (y < images[imageIndex].Height) { y = images[imageIndex].Height; } } // Update the filesize size of the bitmap list size += (new FileInfo(imagePath)).Length; imageIndex++; } // Merge the final list of bitmaps into a sprite if (imageIndex != 0) GenerateSprite(path, settings, x, y, spriteNumber, images, cssHighCompatOutput, cssLowCompatOutput); } finally // Close the CSS file and clear the images list { foreach (Bitmap image in images) { image.Dispose(); } images.Clear(); } }