annotate core/lib/Drupal/Component/Utility/Rectangle.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Component\Utility;
Chris@0 4
Chris@0 5 /**
Chris@0 6 * Rectangle rotation algebra class.
Chris@0 7 *
Chris@0 8 * This class is used by the image system to abstract, from toolkit
Chris@0 9 * implementations, the calculation of the expected dimensions resulting from
Chris@0 10 * an image rotate operation.
Chris@0 11 *
Chris@0 12 * Different versions of PHP for the GD toolkit, and alternative toolkits, use
Chris@0 13 * different algorithms to perform the rotation of an image and result in
Chris@0 14 * different dimensions of the output image. This prevents predictability of
Chris@0 15 * the final image size for instance by the image rotate effect, or by image
Chris@0 16 * toolkit rotate operations.
Chris@0 17 *
Chris@0 18 * This class implements a calculation algorithm that returns, given input
Chris@0 19 * width, height and rotation angle, dimensions of the expected image after
Chris@0 20 * rotation that are consistent with those produced by the GD rotate image
Chris@0 21 * toolkit operation using PHP 5.5 and above.
Chris@0 22 *
Chris@0 23 * @see \Drupal\system\Plugin\ImageToolkit\Operation\gd\Rotate
Chris@0 24 */
Chris@0 25 class Rectangle {
Chris@0 26
Chris@0 27 /**
Chris@0 28 * The width of the rectangle.
Chris@0 29 *
Chris@0 30 * @var int
Chris@0 31 */
Chris@0 32 protected $width;
Chris@0 33
Chris@0 34 /**
Chris@0 35 * The height of the rectangle.
Chris@0 36 *
Chris@0 37 * @var int
Chris@0 38 */
Chris@0 39 protected $height;
Chris@0 40
Chris@0 41 /**
Chris@0 42 * The width of the rotated rectangle.
Chris@0 43 *
Chris@0 44 * @var int
Chris@0 45 */
Chris@0 46 protected $boundingWidth;
Chris@0 47
Chris@0 48 /**
Chris@0 49 * The height of the rotated rectangle.
Chris@0 50 *
Chris@0 51 * @var int
Chris@0 52 */
Chris@0 53 protected $boundingHeight;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * Constructs a new Rectangle object.
Chris@0 57 *
Chris@0 58 * @param int $width
Chris@0 59 * The width of the rectangle.
Chris@0 60 * @param int $height
Chris@0 61 * The height of the rectangle.
Chris@0 62 */
Chris@0 63 public function __construct($width, $height) {
Chris@0 64 if ($width > 0 && $height > 0) {
Chris@0 65 $this->width = $width;
Chris@0 66 $this->height = $height;
Chris@0 67 $this->boundingWidth = $width;
Chris@0 68 $this->boundingHeight = $height;
Chris@0 69 }
Chris@0 70 else {
Chris@0 71 throw new \InvalidArgumentException("Invalid dimensions ({$width}x{$height}) specified for a Rectangle object");
Chris@0 72 }
Chris@0 73 }
Chris@0 74
Chris@0 75 /**
Chris@0 76 * Rotates the rectangle.
Chris@0 77 *
Chris@0 78 * @param float $angle
Chris@0 79 * Rotation angle.
Chris@0 80 *
Chris@0 81 * @return $this
Chris@0 82 */
Chris@0 83 public function rotate($angle) {
Chris@0 84 // PHP 5.5 GD bug: https://bugs.php.net/bug.php?id=65148: To prevent buggy
Chris@0 85 // behavior on negative multiples of 30 degrees we convert any negative
Chris@0 86 // angle to a positive one between 0 and 360 degrees.
Chris@0 87 $angle -= floor($angle / 360) * 360;
Chris@0 88
Chris@0 89 // For some rotations that are multiple of 30 degrees, we need to correct
Chris@0 90 // an imprecision between GD that uses C floats internally, and PHP that
Chris@0 91 // uses C doubles. Also, for rotations that are not multiple of 90 degrees,
Chris@0 92 // we need to introduce a correction factor of 0.5 to match the GD
Chris@0 93 // algorithm used in PHP 5.5 (and above) to calculate the width and height
Chris@0 94 // of the rotated image.
Chris@0 95 if ((int) $angle == $angle && $angle % 90 == 0) {
Chris@0 96 $imprecision = 0;
Chris@0 97 $correction = 0;
Chris@0 98 }
Chris@0 99 else {
Chris@0 100 $imprecision = -0.00001;
Chris@0 101 $correction = 0.5;
Chris@0 102 }
Chris@0 103
Chris@0 104 // Do the trigonometry, applying imprecision fixes where needed.
Chris@0 105 $rad = deg2rad($angle);
Chris@0 106 $cos = cos($rad);
Chris@0 107 $sin = sin($rad);
Chris@0 108 $a = $this->width * $cos;
Chris@0 109 $b = $this->height * $sin + $correction;
Chris@0 110 $c = $this->width * $sin;
Chris@0 111 $d = $this->height * $cos + $correction;
Chris@0 112 if ((int) $angle == $angle && in_array($angle, [60, 150, 300])) {
Chris@0 113 $a = $this->fixImprecision($a, $imprecision);
Chris@0 114 $b = $this->fixImprecision($b, $imprecision);
Chris@0 115 $c = $this->fixImprecision($c, $imprecision);
Chris@0 116 $d = $this->fixImprecision($d, $imprecision);
Chris@0 117 }
Chris@0 118
Chris@0 119 // This is how GD on PHP5.5 calculates the new dimensions.
Chris@0 120 $this->boundingWidth = abs((int) $a) + abs((int) $b);
Chris@0 121 $this->boundingHeight = abs((int) $c) + abs((int) $d);
Chris@0 122
Chris@0 123 return $this;
Chris@0 124 }
Chris@0 125
Chris@0 126 /**
Chris@0 127 * Performs an imprecision check on the input value and fixes it if needed.
Chris@0 128 *
Chris@0 129 * GD that uses C floats internally, whereas we at PHP level use C doubles.
Chris@0 130 * In some cases, we need to compensate imprecision.
Chris@0 131 *
Chris@0 132 * @param float $input
Chris@0 133 * The input value.
Chris@0 134 * @param float $imprecision
Chris@0 135 * The imprecision factor.
Chris@0 136 *
Chris@0 137 * @return float
Chris@0 138 * A value, where imprecision is added to input if the delta part of the
Chris@0 139 * input is lower than the absolute imprecision.
Chris@0 140 */
Chris@0 141 protected function fixImprecision($input, $imprecision) {
Chris@0 142 if ($this->delta($input) < abs($imprecision)) {
Chris@0 143 return $input + $imprecision;
Chris@0 144 }
Chris@0 145 return $input;
Chris@0 146 }
Chris@0 147
Chris@0 148 /**
Chris@0 149 * Returns the fractional part of a float number, unsigned.
Chris@0 150 *
Chris@0 151 * @param float $input
Chris@0 152 * The input value.
Chris@0 153 *
Chris@0 154 * @return float
Chris@0 155 * The fractional part of the input number, unsigned.
Chris@0 156 */
Chris@0 157 protected function fraction($input) {
Chris@0 158 return abs((int) $input - $input);
Chris@0 159 }
Chris@0 160
Chris@0 161 /**
Chris@0 162 * Returns the difference of a fraction from the closest between 0 and 1.
Chris@0 163 *
Chris@0 164 * @param float $input
Chris@0 165 * The input value.
Chris@0 166 *
Chris@0 167 * @return float
Chris@0 168 * the difference of a fraction from the closest between 0 and 1.
Chris@0 169 */
Chris@0 170 protected function delta($input) {
Chris@0 171 $fraction = $this->fraction($input);
Chris@0 172 return $fraction > 0.5 ? (1 - $fraction) : $fraction;
Chris@0 173 }
Chris@0 174
Chris@0 175 /**
Chris@0 176 * Gets the bounding width of the rectangle.
Chris@0 177 *
Chris@0 178 * @return int
Chris@0 179 * The bounding width of the rotated rectangle.
Chris@0 180 */
Chris@0 181 public function getBoundingWidth() {
Chris@0 182 return $this->boundingWidth;
Chris@0 183 }
Chris@0 184
Chris@0 185 /**
Chris@0 186 * Gets the bounding height of the rectangle.
Chris@0 187 *
Chris@0 188 * @return int
Chris@0 189 * The bounding height of the rotated rectangle.
Chris@0 190 */
Chris@0 191 public function getBoundingHeight() {
Chris@0 192 return $this->boundingHeight;
Chris@0 193 }
Chris@0 194
Chris@0 195 }