Size.php 9.3 KB

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