comparison 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
comparison
equal deleted inserted replaced
-1:000000000000 0:4c8ae668cc8c
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 }