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