Index: branches/RC/core/units/general/helpers/image_helper.php =================================================================== diff -u -N --- branches/RC/core/units/general/helpers/image_helper.php (revision 11939) +++ branches/RC/core/units/general/helpers/image_helper.php (revision 0) @@ -1,593 +0,0 @@ - 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20) - */ - function parseFormat($format) - { - $res = Array (); - - $format_parts = explode(';', $format); - foreach ($format_parts as $format_part) { - if (preg_match('/resize:(\d*)x(\d*)/', $format_part, $regs)) { - $res['max_width'] = $regs[1]; - $res['max_height'] = $regs[2]; - } - elseif (preg_match('/wm:([^\|]*)\|([^\|]*)\|([^\|]*)/', $format_part, $regs)) { - $res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1]; - $res['h_margin'] = strtolower($regs[2]); - $res['v_margin'] = strtolower($regs[3]); - } - elseif (preg_match('/crop:([^\|]*)\|([^\|]*)/', $format_part, $regs)) { - $res['crop_x'] = strtolower($regs[1]); - $res['crop_y'] = strtolower($regs[2]); - } - elseif ($format_part == 'img_size' || $format_part == 'img_sizes') { - $res['image_size'] = true; - } - elseif (preg_match('/fill:(.*)/', $format_part, $regs)) { - $res['fill'] = $regs[1]; - } - } - - return $res; - } - - /** - * Resized given image to required dimensions & saves resized image to "resized" subfolder in source image folder - * - * @param string $src_image full path to image (on server) - * @param mixed $max_width maximal allowed resized image width or false if no limit - * @param mixed $max_height maximal allowed resized image height or false if no limit - * @return string direct url to resized image - */ - function ResizeImage($src_image, $max_width, $max_height = null) - { - $image_size = false; - - if(isset($max_height)) { - $params['max_height'] = $max_height; - $params['max_width'] = $max_width; - } - else { - $params = $this->parseFormat($max_width); - - if (array_key_exists('image_size', $params)) { - // image_size param shouldn't affect resized file name (crc part) - $image_size = $params['image_size']; - unset($params['image_size']); - } - } - - if ($params['max_width'] > 0 || $params['max_height'] > 0) { - list ($params['target_width'], $params['target_height'], $needs_resize) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); - - if (!is_numeric($params['max_width'])) { - $params['max_width'] = $params['target_width']; - } - - if (!is_numeric($params['max_height'])) { - $params['max_height'] = $params['target_height']; - } - - $src_path = dirname($src_image); - - if ($needs_resize || array_key_exists('wm_filename', $params) && $params['wm_filename']) { - // resize required OR watermarking required -> change resulting image name ! - $dst_image = preg_replace('/^'.preg_quote($src_path, '/').'(.*)\.(.*)$/', $src_path . DIRECTORY_SEPARATOR . 'resized\\1_' . crc32(serialize($params)) . '.\\2', $src_image); - if (!file_exists($dst_image) || filemtime($src_image) > filemtime($dst_image)) { - // resized image not available OR should be recreated due source image change - $params['dst_image'] = $dst_image; - $image_resized = $this->ScaleImage($src_image, $params); - if (!$image_resized) { - // resize failed, because of server error - $dst_image = $src_image; - } - } - - // resize/watermarking ok - $src_image = $dst_image; - } - } - - if ($image_size) { - // return only image size (resized or not) - $image_info = $this->getImageInfo($src_image); - return $image_info ? $image_info[3] : ''; - } - - $base_url = rtrim($this->Application->BaseURL(), '/'); - return str_replace(DIRECTORY_SEPARATOR, '/', preg_replace('/^'.preg_quote(FULL_PATH, '/').'(.*)/', $base_url.'\\1', $src_image)); - } - - /** - * Proportionally resizes given image to destination dimensions - * - * @param string $src_image full path to source image (already existing) - * @param string $dst_image full path to destination image (will be created) - * @param int $dst_width destination image width (in pixels) - * @param int $dst_height destination image height (in pixels) - */ - function ScaleImage($src_image, $params) - { - $image_info = $this->getImageInfo($src_image); - if (!$image_info) { - return false; - } - - /*list ($params['max_width'], $params['max_height'], $resized) = $this->GetImageDimensions($src_image, $params['max_width'], $params['max_height'], $params); - if (!$resized) { - // image dimensions are smaller or equals to required dimensions - return false; - }*/ - - if (!$this->Application->ConfigValue('ForceImageMagickResize') && function_exists('imagecreatefromjpeg')) { - // try to resize using GD - $resize_map = Array ( - 'image/jpeg' => 'imagecreatefromjpeg:imagejpeg:jpg', - 'image/gif' => 'imagecreatefromgif:imagegif:gif', - 'image/png' => 'imagecreatefrompng:imagepng:png', - 'image/bmp' => 'imagecreatefrombmp:imagejpeg:bmp', - ); - - $mime_type = $image_info['mime']; - if (!isset($resize_map[$mime_type])) { - return false; - } - - list ($read_function, $write_function, $file_extension) = explode(':', $resize_map[$mime_type]); - - $src_image_rs = @$read_function($src_image); - if ($src_image_rs) { - // when source image has large dimensions (over 1MB filesize), then 16M is not enough - set_time_limit(0); - ini_set('memory_limit', -1); - - $dst_image_rs = imagecreatetruecolor($params['target_width'], $params['target_height']); // resize target size - - if ($file_extension == 'png') { - // preserve transparency of PNG images - $transparent_color = imagecolorallocate($dst_image_rs, 0, 0, 0); - imagecolortransparent($dst_image_rs, $transparent_color); - } - - // 1. resize - imagecopyresampled($dst_image_rs, $src_image_rs, 0, 0, 0, 0, $params['target_width'], $params['target_height'], $image_info[0], $image_info[1]); - - $watermark_size = 'target'; - - if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) { - // 2.1. crop image to given size - $dst_image_rs =& $this->_cropImage($dst_image_rs, $params); - $watermark_size = 'max'; - } elseif (array_key_exists('fill', $params)) { - // 2.2. fill image margins from resize with given color - $dst_image_rs =& $this->_applyFill($dst_image_rs, $params); - $watermark_size = 'max'; - } - - // 3. apply watermark - $dst_image_rs =& $this->_applyWatermark($dst_image_rs, $params[$watermark_size . '_width'], $params[$watermark_size . '_height'], $params); - - return @$write_function($dst_image_rs, $params['dst_image'], 100); - } - } - else { - // try to resize using ImageMagick - // TODO: implement crop and watermarking using imagemagick - exec('/usr/bin/convert '.$src_image.' -resize '.$params['target_width'].'x'.$params['target_height'].' '.$params['dst_image'], $shell_output, $exec_status); - return $exec_status == 0; - } - - return false; - } - - /** - * Fills margins (if any) of resized are with given color - * - * @param resource $src_image_rs resized image resource - * @param Array $params crop parameters - * @return resource - */ - function &_applyFill(&$src_image_rs, $params) - { - $x_position = round(($params['max_width'] - $params['target_width']) / 2); // center - $y_position = round(($params['max_height'] - $params['target_height']) / 2); // center - - // crop resized image - $fill_image_rs = imagecreatetruecolor($params['max_width'], $params['max_height']); - - $fill = $params['fill']; - - if (substr($fill, 0, 1) == '#') { - // hexdecimal color - $color = imagecolorallocate($fill_image_rs, hexdec( substr($fill, 1, 2) ), hexdec( substr($fill, 3, 2) ), hexdec( substr($fill, 5, 2) )); - } - else { - // for now we don't support color names, but we will in future - return $src_image_rs; - } - - imagefill($fill_image_rs, 0, 0, $color); - imagecopy($fill_image_rs, $src_image_rs, $x_position, $y_position, 0, 0, $params['target_width'], $params['target_height']); - - return $fill_image_rs; - } - - /** - * Crop given image resource using given params and return resulting image resource - * - * @param resource $src_image_rs resized image resource - * @param Array $params crop parameters - * @return resource - */ - function &_cropImage(&$src_image_rs, $params) - { - if ($params['crop_x'] == 'c') { - $x_position = round(($params['max_width'] - $params['target_width']) / 2); // center - } - elseif ($params['crop_x'] >= 0) { - $x_position = $params['crop_x']; // margin from left - } - else { - $x_position = $params['target_width'] - ($params['max_width'] - $params['crop_x']); // margin from right - } - - if ($params['crop_y'] == 'c') { - $y_position = round(($params['max_height'] - $params['target_height']) / 2); // center - } - elseif ($params['crop_y'] >= 0) { - $y_position = $params['crop_y']; // margin from top - } - else { - $y_position = $params['target_height'] - ($params['max_height'] - $params['crop_y']); // margin from bottom - } - - // crop resized image - $crop_image_rs = imagecreatetruecolor($params['max_width'], $params['max_height']); - $crop_image_rs =& $this->_applyFill($crop_image_rs, $params); - - imagecopy($crop_image_rs, $src_image_rs, $x_position, $y_position, 0, 0, $params['target_width'], $params['target_height']); - - return $crop_image_rs; - } - - /** - * Apply watermark (transparent PNG image) to given resized image resource - * - * @param resource $src_image_rs - * @param int $max_width - * @param int $max_height - * @param Array $params - * @return resource - */ - function &_applyWatermark(&$src_image_rs, $max_width, $max_height, $params) - { - $watermark_file = array_key_exists('wm_filename', $params) ? $params['wm_filename'] : false; - - if (!$watermark_file || !file_exists($watermark_file)) { - // no watermark required, or provided watermark image is missing - return $src_image_rs; - } - - $watermark_img_rs = imagecreatefrompng($watermark_file); - list ($watermark_width, $watermark_height) = $this->getImageInfo($watermark_file); - - imagealphablending($src_image_rs, true); - - if ($params['h_margin'] == 'c') { - $x_position = round($max_width / 2 - $watermark_width / 2); // center - } - elseif ($params['h_margin'] >= 0) { - $x_position = $params['h_margin']; // margin from left - } - else { - $x_position = $max_width - ($watermark_width - $params['h_margin']); // margin from right - } - - if ($params['v_margin'] == 'c') { - $y_position = round($max_height / 2 - $watermark_height / 2); // center - } - elseif ($params['v_margin'] >= 0) { - $y_position = $params['v_margin']; // margin from top - } - else { - $y_position = $max_height - ($watermark_height - $params['v_margin']); // margin from bottom - } - - imagecopy($src_image_rs, $watermark_img_rs, $x_position, $y_position, 0, 0, $watermark_width, $watermark_height); - - return $src_image_rs; - } - - /** - * Returns destination image size without actual resizing (useful for HTML tag) - * - * @param string $src_image full path to source image (already existing) - * @param int $dst_width destination image width (in pixels) - * @param int $dst_height destination image height (in pixels) - * @param Array $params - * @return Array resized image dimensions (0 - width, 1 - height) - */ - function GetImageDimensions($src_image, $dst_width, $dst_height, $params) - { - $image_info = $this->getImageInfo($src_image); - if (!$image_info) { - return false; - } - - $orig_width = $image_info[0]; - $orig_height = $image_info[1]; - - $too_large = is_numeric($dst_width) ? ($orig_width > $dst_width) : false; - $too_large = $too_large || (is_numeric($dst_height) ? ($orig_height > $dst_height) : false); - - if ($too_large) { - $width_ratio = $dst_width ? $dst_width / $orig_width : 1; - $height_ratio = $dst_height ? $dst_height / $orig_height : 1; - - if (array_key_exists('crop_x', $params) || array_key_exists('crop_y', $params)) { - // resize by smallest inverted radio - $resize_by = $this->_getCropImageMinRatio($image_info, $dst_width, $dst_height); - $ratio = $resize_by == 'width' ? $width_ratio : $height_ratio; - } - else { - $ratio = min($width_ratio, $height_ratio); - } - - $width = ceil($orig_width * $ratio); - $height = ceil($orig_height * $ratio); - } - else { - $width = $orig_width; - $height = $orig_height; - } - - return Array ($width, $height, $too_large); - } - - /** - * Returns ratio type with smaller relation of original size to target size - * - * @param Array $image_info image information from "ImageHelper::getImageInfo" - * @param int $dst_width destination image width (in pixels) - * @param int $dst_height destination image height (in pixels) - * @return Array - */ - function _getCropImageMinRatio($image_info, $dst_width, $dst_height) - { - $width_ratio = $dst_width ? $image_info[0] / $dst_width : 1; - $height_ratio = $dst_height ? $image_info[1] / $dst_height : 1; - - return $width_ratio < $height_ratio ? 'width' : 'height'; - } - - /** - * Returns image dimensions + checks if given file is existing image - * - * @param string $src_image full path to source image (already existing) - * @return mixed - */ - function getImageInfo($src_image) - { - if (!file_exists($src_image)) { - return false; - } - - $image_info = @getimagesize($src_image); - if (!$image_info) { - trigger_error('Image '.$src_image.' missing or invalid', E_USER_WARNING); - return false; - } - - return $image_info; - } - - /** - * Returns maximal image size (width & height) among fields specified - * - * @param kDBItem $object - * @param string $fields - * @param string $format any format, that returns full url (e.g. files_resized:WxH, resize:WxH, full_url, full_urls) - * @return string - */ - function MaxImageSize(&$object, $fields, $format = null) - { - static $cached_sizes = Array (); - - $cache_key = $object->getPrefixSpecial().'_'.$object->GetID(); - if (!isset($cached_sizes[$cache_key])) { - $images = Array (); - - $fields = explode(',', $fields); - foreach ($fields as $field) { - $image_data = $object->GetField($field, $format); - if (!$image_data) { - continue; - } - - $images = array_merge($images, explode('|', $image_data)); - } - - $max_width = 0; - $max_height = 0; - $base_url = rtrim($this->Application->BaseURL(), '/'); - - foreach ($images as $image_url) { - $image_path = preg_replace('/^'.preg_quote($base_url, '/').'(.*)/', FULL_PATH.'\\1', $image_url); - $image_info = $this->getImageInfo($image_path); - $max_width = max($max_width, $image_info[0]); - $max_height = max($max_height, $image_info[1]); - } - - $cached_sizes[$cache_key] = Array ($max_width, $max_height); - } - - return $cached_sizes[$cache_key]; - } - - /** - * Puts existing item images (from subitem) to virtual fields (in main item) - * - * @param kCatDBItem $object - */ - function LoadItemImages(&$object) - { - if (!$this->Application->prefixRegistred($object->Prefix.'-img')) { - return ; - } - - $max_image_count = $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); - - $sql = 'SELECT * - FROM '.TABLE_PREFIX.'Images - WHERE ResourceId = '.$object->GetDBField('ResourceId').' - ORDER BY Priority DESC - LIMIT 0, ' . (int)$max_image_count; - $item_images = $this->Conn->Query($sql); - - $image_counter = 1; - foreach ($item_images as $item_image) { - $image_path = $item_image['ThumbPath']; - if ($item_image['DefaultImg'] == 1 || $item_image['Name'] == 'main') { - // process primary image separately - if (array_key_exists('PrimaryImage', $object->Fields)) { - $object->SetDBField('PrimaryImage', $image_path); - $object->SetOriginalField('PrimaryImage', $image_path); - $object->Fields['PrimaryImage']['original_field'] = $item_image['Name']; - - $this->_loadCustomFields($object, $item_image, 0); - } - continue; - } - - if (abs($item_image['Priority'])) { - // use Priority as image counter, when specified - $image_counter = abs($item_image['Priority']); - } - - if (array_key_exists('Image'.$image_counter, $object->Fields)) { - $object->SetDBField('Image'.$image_counter, $image_path); - $object->SetOriginalField('Image'.$image_counter, $image_path); - $object->Fields['Image'.$image_counter]['original_field'] = $item_image['Name']; - - $this->_loadCustomFields($object, $item_image, $image_counter); - } - $image_counter++; - } - } - - /** - * Saves newly uploaded images to external image table - * - * @param kCatDBItem $object - */ - function SaveItemImages(&$object) - { - if (!$this->Application->prefixRegistred($object->Prefix.'-img')) { - return ; - } - - $table_name = $this->Application->getUnitOption('img', 'TableName'); - $max_image_count = $this->Application->getUnitOption($object->Prefix, 'ImageCount'); // $this->Application->ConfigValue($object->Prefix.'_MaxImageCount'); - - $i = 0; - while ($i < $max_image_count) { - $field = $i ? 'Image'.$i : 'PrimaryImage'; - $field_options = $object->GetFieldOptions($field); - - $image_src = $object->GetDBField($field); - if ($image_src) { - if (isset($field_options['original_field'])) { - $key_clause = 'Name = '.$this->Conn->qstr($field_options['original_field']).' AND ResourceId = '.$object->GetDBField('ResourceId'); - - if ($object->GetDBField('Delete'.$field)) { - // if item was cloned, then new filename is in db (not in $image_src) - $sql = 'SELECT ThumbPath - FROM '.$table_name.' - WHERE '.$key_clause; - $image_src = $this->Conn->GetOne($sql); - if (@unlink(FULL_PATH.$image_src)) { - $sql = 'DELETE FROM '.$table_name.' - WHERE '.$key_clause; - $this->Conn->Query($sql); - } - } - else { - // image record found -> update - $fields_hash = Array ( - 'ThumbPath' => $image_src, - ); - $this->_saveCustomFields($object, $fields_hash, $i); - - $this->Conn->doUpdate($fields_hash, $table_name, $key_clause); - } - } - else { - // image record not found -> create - $fields_hash = Array ( - 'ResourceId' => $object->GetDBField('ResourceId'), - 'Name' => $field, - 'AltName' => $field, - 'Enabled' => STATUS_ACTIVE, - 'DefaultImg' => $i ? 0 : 1, // first image is primary, others not primary - 'ThumbPath' => $image_src, - ); - $this->_saveCustomFields($object, $fields_hash, $i); - - $this->Conn->doInsert($fields_hash, $table_name); - $field_options['original_field'] = $field; - $object->SetFieldOptions($field, $field_options); - } - } - $i++; - } - } - - /** - * Adds ability to load custom fields along with main image field - * - * @param kCatDBItem $object - * @param Array $fields_hash - * @param int $counter 0 - primary image, other number - additional image number - */ - function _loadCustomFields(&$object, $fields_hash, $counter) - { - $field_name = $counter ? 'Image' . $counter . 'Alt' : 'PrimaryImageAlt'; - - $object->SetDBField($field_name, (string)$fields_hash['AltName']); - } - - /** - * Adds ability to save custom field along with main image save - * - * @param kCatDBItem $object - * @param Array $fields_hash - * @param int $counter 0 - primary image, other number - additional image number - */ - function _saveCustomFields(&$object, &$fields_hash, $counter) - { - $field_name = $counter ? 'Image' . $counter . 'Alt' : 'PrimaryImageAlt'; - - $fields_hash['AltName'] = (string)$object->GetDBField($field_name); - } - } - -?> \ No newline at end of file