PNG Transparency not preserved in thumbnails

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
Hace 13 años
If you upload a PNG with a transparent background the thumbnails will not be transparent.

This is due to the PictureManager only generating JPEGs.

I have corrected this by implementing a GetImageCodeInfoFromExtension method and replacing all of the getImageCodeInfo('Image/jpeg") refrences with a getImageCodeInfoFromExtension( fileExt) call.

This allows the original format to be preserved along with transparencies.

If anyone would like to take a look at my updated file please let me know.
Hace 13 años
Would you be able to share your file with me?  I'm really new to the programming.  Could you tell me what file this was that you modified?  Thanks.  You can email me at [email protected].

Doug
Hace 13 años
I have encountered the same problem.  Can you share the code with me?
Hace 13 años
[code]//------------------------------------------------------------------------------
// The contents of this file are subject to the nopCommerce Public License Version 1.0 ("License"); you may not use this file except in compliance with the License.
// You may obtain a copy of the License at  https://www.nopcommerce.com/License.aspx.
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
// See the License for the specific language governing rights and limitations under the License.
//
// The Original Code is nopCommerce.
// The Initial Developer of the Original Code is NopSolutions.
// All Rights Reserved.
//
// Contributor(s): _______.
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
using NopSolutions.NopCommerce.BusinessLogic.Configuration.Settings;
using NopSolutions.NopCommerce.Common.Utils;
using NopSolutions.NopCommerce.DataAccess;
using NopSolutions.NopCommerce.DataAccess.Media;
using System.Linq;
namespace NopSolutions.NopCommerce.BusinessLogic.Media
{
    /// <summary>
    /// Picture manager
    /// </summary>
    public static partial class PictureManager
    {
        #region Fields
        private static object s_lock;
        #endregion

        #region Ctor
        static PictureManager()
        {
            s_lock = new object();
        }
        #endregion

        #region Utilities
        private static PictureCollection DBMapping(DBPictureCollection dbCollection)
        {
            if (dbCollection == null)
                return null;

            var collection = new PictureCollection();
            foreach (var dbItem in dbCollection)
            {
                var item = DBMapping(dbItem);
                collection.Add(item);
            }

            return collection;
        }

        private static Picture DBMapping(DBPicture dbItem)
        {
            if (dbItem == null)
                return null;

            var item = new Picture();
            item.PictureId = dbItem.PictureId;
            item.PictureBinary = dbItem.PictureBinary;
            item.Extension = dbItem.Extension;
            item.IsNew = dbItem.IsNew;

            return item;
        }
        /// <summary>
        /// Returns the first ImageCodeInfo instance with the specified extension.
        /// </summary>
        /// <param name="fileExt"></param>
        /// <returns></returns>
        private static ImageCodecInfo getImageCodeInfoFromExtension(string fileExt)
        {
            
            fileExt = fileExt.TrimStart(".".ToCharArray()).ToLower().Trim();
            switch (fileExt)
            {
                case "jpg":
                case "jpeg":
                    return getImageCodeInfo("image/jpeg");
                case "png":
                    return getImageCodeInfo("image/png");
                case "gif":
                    return getImageCodeInfo("image/gif");
                default:
                    return getImageCodeInfo("image/jpeg");
            }
            /*
            foreach (var ici in ImageCodecInfo.GetImageEncoders())
            {
                if (ici.FilenameExtension.Split(";".ToCharArray()).Contains(fileExt, StringComparer.OrdinalIgnoreCase))
                {
                    return ici;
                }
            }
            return null;
             * */
        }

        /// <summary>
        /// Returns the first ImageCodeInfo instance with the specified mime type. Some people try to get the ImageCodeInfo instance by index - sounds rather fragile to me.
        /// </summary>
        /// <param name="mimeType"></param>
        /// <returns></returns>
        private static ImageCodecInfo getImageCodeInfo(string mimeType)
        {
            
            var info = ImageCodecInfo.GetImageEncoders();
            foreach (var ici in info)
            {
                
                if (ici.MimeType.Equals(mimeType, StringComparison.OrdinalIgnoreCase))
                    return ici;
            }
            return null;
        }

        private static void SavePictureInFile(int PictureId, byte[] pictureBinary, string extension)
        {
            string[] parts = extension.Split('/');
            string lastPart = parts[parts.Length - 1];
            switch(lastPart)
            {
                case "pjpeg":
                    lastPart = "jpg";
                    break;
                case "x-png":
                    lastPart = "png";
                    break;
                case "x-icon":
                    lastPart = "ico";
                    break;
            }
            string localFilename = string.Empty;
            localFilename = string.Format("{0}_0.{1}", PictureId.ToString("0000000"), lastPart);
            if(!File.Exists(Path.Combine(LocalImagePath, localFilename)) && !System.IO.Directory.Exists(LocalImagePath))
            {
                System.IO.Directory.CreateDirectory(LocalImagePath);
            }
            File.WriteAllBytes(Path.Combine(LocalImagePath, localFilename), pictureBinary);
        }

        private static byte[] LoadPictureFromFile(int pictureId, string extension)
        {
            string[] parts = extension.Split('/');
            string lastPart = parts[parts.Length - 1];
            switch(lastPart)
            {
                case "pjpeg":
                    lastPart = "jpg";
                    break;
                case "x-png":
                    lastPart = "png";
                    break;
                case "x-icon":
                    lastPart = "ico";
                    break;
            }
            string localFilename = string.Empty;
            localFilename = string.Format("{0}_0.{1}", pictureId.ToString("0000000"), lastPart);
            if(!File.Exists(Path.Combine(LocalImagePath, localFilename)))
            {
                return new byte[0];
            }
            return File.ReadAllBytes(Path.Combine(LocalImagePath, localFilename));
        }
        #endregion

        #region Methods
        /// <summary>
        /// Get a picture URL
        /// </summary>
        /// <param name="imageId">Picture identifier</param>
        /// <returns>Picture URL</returns>
        public static string GetPictureUrl(int imageId)
        {
            Picture picture = GetPictureById(imageId);
            return GetPictureUrl(picture);
        }

        /// <summary>
        /// Get a picture URL
        /// </summary>
        /// <param name="picture">Picture instance</param>
        /// <returns>Picture URL</returns>
        public static string GetPictureUrl(Picture picture)
        {
            return GetPictureUrl(picture, 0);
        }

        /// <summary>
        /// Get a picture URL
        /// </summary>
        /// <param name="imageId">Picture identifier</param>
        /// <param name="targetSize">The target picture size (longest side)</param>
        /// <returns>Picture URL</returns>
        public static string GetPictureUrl(int imageId, int targetSize)
        {
            var picture = GetPictureById(imageId);
            return GetPictureUrl(picture, targetSize);
        }

        /// <summary>
        /// Get a picture URL
        /// </summary>
        /// <param name="picture">Picture instance</param>
        /// <param name="targetSize">The target picture size (longest side)</param>
        /// <returns>Picture URL</returns>
        public static string GetPictureUrl(Picture picture, int targetSize)
        {
            return GetPictureUrl(picture, targetSize, true);
        }

        /// <summary>
        /// Get a picture URL
        /// </summary>
        /// <param name="imageId">Picture identifier</param>
        /// <param name="targetSize">The target picture size (longest side)</param>
        /// <param name="showDefaultPicture">A value indicating whether the default picture is shown</param>
        /// <returns></returns>
        public static string GetPictureUrl(int imageId, int targetSize,
            bool showDefaultPicture)
        {
            var picture = GetPictureById(imageId);
            return GetPictureUrl(picture, targetSize, showDefaultPicture);
        }
        
        /// <summary>
        /// Gets all picture urls as a string array
        /// </summary>
        /// <param name="pictureId">Id of picture</param>
        /// <returns>Array containing urls for a picture in all sizes avaliable</returns>
        public static List<String> GetPictureUrls(int pictureId)
        {
            string filter = string.Format("*{0}*.*", pictureId.ToString("0000000"));

            List<String> urls = new List<string>();

            string orginalURL = GetPictureUrl(pictureId);

            string[] currentFiles = System.IO.Directory.GetFiles(PictureManager.LocalThumbImagePath, filter);

            foreach (string currentFileName in currentFiles)
            {
                string url = CommonHelper.GetStoreLocation() + "images/thumbs/" + Path.GetFileName(currentFileName);

                if (url != orginalURL)
                    urls.Add(url);
            }
            
            //add original picture to array
            urls.Add(orginalURL);

            if (urls.Count > 0)
            {
                //reverse sort order (this way the biggest picture usally comes first..)
                urls.Reverse();
            }

            return urls;
        }

        /// <summary>
        /// Gets the default picture URL
        /// </summary>
        /// <param name="targetSize">The target picture size (longest side)</param>
        /// <returns></returns>
        public static string GetDefaultPictureUrl(int targetSize)
        {
            return GetDefaultPictureUrl(PictureTypeEnum.Entity, targetSize);
        }

        /// <summary>
        /// Gets the default picture URL
        /// </summary>
        /// <param name="defaultPictureType">Default picture type</param>
        /// <param name="targetSize">The target picture size (longest side)</param>
        /// <returns></returns>
        public static string GetDefaultPictureUrl(PictureTypeEnum defaultPictureType,
            int targetSize)
        {
            string defaultImageName = string.Empty;
            switch (defaultPictureType)
            {
                case PictureTypeEnum.Entity:
                    defaultImageName = SettingManager.GetSettingValue("Media.DefaultImageName");
                    break;
                case PictureTypeEnum.Avatar:
                    defaultImageName = SettingManager.GetSettingValue("Media.Customer.DefaultAvatarImageName");
                    break;
                default:
                    defaultImageName = SettingManager.GetSettingValue("Media.DefaultImageName");
                    break;
            }


            string relPath = CommonHelper.GetStoreLocation() + "images/" + defaultImageName;
            if (targetSize == 0)
                return relPath;
            else
            {
                string filePath = Path.Combine(LocalImagePath, defaultImageName);
                if (File.Exists(filePath))
                {
                    string fname = string.Format("{0}_{1}{2}",
                        Path.GetFileNameWithoutExtension(filePath),
                        targetSize,
                        Path.GetExtension(filePath));
                    if (!File.Exists(Path.Combine(LocalThumbImagePath, fname)))
                    {
                        var b = new Bitmap(filePath);
                        var newSize = CalculateDimensions(b.Size, targetSize);

                        if (newSize.Width < 1)
                            newSize.Width = 1;
                        if (newSize.Height < 1)
                            newSize.Height = 1;

                        var newBitMap = new Bitmap(newSize.Width, newSize.Height);
                        var g = Graphics.FromImage(newBitMap);
                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                        g.DrawImage(b, 0, 0, newSize.Width, newSize.Height);
                        var ep = new EncoderParameters();
                        ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, PictureManager.ImageQuality);
                        
                        newBitMap.Save(Path.Combine(LocalThumbImagePath, fname), getImageCodeInfoFromExtension(Path.GetExtension(fname)), ep);
                        //newBitMap.Save(Path.Combine(LocalThumbImagePath, fname), getImageCodeInfo("image/jpeg"), ep);


                        newBitMap.Dispose();
                        b.Dispose();


                    }
                    return CommonHelper.GetStoreLocation() + "images/thumbs/" + fname;
                }
                return relPath;
            }
        }

      

        /// <summary>
        /// Get a picture URL
        /// </summary>
        /// <param name="picture">Picture instance</param>
        /// <param name="targetSize">The target picture size (longest side)</param>
        /// <param name="showDefaultPicture">A value indicating whether the default picture is shown</param>
        /// <returns></returns>
        public static string GetPictureUrl(Picture picture, int targetSize,
            bool showDefaultPicture)
        {
            string url = string.Empty;
            if (picture == null || picture.PictureBinary.Length == 0)
            {
                if(showDefaultPicture)
                {
                    url = GetDefaultPictureUrl(targetSize);
                }
                return url;
            }

            string[] parts = picture.Extension.Split('/');
            string lastPart = parts[parts.Length - 1];
            switch (lastPart)
            {
                case "pjpeg":
                    lastPart = "jpg";
                    break;
                case "x-png":
                    lastPart = "png";
                    break;
                case "x-icon":
                    lastPart = "ico";
                    break;
            
Hace 13 años
Thanks ozzied.
Hace 13 años
cool, thanks!
Hace 13 años
It looks like the code got truncated, Anyone interested in the full .cs file let me know and I will email it to you.
Hace 13 años
Hi

Just replace every image/jpeg by image/png in the file PictureManager.cs.
In version 1.80 this can be found on lines 154, 376 and 500 in the file.
The file can be found in the folder $(Root)\Libraries\Nop.BusinessLogic\Media

The thumnails will increase in size because of the png format, so traffic wil increase.

Hans
Hace 13 años
ozzied wrote:
If you upload a PNG with a transparent background the thumbnails will not be transparent.

This is due to the PictureManager only generating JPEGs.

I have corrected this by implementing a GetImageCodeInfoFromExtension method and replacing all of the getImageCodeInfo('Image/jpeg") refrences with a getImageCodeInfoFromExtension( fileExt) call.

This allows the original format to be preserved along with transparencies.

If anyone would like to take a look at my updated file please let me know.


Ozzied, there are 3 lines of code where is necessary to replace the method call to the new function, but in the 3rd call the ValidatePicture function doesn't have a filename variable, since it receives a byte array stream, what I did was to modify also this function so it receives a second parameter with the extension:


        /// <summary>
        /// Validates input picture dimensions
        /// </summary>
        /// <param name="pictureBinary">Picture binary</param>
        /// <returns>Picture binary or throws an exception</returns>
        public static byte[] ValidatePicture(byte[] pictureBinary, string extension)
        {
            using (MemoryStream stream = new MemoryStream(pictureBinary))
            {
                var b = new Bitmap(stream);
                int maxSize = SettingManager.GetSettingValueInteger("Media.MaximumImageSize", 1280);

                if ((b.Height > maxSize) || (b.Width > maxSize))
                {
                    var newSize = CalculateDimensions(b.Size, maxSize);
                    var newBitMap = new Bitmap(newSize.Width, newSize.Height);
                    var g = Graphics.FromImage(newBitMap);
                    g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                    g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                    g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                    g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                    g.DrawImage(b, 0, 0, newSize.Width, newSize.Height);

                    var m = new MemoryStream();
                    var ep = new EncoderParameters();
                    ep.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, PictureManager.ImageQuality);
                    newBitMap.Save(m, getImageCodeInfoFromExtension(extension), ep);
                    newBitMap.Dispose();
                    b.Dispose();

                    return m.GetBuffer();
                }
                else
                {
                    b.Dispose();
                    return pictureBinary;
                }
            }
        }


The ValidatePicture is called from two other lines of code, in both cases the extension is an available variable, so it's only necessary to pass that as the second parameter
Hace 13 años
https://www.nopcommerce.com/boards/t/6409/changes-to-picturemanager-for-png-transparency.aspx
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.