pedroserapio Posted June 16, 2015 Share Posted June 16, 2015 (edited) I found this topic https://www.prestashop.com/forums/topic/340771-ps-1606-poor-quality-on-uploaded-images/ very interesting but because I can not reply or add any comment I created this new topic based on hideaki idea. First steps in Ubuntu 14.04 to add imagemagick: sudo apt-get update sudo apt-get install php5-imagick sudo php5enmod imagick and don't forget restart or reload Apache sudo service apache2 restart Then I changed the file classes/ImageManager.php Prestashop 1.6.0.14 with the following final result: class ImageManagerCore { const ERROR_FILE_NOT_EXIST = 1; const ERROR_FILE_WIDTH = 2; const ERROR_MEMORY_LIMIT = 3; /** * Generate a cached thumbnail for object lists (eg. carrier, order statuses...etc) * * @param string $image Real image filename * @param string $cache_image Cached filename * @param int $size Desired size * @param string $image_type Image type * @param bool $disable_cache When turned on a timestamp will be added to the image URI to disable the HTTP cache * @param bool $regenerate When turned on and the file already exist, the file will be regenerated * @return string */ public static function thumbnail($image, $cache_image, $size, $image_type = 'jpg', $disable_cache = true, $regenerate = false) { if (!file_exists($image)) return ''; if (file_exists(_PS_TMP_IMG_DIR_.$cache_image) && $regenerate) @unlink(_PS_TMP_IMG_DIR_.$cache_image); if ($regenerate || !file_exists(_PS_TMP_IMG_DIR_.$cache_image)) { $infos = getimagesize($image); // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (!ImageManager::checkImageMemoryLimit($image)) return false; $x = $infos[0]; $y = $infos[1]; $max_x = $size * 3; // Size is already ok if ($y < $size && $x <= $max_x) copy($image, _PS_TMP_IMG_DIR_.$cache_image); // We need to resize */ else { $ratio_x = $x / ($y / $size); if ($ratio_x > $max_x) { $ratio_x = $max_x; $size = $y / ($x / $max_x); } ImageManager::resize($image, _PS_TMP_IMG_DIR_.$cache_image, $ratio_x, $size, $image_type); } } // Relative link will always work, whatever the base uri set in the admin if (Context::getContext()->controller->controller_type == 'admin') return '<img src="../img/tmp/'.$cache_image.($disable_cache ? '?time='.time() : '').'" alt="" class="imgm img-thumbnail" />'; else return '<img src="'._PS_TMP_IMG_.$cache_image.($disable_cache ? '?time='.time() : '').'" alt="" class="imgm img-thumbnail" />'; } /** * Check if memory limit is too long or not * * @static * @param $image * @return bool */ public static function checkImageMemoryLimit($image) { $infos = @getimagesize($image); if (!is_array($infos) || !isset($infos['bits'])) return true; $memory_limit = Tools::getMemoryLimit(); // memory_limit == -1 => unlimited memory if (function_exists('memory_get_usage') && (int)$memory_limit != -1) { $current_memory = memory_get_usage(); $channel = isset($infos['channels']) ? ($infos['channels'] / 8) : 1; // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (($infos[0] * $infos[1] * $infos['bits'] * $channel + pow(2, 16)) * 1.8 + $current_memory > $memory_limit - 1024 * 1024) return false; } return true; } /** * Resize, cut and optimize image * * @param string $src_file Image object from $_FILE * @param string $dst_file Destination filename * @param integer $dst_width Desired width (optional) * @param integer $dst_height Desired height (optional) * @param string $file_type * @return boolean Operation result */ public static function resize($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $force_type = false, &$error = 0) { if (PHP_VERSION_ID < 50300) clearstatcache(); else clearstatcache(true, $src_file); if (!file_exists($src_file) || !filesize($src_file)) return !($error = self::ERROR_FILE_NOT_EXIST); list($src_width, $src_height, $type) = getimagesize($src_file); // If PS_IMAGE_QUALITY is activated, the generated image will be a PNG with .jpg as a file extension. // This allow for higher quality and for transparency. JPG source files will also benefit from a higher quality // because JPG reencoding by GD, even with max quality setting, degrades the image. if (Configuration::get('PS_IMAGE_QUALITY') == 'png_all' || (Configuration::get('PS_IMAGE_QUALITY') == 'png' && $type == IMAGETYPE_PNG) && !$force_type) $file_type = 'png'; if (!$src_width) return !($error = self::ERROR_FILE_WIDTH); if (!$dst_width) $dst_width = $src_width; if (!$dst_height) $dst_height = $src_height; //$src_image = ImageManager::create($type, $src_file); $src_image = new Imagick($src_file); $width_diff = $dst_width / $src_width; $height_diff = $dst_height / $src_height; if ($width_diff > 1 && $height_diff > 1) { $next_width = $src_width; $next_height = $src_height; } else { if (Configuration::get('PS_IMAGE_GENERATION_METHOD') == 2 || (!Configuration::get('PS_IMAGE_GENERATION_METHOD') && $width_diff < $height_diff)) { $next_height = $dst_height; $next_width = round(($src_width * $next_height) / $src_height); $dst_width = (int)(!Configuration::get('PS_IMAGE_GENERATION_METHOD') ? $dst_width : $next_width); } else { $next_width = $dst_width; $next_height = round($src_height * $dst_width / $src_width); $dst_height = (int)(!Configuration::get('PS_IMAGE_GENERATION_METHOD') ? $dst_height : $next_height); } } if (!ImageManager::checkImageMemoryLimit($src_file)) return !($error = self::ERROR_MEMORY_LIMIT); $dest_image = $src_image; $dest_image->resizeImage($dst_width, $dst_height, Imagick::FILTER_LANCZOS, 1); //$dest_image->writeImage('mythumb.gif'); //$dest_image->destroy(); //$dest_image = imagecreatetruecolor($dst_width, $dst_height); // If image is a PNG and the output is PNG, fill with transparency. Else fill with white background. /*if ($file_type == 'png' && $type == IMAGETYPE_PNG) { imagealphablending($dest_image, false); imagesavealpha($dest_image, true); $transparent = imagecolorallocatealpha($dest_image, 255, 255, 255, 127); imagefilledrectangle($dest_image, 0, 0, $dst_width, $dst_height, $transparent); } else { $white = imagecolorallocate($dest_image, 255, 255, 255); imagefilledrectangle ($dest_image, 0, 0, $dst_width, $dst_height, $white); }*/ /*imagecopyresampled($dest_image, $src_image, (int)(($dst_width - $next_width) / 2), (int)(($dst_height - $next_height) / 2), 0, 0, $next_width, $next_height, $src_width, $src_height);*/ //return (ImageManager::write($file_type, $dest_image, $dst_file)); return $dest_image->writeImage($dst_file); } /** * Check if file is a real image * * @param string $filename File path to check * @param string $file_mime_type File known mime type (generally from $_FILES) * @param array $mime_type_list Allowed MIME types * @return bool */ public static function isRealImage($filename, $file_mime_type = null, $mime_type_list = null) { // Detect mime content type $mime_type = false; if (!$mime_type_list) $mime_type_list = array('image/gif', 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png'); // Try 4 different methods to determine the mime type if (function_exists('getimagesize')) { $image_info = @getimagesize($filename); if ($image_info) $mime_type = $image_info['mime']; else $file_mime_type = false; } elseif (function_exists('finfo_open')) { $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME; $finfo = finfo_open($const); $mime_type = finfo_file($finfo, $filename); finfo_close($finfo); } elseif (function_exists('mime_content_type')) $mime_type = mime_content_type($filename); elseif (function_exists('exec')) { $mime_type = trim(exec('file -b --mime-type '.escapeshellarg($filename))); if (!$mime_type) $mime_type = trim(exec('file --mime '.escapeshellarg($filename))); if (!$mime_type) $mime_type = trim(exec('file -bi '.escapeshellarg($filename))); } if ($file_mime_type && (empty($mime_type) || $mime_type == 'regular file' || $mime_type == 'text/plain')) $mime_type = $file_mime_type; // For each allowed MIME type, we are looking for it inside the current MIME type foreach ($mime_type_list as $type) if (strstr($mime_type, $type)) return true; return false; } /** * Check if image file extension is correct * * @static * @param $filename real filename * @return bool true if it's correct */ public static function isCorrectImageFileExt($filename, $authorized_extensions = null) { // Filter on file extension if ($authorized_extensions === null) $authorized_extensions = array('gif', 'jpg', 'jpeg', 'jpe', 'png'); $name_explode = explode('.', $filename); if (count($name_explode) >= 2) { $current_extension = strtolower($name_explode[count($name_explode) - 1]); if (!in_array($current_extension, $authorized_extensions)) return false; } else return false; return true; } /** * Validate image upload (check image type and weight) * * @param array $file Upload $_FILE value * @param integer $max_file_size Maximum upload size * @return bool|string Return false if no error encountered */ public static function validateUpload($file, $max_file_size = 0, $types = null) { if ((int)$max_file_size > 0 && $file['size'] > (int)$max_file_size) return sprintf(Tools::displayError('Image is too large (%1$d kB). Maximum allowed: %2$d kB'), $file['size'] / 1024, $max_file_size / 1024); if (!ImageManager::isRealImage($file['tmp_name'], $file['type']) || !ImageManager::isCorrectImageFileExt($file['name'], $types) || preg_match('/\%00/', $file['name'])) return Tools::displayError('Image format not recognized, allowed formats are: .gif, .jpg, .png'); if ($file['error']) return sprintf(Tools::displayError('Error while uploading image; please change your server\'s settings. (Error code: %s)'), $file['error']); return false; } /** * Validate icon upload * * @param array $file Upload $_FILE value * @param int $max_file_size Maximum upload size * @return bool|string Return false if no error encountered */ public static function validateIconUpload($file, $max_file_size = 0) { if ((int)$max_file_size > 0 && $file['size'] > $max_file_size) return sprintf( Tools::displayError('Image is too large (%1$d kB). Maximum allowed: %2$d kB'), $file['size'] / 1000, $max_file_size / 1000 ); if (substr($file['name'], -4) != '.ico') return Tools::displayError('Image format not recognized, allowed formats are: .ico'); if ($file['error']) return Tools::displayError('Error while uploading image; please change your server\'s settings.'); return false; } /** * Cut image * * @param array $src_file Origin filename * @param string $dst_file Destination filename * @param integer $dst_width Desired width * @param integer $dst_height Desired height * @param string $file_type * @param int $dst_x * @param int $dst_y * * @return bool Operation result */ public static function cut($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $dst_x = 0, $dst_y = 0) { if (!file_exists($src_file)) return false; // Source information $src_info = getimagesize($src_file); $src = array( 'width' => $src_info[0], 'height' => $src_info[1], 'ressource' => ImageManager::create($src_info[2], $src_file), ); // Destination information $dest = array(); $dest['x'] = $dst_x; $dest['y'] = $dst_y; $dest['width'] = !is_null($dst_width) ? $dst_width : $src['width']; $dest['height'] = !is_null($dst_height) ? $dst_height : $src['height']; $dest['ressource'] = ImageManager::createWhiteImage($dest['width'], $dest['height']); $white = imagecolorallocate($dest['ressource'], 255, 255, 255); imagecopyresampled($dest['ressource'], $src['ressource'], 0, 0, $dest['x'], $dest['y'], $dest['width'], $dest['height'], $dest['width'], $dest['height']); imagecolortransparent($dest['ressource'], $white); $return = ImageManager::write($file_type, $dest['ressource'], $dst_file); return $return; } /** * Create an image with GD extension from a given type * * @param string $type * @param string $filename * @return resource */ public static function create($type, $filename) { switch ($type) { case IMAGETYPE_GIF : return imagecreatefromgif($filename); break; case IMAGETYPE_PNG : return imagecreatefrompng($filename); break; case IMAGETYPE_JPEG : default: return imagecreatefromjpeg($filename); break; } } /** * Create an empty image with white background * * @param int $width * @param int $height * @return resource */ public static function createWhiteImage($width, $height) { $image = imagecreatetruecolor($width, $height); $white = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $white); return $image; } /** * Generate and write image * * @param string $type * @param resource $resource * @param string $filename * @return bool */ public static function write($type, $resource, $filename) { switch ($type) { case 'gif': $success = imagegif($resource, $filename); break; case 'png': $quality = (Configuration::get('PS_PNG_QUALITY') === false ? 7 : Configuration::get('PS_PNG_QUALITY')); $success = imagepng($resource, $filename, (int)$quality); break; case 'jpg': case 'jpeg': default: $quality = (Configuration::get('PS_JPEG_QUALITY') === false ? 90 : Configuration::get('PS_JPEG_QUALITY')); imageinterlace($resource, 1); /// make it PROGRESSIVE $success = imagejpeg($resource, $filename, (int)$quality); break; } imagedestroy($resource); @chmod($filename, 0664); return $success; } /** * Return the mime type by the file extension * * @param string $file_name * @return string */ public static function getMimeTypeByExtension($file_name) { $types = array( 'image/gif' => array('gif'), 'image/jpeg' => array('jpg', 'jpeg'), 'image/png' => array('png') ); $extension = substr($file_name, strrpos($file_name, '.') + 1); $mime_type = null; foreach ($types as $mime => $exts) if (in_array($extension, $exts)) { $mime_type = $mime; break; } if ($mime_type === null) $mime_type = 'image/jpeg'; return $mime_type; } } The image quality it's significantly improved and the files sizes are very small, producing great results but I have a issue, instead of producing images with the correct size ratio, the images are deformed. I'm I putting this in wrong order? Or something it's missing to calculate the correct ratio? Thank you very much. Edited June 16, 2015 by pedroserapio (see edit history) 1 Link to comment Share on other sites More sharing options...
pedroserapio Posted June 18, 2015 Author Share Posted June 18, 2015 Any idea? It's strange why can not find more information related with this topic because the quality provided by GD it's very low. Link to comment Share on other sites More sharing options...
RevolutionGrow Posted November 1, 2015 Share Posted November 1, 2015 Any news, Pedro? I am curious as I am also unsatisfied with the PS image rendering... Link to comment Share on other sites More sharing options...
jano.jakubik Posted November 2, 2015 Share Posted November 2, 2015 I am also interested about solution how to change GD library to ImageMagick library in Prestashop. The best will be some module or full working solution for latest version of Prestashop 1.6.1.2. Link to comment Share on other sites More sharing options...
pedroserapio Posted November 3, 2015 Author Share Posted November 3, 2015 (edited) In Prestashop 1.6.2 I created the following override: override/classes/ImageManager.php with the following code: <?php class ImageManager extends ImageManagerCore { /** * Resize, cut and optimize image * * @param string $src_file Image object from $_FILE * @param string $dst_file Destination filename * @param int $dst_width Desired width (optional) * @param int $dst_height Desired height (optional) * @param string $file_type * @param bool $force_type * @param int $error * @param int $tgt_width * @param int $tgt_height * @param int $quality * @param int $src_width * @param int $src_height * @return bool Operation result */ public static function resize($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $force_type = false, &$error = 0, &$tgt_width = null, &$tgt_height = null, $quality = 5, &$src_width = null, &$src_height = null) { if (PHP_VERSION_ID < 50300) { clearstatcache(); } else { clearstatcache(true, $src_file); } if (!file_exists($src_file) || !filesize($src_file)) { return !($error = self::ERROR_FILE_NOT_EXIST); } list($src_width, $src_height, $type) = getimagesize($src_file); // If PS_IMAGE_QUALITY is activated, the generated image will be a PNG with .jpg as a file extension. // This allow for higher quality and for transparency. JPG source files will also benefit from a higher quality // because JPG reencoding by GD, even with max quality setting, degrades the image. if (Configuration::get('PS_IMAGE_QUALITY') == 'png_all' || (Configuration::get('PS_IMAGE_QUALITY') == 'png' && $type == IMAGETYPE_PNG) && !$force_type) { $file_type = 'png'; } if (!$src_width) { return !($error = self::ERROR_FILE_WIDTH); } if (!$dst_width) { $dst_width = $src_width; } if (!$dst_height) { $dst_height = $src_height; } $width_diff = $dst_width / $src_width; $height_diff = $dst_height / $src_height; $ps_image_generation_method = Configuration::get('PS_IMAGE_GENERATION_METHOD'); if ($width_diff > 1 && $height_diff > 1) { $next_width = $src_width; $next_height = $src_height; } else { if ($ps_image_generation_method == 2 || (!$ps_image_generation_method && $width_diff > $height_diff)) { $next_height = $dst_height; $next_width = round(($src_width * $next_height) / $src_height); $dst_width = (int)(!$ps_image_generation_method ? $dst_width : $next_width); } else { $next_width = $dst_width; $next_height = round($src_height * $dst_width / $src_width); $dst_height = (int)(!$ps_image_generation_method ? $dst_height : $next_height); } } if (!ImageManager::checkImageMemoryLimit($src_file)) { return !($error = self::ERROR_MEMORY_LIMIT); } //Set Imagick Object values $src_image = new Imagick(); $src_image->readImage($src_file); $src_image->setImageCompression(Imagick::COMPRESSION_JPEG); $src_image->setInterlaceScheme(Imagick::INTERLACE_PLANE); $src_image->setImageCompressionQuality(82); $src_image->gaussianBlurImage(0.05,0.05); $src_image->stripImage(); $src_image->thumbnailImage($dst_width, $dst_height, Imagick::FILTER_TRIANGLE, 1); //Output the final Image using Imagick return $src_image->writeImage($dst_file); } } I saw image improvements and lower file sizes. DO NOT FORGET: clear the Cache and Regenerate the Images, and just in case, clear the browser cache. This code it's far from perfect and all the suggestions and improvements and very welcome. I think this can be implemented in PS core with a simple detection if Imagick it's present, if not present, give the possibility to roll back to default GD. Thank you. Edited November 3, 2015 by pedroserapio (see edit history) 1 Link to comment Share on other sites More sharing options...
jano.jakubik Posted November 3, 2015 Share Posted November 3, 2015 (edited) Hi pedroserapio. Thank you very much for your solution . I have to tell you that it just works! The only problem I have is the speed of your solution. It is 2 - 3 times slower then previos GD library. Do you see some parts of code which should be more optimized for speed? Or you think the ImageMagick is generally slower then GD (I do not think so). EDIT: There is also another problem. Sometimes when I try to regenerate thumbnails with your modification I have error message on white blank page. The error is about max execution time 90s on line 88 (which is set by my provider to php). But this error happens only after about 30s (so not after 90s). So actually there should be some problem. It happens only sometimes not always. Edited November 3, 2015 by jano.jakubik (see edit history) Link to comment Share on other sites More sharing options...
pedroserapio Posted November 3, 2015 Author Share Posted November 3, 2015 (edited) Hi pedroserapio. Thank you very much for your solution . I have to tell you that it just works! The only problem I have is the speed of your solution. It is 2 - 3 times slower then previos GD library. Do you see some parts of code which should be more optimized for speed? Or you think the ImageMagick is generally slower then GD (I do not think so). I felt the same, more slower when I regenerated all the images. Probably I'm missing some optimization. Edited November 3, 2015 by pedroserapio (see edit history) Link to comment Share on other sites More sharing options...
jano.jakubik Posted November 3, 2015 Share Posted November 3, 2015 I hope there will be some optimizations and also the problem with randomly max execution time will be solved too . Link to comment Share on other sites More sharing options...
jano.jakubik Posted November 9, 2015 Share Posted November 9, 2015 Hello, Do you have any news? I still have errors when I try to regenerate thumbnails and also speed issues. Errors are about max execution time limit (which is in my hosting 90s) but the script is working only about 10s. This is really random. Sometimes it is gointg to max limit 90s but sometimes it stops about 10 seconds from the begin. Link to comment Share on other sites More sharing options...
Netzfischer Posted January 14, 2016 Share Posted January 14, 2016 (edited) Hi pedroserapio, thanks for the imageMagic code, it does improve images visibly. Just one question: when resizing, ImageMagic doesn't keep the aspect ratio. With GD, the aspect ratio was kept the same as for the original. How can I change the code so that the aspect ration is kept the same as the original? UPDATE: I apologize - after using the override with the code mentioned above, it worked. many thanks in advance, Roland Edited February 12, 2016 by Netzfischer (see edit history) Link to comment Share on other sites More sharing options...
jano.jakubik Posted January 28, 2016 Share Posted January 28, 2016 Hi! Still do not you have any news how to improve performance of this Imagemagick solution? It is really lots of slower the GD but I love the image quality. Thanks. Link to comment Share on other sites More sharing options...
garyjj127 Posted February 12, 2016 Share Posted February 12, 2016 Hi My apologies for posting on here if it's deemed off topic, but I've noticed that if I import product images with a CSV file, the image quality is blurred, compared to adding the images manually in the back office. I've searched for a reason/solution and have been unable to find one! Link to comment Share on other sites More sharing options...
hakeryk2 Posted April 12, 2016 Share Posted April 12, 2016 (edited) I think that this method supossed to be included in new version. Good work. I have changed //Set Imagick Object values $src_image = new Imagick(); $src_image->readImage($src_file); $src_image->setImageCompression(Imagick::COMPRESSION_JPEG); $src_image->setInterlaceScheme(Imagick::INTERLACE_PLANE); $src_image->setImageCompressionQuality(82); $src_image->gaussianBlurImage(0.05,0.05); $src_image->stripImage(); $src_image->thumbnailImage($dst_width, $dst_height, Imagick::FILTER_TRIANGLE, 1); to this //Set Imagick Object values $src_image = new Imagick(); $src_image->readImage($src_file); $src_image->setImageCompression(Imagick::COMPRESSION_JPEG); $src_image->setInterlaceScheme(Imagick::INTERLACE_PLANE); $src_image->setImageCompressionQuality(89); $src_image->sharpenimage(2, 0.5, 134217727); $src_image->gaussianBlurImage(0.03,0.03); $src_image->stripImage(); $src_image->thumbnailImage($dst_width, $dst_height, Imagick::FILTER_SINC, 1); Images are much more sharper and with better quality but they're a little bit more heavier. You can check them and let me know. Edited June 1, 2016 by hakeryk2 (see edit history) Link to comment Share on other sites More sharing options...
Vitamin Posted June 13, 2016 Share Posted June 13, 2016 It does not work with transparent (png) images. The generated image is white backgrounds. Link to comment Share on other sites More sharing options...
roband Posted June 13, 2016 Share Posted June 13, 2016 Check out https://www.prestashop.com/forums/topic/530451-free-module-imagemagick-support-for-prestashop/ Link to comment Share on other sites More sharing options...
dandumit Posted February 20, 2019 Share Posted February 20, 2019 that thread doesn't exist anymore. More than that , generally ImageMagick creates bigger files than GD. Link to comment Share on other sites More sharing options...
hakeryk2 Posted February 20, 2019 Share Posted February 20, 2019 Just now, dandumit said: that thread doesn't exist anymore. More than that , generally ImageMagick creates bigger files than GD. No, I made it with ImageMagick to make 360x360 pictures with awesome quality and they are like 5-13 kb per size and without those flaws like GD do. Link to comment Share on other sites More sharing options...
dandumit Posted February 20, 2019 Share Posted February 20, 2019 look at this article : https://foliovision.com/2010/03/imagemagick-vs-gd it's old but looks that GD files are less than a half of imagemagick Link to comment Share on other sites More sharing options...
hakeryk2 Posted February 20, 2019 Share Posted February 20, 2019 There is no settings of Imagick shown in this article so I assume that they were run on basic settings which are indeed heavy in weight. After some tweeks Imagick was better for me and it is really powerfull tool to do basicaly everything with images Link to comment Share on other sites More sharing options...
Afriluka Posted March 30, 2020 Share Posted March 30, 2020 (edited) Hello to everyone, still with Prestashop 1.7 I faced the same issues then described before here: 1. Pictures uploaded with Prestashop were blurred 2. Also the filesize was too big So I wondered if those recommended changes would work with 1.7 too. And well it didn't work straight out of the box. But after some try and error I could make it run so that both problems were solved. My Prestashop Version is 1.7.6.3 To make it work I have first changed classes/ImageManager.php as described by pedroserapio for Prestashop 1.6.0.14 in his first post. It worked straight: After Regenerate thumbnails all the pictures looked straight sharp not blurred anymore. BUT the files where quite too big. So I tried to implement also the second change suggested by pedroserapio for Prestashop 1.6.2. Unfortunately it seems like 1.7 don't allow overrides of core classes anymore. At least I couldn't make it work. So I have just edit the first version from pedroserapio with the second change from him. Then I also implemented the suggested change from hakeryk2. The change from hakeryk2 didn't bring a very big improvement for me but when you look close indeed you can see an improvement. Here is the whole change I did for classes/ImageManager.php: <?php class ImageManagerCore { const ERROR_FILE_NOT_EXIST = 1; const ERROR_FILE_WIDTH = 2; const ERROR_MEMORY_LIMIT = 3; /** * Generate a cached thumbnail for object lists (eg. carrier, order statuses...etc) * * @param string $image Real image filename * @param string $cache_image Cached filename * @param int $size Desired size * @param string $image_type Image type * @param bool $disable_cache When turned on a timestamp will be added to the image URI to disable the HTTP cache * @param bool $regenerate When turned on and the file already exist, the file will be regenerated * @return string */ public static function thumbnail($image, $cache_image, $size, $image_type = 'jpg', $disable_cache = true, $regenerate = false) { if (!file_exists($image)) return ''; if (file_exists(_PS_TMP_IMG_DIR_.$cache_image) && $regenerate) @unlink(_PS_TMP_IMG_DIR_.$cache_image); if ($regenerate || !file_exists(_PS_TMP_IMG_DIR_.$cache_image)) { $infos = getimagesize($image); // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (!ImageManager::checkImageMemoryLimit($image)) return false; $x = $infos[0]; $y = $infos[1]; $max_x = $size * 3; // Size is already ok if ($y < $size && $x <= $max_x) copy($image, _PS_TMP_IMG_DIR_.$cache_image); // We need to resize */ else { $ratio_x = $x / ($y / $size); if ($ratio_x > $max_x) { $ratio_x = $max_x; $size = $y / ($x / $max_x); } ImageManager::resize($image, _PS_TMP_IMG_DIR_.$cache_image, $ratio_x, $size, $image_type); } } // Relative link will always work, whatever the base uri set in the admin if (Context::getContext()->controller->controller_type == 'admin') return '<img src="../img/tmp/'.$cache_image.($disable_cache ? '?time='.time() : '').'" alt="" class="imgm img-thumbnail" />'; else return '<img src="'._PS_TMP_IMG_.$cache_image.($disable_cache ? '?time='.time() : '').'" alt="" class="imgm img-thumbnail" />'; } /** * Check if memory limit is too long or not * * @static * @param $image * @return bool */ public static function checkImageMemoryLimit($image) { $infos = @getimagesize($image); if (!is_array($infos) || !isset($infos['bits'])) return true; $memory_limit = Tools::getMemoryLimit(); // memory_limit == -1 => unlimited memory if (function_exists('memory_get_usage') && (int)$memory_limit != -1) { $current_memory = memory_get_usage(); $channel = isset($infos['channels']) ? ($infos['channels'] / 8) : 1; // Evaluate the memory required to resize the image: if it's too much, you can't resize it. if (($infos[0] * $infos[1] * $infos['bits'] * $channel + pow(2, 16)) * 1.8 + $current_memory > $memory_limit - 1024 * 1024) return false; } return true; } /** * Resize, cut and optimize image * * @param string $src_file Image object from $_FILE * @param string $dst_file Destination filename * @param integer $dst_width Desired width (optional) * @param integer $dst_height Desired height (optional) * @param string $file_type * @return boolean Operation result */ public static function resize($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $force_type = false, &$error = 0) { if (PHP_VERSION_ID < 50300) clearstatcache(); else clearstatcache(true, $src_file); if (!file_exists($src_file) || !filesize($src_file)) return !($error = self::ERROR_FILE_NOT_EXIST); list($src_width, $src_height, $type) = getimagesize($src_file); // If PS_IMAGE_QUALITY is activated, the generated image will be a PNG with .jpg as a file extension. // This allow for higher quality and for transparency. JPG source files will also benefit from a higher quality // because JPG reencoding by GD, even with max quality setting, degrades the image. if (Configuration::get('PS_IMAGE_QUALITY') == 'png_all' || (Configuration::get('PS_IMAGE_QUALITY') == 'png' && $type == IMAGETYPE_PNG) && !$force_type) $file_type = 'png'; if (!$src_width) return !($error = self::ERROR_FILE_WIDTH); if (!$dst_width) $dst_width = $src_width; if (!$dst_height) $dst_height = $src_height; $width_diff = $dst_width / $src_width; $height_diff = $dst_height / $src_height; if ($width_diff > 1 && $height_diff > 1) { $next_width = $src_width; $next_height = $src_height; } else { if (Configuration::get('PS_IMAGE_GENERATION_METHOD') == 2 || (!Configuration::get('PS_IMAGE_GENERATION_METHOD') && $width_diff < $height_diff)) { $next_height = $dst_height; $next_width = round(($src_width * $next_height) / $src_height); $dst_width = (int)(!Configuration::get('PS_IMAGE_GENERATION_METHOD') ? $dst_width : $next_width); } else { $next_width = $dst_width; $next_height = round($src_height * $dst_width / $src_width); $dst_height = (int)(!Configuration::get('PS_IMAGE_GENERATION_METHOD') ? $dst_height : $next_height); } } if (!ImageManager::checkImageMemoryLimit($src_file)) return !($error = self::ERROR_MEMORY_LIMIT); //Set Imagick Object values $src_image = new Imagick(); $src_image->readImage($src_file); $src_image->setImageCompression(Imagick::COMPRESSION_JPEG); $src_image->setInterlaceScheme(Imagick::INTERLACE_PLANE); $src_image->setImageCompressionQuality(70); $src_image->sharpenimage(2, 0.5, 134217727); $src_image->gaussianBlurImage(0.03,0.03); $src_image->stripImage(); $src_image->thumbnailImage($dst_width, $dst_height, Imagick::FILTER_SINC, 1); //Output the final Image using Imagick return $src_image->writeImage($dst_file); } /** * Check if file is a real image * * @param string $filename File path to check * @param string $file_mime_type File known mime type (generally from $_FILES) * @param array $mime_type_list Allowed MIME types * @return bool */ public static function isRealImage($filename, $file_mime_type = null, $mime_type_list = null) { // Detect mime content type $mime_type = false; if (!$mime_type_list) $mime_type_list = array('image/gif', 'image/jpg', 'image/jpeg', 'image/pjpeg', 'image/png', 'image/x-png'); // Try 4 different methods to determine the mime type if (function_exists('getimagesize')) { $image_info = @getimagesize($filename); if ($image_info) $mime_type = $image_info['mime']; else $file_mime_type = false; } elseif (function_exists('finfo_open')) { $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME; $finfo = finfo_open($const); $mime_type = finfo_file($finfo, $filename); finfo_close($finfo); } elseif (function_exists('mime_content_type')) $mime_type = mime_content_type($filename); elseif (function_exists('exec')) { $mime_type = trim(exec('file -b --mime-type '.escapeshellarg($filename))); if (!$mime_type) $mime_type = trim(exec('file --mime '.escapeshellarg($filename))); if (!$mime_type) $mime_type = trim(exec('file -bi '.escapeshellarg($filename))); } if ($file_mime_type && (empty($mime_type) || $mime_type == 'regular file' || $mime_type == 'text/plain')) $mime_type = $file_mime_type; // For each allowed MIME type, we are looking for it inside the current MIME type foreach ($mime_type_list as $type) if (strstr($mime_type, $type)) return true; return false; } /** * Check if image file extension is correct * * @static * @param $filename real filename * @return bool true if it's correct */ public static function isCorrectImageFileExt($filename, $authorized_extensions = null) { // Filter on file extension if ($authorized_extensions === null) $authorized_extensions = array('gif', 'jpg', 'jpeg', 'jpe', 'png'); $name_explode = explode('.', $filename); if (count($name_explode) >= 2) { $current_extension = strtolower($name_explode[count($name_explode) - 1]); if (!in_array($current_extension, $authorized_extensions)) return false; } else return false; return true; } /** * Validate image upload (check image type and weight) * * @param array $file Upload $_FILE value * @param integer $max_file_size Maximum upload size * @return bool|string Return false if no error encountered */ public static function validateUpload($file, $max_file_size = 0, $types = null) { if ((int)$max_file_size > 0 && $file['size'] > (int)$max_file_size) return sprintf(Tools::displayError('Image is too large (%1$d kB). Maximum allowed: %2$d kB'), $file['size'] / 1024, $max_file_size / 1024); if (!ImageManager::isRealImage($file['tmp_name'], $file['type']) || !ImageManager::isCorrectImageFileExt($file['name'], $types) || preg_match('/\%00/', $file['name'])) return Tools::displayError('Image format not recognized, allowed formats are: .gif, .jpg, .png'); if ($file['error']) return sprintf(Tools::displayError('Error while uploading image; please change your server\'s settings. (Error code: %s)'), $file['error']); return false; } /** * Validate icon upload * * @param array $file Upload $_FILE value * @param int $max_file_size Maximum upload size * @return bool|string Return false if no error encountered */ public static function validateIconUpload($file, $max_file_size = 0) { if ((int)$max_file_size > 0 && $file['size'] > $max_file_size) return sprintf( Tools::displayError('Image is too large (%1$d kB). Maximum allowed: %2$d kB'), $file['size'] / 1000, $max_file_size / 1000 ); if (substr($file['name'], -4) != '.ico') return Tools::displayError('Image format not recognized, allowed formats are: .ico'); if ($file['error']) return Tools::displayError('Error while uploading image; please change your server\'s settings.'); return false; } /** * Cut image * * @param array $src_file Origin filename * @param string $dst_file Destination filename * @param integer $dst_width Desired width * @param integer $dst_height Desired height * @param string $file_type * @param int $dst_x * @param int $dst_y * * @return bool Operation result */ public static function cut($src_file, $dst_file, $dst_width = null, $dst_height = null, $file_type = 'jpg', $dst_x = 0, $dst_y = 0) { if (!file_exists($src_file)) return false; // Source information $src_info = getimagesize($src_file); $src = array( 'width' => $src_info[0], 'height' => $src_info[1], 'ressource' => ImageManager::create($src_info[2], $src_file), ); // Destination information $dest = array(); $dest['x'] = $dst_x; $dest['y'] = $dst_y; $dest['width'] = !is_null($dst_width) ? $dst_width : $src['width']; $dest['height'] = !is_null($dst_height) ? $dst_height : $src['height']; $dest['ressource'] = ImageManager::createWhiteImage($dest['width'], $dest['height']); $white = imagecolorallocate($dest['ressource'], 255, 255, 255); imagecopyresampled($dest['ressource'], $src['ressource'], 0, 0, $dest['x'], $dest['y'], $dest['width'], $dest['height'], $dest['width'], $dest['height']); imagecolortransparent($dest['ressource'], $white); $return = ImageManager::write($file_type, $dest['ressource'], $dst_file); return $return; } /** * Create an image with GD extension from a given type * * @param string $type * @param string $filename * @return resource */ public static function create($type, $filename) { switch ($type) { case IMAGETYPE_GIF : return imagecreatefromgif($filename); break; case IMAGETYPE_PNG : return imagecreatefrompng($filename); break; case IMAGETYPE_JPEG : default: return imagecreatefromjpeg($filename); break; } } /** * Create an empty image with white background * * @param int $width * @param int $height * @return resource */ public static function createWhiteImage($width, $height) { $image = imagecreatetruecolor($width, $height); $white = imagecolorallocate($image, 255, 255, 255); imagefill($image, 0, 0, $white); return $image; } /** * Generate and write image * * @param string $type * @param resource $resource * @param string $filename * @return bool */ public static function write($type, $resource, $filename) { switch ($type) { case 'gif': $success = imagegif($resource, $filename); break; case 'png': $quality = (Configuration::get('PS_PNG_QUALITY') === false ? 7 : Configuration::get('PS_PNG_QUALITY')); $success = imagepng($resource, $filename, (int)$quality); break; case 'jpg': case 'jpeg': default: $quality = (Configuration::get('PS_JPEG_QUALITY') === false ? 90 : Configuration::get('PS_JPEG_QUALITY')); imageinterlace($resource, 1); /// make it PROGRESSIVE $success = imagejpeg($resource, $filename, (int)$quality); break; } imagedestroy($resource); @chmod($filename, 0664); return $success; } /** * Return the mime type by the file extension * * @param string $file_name * @return string */ public static function getMimeTypeByExtension($file_name) { $types = array( 'image/gif' => array('gif'), 'image/jpeg' => array('jpg', 'jpeg'), 'image/png' => array('png') ); $extension = substr($file_name, strrpos($file_name, '.') + 1); $mime_type = null; foreach ($types as $mime => $exts) if (in_array($extension, $exts)) { $mime_type = $mime; break; } if ($mime_type === null) $mime_type = 'image/jpeg'; return $mime_type; } } I hope it might be helpful for someone else too. Unfortunately I had to edit the original core files. So with the next Prestashop update it will be lost. So if someone knows how to still use the overrides in 1.7, maybe one can post the right version for it. Edited March 30, 2020 by Afriluka (see edit history) Link to comment Share on other sites More sharing options...
JBW Posted April 1, 2020 Share Posted April 1, 2020 This code seems to be based on old version and does not consider options from the backoffice (e.g. JPG/PNG Compression). I took the latest 1.7.6.4 code and used your Imagick code in an override. You can out the attached file into /override/classes/ - to activate it delete /var/cache/prod/class_index.php. Didn't test the rotate function which is copied from PHP.net comments. ImageManager.php 1 Link to comment Share on other sites More sharing options...
Afriluka Posted April 1, 2020 Share Posted April 1, 2020 (edited) Hey JBW, thanks so much for your help. Unfortunately I have only basic understanding of coding. After using the code that I have posted here I was facing problems with uploading new images for a product. I am actually testing where the problem could be. So the code above is working only on pictures that were already uploaded but not on pictures that will be newly uploaded. I will test your version too and let you know how it worked. Edited April 1, 2020 by Afriluka (see edit history) Link to comment Share on other sites More sharing options...
Afriluka Posted April 1, 2020 Share Posted April 1, 2020 (edited) Hey JBW, I have found my problem. It was another override that I have tried to use before. This was causing the problem. Now I set everything to the original Prestashop files and used your provided file as a new override and deleted the classes_index.php. And voilà your solution works like a charme! Thank you very much for your help! As you can see in the example, the picture before was blurred. And before with the same compression the picture had almost 200kb. Now it looks better and it has only about 140kb. Edited April 1, 2020 by Afriluka (see edit history) Link to comment Share on other sites More sharing options...
netspider1 Posted July 1, 2020 Share Posted July 1, 2020 (edited) Is there any changes required for core files or just you copied the file JWB provided ? Edited July 1, 2020 by netspider1 (see edit history) Link to comment Share on other sites More sharing options...
JBW Posted July 2, 2020 Share Posted July 2, 2020 It's an override file, so no need to change core files and it's save in case of upgrades. Link to comment Share on other sites More sharing options...
netspider1 Posted August 1, 2020 Share Posted August 1, 2020 On 7/2/2020 at 8:02 PM, JBW said: It's an override file, so no need to change core files and it's save in case of upgrades. Thankyou Dear, it worked Fine my product images are much clear, better then before Link to comment Share on other sites More sharing options...
Peter Liska Posted September 30, 2020 Share Posted September 30, 2020 On 4/1/2020 at 11:06 AM, JBW said: This code seems to be based on old version and does not consider options from the backoffice (e.g. JPG/PNG Compression). I took the latest 1.7.6.4 code and used your Imagick code in an override. You can out the attached file into /override/classes/ - to activate it delete /var/cache/prod/class_index.php. Didn't test the rotate function which is copied from PHP.net comments. ImageManager.php 6.23 kB · 23 downloads Only small improvement, because there is 2x ImageManager::checkImageMemoryLimit That can speed up things a little bit 😉 I also deleted the commented code at the beginning. ImageManager.php 1 Link to comment Share on other sites More sharing options...
Peter Liska Posted November 3, 2020 Share Posted November 3, 2020 (edited) Maybe this should still be considered: At he beginning: if (preg_match('/\/img\/(p|c|m|s|st)\/(\d+\/)*\d+\.(jpg|jpeg|png|gif)$/Us', $destinationFile)) { return @copy($sourceFile, $destinationFile); } From here: https://www.sunnytoo.com/42096/an-important-fix-to-optimize-prestashops-image-upload-function To keep the original image as it was. For further possible optimization in the future Edited November 3, 2020 by Peter Liska (see edit history) Link to comment Share on other sites More sharing options...
Peter Liska Posted November 15, 2021 Share Posted November 15, 2021 (edited) Here is the version that also preserves the original image. (Code from my previous comment is included.) It's a little bit polished to make it easier to compare to the original file: PrestaShop version 1.7.8.0 Simple just put in: /override/classes/ ImageManager.php Edited November 15, 2021 by Peter Liska (see edit history) Link to comment Share on other sites More sharing options...
gouna Posted June 21, 2022 Share Posted June 21, 2022 Hello, This version of imagemanager is ok for prestashop 1.7.6.8? Thanks ! Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now