/// <summary> /// Executes the image optimizer on a specific subdirectory of the root image directory (non-recursive) /// </summary> /// <param name="path">The path to the directory to be rebuilt</param> /// <param name="checkIfFilesWereModified">Indicate whether the directory should only be rebuilt if files were modified</param> /// <returns>False if the directory does not exist</returns> internal static bool ProcessDirectory(string path, bool checkIfFilesWereModified) { // Check if directory was deleted if (!Directory.Exists(path)) { return(false); } try { if (checkIfFilesWereModified && !HaveFilesBeenModified(path)) { return(true); } // Make a list of the disk locations of each image List <string> imageLocations = new List <string>(); foreach (string extension in _extensions) { imageLocations.AddRange(Directory.GetFiles(path, extension)); } // Make sure to delete any existing sprites (or other images with the filename sprite###.imageExtension) imageLocations.RemoveAll(DeleteSpriteFile); // Make sure to not include the blank.gif file string blankFile = HostingEnvironment.MapPath(Path.Combine(SpriteDirectoryRelativePath, BlankFileName)).ToUpperInvariant(); imageLocations.RemoveAll(file => blankFile == file.ToUpperInvariant()); // Import settings from settings file ImageSettings settings = GetSettings(path); // Create pointer to the CSS output file lock (_lockObj) { using (TextWriter cssHighCompatOutput = new StreamWriter(Path.Combine(path, HighCompatibilityCssFileName), append: false), cssLowCompatOutput = new StreamWriter(Path.Combine(path, LowCompatibilityCssFileName), append: false)) { PerformOptimizations(path, settings, cssHighCompatOutput, cssLowCompatOutput, imageLocations); // Merge with a user's existing CSS file(s) MergeExternalCss(path, cssHighCompatOutput, cssLowCompatOutput); } } imageLocations.Clear(); foreach (string extension in _extensions) { imageLocations.AddRange(Directory.GetFiles(path, extension)); } SaveFileModificationData(path); return(true); } catch (Exception) { if (!Directory.Exists(path)) { return(false); } throw; } }
private static ImageSettings GetSettings(string path) { ImageSettings settings = new ImageSettings(); string settingFileLocation = LocateSettingFile(path); if (settingFileLocation != null) { XmlTextReader settingsData; // Open the settings file. If it fails here, we throw an exception since we expect the file to be there and readable. using (settingsData = new XmlTextReader(settingFileLocation)) { 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, CultureInfo.InvariantCulture); 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); } return(settings); }
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 // Since we'll be padding each image by 1px later on, we need to increase the sprites's size correspondingly. if (settings.TileInYAxis) { y += images.Count; } else { x += images.Count; } 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) { // pad each image in the sprite with a 1px margin yOffset += image.Height + 1; } else { // pad each image in the sprite with a 1px margin xOffset += image.Width + 1; } } // 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, GenerateSpriteFileName(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, GenerateSpriteFileName(spriteNumber, settings.Format))); } catch (Exception) { // If errors occur again, try to save as a png sprite.Save(Path.Combine(path, GenerateSpriteFileName(spriteNumber, "png"))); } } } } }
private static void PerformOptimizations(string path, 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 = MakeCssSelector(imagePath, SpriteDirectoryRelativePath); // 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(); } } }