Mercurial > hg > isophonics-drupal-site
diff core/lib/Drupal/Component/Utility/Rectangle.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/lib/Drupal/Component/Utility/Rectangle.php Wed Nov 29 16:09:58 2017 +0000 @@ -0,0 +1,195 @@ +<?php + +namespace Drupal\Component\Utility; + +/** + * 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, or by image + * toolkit rotate operations. + * + * This class implements a calculation algorithm that returns, given input + * width, height and rotation angle, dimensions of the expected image after + * rotation that are consistent with those produced by the GD rotate image + * toolkit operation using PHP 5.5 and above. + * + * @see \Drupal\system\Plugin\ImageToolkit\Operation\gd\Rotate + */ +class Rectangle { + + /** + * The width of the rectangle. + * + * @var int + */ + protected $width; + + /** + * The height of the rectangle. + * + * @var int + */ + protected $height; + + /** + * The width of the rotated rectangle. + * + * @var int + */ + protected $boundingWidth; + + /** + * The height of the rotated rectangle. + * + * @var int + */ + protected $boundingHeight; + + /** + * Constructs a new Rectangle object. + * + * @param int $width + * The width of the rectangle. + * @param int $height + * The height of the rectangle. + */ + public function __construct($width, $height) { + if ($width > 0 && $height > 0) { + $this->width = $width; + $this->height = $height; + $this->boundingWidth = $width; + $this->boundingHeight = $height; + } + else { + throw new \InvalidArgumentException("Invalid dimensions ({$width}x{$height}) specified for a Rectangle object"); + } + } + + /** + * Rotates the rectangle. + * + * @param float $angle + * Rotation angle. + * + * @return $this + */ + public function rotate($angle) { + // PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148: To prevent buggy + // behavior on negative multiples of 30 degrees we convert any negative + // angle to a positive one between 0 and 360 degrees. + $angle -= floor($angle / 360) * 360; + + // For some rotations that are multiple of 30 degrees, we need to correct + // an imprecision between GD that uses C floats internally, and PHP that + // uses C doubles. Also, for rotations that are not multiple of 90 degrees, + // we need to introduce a correction factor of 0.5 to match the GD + // algorithm used in PHP 5.5 (and above) to calculate the width and height + // of the rotated image. + if ((int) $angle == $angle && $angle % 90 == 0) { + $imprecision = 0; + $correction = 0; + } + else { + $imprecision = -0.00001; + $correction = 0.5; + } + + // Do the trigonometry, applying imprecision fixes where needed. + $rad = deg2rad($angle); + $cos = cos($rad); + $sin = sin($rad); + $a = $this->width * $cos; + $b = $this->height * $sin + $correction; + $c = $this->width * $sin; + $d = $this->height * $cos + $correction; + if ((int) $angle == $angle && in_array($angle, [60, 150, 300])) { + $a = $this->fixImprecision($a, $imprecision); + $b = $this->fixImprecision($b, $imprecision); + $c = $this->fixImprecision($c, $imprecision); + $d = $this->fixImprecision($d, $imprecision); + } + + // This is how GD on PHP5.5 calculates the new dimensions. + $this->boundingWidth = abs((int) $a) + abs((int) $b); + $this->boundingHeight = abs((int) $c) + abs((int) $d); + + return $this; + } + + /** + * Performs an imprecision check on the input value and fixes it if needed. + * + * GD that uses C floats internally, whereas we at PHP level use C doubles. + * In some cases, we need to compensate imprecision. + * + * @param float $input + * The input value. + * @param float $imprecision + * The imprecision factor. + * + * @return float + * A value, where imprecision is added to input if the delta part of the + * input is lower than the absolute imprecision. + */ + protected function fixImprecision($input, $imprecision) { + if ($this->delta($input) < abs($imprecision)) { + return $input + $imprecision; + } + return $input; + } + + /** + * Returns the fractional part of a float number, unsigned. + * + * @param float $input + * The input value. + * + * @return float + * The fractional part of the input number, unsigned. + */ + protected function fraction($input) { + return abs((int) $input - $input); + } + + /** + * Returns the difference of a fraction from the closest between 0 and 1. + * + * @param float $input + * The input value. + * + * @return float + * the difference of a fraction from the closest between 0 and 1. + */ + protected function delta($input) { + $fraction = $this->fraction($input); + return $fraction > 0.5 ? (1 - $fraction) : $fraction; + } + + /** + * Gets the bounding width of the rectangle. + * + * @return int + * The bounding width of the rotated rectangle. + */ + public function getBoundingWidth() { + return $this->boundingWidth; + } + + /** + * Gets the bounding height of the rectangle. + * + * @return int + * The bounding height of the rotated rectangle. + */ + public function getBoundingHeight() { + return $this->boundingHeight; + } + +}