File indexing completed on 2025-03-02 05:33:39

0001 <?php
0002 
0003 namespace Intervention\Image;
0004 
0005 use Closure;
0006 
0007 class Size
0008 {
0009     /**
0010      * Width
0011      *
0012      * @var int
0013      */
0014     public $width;
0015 
0016     /**
0017      * Height
0018      *
0019      * @var int
0020      */
0021     public $height;
0022 
0023     /**
0024      * Pivot point
0025      *
0026      * @var Point
0027      */
0028     public $pivot;
0029 
0030     /**
0031      * Creates a new Size instance
0032      *
0033      * @param int   $width
0034      * @param int   $height
0035      * @param Point $pivot
0036      */
0037     public function __construct($width = null, $height = null, Point $pivot = null)
0038     {
0039         $this->width = is_numeric($width) ? intval($width) : 1;
0040         $this->height = is_numeric($height) ? intval($height) : 1;
0041         $this->pivot = $pivot ? $pivot : new Point;
0042     }
0043 
0044     /**
0045      * Set the width and height absolutely
0046      *
0047      * @param int $width
0048      * @param int $height
0049      */
0050     public function set($width, $height)
0051     {
0052         $this->width = $width;
0053         $this->height = $height;
0054     }
0055 
0056     /**
0057      * Set current pivot point
0058      *
0059      * @param Point $point
0060      */
0061     public function setPivot(Point $point)
0062     {
0063         $this->pivot = $point;
0064     }
0065 
0066     /**
0067      * Get the current width
0068      *
0069      * @return int
0070      */
0071     public function getWidth()
0072     {
0073         return $this->width;
0074     }
0075 
0076     /**
0077      * Get the current height
0078      *
0079      * @return int
0080      */
0081     public function getHeight()
0082     {
0083         return $this->height;
0084     }
0085 
0086     /**
0087      * Calculate the current aspect ratio
0088      *
0089      * @return float
0090      */
0091     public function getRatio()
0092     {
0093         return $this->width / $this->height;
0094     }
0095 
0096     /**
0097      * Resize to desired width and/or height
0098      *
0099      * @param  int     $width
0100      * @param  int     $height
0101      * @param  Closure $callback
0102      * @return Size
0103      */
0104     public function resize($width, $height, Closure $callback = null)
0105     {
0106         if (is_null($width) && is_null($height)) {
0107             throw new \Intervention\Image\Exception\InvalidArgumentException(
0108                 "Width or height needs to be defined."
0109             );
0110         }
0111 
0112         // new size with dominant width
0113         $dominant_w_size = clone $this;
0114         $dominant_w_size->resizeHeight($height, $callback);
0115         $dominant_w_size->resizeWidth($width, $callback);
0116 
0117         // new size with dominant height
0118         $dominant_h_size = clone $this;
0119         $dominant_h_size->resizeWidth($width, $callback);
0120         $dominant_h_size->resizeHeight($height, $callback);
0121 
0122         // decide which size to use
0123         if ($dominant_h_size->fitsInto(new self($width, $height))) {
0124             $this->set($dominant_h_size->width, $dominant_h_size->height);
0125         } else {
0126             $this->set($dominant_w_size->width, $dominant_w_size->height);
0127         }
0128 
0129         return $this;
0130     }
0131 
0132     /**
0133      * Scale size according to given constraints
0134      *
0135      * @param  int     $width
0136      * @param  Closure $callback
0137      * @return Size
0138      */
0139     private function resizeWidth($width, Closure $callback = null)
0140     {
0141         $constraint = $this->getConstraint($callback);
0142 
0143         if ($constraint->isFixed(Constraint::UPSIZE)) {
0144             $max_width = $constraint->getSize()->getWidth();
0145             $max_height = $constraint->getSize()->getHeight();
0146         }
0147 
0148         if (is_numeric($width)) {
0149 
0150             if ($constraint->isFixed(Constraint::UPSIZE)) {
0151                 $this->width = ($width > $max_width) ? $max_width : $width;
0152             } else {
0153                 $this->width = $width;
0154             }
0155 
0156             if ($constraint->isFixed(Constraint::ASPECTRATIO)) {
0157                 $h = intval(round($this->width / $constraint->getSize()->getRatio()));
0158 
0159                 if ($constraint->isFixed(Constraint::UPSIZE)) {
0160                     $this->height = ($h > $max_height) ? $max_height : $h;
0161                 } else {
0162                     $this->height = $h;
0163                 }
0164             }
0165         }
0166     }
0167 
0168     /**
0169      * Scale size according to given constraints
0170      *
0171      * @param  int     $height
0172      * @param  Closure $callback
0173      * @return Size
0174      */
0175     private function resizeHeight($height, Closure $callback = null)
0176     {
0177         $constraint = $this->getConstraint($callback);
0178 
0179         if ($constraint->isFixed(Constraint::UPSIZE)) {
0180             $max_width = $constraint->getSize()->getWidth();
0181             $max_height = $constraint->getSize()->getHeight();
0182         }
0183 
0184         if (is_numeric($height)) {
0185 
0186             if ($constraint->isFixed(Constraint::UPSIZE)) {
0187                 $this->height = ($height > $max_height) ? $max_height : $height;
0188             } else {
0189                 $this->height = $height;
0190             }
0191 
0192             if ($constraint->isFixed(Constraint::ASPECTRATIO)) {
0193                 $w = intval(round($this->height * $constraint->getSize()->getRatio()));
0194 
0195                 if ($constraint->isFixed(Constraint::UPSIZE)) {
0196                     $this->width = ($w > $max_width) ? $max_width : $w;
0197                 } else {
0198                     $this->width = $w;
0199                 }
0200             }
0201         }
0202     }
0203 
0204     /**
0205      * Calculate the relative position to another Size
0206      * based on the pivot point settings of both sizes.
0207      *
0208      * @param  Size   $size
0209      * @return \Intervention\Image\Point
0210      */
0211     public function relativePosition(Size $size)
0212     {
0213         $x = $this->pivot->x - $size->pivot->x;
0214         $y = $this->pivot->y - $size->pivot->y;
0215 
0216         return new Point($x, $y);
0217     }
0218 
0219     /**
0220      * Resize given Size to best fitting size of current size.
0221      *
0222      * @param  Size   $size
0223      * @return \Intervention\Image\Size
0224      */
0225     public function fit(Size $size, $position = 'center')
0226     {
0227         // create size with auto height
0228         $auto_height = clone $size;
0229 
0230         $auto_height->resize($this->width, null, function ($constraint) {
0231             $constraint->aspectRatio();
0232         });
0233 
0234         // decide which version to use
0235         if ($auto_height->fitsInto($this)) {
0236 
0237             $size = $auto_height;
0238 
0239         } else {
0240 
0241             // create size with auto width
0242             $auto_width = clone $size;
0243 
0244             $auto_width->resize(null, $this->height, function ($constraint) {
0245                 $constraint->aspectRatio();
0246             });
0247 
0248             $size = $auto_width;
0249         }
0250 
0251         $this->align($position);
0252         $size->align($position);
0253         $size->setPivot($this->relativePosition($size));
0254 
0255         return $size;
0256     }
0257 
0258     /**
0259      * Checks if given size fits into current size
0260      *
0261      * @param  Size   $size
0262      * @return boolean
0263      */
0264     public function fitsInto(Size $size)
0265     {
0266         return ($this->width <= $size->width) && ($this->height <= $size->height);
0267     }
0268 
0269     /**
0270      * Aligns current size's pivot point to given position
0271      * and moves point automatically by offset.
0272      *
0273      * @param  string  $position
0274      * @param  int     $offset_x
0275      * @param  int     $offset_y
0276      * @return \Intervention\Image\Size
0277      */
0278     public function align($position, $offset_x = 0, $offset_y = 0)
0279     {
0280         switch (strtolower($position)) {
0281 
0282             case 'top':
0283             case 'top-center':
0284             case 'top-middle':
0285             case 'center-top':
0286             case 'middle-top':
0287                 $x = intval($this->width / 2);
0288                 $y = 0 + $offset_y;
0289                 break;
0290 
0291             case 'top-right':
0292             case 'right-top':
0293                 $x = $this->width - $offset_x;
0294                 $y = 0 + $offset_y;
0295                 break;
0296 
0297             case 'left':
0298             case 'left-center':
0299             case 'left-middle':
0300             case 'center-left':
0301             case 'middle-left':
0302                 $x = 0 + $offset_x;
0303                 $y = intval($this->height / 2);
0304                 break;
0305 
0306             case 'right':
0307             case 'right-center':
0308             case 'right-middle':
0309             case 'center-right':
0310             case 'middle-right':
0311                 $x = $this->width - $offset_x;
0312                 $y = intval($this->height / 2);
0313                 break;
0314 
0315             case 'bottom-left':
0316             case 'left-bottom':
0317                 $x = 0 + $offset_x;
0318                 $y = $this->height - $offset_y;
0319                 break;
0320 
0321             case 'bottom':
0322             case 'bottom-center':
0323             case 'bottom-middle':
0324             case 'center-bottom':
0325             case 'middle-bottom':
0326                 $x = intval($this->width / 2);
0327                 $y = $this->height - $offset_y;
0328                 break;
0329 
0330             case 'bottom-right':
0331             case 'right-bottom':
0332                 $x = $this->width - $offset_x;
0333                 $y = $this->height - $offset_y;
0334                 break;
0335 
0336             case 'center':
0337             case 'middle':
0338             case 'center-center':
0339             case 'middle-middle':
0340                 $x = intval($this->width / 2);
0341                 $y = intval($this->height / 2);
0342                 break;
0343 
0344             default:
0345             case 'top-left':
0346             case 'left-top':
0347                 $x = 0 + $offset_x;
0348                 $y = 0 + $offset_y;
0349                 break;
0350         }
0351 
0352         $this->pivot->setPosition($x, $y);
0353 
0354         return $this;
0355     }
0356 
0357     /**
0358      * Runs constraints on current size
0359      *
0360      * @param  Closure $callback
0361      * @return \Intervention\Image\Constraint
0362      */
0363     private function getConstraint(Closure $callback = null)
0364     {
0365         $constraint = new Constraint(clone $this);
0366 
0367         if (is_callable($callback)) {
0368             $callback($constraint);
0369         }
0370 
0371         return $constraint;
0372     }
0373 }