Size.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <?php
  2. namespace Intervention\Image;
  3. use Closure;
  4. use Intervention\Image\Exception\InvalidArgumentException;
  5. class Size
  6. {
  7. /**
  8. * Width
  9. *
  10. * @var int
  11. */
  12. public $width;
  13. /**
  14. * Height
  15. *
  16. * @var int
  17. */
  18. public $height;
  19. /**
  20. * Pivot point
  21. *
  22. * @var Point
  23. */
  24. public $pivot;
  25. /**
  26. * Creates a new Size instance
  27. *
  28. * @param int $width
  29. * @param int $height
  30. * @param Point $pivot
  31. */
  32. public function __construct($width = null, $height = null, Point $pivot = null)
  33. {
  34. $this->width = is_numeric($width) ? intval($width) : 1;
  35. $this->height = is_numeric($height) ? intval($height) : 1;
  36. $this->pivot = $pivot ? $pivot : new Point;
  37. }
  38. /**
  39. * Set the width and height absolutely
  40. *
  41. * @param int $width
  42. * @param int $height
  43. */
  44. public function set($width, $height)
  45. {
  46. $this->width = $width;
  47. $this->height = $height;
  48. }
  49. /**
  50. * Set current pivot point
  51. *
  52. * @param Point $point
  53. */
  54. public function setPivot(Point $point)
  55. {
  56. $this->pivot = $point;
  57. }
  58. /**
  59. * Get the current width
  60. *
  61. * @return int
  62. */
  63. public function getWidth()
  64. {
  65. return $this->width;
  66. }
  67. /**
  68. * Get the current height
  69. *
  70. * @return int
  71. */
  72. public function getHeight()
  73. {
  74. return $this->height;
  75. }
  76. /**
  77. * Calculate the current aspect ratio
  78. *
  79. * @return float
  80. */
  81. public function getRatio()
  82. {
  83. return $this->width / $this->height;
  84. }
  85. /**
  86. * Resize to desired width and/or height
  87. *
  88. * @param int $width
  89. * @param int $height
  90. * @param Closure $callback
  91. * @return Size
  92. */
  93. public function resize($width, $height, Closure $callback = null)
  94. {
  95. if (is_null($width) && is_null($height)) {
  96. throw new InvalidArgumentException(
  97. "Width or height needs to be defined."
  98. );
  99. }
  100. // new size with dominant width
  101. $dominant_w_size = clone $this;
  102. $dominant_w_size->resizeHeight($height, $callback);
  103. $dominant_w_size->resizeWidth($width, $callback);
  104. // new size with dominant height
  105. $dominant_h_size = clone $this;
  106. $dominant_h_size->resizeWidth($width, $callback);
  107. $dominant_h_size->resizeHeight($height, $callback);
  108. // decide which size to use
  109. if ($dominant_h_size->fitsInto(new self($width, $height))) {
  110. $this->set($dominant_h_size->width, $dominant_h_size->height);
  111. } else {
  112. $this->set($dominant_w_size->width, $dominant_w_size->height);
  113. }
  114. return $this;
  115. }
  116. /**
  117. * Scale size according to given constraints
  118. *
  119. * @param int $width
  120. * @param Closure $callback
  121. * @return Size
  122. */
  123. private function resizeWidth($width, Closure $callback = null)
  124. {
  125. $constraint = $this->getConstraint($callback);
  126. if ($constraint->isFixed(Constraint::UPSIZE)) {
  127. $max_width = $constraint->getSize()->getWidth();
  128. $max_height = $constraint->getSize()->getHeight();
  129. }
  130. if (is_numeric($width)) {
  131. if ($constraint->isFixed(Constraint::UPSIZE)) {
  132. $this->width = ($width > $max_width) ? $max_width : $width;
  133. } else {
  134. $this->width = $width;
  135. }
  136. if ($constraint->isFixed(Constraint::ASPECTRATIO)) {
  137. $h = max(1, intval(round($this->width / $constraint->getSize()->getRatio())));
  138. if ($constraint->isFixed(Constraint::UPSIZE)) {
  139. $this->height = ($h > $max_height) ? $max_height : $h;
  140. } else {
  141. $this->height = $h;
  142. }
  143. }
  144. }
  145. }
  146. /**
  147. * Scale size according to given constraints
  148. *
  149. * @param int $height
  150. * @param Closure $callback
  151. * @return Size
  152. */
  153. private function resizeHeight($height, Closure $callback = null)
  154. {
  155. $constraint = $this->getConstraint($callback);
  156. if ($constraint->isFixed(Constraint::UPSIZE)) {
  157. $max_width = $constraint->getSize()->getWidth();
  158. $max_height = $constraint->getSize()->getHeight();
  159. }
  160. if (is_numeric($height)) {
  161. if ($constraint->isFixed(Constraint::UPSIZE)) {
  162. $this->height = ($height > $max_height) ? $max_height : $height;
  163. } else {
  164. $this->height = $height;
  165. }
  166. if ($constraint->isFixed(Constraint::ASPECTRATIO)) {
  167. $w = max(1, intval(round($this->height * $constraint->getSize()->getRatio())));
  168. if ($constraint->isFixed(Constraint::UPSIZE)) {
  169. $this->width = ($w > $max_width) ? $max_width : $w;
  170. } else {
  171. $this->width = $w;
  172. }
  173. }
  174. }
  175. }
  176. /**
  177. * Calculate the relative position to another Size
  178. * based on the pivot point settings of both sizes.
  179. *
  180. * @param Size $size
  181. * @return \Intervention\Image\Point
  182. */
  183. public function relativePosition(Size $size)
  184. {
  185. $x = $this->pivot->x - $size->pivot->x;
  186. $y = $this->pivot->y - $size->pivot->y;
  187. return new Point($x, $y);
  188. }
  189. /**
  190. * Resize given Size to best fitting size of current size.
  191. *
  192. * @param Size $size
  193. * @return \Intervention\Image\Size
  194. */
  195. public function fit(Size $size, $position = 'center')
  196. {
  197. // create size with auto height
  198. $auto_height = clone $size;
  199. $auto_height->resize($this->width, null, function ($constraint) {
  200. $constraint->aspectRatio();
  201. });
  202. // decide which version to use
  203. if ($auto_height->fitsInto($this)) {
  204. $size = $auto_height;
  205. } else {
  206. // create size with auto width
  207. $auto_width = clone $size;
  208. $auto_width->resize(null, $this->height, function ($constraint) {
  209. $constraint->aspectRatio();
  210. });
  211. $size = $auto_width;
  212. }
  213. $this->align($position);
  214. $size->align($position);
  215. $size->setPivot($this->relativePosition($size));
  216. return $size;
  217. }
  218. /**
  219. * Checks if given size fits into current size
  220. *
  221. * @param Size $size
  222. * @return boolean
  223. */
  224. public function fitsInto(Size $size)
  225. {
  226. return ($this->width <= $size->width) && ($this->height <= $size->height);
  227. }
  228. /**
  229. * Aligns current size's pivot point to given position
  230. * and moves point automatically by offset.
  231. *
  232. * @param string $position
  233. * @param int $offset_x
  234. * @param int $offset_y
  235. * @return \Intervention\Image\Size
  236. */
  237. public function align($position, $offset_x = 0, $offset_y = 0)
  238. {
  239. switch (strtolower($position)) {
  240. case 'top':
  241. case 'top-center':
  242. case 'top-middle':
  243. case 'center-top':
  244. case 'middle-top':
  245. $x = intval($this->width / 2);
  246. $y = 0 + $offset_y;
  247. break;
  248. case 'top-right':
  249. case 'right-top':
  250. $x = $this->width - $offset_x;
  251. $y = 0 + $offset_y;
  252. break;
  253. case 'left':
  254. case 'left-center':
  255. case 'left-middle':
  256. case 'center-left':
  257. case 'middle-left':
  258. $x = 0 + $offset_x;
  259. $y = intval($this->height / 2);
  260. break;
  261. case 'right':
  262. case 'right-center':
  263. case 'right-middle':
  264. case 'center-right':
  265. case 'middle-right':
  266. $x = $this->width - $offset_x;
  267. $y = intval($this->height / 2);
  268. break;
  269. case 'bottom-left':
  270. case 'left-bottom':
  271. $x = 0 + $offset_x;
  272. $y = $this->height - $offset_y;
  273. break;
  274. case 'bottom':
  275. case 'bottom-center':
  276. case 'bottom-middle':
  277. case 'center-bottom':
  278. case 'middle-bottom':
  279. $x = intval($this->width / 2);
  280. $y = $this->height - $offset_y;
  281. break;
  282. case 'bottom-right':
  283. case 'right-bottom':
  284. $x = $this->width - $offset_x;
  285. $y = $this->height - $offset_y;
  286. break;
  287. case 'center':
  288. case 'middle':
  289. case 'center-center':
  290. case 'middle-middle':
  291. $x = intval($this->width / 2);
  292. $y = intval($this->height / 2);
  293. break;
  294. default:
  295. case 'top-left':
  296. case 'left-top':
  297. $x = 0 + $offset_x;
  298. $y = 0 + $offset_y;
  299. break;
  300. }
  301. $this->pivot->setPosition($x, $y);
  302. return $this;
  303. }
  304. /**
  305. * Runs constraints on current size
  306. *
  307. * @param Closure $callback
  308. * @return \Intervention\Image\Constraint
  309. */
  310. private function getConstraint(Closure $callback = null)
  311. {
  312. $constraint = new Constraint(clone $this);
  313. if (is_callable($callback)) {
  314. $callback($constraint);
  315. }
  316. return $constraint;
  317. }
  318. }