Esempio n. 1
0
        private ImageSettings GetSettings(string settingFileLocation)
        {
            ImageSettings settings = new ImageSettings();

            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);
        }
Esempio n. 2
0
        private void PerformOptimizations(string path, ImageSettings settings, TextWriter cssHighCompatOutput, TextWriter cssLowCompatOutput, List <string> imageLocations, ref DateTime mostRecentCreationTimeUtc, ref DateTime mostRecentLastWriteTimeUtc)
        {
            // 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)
                {
                    var imageFile = new FileInfo(imagePath);

                    // 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 += imageFile.Length;

                    // ensure the create and modify date time stamps are the most recent
                    if (mostRecentCreationTimeUtc < imageFile.CreationTimeUtc)
                    {
                        mostRecentCreationTimeUtc = imageFile.CreationTimeUtc;
                    }
                    if (mostRecentLastWriteTimeUtc < imageFile.LastWriteTimeUtc)
                    {
                        mostRecentLastWriteTimeUtc = imageFile.LastWriteTimeUtc;
                    }

                    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();
                }
            }
        }
Esempio n. 3
0
        private 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;
                    var cssImages = new List <CssImageInfo>();
                    foreach (Bitmap image in images)
                    {
                        drawingSurface.DrawImage(image, new Rectangle(xOffset, yOffset, image.Width, image.Height));

                        cssImages.Add(new CssImageInfo()
                        {
                            XOffset = xOffset,
                            YOffset = yOffset,
                            Image   = image,
                        });

                        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
                    string spriteMD5;
                    using (var spriteMemoryStream = new System.IO.MemoryStream())
                    {
                        string spriteFileExt;
                        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
                                spriteFileExt = settings.Format;
                                sprite.Save(spriteMemoryStream, 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
                            {
                                spriteFileExt = settings.Format;
                                sprite.Save(spriteMemoryStream, GetImageFormat(settings.Format));
                            }
                            catch (Exception)
                            {
                                // If errors occur again, try to save as a png
                                spriteFileExt = "png";
                                sprite.Save(spriteMemoryStream, ImageFormat.Png);
                            }
                        }

                        // create MD5 of the memory stream

                        // reset the stream to the beginning
                        spriteMemoryStream.Position = 0;

                        // create MD5 of the memory stream
                        // Use HttpServerUtility.UrlTokenEncode so it's url safe
                        spriteMD5 = HttpServerUtility.UrlTokenEncode(System.Security.Cryptography.MD5.Create().ComputeHash(spriteMemoryStream));

                        // reset the stream to the beginning
                        spriteMemoryStream.Position = 0;

                        // save to final file
                        var spriteFile = Path.Combine(path, GenerateSpriteFileName(spriteNumber, spriteMD5, settings.Format));
                        File.WriteAllBytes(spriteFile, spriteMemoryStream.ToArray());

                        if (IsLoggingEnabled)
                        {
                            LogMessage($"Saved sprite image \"{spriteFile}\".");
                        }
                    }

                    foreach (var cssImage in cssImages)
                    {
                        // Add the CSS data
                        GenerateCss(cssImage.XOffset, cssImage.YOffset, spriteNumber, spriteMD5, settings.Format, settings.Base64, cssImage.Image, cssHighCompatOutput);
                        GenerateCss(cssImage.XOffset, cssImage.YOffset, spriteNumber, spriteMD5, settings.Format, false, cssImage.Image, cssLowCompatOutput);
                    }
                }
            }
        }
Esempio n. 4
0
        /// <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>A list of the files which are dependencies of the cache.</returns>
        public List <string> ProcessDirectory(string spriteDirectory)
        {
            // Check if directory was deleted
            if (!Directory.Exists(spriteDirectory))
            {
                return(new List <string>(0));
            }

            var dependencies = new List <string>();

            // add the current directory as a dependency so if any files are added the directory will get reprocessed
            dependencies.Add(spriteDirectory);

            var hashedFiles = new List <string>();

            // get the settings file so we can add it to the hashed files
            string settingsFile = LocateSettingFile(spriteDirectory);

            if (File.Exists(settingsFile))
            {
                hashedFiles.Add(settingsFile);

                if (IsLoggingEnabled)
                {
                    LogMessage($"Loaded sprite settings from \"{settingsFile}\" for directory \"{spriteDirectory}\".");
                }
            }

            var allFiles       = Directory.GetFiles(spriteDirectory);
            var generatedFiles = new List <string>();
            var imageFiles     = new List <string>();

            foreach (var file in allFiles)
            {
                if (IsGeneratedFile(file))
                {
                    generatedFiles.Add(file);
                    hashedFiles.Add(file);
                }
                else if (IsImageFile(file))
                {
                    imageFiles.Add(file);
                    hashedFiles.Add(file);

                    if (IsLoggingEnabled)
                    {
                        LogMessage($"Found image \"{file}\" to sprite.");
                    }
                }
            }


            // Make sure to not include the hash file
            string md5HashFile         = GetMD5HashFile(spriteDirectory);
            string currentDirectoryMD5 = ComputeHash(hashedFiles);
            string savedDirectoryMD5   = GetDirectoryMD5(spriteDirectory);

            if (savedDirectoryMD5 == currentDirectoryMD5)
            {
                // add all the hashed files to the dependencies so if any change we can reload
                dependencies.AddRange(hashedFiles);

                return(dependencies);
            }

            // Import settings from settings file
            ImageSettings settings = GetSettings(settingsFile);

            // delete the generated files
            generatedFiles.ForEach(f => File.Delete(f));

            // Create pointer to the CSS output file
            lock (_lockObj)
            {
                string newCssMD5       = ComputeHash(imageFiles);
                string highCompCssFile = Path.Combine(spriteDirectory, CreateHighCompatibilityCssFileName(newCssMD5));
                string lowCompCssFile  = Path.Combine(spriteDirectory, CreateLowCompatibilityCssFileName(newCssMD5));

                DateTime mostRecentCreationTimeUtc  = DateTime.MinValue;
                DateTime mostRecentLastWriteTimeUtc = DateTime.MinValue;
                using (TextWriter cssHighCompatOutput = new StreamWriter(highCompCssFile, append: false),
                       cssLowCompatOutput = new StreamWriter(lowCompCssFile, append: false))
                {
                    PerformOptimizations(spriteDirectory, settings, cssHighCompatOutput, cssLowCompatOutput, imageFiles, ref mostRecentCreationTimeUtc, ref mostRecentLastWriteTimeUtc);

                    // Merge with a user's existing CSS file(s)
                    MergeExternalCss(spriteDirectory, cssHighCompatOutput, cssLowCompatOutput, ref mostRecentCreationTimeUtc, ref mostRecentLastWriteTimeUtc);
                }
                if (IsLoggingEnabled)
                {
                    LogMessage($"Saved sprite high compatiability CSS to \"{highCompCssFile}\".");
                }
                if (IsLoggingEnabled)
                {
                    LogMessage($"Saved sprite low compatiability CSS to \"{lowCompCssFile}\".");
                }

                // look at the directory and get all the files we're going to make the new hash from
                // since the directory now contains all the image files and generated files
                hashedFiles = Directory.GetFiles(spriteDirectory).Where(f => IsGeneratedFile(f) || IsImageFile(f)).ToList();

                // add the settings file to the hashed files
                if (File.Exists(settingsFile))
                {
                    hashedFiles.Add(settingsFile);
                }

                // re-compute the hash now that we've generated all the files
                string newDirectoryMD5 = ComputeHash(hashedFiles);

                // add the hashedFiles to the dependencies since we've not update it properly
                dependencies.AddRange(hashedFiles);

                // write the md5s to the hash file
                File.WriteAllLines(md5HashFile, new string[] { newCssMD5, newDirectoryMD5 });
                if (IsLoggingEnabled)
                {
                    LogMessage($"Saved CSS MD5 hash \"{newCssMD5}\" and directory MD5 has \"{newDirectoryMD5}\" to \"{md5HashFile}\".");
                }

                // ensure the modify date is after or equal to the create date
                if (mostRecentCreationTimeUtc > mostRecentLastWriteTimeUtc)
                {
                    mostRecentLastWriteTimeUtc = mostRecentCreationTimeUtc;
                }

                // set the output files to the correct time
                // this ensures cached items relying on these times
                // stay in sync and only change which the underlying
                // is modified
                File.SetCreationTimeUtc(md5HashFile, mostRecentCreationTimeUtc);
                File.SetLastWriteTimeUtc(md5HashFile, mostRecentLastWriteTimeUtc);
                hashedFiles.ForEach(f =>
                {
                    File.SetCreationTimeUtc(f, mostRecentCreationTimeUtc);
                    File.SetLastWriteTimeUtc(f, mostRecentLastWriteTimeUtc);
                });
            }

            return(dependencies);
        }