diff --git a/src/Plugin/ImageEffect/AutoOrientImageEffect.php b/src/Plugin/ImageEffect/AutoOrientImageEffect.php index 0b48ec7..76b4855 100644 --- a/src/Plugin/ImageEffect/AutoOrientImageEffect.php +++ b/src/Plugin/ImageEffect/AutoOrientImageEffect.php @@ -28,4 +28,11 @@ class AutoOrientImageEffect extends ImageEffectBase { return $image->apply('auto_orient'); } + /** + * {@inheritdoc} + */ + public function transformDimensions(array &$dimensions) { + $dimensions['width'] = $dimensions['height'] = NULL; + } + } diff --git a/src/Plugin/ImageEffect/StripMetadataImageEffect.php b/src/Plugin/ImageEffect/StripMetadataImageEffect.php index 2311750..5ec3e7d 100644 --- a/src/Plugin/ImageEffect/StripMetadataImageEffect.php +++ b/src/Plugin/ImageEffect/StripMetadataImageEffect.php @@ -28,10 +28,4 @@ class StripMetadataImageEffect extends ImageEffectBase { return $image->apply('strip'); } - /** - * {@inheritdoc} - */ - public function transformDimensions(array &$dimensions) { - } - } diff --git a/src/Plugin/ImageToolkit/ImagemagickToolkit.php b/src/Plugin/ImageToolkit/ImagemagickToolkit.php index 717a041..178172f 100644 --- a/src/Plugin/ImageToolkit/ImagemagickToolkit.php +++ b/src/Plugin/ImageToolkit/ImagemagickToolkit.php @@ -30,6 +30,7 @@ class ImagemagickToolkit extends ImageToolkitBase { /** * The MIME type guessing service. + * @todo change if extension mapping service gets in, see #2311679 * * @var \Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface */ @@ -136,7 +137,7 @@ class ImagemagickToolkit extends ImageToolkitBase { $container->get('image.toolkit.operation.manager'), $container->get('logger.channel.image'), $container->get('config.factory'), - $container->get('file.mime_type.guesser'), + $container->get('file.mime_type.guesser.extension'), $container->get('module_handler') ); } diff --git a/src/Rectangle.php b/src/Rectangle.php index b92b6f8..e326428 100644 --- a/src/Rectangle.php +++ b/src/Rectangle.php @@ -8,7 +8,21 @@ namespace Drupal\imagemagick; /** - * Rectangle algebra class. + * Rectangle rotation algebra class. + * + * This class is used by the image system to abstract, from toolkit + * implementations, the calculation of the expected dimensions resulting + * from an image rotate operation. + * + * Different versions of PHP for the GD toolkit, and alternative toolkits, + * use different algorithms to perform the rotation of an image and result + * in different dimensions of the output image. + * This prevents predictability of the final image size for instance by the + * image rotate effect, and by the image toolkit rotate operation. + * + * Here we are using an algorithm that produces the same results as PHP 5.4. + * + * @todo drop this if the class gets in Drupal core, see #1551686-23 */ class Rectangle { @@ -24,178 +38,68 @@ class Rectangle { * is rotated * 'o_c' - top right corner of the bounding rectangle, once the rectangle * is rotated - * Additional points can be added through the setPoint() method. These will - * be subject to translation/rotation with the rest of the points when - * getTranslatedRectangle() method is executed. * - * @var array - */ - protected $points = []; - - /** - * The width of the rectangle. - * - * The width is not influenced by rotation/translation. - * - * @var int - */ - protected $width = 0; - - /** - * The height of the rectangle. + * c_d +-----------------+ c_c + * | | + * | | + * c_a +-----------------+ c_b * - * The height is not influenced by rotation/translation. * - * @var int - */ - protected $height = 0; - - /** - * The angle at which the rectangle has been rotated. - * - * @var float - */ - protected $angle = 0; - - /** - * The offset needed to reposition the rectangle fully into first quadrant. - * - * Rotating a rectangle which is sticking to axes in the first quadrant - * results in some of its corners to shift to other quadrants. The x/y - * offset required to reposition it fully in the first quadrant is stored - * here. + * +-----------------+--+ o_c + * | c_c | + * + c_d | + * | c_b + + * | c_a | + * o_a +--+-----------------+ * * @var array */ - protected $rotationOffset = [0, 0]; + protected $points = []; /** * Constructs a new Rectangle object. * * @param int $width - * (Optional) The width of the rectangle. - * @param int $height - * (Optional) The height of the rectangle. - */ - public function __construct($width = 0, $height = 0) { - if ($width !== 0 && $height !== 0) { - $this->setFromDimensions($width, $height); - } - } - - /** - * Sets a rectangle from its width and height. - * - * @param int $width * The width of the rectangle. * @param int $height * The height of the rectangle. - * - * @return $this */ - public function setFromDimensions($width, $height) { - $this->setFromCorners([ - 'c_a' => [0, 0], - 'c_b' => [$width - 1, 0], - 'c_c' => [$width - 1, $height - 1], - 'c_d' => [0, $height - 1], - ]); - return $this; - } - - /** - * Sets a rectangle from the coordinates of its corners. - * - * @param array $corners - * An associative array of point coordinates. The keys 'c_a', 'c_b', - * 'c_c' and 'c_d' represent each of the four a, b, c, d corners of the - * rectangle in the format - * D +-----------------+ C - * | | - * | | - * A +-----------------+ B - * - * @return $this - */ - public function setFromCorners(array $corners) { - $this - ->setPoint('c_a', $corners['c_a']) - ->setPoint('c_b', $corners['c_b']) - ->setPoint('c_c', $corners['c_c']) - ->setPoint('c_d', $corners['c_d']) - ->determineBoundingCorners(); - $this->width = $this->getBoundingWidth(); - $this->height = $this->getBoundingHeight(); - return $this; + public function __construct($width, $height) { + if ($width !== 0 && $height !== 0) { + $this + ->setPoint('c_a', [0, 0]) + ->setPoint('c_b', [$width, 0]) + ->setPoint('c_c', [$width, $height]) + ->setPoint('c_d', [0, $height]) + ->setPoint('o_a', [0, 0]) + ->setPoint('o_c', [$width, $height]); + } } /** - * Sets a point and its coordinates. - * - * @param string $id - * The point ID. - * @param array $coords - * An array of x, y coordinates. + * Rotates the rectangle and any additional point. * - * @return $this + * @param float $angle + * Rotation angle. */ - public function setPoint($id, array $coords = [0, 0]) { - $this->points[$id] = $coords; + public function rotate($angle) { + if ($angle) { + foreach ($this->points as &$point) { + $this->rotatePoint($point, $angle); + } + $this->determineBoundingCorners(); + } return $this; } /** - * Gets the coordinates of a point. - * - * @param string $id - * The point ID. - * - * @return array - * An array of x, y coordinates. - */ - public function getPoint($id) { - return $this->points[$id]; - } - - /** - * Gets the width of the rectangle. - * - * @return int - * The width of the rectangle. - */ - public function getWidth() { - return $this->width; - } - - /** - * Gets the height of the rectangle. - * - * @return int - * The height of the rectangle. - */ - public function getHeight() { - return $this->height; - } - - /** - * Gets the rotation offset of the rectangle. - * - * @return array - * The x/y offset required to reposition the rectangle fully in the first - * quadrant after it has been rotated. - */ - public function getRotationOffset() { - return $this->rotationOffset; - } - - /** * Gets the bounding width of the rectangle. * * @return int * The bounding width of the rotated rectangle. */ public function getBoundingWidth() { - return $this->points['o_c'][0] - $this->points['o_a'][0] + 1; + return (int) ceil($this->points['o_c'][0] - $this->points['o_a'][0] - 0.000001); } /** @@ -205,22 +109,21 @@ class Rectangle { * The bounding height of the rotated rectangle. */ public function getBoundingHeight() { - return $this->points['o_c'][1] - $this->points['o_a'][1] + 1; + return (int) ceil($this->points['o_c'][1] - $this->points['o_a'][1] - 0.000001); } /** - * Translates a point by an offset. + * Sets a point and its coordinates. * - * @param array $point + * @param string $id + * The point ID. + * @param array $coords * An array of x, y coordinates. - * @param array $offset - * Offset array (x, y). * * @return $this */ - protected function translatePoint(array &$point, array $offset) { - $point[0] += $offset[0]; - $point[1] += $offset[1]; + protected function setPoint($id, array $coords = [0, 0]) { + $this->points[$id] = $coords; return $this; } @@ -239,45 +142,8 @@ class Rectangle { $sin = sin($rad); $cos = cos($rad); list($x, $y) = $point; - $tx = round(($x * $cos + $y * -$sin), 3); - $ty = round(($y * $cos - $x * -$sin), 3); - $point[0] = ($tx >= 0) ? ceil($tx) : -ceil(-$tx); - $point[1] = ($ty >= 0) ? ceil($ty) : -ceil(-$ty); - return $this; - } - - /** - * Rotates the rectangle and any additional point. - * - * @param float $angle - * Rotation angle. - */ - public function rotate($angle) { - if ($angle) { - $this->angle = $angle; - foreach ($this->points as &$point) { - $this->rotatePoint($point, $angle); - } - $this->determineBoundingCorners(); - $this->rotationOffset = [-$this->points['o_a'][0], -$this->points['o_a'][1]]; - } - return $this; - } - - /** - * Translates the rectangle and any additional point. - * - * @param array $offset - * Offset array (x, y). - * - * @return $this - */ - public function translate(array $offset) { - if ($offset[0] || $offset[1]) { - foreach ($this->points as &$point) { - $this->translatePoint($point, $offset); - } - } + $point[0] = $x * $cos + $y * -$sin; + $point[1] = $y * $cos - $x * -$sin; return $this; } @@ -286,7 +152,7 @@ class Rectangle { * * The bottom left ('o_a') and top right ('o_c') corners of the bounding * rectangle of a rotated rectangle are needed to determine the bounding - * width and height, and to calculate rotation-induced offest. + * width and height. * * @return $this */