Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F803832
D368.id1177.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Wed, Feb 26, 8:59 AM
Size
7 KB
Mime Type
text/x-diff
Expires
Thu, Feb 27, 8:59 AM (7 h, 43 m)
Engine
blob
Format
Raw Data
Handle
576556
Attached To
D368: INP-1668 - Allow specifying image quality, orientation and output format during resizing
D368.id1177.diff
View Options
Index: branches/5.2.x/core/units/helpers/image_helper.php
===================================================================
--- branches/5.2.x/core/units/helpers/image_helper.php
+++ branches/5.2.x/core/units/helpers/image_helper.php
@@ -35,11 +35,15 @@
* Parses format string into array
*
* @param string $format sample format: "resize:300x500;wm:inc/wm.png|c|-20"
- * @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20)
+ * @return Array sample result: Array('max_width' => 300, 'max_height' => 500, 'wm_filename' => 'inc/wm.png', 'h_margin' => 'c', 'v_margin' => -20, 'quality' => 100, output_format => 'auto', orientation => 'manual')
+ *
+ * @throws InvalidArgumentException When requested quality is out of 0..100 range.
+ * @throws InvalidArgumentException When requested output_format is out of [jpg,png,gif,bmp,auto] range.
+ * @throws InvalidArgumentException When requested orientation is out of [auto,manual,portrait,landscape] range.
*/
function parseFormat($format)
{
- $res = Array ();
+ $res = array('quality' => 100, 'output_format' => 'auto', 'orientation' => 'manual');
$format_parts = explode(';', $format);
foreach ($format_parts as $format_part) {
@@ -47,6 +51,35 @@
$res['max_width'] = $regs[1];
$res['max_height'] = $regs[2];
}
+ elseif ( preg_match('/^quality:(.*)$/', $format_part, $regs) ) {
+ $quality = (int)$regs[1];
+
+ if ( $quality > 100 || $quality < 0 || (string)$quality !== $regs[1] ) {
+ throw new InvalidArgumentException(
+ 'Quality value "' . $regs[1] . '" is out of 0..100 integer range.'
+ );
+ }
+
+ $res['quality'] = $quality;
+ }
+ elseif ( preg_match('/^output_format:(.*)$/', $format_part, $regs) ) {
+ if ( !in_array($regs[1], array('jpg', 'png', 'gif', 'bmp', 'auto')) ) {
+ throw new InvalidArgumentException(
+ 'Output format value "' . $regs[1] . '" is out of [jpg,png,gif,bmp,auto] range.'
+ );
+ }
+
+ $res['output_format'] = $regs[1];
+ }
+ elseif ( preg_match('/^orientation:(.*)$/', $format_part, $regs) ) {
+ if ( !in_array($regs[1], array('auto', 'manual', 'portrait', 'landscape')) ) {
+ throw new InvalidArgumentException(
+ 'Orientation value "' . $regs[1] . '" is out of [auto,manual,portrait,landscape] range.'
+ );
+ }
+
+ $res['orientation'] = $regs[1];
+ }
elseif (preg_match('/^wm:([^\|]*)\|([^\|]*)\|([^\|]*)$/', $format_part, $regs)) {
$res['wm_filename'] = FULL_PATH.THEMES_PATH.'/'.$regs[1];
$res['h_margin'] = strtolower($regs[2]);
@@ -113,6 +146,16 @@
}
if ( !$this->isSVG($src_image) && ($params['max_width'] > 0 || $params['max_height'] > 0) ) {
+ if ( $this->shouldRotateDimensions($src_image, $params['max_width'], $params['max_height'], $params) ) {
+ list ($params['max_width'], $params['max_height']) = array(
+ $params['max_height'], $params['max_width'],
+ );
+
+ if ( isset($params['crop_x']) && isset($params['crop_y']) ) {
+ list ($params['crop_x'], $params['crop_y']) = array($params['crop_y'], $params['crop_x']);
+ }
+ }
+
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'])) {
@@ -136,13 +179,17 @@
$transform_keys = Array ('crop_x', 'crop_y', 'fill', 'wm_filename');
// Resize required OR watermarking required -> change resulting image name !
- if ( $needs_resize || array_intersect(array_keys($params), $transform_keys) ) {
+ if ( $needs_resize
+ || array_intersect(array_keys($params), $transform_keys)
+ || $this->shouldChangeOutputFormat($src_image, $params['output_format'])
+ ) {
// Escape replacement patterns, like "\<number>".
$src_path_escaped = preg_replace('/(\\\[\d]+)/', '\\\\\1', $src_path);
$params_hash = kUtil::crc32(serialize($this->fileHelper->makeRelative($params)));
+ $file_extension = $params['output_format'] === 'auto' ? '\\2' : $params['output_format'];
$dst_image = preg_replace(
'/^' . preg_quote($src_path, '/') . '(.*)\.(.*)$/',
- $src_path_escaped . '\\1_' . $params_hash . '.\\2',
+ $src_path_escaped . '\\1_' . $params_hash . '.' . $file_extension,
$src_image
);
@@ -217,6 +264,18 @@
list ($read_function, $write_function, $file_extension) = explode(':', $resize_map[$mime_type]);
+ $output_format_map = array(
+ 'jpg' => 'imagejpeg:jpg',
+ 'png' => 'imagepng:png',
+ 'gif' => 'imagegif:gif',
+ 'bmp' => 'imagejpeg:bmp',
+ );
+ $output_format = $params['output_format'];
+
+ if ( isset($output_format_map[$output_format]) ) {
+ list ($write_function, $file_extension) = explode(':', $output_format_map[$output_format]);
+ }
+
// when source image has large dimensions (over 1MB filesize), then 16M is not enough
kUtil::setResourceLimit();
@@ -253,7 +312,11 @@
return @$write_function($dst_image_rs, $params['dst_image']);
}
- return @$write_function($dst_image_rs, $params['dst_image'], $write_function == 'imagepng' ? 0 : 100);
+ if ( $write_function == 'imagepng' ) {
+ $params['quality'] = $this->convertQualityToCompression($params['quality']);
+ }
+
+ return @$write_function($dst_image_rs, $params['dst_image'], $params['quality']);
}
}
else {
@@ -267,6 +330,18 @@
}
/**
+ * Converts quality to compression
+ *
+ * @param integer $quality Quality.
+ *
+ * @return integer
+ */
+ protected function convertQualityToCompression($quality)
+ {
+ return round((100 - $quality) / 10);
+ }
+
+ /**
* Preserve transparency for GIF and PNG images
*
* @param resource $src_image_rs
@@ -557,6 +632,76 @@
}
/**
+ * Determines when dimensions must be rotated
+ *
+ * @param string $src_image Source image path.
+ * @param integer $dst_width Destination width.
+ * @param integer $dst_height Destination height.
+ * @param array $params Parameters.
+ *
+ * @return boolean
+ * @throws InvalidArgumentException When orientation is "auto", but some of $dst_width/$dst_height is empty.
+ */
+ protected function shouldRotateDimensions($src_image, $dst_width, $dst_height, array $params)
+ {
+ $orientation = $params['orientation'];
+
+ if ( $orientation === 'manual' ) {
+ return false;
+ }
+
+ if ( $orientation === 'auto' ) {
+ if ( $dst_width === '' || $dst_height === '' ) {
+ throw new InvalidArgumentException('Both width & height parameters must be specified.');
+ }
+
+ $resized_orientation = $dst_width > $dst_height ? 'landscape' : 'portrait';
+ }
+ else {
+ $resized_orientation = $orientation;
+ }
+
+ list ($src_width, $src_height) = $this->getImageInfo($src_image);
+
+ $src_image_orientation = $src_width > $src_height ? 'landscape' : 'portrait';
+
+ return $src_image_orientation != $resized_orientation;
+ }
+
+ /**
+ * Determines when output format should be changed.
+ *
+ * @param string $src_image Source image path.
+ * @param string $output_format Output format.
+ *
+ * @return boolean
+ */
+ protected function shouldChangeOutputFormat($src_image, $output_format)
+ {
+ if ( $output_format === 'auto' ) {
+ return false;
+ }
+
+ $image_info = $this->getImageInfo($src_image);
+
+ if ( !$image_info ) {
+ return false;
+ }
+
+ $resize_map = array(
+ 'image/jpeg' => 'jpg',
+ 'image/gif' => 'gif',
+ 'image/png' => 'png',
+ 'image/bmp' => 'bmp',
+ 'image/x-ms-bmp' => 'bmp',
+ );
+
+ $current_mime = $image_info['mime'];
+
+ return $resize_map[$current_mime] !== $output_format;
+ }
+
+ /**
* Returns maximal image size (width & height) among fields specified
*
* @param kDBItem $object
Event Timeline
Log In to Comment