[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Image/ -> Image.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright   (C) 2017 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license     GNU General Public License version 2 or later; see LICENSE
   8   */
   9  
  10  namespace Joomla\CMS\Image;
  11  
  12  // phpcs:disable PSR1.Files.SideEffects
  13  \defined('JPATH_PLATFORM') or die;
  14  // phpcs:enable PSR1.Files.SideEffects
  15  
  16  /**
  17   * Class to manipulate an image.
  18   *
  19   * @since  1.7.3
  20   */
  21  class Image
  22  {
  23      /**
  24       * @const  integer
  25       * @since  2.5.0
  26       */
  27      public const SCALE_FILL = 1;
  28  
  29      /**
  30       * @const  integer
  31       * @since  2.5.0
  32       */
  33      public const SCALE_INSIDE = 2;
  34  
  35      /**
  36       * @const  integer
  37       * @since  2.5.0
  38       */
  39      public const SCALE_OUTSIDE = 3;
  40  
  41      /**
  42       * @const  integer
  43       * @since  2.5.0
  44       */
  45      public const CROP = 4;
  46  
  47      /**
  48       * @const  integer
  49       * @since  2.5.0
  50       */
  51      public const CROP_RESIZE = 5;
  52  
  53      /**
  54       * @const  integer
  55       * @since  2.5.0
  56       */
  57      public const SCALE_FIT = 6;
  58  
  59      /**
  60       * @const  string
  61       * @since  3.4.2
  62       */
  63      public const ORIENTATION_LANDSCAPE = 'landscape';
  64  
  65      /**
  66       * @const  string
  67       * @since  3.4.2
  68       */
  69      public const ORIENTATION_PORTRAIT = 'portrait';
  70  
  71      /**
  72       * @const  string
  73       * @since  3.4.2
  74       */
  75      public const ORIENTATION_SQUARE = 'square';
  76  
  77      /**
  78       * @var    resource  The image resource handle.
  79       * @since  2.5.0
  80       */
  81      protected $handle;
  82  
  83      /**
  84       * @var    string  The source image path.
  85       * @since  2.5.0
  86       */
  87      protected $path = null;
  88  
  89      /**
  90       * @var    array  Whether or not different image formats are supported.
  91       * @since  2.5.0
  92       */
  93      protected static $formats = [];
  94  
  95      /**
  96       * @var    boolean  Flag if an image should use the best quality available.  Disable for improved performance.
  97       * @since  3.7.0
  98       */
  99      protected $generateBestQuality = true;
 100  
 101      /**
 102       * Class constructor.
 103       *
 104       * @param   mixed  $source  Either a file path for a source image or a GD resource handler for an image.
 105       *
 106       * @since   1.7.3
 107       * @throws  \RuntimeException
 108       */
 109      public function __construct($source = null)
 110      {
 111          // Verify that GD support for PHP is available.
 112          if (!\extension_loaded('gd')) {
 113              // @codeCoverageIgnoreStart
 114              throw new \RuntimeException('The GD extension for PHP is not available.');
 115  
 116              // @codeCoverageIgnoreEnd
 117          }
 118  
 119          // Determine which image types are supported by GD, but only once.
 120          if (empty(static::$formats)) {
 121              $info = gd_info();
 122              static::$formats[IMAGETYPE_JPEG] = $info['JPEG Support'];
 123              static::$formats[IMAGETYPE_PNG]  = $info['PNG Support'];
 124              static::$formats[IMAGETYPE_GIF]  = $info['GIF Read Support'];
 125              static::$formats[IMAGETYPE_WEBP] = $info['WebP Support'];
 126          }
 127  
 128          /**
 129           * If the source input is a resource, set it as the image handle.
 130           * @todo: Remove check for resource when we only support PHP 8
 131           */
 132          if (
 133              $source && (\is_object($source) && get_class($source) == 'GdImage')
 134              || (\is_resource($source) && get_resource_type($source) == 'gd')
 135          ) {
 136              $this->handle = $source;
 137          } elseif (!empty($source) && \is_string($source)) {
 138              // If the source input is not empty, assume it is a path and populate the image handle.
 139              $this->loadFile($source);
 140          }
 141      }
 142  
 143      /**
 144       * Get the image resource handle
 145       *
 146       * @return  resource
 147       *
 148       * @since   3.8.0
 149       * @throws  \LogicException if an image has not been loaded into the instance
 150       */
 151      public function getHandle()
 152      {
 153          // Make sure the resource handle is valid.
 154          if (!$this->isLoaded()) {
 155              throw new \LogicException('No valid image was loaded.');
 156          }
 157  
 158          return $this->handle;
 159      }
 160  
 161      /**
 162       * Method to return a properties object for an image given a filesystem path.
 163       *
 164       * The result object has values for image width, height, type, attributes, mime type, bits, and channels.
 165       *
 166       * @param   string  $path  The filesystem path to the image for which to get properties.
 167       *
 168       * @return  \stdClass
 169       *
 170       * @since   2.5.0
 171       * @throws  \InvalidArgumentException
 172       * @throws  \RuntimeException
 173       */
 174      public static function getImageFileProperties($path)
 175      {
 176          // Make sure the file exists.
 177          if (!is_file($path)) {
 178              throw new \InvalidArgumentException('The image file does not exist.');
 179          }
 180  
 181          // Get the image file information.
 182          $info = getimagesize($path);
 183  
 184          if (!$info) {
 185              throw new Exception\UnparsableImageException('Unable to get properties for the image.');
 186          }
 187  
 188          // Build the response object.
 189          return (object) [
 190              'width'       => $info[0],
 191              'height'      => $info[1],
 192              'type'        => $info[2],
 193              'attributes'  => $info[3],
 194              'bits'        => $info['bits'] ?? null,
 195              'channels'    => $info['channels'] ?? null,
 196              'mime'        => $info['mime'],
 197              'filesize'    => filesize($path),
 198              'orientation' => self::getOrientationString((int) $info[0], (int) $info[1]),
 199          ];
 200      }
 201  
 202      /**
 203       * Method to detect whether an image's orientation is landscape, portrait or square.
 204       *
 205       * The orientation will be returned as a string.
 206       *
 207       * @return  mixed   Orientation string or null.
 208       *
 209       * @since   3.4.2
 210       */
 211      public function getOrientation()
 212      {
 213          if ($this->isLoaded()) {
 214              return self::getOrientationString($this->getWidth(), $this->getHeight());
 215          }
 216  
 217          return null;
 218      }
 219  
 220      /**
 221       * Compare width and height integers to determine image orientation.
 222       *
 223       * @param   integer  $width   The width value to use for calculation
 224       * @param   integer  $height  The height value to use for calculation
 225       *
 226       * @return  string   Orientation string
 227       *
 228       * @since   3.4.2
 229       */
 230      private static function getOrientationString(int $width, int $height): string
 231      {
 232          switch (true) {
 233              case ($width > $height):
 234                  return self::ORIENTATION_LANDSCAPE;
 235  
 236              case ($width < $height):
 237                  return self::ORIENTATION_PORTRAIT;
 238  
 239              default:
 240                  return self::ORIENTATION_SQUARE;
 241          }
 242      }
 243  
 244      /**
 245       * Method to generate thumbnails from the current image. It allows creation by resizing or cropping the original image.
 246       *
 247       * @param   mixed    $thumbSizes      String or array of strings. Example: $thumbSizes = array('150x75','250x150');
 248       * @param   integer  $creationMethod  1-3 resize $scaleMethod | 4 create cropping | 5 resize then crop
 249       *
 250       * @return  array
 251       *
 252       * @since   2.5.0
 253       * @throws  \LogicException
 254       * @throws  \InvalidArgumentException
 255       */
 256      public function generateThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE)
 257      {
 258          // Make sure the resource handle is valid.
 259          if (!$this->isLoaded()) {
 260              throw new \LogicException('No valid image was loaded.');
 261          }
 262  
 263          // Accept a single thumbsize string as parameter
 264          if (!\is_array($thumbSizes)) {
 265              $thumbSizes = [$thumbSizes];
 266          }
 267  
 268          // Process thumbs
 269          $generated = [];
 270  
 271          if (!empty($thumbSizes)) {
 272              foreach ($thumbSizes as $thumbSize) {
 273                  // Desired thumbnail size
 274                  $size = explode('x', strtolower($thumbSize));
 275  
 276                  if (\count($size) != 2) {
 277                      throw new \InvalidArgumentException('Invalid thumb size received: ' . $thumbSize);
 278                  }
 279  
 280                  $thumbWidth  = $size[0];
 281                  $thumbHeight = $size[1];
 282  
 283                  switch ($creationMethod) {
 284                      case self::CROP:
 285                          $thumb = $this->crop($thumbWidth, $thumbHeight, null, null, true);
 286                          break;
 287  
 288                      case self::CROP_RESIZE:
 289                          $thumb = $this->cropResize($thumbWidth, $thumbHeight, true);
 290                          break;
 291  
 292                      default:
 293                          $thumb = $this->resize($thumbWidth, $thumbHeight, true, $creationMethod);
 294                          break;
 295                  }
 296  
 297                  // Store the thumb in the results array
 298                  $generated[] = $thumb;
 299              }
 300          }
 301  
 302          return $generated;
 303      }
 304  
 305      /**
 306       * Method to create thumbnails from the current image and save them to disk. It allows creation by resizing or cropping the original image.
 307       *
 308       * @param   mixed    $thumbSizes      string or array of strings. Example: $thumbSizes = array('150x75','250x150');
 309       * @param   integer  $creationMethod  1-3 resize $scaleMethod | 4 create cropping
 310       * @param   string   $thumbsFolder    destination thumbs folder. null generates a thumbs folder in the image folder
 311       *
 312       * @return  array
 313       *
 314       * @since   2.5.0
 315       * @throws  \LogicException
 316       * @throws  \InvalidArgumentException
 317       */
 318      public function createThumbs($thumbSizes, $creationMethod = self::SCALE_INSIDE, $thumbsFolder = null)
 319      {
 320          // Make sure the resource handle is valid.
 321          if (!$this->isLoaded()) {
 322              throw new \LogicException('No valid image was loaded.');
 323          }
 324  
 325          // No thumbFolder set -> we will create a thumbs folder in the current image folder
 326          if (\is_null($thumbsFolder)) {
 327              $thumbsFolder = \dirname($this->getPath()) . '/thumbs';
 328          }
 329  
 330          // Check destination
 331          if (!is_dir($thumbsFolder) && (!is_dir(\dirname($thumbsFolder)) || !@mkdir($thumbsFolder))) {
 332              throw new \InvalidArgumentException('Folder does not exist and cannot be created: ' . $thumbsFolder);
 333          }
 334  
 335          // Process thumbs
 336          $thumbsCreated = [];
 337  
 338          if ($thumbs = $this->generateThumbs($thumbSizes, $creationMethod)) {
 339              // Parent image properties
 340              $imgProperties = static::getImageFileProperties($this->getPath());
 341  
 342              // Get image filename and extension.
 343              $pathInfo      = pathinfo($this->getPath());
 344              $filename      = $pathInfo['filename'];
 345              $fileExtension = $pathInfo['extension'] ?? '';
 346  
 347              foreach ($thumbs as $thumb) {
 348                  // Get thumb properties
 349                  $thumbWidth  = $thumb->getWidth();
 350                  $thumbHeight = $thumb->getHeight();
 351  
 352                  // Generate thumb name
 353                  $thumbFileName = $filename . '_' . $thumbWidth . 'x' . $thumbHeight . '.' . $fileExtension;
 354  
 355                  // Save thumb file to disk
 356                  $thumbFileName = $thumbsFolder . '/' . $thumbFileName;
 357  
 358                  if ($thumb->toFile($thumbFileName, $imgProperties->type)) {
 359                      // Return Image object with thumb path to ease further manipulation
 360                      $thumb->path = $thumbFileName;
 361                      $thumbsCreated[] = $thumb;
 362                  }
 363              }
 364          }
 365  
 366          return $thumbsCreated;
 367      }
 368  
 369      /**
 370       * Method to crop the current image.
 371       *
 372       * @param   mixed    $width      The width of the image section to crop in pixels or a percentage.
 373       * @param   mixed    $height     The height of the image section to crop in pixels or a percentage.
 374       * @param   integer  $left       The number of pixels from the left to start cropping.
 375       * @param   integer  $top        The number of pixels from the top to start cropping.
 376       * @param   boolean  $createNew  If true the current image will be cloned, cropped and returned; else
 377       *                               the current image will be cropped and returned.
 378       *
 379       * @return  Image
 380       *
 381       * @since   2.5.0
 382       * @throws  \LogicException
 383       */
 384      public function crop($width, $height, $left = null, $top = null, $createNew = true)
 385      {
 386          // Sanitize width.
 387          $width = $this->sanitizeWidth($width, $height);
 388  
 389          // Sanitize height.
 390          $height = $this->sanitizeHeight($height, $width);
 391  
 392          // Autocrop offsets
 393          if (\is_null($left)) {
 394              $left = round(($this->getWidth() - $width) / 2);
 395          }
 396  
 397          if (\is_null($top)) {
 398              $top = round(($this->getHeight() - $height) / 2);
 399          }
 400  
 401          // Sanitize left.
 402          $left = $this->sanitizeOffset($left);
 403  
 404          // Sanitize top.
 405          $top = $this->sanitizeOffset($top);
 406  
 407          // Create the new truecolor image handle.
 408          $handle = imagecreatetruecolor($width, $height);
 409  
 410          // Allow transparency for the new image handle.
 411          imagealphablending($handle, false);
 412          imagesavealpha($handle, true);
 413  
 414          if ($this->isTransparent()) {
 415              // Get the transparent color values for the current image.
 416              $rgba  = imagecolorsforindex($this->getHandle(), imagecolortransparent($this->getHandle()));
 417              $color = imagecolorallocatealpha($handle, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']);
 418  
 419              // Set the transparent color values for the new image.
 420              imagecolortransparent($handle, $color);
 421              imagefill($handle, 0, 0, $color);
 422          }
 423  
 424          if (!$this->generateBestQuality) {
 425              imagecopyresized($handle, $this->getHandle(), 0, 0, $left, $top, $width, $height, $width, $height);
 426          } else {
 427              imagecopyresampled($handle, $this->getHandle(), 0, 0, $left, $top, $width, $height, $width, $height);
 428          }
 429  
 430          // If we are cropping to a new image, create a new Image object.
 431          if ($createNew) {
 432              return new static($handle);
 433          }
 434  
 435          // Swap out the current handle for the new image handle.
 436          $this->destroy();
 437  
 438          $this->handle = $handle;
 439  
 440          return $this;
 441      }
 442  
 443      /**
 444       * Method to apply a filter to the image by type.  Two examples are: grayscale and sketchy.
 445       *
 446       * @param   string  $type     The name of the image filter to apply.
 447       * @param   array   $options  An array of options for the filter.
 448       *
 449       * @return  Image
 450       *
 451       * @since   2.5.0
 452       * @see     \Joomla\CMS\Image\Filter
 453       * @throws  \LogicException
 454       */
 455      public function filter($type, array $options = [])
 456      {
 457          // Make sure the resource handle is valid.
 458          if (!$this->isLoaded()) {
 459              throw new \LogicException('No valid image was loaded.');
 460          }
 461  
 462          // Get the image filter instance.
 463          $filter = $this->getFilterInstance($type);
 464  
 465          // Execute the image filter.
 466          $filter->execute($options);
 467  
 468          return $this;
 469      }
 470  
 471      /**
 472       * Method to get the height of the image in pixels.
 473       *
 474       * @return  integer
 475       *
 476       * @since   2.5.0
 477       * @throws  \LogicException
 478       */
 479      public function getHeight()
 480      {
 481          return imagesy($this->getHandle());
 482      }
 483  
 484      /**
 485       * Method to get the width of the image in pixels.
 486       *
 487       * @return  integer
 488       *
 489       * @since   2.5.0
 490       * @throws  \LogicException
 491       */
 492      public function getWidth()
 493      {
 494          return imagesx($this->getHandle());
 495      }
 496  
 497      /**
 498       * Method to return the path
 499       *
 500       * @return  string
 501       *
 502       * @since   2.5.0
 503       */
 504      public function getPath()
 505      {
 506          return $this->path;
 507      }
 508  
 509      /**
 510       * Method to determine whether or not an image has been loaded into the object.
 511       *
 512       * @return  boolean
 513       *
 514       * @since   2.5.0
 515       */
 516      public function isLoaded()
 517      {
 518          /**
 519           * Make sure the resource handle is valid.
 520           * @todo: Remove check for resource when we only support PHP 8
 521           */
 522          if (
 523              !((\is_object($this->handle) && get_class($this->handle) == 'GdImage')
 524              || (\is_resource($this->handle) && get_resource_type($this->handle) == 'gd'))
 525          ) {
 526              return false;
 527          }
 528  
 529          return true;
 530      }
 531  
 532      /**
 533       * Method to determine whether or not the image has transparency.
 534       *
 535       * @return  boolean
 536       *
 537       * @since   2.5.0
 538       * @throws  \LogicException
 539       */
 540      public function isTransparent()
 541      {
 542          return imagecolortransparent($this->getHandle()) >= 0;
 543      }
 544  
 545      /**
 546       * Method to load a file into the Image object as the resource.
 547       *
 548       * @param   string  $path  The filesystem path to load as an image.
 549       *
 550       * @return  void
 551       *
 552       * @since   2.5.0
 553       * @throws  \InvalidArgumentException
 554       * @throws  \RuntimeException
 555       */
 556      public function loadFile($path)
 557      {
 558          // Destroy the current image handle if it exists
 559          $this->destroy();
 560  
 561          // Make sure the file exists.
 562          if (!is_file($path)) {
 563              throw new \InvalidArgumentException('The image file does not exist.');
 564          }
 565  
 566          // Get the image properties.
 567          $properties = static::getImageFileProperties($path);
 568  
 569          // Attempt to load the image based on the MIME-Type
 570          switch ($properties->mime) {
 571              case 'image/gif':
 572                  // Make sure the image type is supported.
 573                  if (empty(static::$formats[IMAGETYPE_GIF])) {
 574                      throw new \RuntimeException('Attempting to load an image of unsupported type GIF.');
 575                  }
 576  
 577                  // Attempt to create the image handle.
 578                  $handle = imagecreatefromgif($path);
 579                  $type = 'GIF';
 580  
 581                  break;
 582  
 583              case 'image/jpeg':
 584                  // Make sure the image type is supported.
 585                  if (empty(static::$formats[IMAGETYPE_JPEG])) {
 586                      throw new \RuntimeException('Attempting to load an image of unsupported type JPG.');
 587                  }
 588  
 589                  // Attempt to create the image handle.
 590                  $handle = imagecreatefromjpeg($path);
 591                  $type = 'JPEG';
 592  
 593                  break;
 594  
 595              case 'image/png':
 596                  // Make sure the image type is supported.
 597                  if (empty(static::$formats[IMAGETYPE_PNG])) {
 598                      throw new \RuntimeException('Attempting to load an image of unsupported type PNG.');
 599                  }
 600  
 601                  // Attempt to create the image handle.
 602                  $handle = imagecreatefrompng($path);
 603                  $type = 'PNG';
 604  
 605                  break;
 606  
 607              case 'image/webp':
 608                  // Make sure the image type is supported.
 609                  if (empty(static::$formats[IMAGETYPE_WEBP])) {
 610                      throw new \RuntimeException('Attempting to load an image of unsupported type WebP.');
 611                  }
 612  
 613                  // Attempt to create the image handle.
 614                  $handle = imagecreatefromwebp($path);
 615                  $type = 'WebP';
 616  
 617                  break;
 618  
 619              default:
 620                  throw new \InvalidArgumentException('Attempting to load an image of unsupported type ' . $properties->mime);
 621          }
 622  
 623          /**
 624           * Check if handle has been created successfully
 625           * @todo: Remove check for resource when we only support PHP 8
 626           */
 627          if (!(\is_object($handle) || \is_resource($handle))) {
 628              throw new \RuntimeException('Unable to process ' . $type . ' image.');
 629          }
 630  
 631          $this->handle = $handle;
 632  
 633          // Set the filesystem path to the source image.
 634          $this->path = $path;
 635      }
 636  
 637      /**
 638       * Method to resize the current image.
 639       *
 640       * @param   mixed    $width        The width of the resized image in pixels or a percentage.
 641       * @param   mixed    $height       The height of the resized image in pixels or a percentage.
 642       * @param   boolean  $createNew    If true the current image will be cloned, resized and returned; else
 643       *                                 the current image will be resized and returned.
 644       * @param   integer  $scaleMethod  Which method to use for scaling
 645       *
 646       * @return  Image
 647       *
 648       * @since   2.5.0
 649       * @throws  \LogicException
 650       */
 651      public function resize($width, $height, $createNew = true, $scaleMethod = self::SCALE_INSIDE)
 652      {
 653          // Sanitize width.
 654          $width = $this->sanitizeWidth($width, $height);
 655  
 656          // Sanitize height.
 657          $height = $this->sanitizeHeight($height, $width);
 658  
 659          // Prepare the dimensions for the resize operation.
 660          $dimensions = $this->prepareDimensions($width, $height, $scaleMethod);
 661  
 662          // Instantiate offset.
 663          $offset = new \stdClass();
 664          $offset->x = $offset->y = 0;
 665  
 666          // Center image if needed and create the new truecolor image handle.
 667          if ($scaleMethod == self::SCALE_FIT) {
 668              // Get the offsets
 669              $offset->x = round(($width - $dimensions->width) / 2);
 670              $offset->y = round(($height - $dimensions->height) / 2);
 671  
 672              $handle = imagecreatetruecolor($width, $height);
 673  
 674              // Make image transparent, otherwise canvas outside initial image would default to black
 675              if (!$this->isTransparent()) {
 676                  $transparency = imagecolorallocatealpha($this->getHandle(), 0, 0, 0, 127);
 677                  imagecolortransparent($this->getHandle(), $transparency);
 678              }
 679          } else {
 680              $handle = imagecreatetruecolor($dimensions->width, $dimensions->height);
 681          }
 682  
 683          // Allow transparency for the new image handle.
 684          imagealphablending($handle, false);
 685          imagesavealpha($handle, true);
 686  
 687          if ($this->isTransparent()) {
 688              // Get the transparent color values for the current image.
 689              $rgba = imagecolorsforindex($this->getHandle(), imagecolortransparent($this->getHandle()));
 690              $color = imagecolorallocatealpha($handle, $rgba['red'], $rgba['green'], $rgba['blue'], $rgba['alpha']);
 691  
 692              // Set the transparent color values for the new image.
 693              imagecolortransparent($handle, $color);
 694              imagefill($handle, 0, 0, $color);
 695          }
 696  
 697          if (!$this->generateBestQuality) {
 698              imagecopyresized(
 699                  $handle,
 700                  $this->getHandle(),
 701                  $offset->x,
 702                  $offset->y,
 703                  0,
 704                  0,
 705                  $dimensions->width,
 706                  $dimensions->height,
 707                  $this->getWidth(),
 708                  $this->getHeight()
 709              );
 710          } else {
 711              // Use resampling for better quality
 712              imagecopyresampled(
 713                  $handle,
 714                  $this->getHandle(),
 715                  $offset->x,
 716                  $offset->y,
 717                  0,
 718                  0,
 719                  $dimensions->width,
 720                  $dimensions->height,
 721                  $this->getWidth(),
 722                  $this->getHeight()
 723              );
 724          }
 725  
 726          // If we are resizing to a new image, create a new Image object.
 727          if ($createNew) {
 728              return new static($handle);
 729          }
 730  
 731          // Swap out the current handle for the new image handle.
 732          $this->destroy();
 733  
 734          $this->handle = $handle;
 735  
 736          return $this;
 737      }
 738  
 739      /**
 740       * Method to crop an image after resizing it to maintain
 741       * proportions without having to do all the set up work.
 742       *
 743       * @param   integer  $width      The desired width of the image in pixels or a percentage.
 744       * @param   integer  $height     The desired height of the image in pixels or a percentage.
 745       * @param   boolean  $createNew  If true the current image will be cloned, resized, cropped and returned.
 746       *
 747       * @return  Image
 748       *
 749       * @since   2.5.0
 750       */
 751      public function cropResize($width, $height, $createNew = true)
 752      {
 753          $width   = $this->sanitizeWidth($width, $height);
 754          $height  = $this->sanitizeHeight($height, $width);
 755  
 756          $resizewidth = $width;
 757          $resizeheight = $height;
 758  
 759          if (($this->getWidth() / $width) < ($this->getHeight() / $height)) {
 760              $resizeheight = 0;
 761          } else {
 762              $resizewidth = 0;
 763          }
 764  
 765          return $this->resize($resizewidth, $resizeheight, $createNew)->crop($width, $height, null, null, false);
 766      }
 767  
 768      /**
 769       * Method to rotate the current image.
 770       *
 771       * @param   mixed    $angle       The angle of rotation for the image
 772       * @param   integer  $background  The background color to use when areas are added due to rotation
 773       * @param   boolean  $createNew   If true the current image will be cloned, rotated and returned; else
 774       *                                the current image will be rotated and returned.
 775       *
 776       * @return  Image
 777       *
 778       * @since   2.5.0
 779       * @throws  \LogicException
 780       */
 781      public function rotate($angle, $background = -1, $createNew = true)
 782      {
 783          // Sanitize input
 784          $angle = (float) $angle;
 785  
 786          // Create the new truecolor image handle.
 787          $handle = imagecreatetruecolor($this->getWidth(), $this->getHeight());
 788  
 789          // Make background transparent if no external background color is provided.
 790          if ($background == -1) {
 791              // Allow transparency for the new image handle.
 792              imagealphablending($handle, false);
 793              imagesavealpha($handle, true);
 794  
 795              $background = imagecolorallocatealpha($handle, 0, 0, 0, 127);
 796          }
 797  
 798          // Copy the image
 799          imagecopy($handle, $this->getHandle(), 0, 0, 0, 0, $this->getWidth(), $this->getHeight());
 800  
 801          // Rotate the image
 802          $handle = imagerotate($handle, $angle, $background);
 803  
 804          // If we are resizing to a new image, create a new Image object.
 805          if ($createNew) {
 806              return new static($handle);
 807          }
 808  
 809          // Swap out the current handle for the new image handle.
 810          $this->destroy();
 811  
 812          $this->handle = $handle;
 813  
 814          return $this;
 815      }
 816  
 817      /**
 818       * Method to flip the current image.
 819       *
 820       * @param   integer  $mode       The flip mode for flipping the image {@link http://php.net/imageflip#refsect1-function.imageflip-parameters}
 821       * @param   boolean  $createNew  If true the current image will be cloned, flipped and returned; else
 822       *                               the current image will be flipped and returned.
 823       *
 824       * @return  Image
 825       *
 826       * @since   3.4.2
 827       * @throws  \LogicException
 828       */
 829      public function flip($mode, $createNew = true)
 830      {
 831          // Create the new truecolor image handle.
 832          $handle = imagecreatetruecolor($this->getWidth(), $this->getHeight());
 833  
 834          // Copy the image
 835          imagecopy($handle, $this->getHandle(), 0, 0, 0, 0, $this->getWidth(), $this->getHeight());
 836  
 837          // Flip the image
 838          if (!imageflip($handle, $mode)) {
 839              throw new \LogicException('Unable to flip the image.');
 840          }
 841  
 842          // If we are resizing to a new image, create a new Image object.
 843          if ($createNew) {
 844              // @codeCoverageIgnoreStart
 845              return new static($handle);
 846  
 847              // @codeCoverageIgnoreEnd
 848          }
 849  
 850          // Free the memory from the current handle
 851          $this->destroy();
 852  
 853          // Swap out the current handle for the new image handle.
 854          $this->handle = $handle;
 855  
 856          return $this;
 857      }
 858  
 859      /**
 860       * Watermark the image
 861       *
 862       * @param   Image    $watermark     The Image object containing the watermark graphic
 863       * @param   integer  $transparency  The transparency to use for the watermark graphic
 864       * @param   integer  $bottomMargin  The margin from the bottom of this image
 865       * @param   integer  $rightMargin   The margin from the right side of this image
 866       *
 867       * @return  Image
 868       *
 869       * @since   3.8.0
 870       * @link    https://secure.php.net/manual/en/image.examples-watermark.php
 871       */
 872      public function watermark(Image $watermark, $transparency = 50, $bottomMargin = 0, $rightMargin = 0)
 873      {
 874          imagecopymerge(
 875              $this->getHandle(),
 876              $watermark->getHandle(),
 877              $this->getWidth() - $watermark->getWidth() - $rightMargin,
 878              $this->getHeight() - $watermark->getHeight() - $bottomMargin,
 879              0,
 880              0,
 881              $watermark->getWidth(),
 882              $watermark->getHeight(),
 883              $transparency
 884          );
 885  
 886          return $this;
 887      }
 888  
 889      /**
 890       * Method to write the current image out to a file or output directly.
 891       *
 892       * @param   mixed    $path     The filesystem path to save the image.
 893       *                             When null, the raw image stream will be outputted directly.
 894       * @param   integer  $type     The image type to save the file as.
 895       * @param   array    $options  The image type options to use in saving the file.
 896       *                             For PNG and JPEG formats use `quality` key to set compression level (0..9 and 0..100)
 897       *
 898       * @return  boolean
 899       *
 900       * @link    http://www.php.net/manual/image.constants.php
 901       * @since   2.5.0
 902       * @throws  \LogicException
 903       */
 904      public function toFile($path, $type = IMAGETYPE_JPEG, array $options = [])
 905      {
 906          switch ($type) {
 907              case IMAGETYPE_GIF:
 908                  return imagegif($this->getHandle(), $path);
 909  
 910              case IMAGETYPE_PNG:
 911                  return imagepng($this->getHandle(), $path, (\array_key_exists('quality', $options)) ? $options['quality'] : 0);
 912  
 913              case IMAGETYPE_WEBP:
 914                  return imagewebp($this->getHandle(), $path, (\array_key_exists('quality', $options)) ? $options['quality'] : 100);
 915          }
 916  
 917          // Case IMAGETYPE_JPEG & default
 918          return imagejpeg($this->getHandle(), $path, (\array_key_exists('quality', $options)) ? $options['quality'] : 100);
 919      }
 920  
 921      /**
 922       * Method to get an image filter instance of a specified type.
 923       *
 924       * @param   string  $type  The image filter type to get.
 925       *
 926       * @return  ImageFilter
 927       *
 928       * @since   2.5.0
 929       * @throws  \RuntimeException
 930       */
 931      protected function getFilterInstance($type)
 932      {
 933          // Sanitize the filter type.
 934          $type = strtolower(preg_replace('#[^A-Z0-9_]#i', '', $type));
 935  
 936          // Verify that the filter type exists.
 937          $className = 'JImageFilter' . ucfirst($type);
 938  
 939          if (!class_exists($className)) {
 940              $className = __NAMESPACE__ . '\\Filter\\' . ucfirst($type);
 941  
 942              if (!class_exists($className)) {
 943                  throw new \RuntimeException('The ' . ucfirst($type) . ' image filter is not available.');
 944              }
 945          }
 946  
 947          // Instantiate the filter object.
 948          $instance = new $className($this->getHandle());
 949  
 950          // Verify that the filter type is valid.
 951          if (!($instance instanceof ImageFilter)) {
 952              throw new \RuntimeException('The ' . ucfirst($type) . ' image filter is not valid.');
 953          }
 954  
 955          return $instance;
 956      }
 957  
 958      /**
 959       * Method to get the new dimensions for a resized image.
 960       *
 961       * @param   integer  $width        The width of the resized image in pixels.
 962       * @param   integer  $height       The height of the resized image in pixels.
 963       * @param   integer  $scaleMethod  The method to use for scaling
 964       *
 965       * @return  \stdClass
 966       *
 967       * @since   2.5.0
 968       * @throws  \InvalidArgumentException  If width, height or both given as zero
 969       */
 970      protected function prepareDimensions($width, $height, $scaleMethod)
 971      {
 972          // Instantiate variables.
 973          $dimensions = new \stdClass();
 974  
 975          switch ($scaleMethod) {
 976              case self::SCALE_FILL:
 977                  $dimensions->width = (int) round($width);
 978                  $dimensions->height = (int) round($height);
 979                  break;
 980  
 981              case self::SCALE_INSIDE:
 982              case self::SCALE_OUTSIDE:
 983              case self::SCALE_FIT:
 984                  $rx = ($width > 0) ? ($this->getWidth() / $width) : 0;
 985                  $ry = ($height > 0) ? ($this->getHeight() / $height) : 0;
 986  
 987                  if ($scaleMethod != self::SCALE_OUTSIDE) {
 988                      $ratio = max($rx, $ry);
 989                  } else {
 990                      $ratio = min($rx, $ry);
 991                  }
 992  
 993                  $dimensions->width = (int) round($this->getWidth() / $ratio);
 994                  $dimensions->height = (int) round($this->getHeight() / $ratio);
 995                  break;
 996  
 997              default:
 998                  throw new \InvalidArgumentException('Invalid scale method.');
 999          }
1000  
1001          return $dimensions;
1002      }
1003  
1004      /**
1005       * Method to sanitize a height value.
1006       *
1007       * @param   mixed  $height  The input height value to sanitize.
1008       * @param   mixed  $width   The input width value for reference.
1009       *
1010       * @return  integer
1011       *
1012       * @since   2.5.0
1013       */
1014      protected function sanitizeHeight($height, $width)
1015      {
1016          // If no height was given we will assume it is a square and use the width.
1017          $height = ($height === null) ? $width : $height;
1018  
1019          // If we were given a percentage, calculate the integer value.
1020          if (preg_match('/^[0-9]+(\.[0-9]+)?\%$/', $height)) {
1021              $height = (int) round($this->getHeight() * (float) str_replace('%', '', $height) / 100);
1022          } else // Else do some rounding so we come out with a sane integer value.
1023          {
1024              $height = (int) round((float) $height);
1025          }
1026  
1027          return $height;
1028      }
1029  
1030      /**
1031       * Method to sanitize an offset value like left or top.
1032       *
1033       * @param   mixed  $offset  An offset value.
1034       *
1035       * @return  integer
1036       *
1037       * @since   2.5.0
1038       */
1039      protected function sanitizeOffset($offset)
1040      {
1041          return (int) round((float) $offset);
1042      }
1043  
1044      /**
1045       * Method to sanitize a width value.
1046       *
1047       * @param   mixed  $width   The input width value to sanitize.
1048       * @param   mixed  $height  The input height value for reference.
1049       *
1050       * @return  integer
1051       *
1052       * @since   2.5.0
1053       */
1054      protected function sanitizeWidth($width, $height)
1055      {
1056          // If no width was given we will assume it is a square and use the height.
1057          $width = ($width === null) ? $height : $width;
1058  
1059          // If we were given a percentage, calculate the integer value.
1060          if (preg_match('/^[0-9]+(\.[0-9]+)?\%$/', $width)) {
1061              $width = (int) round($this->getWidth() * (float) str_replace('%', '', $width) / 100);
1062          } else // Else do some rounding so we come out with a sane integer value.
1063          {
1064              $width = (int) round((float) $width);
1065          }
1066  
1067          return $width;
1068      }
1069  
1070      /**
1071       * Method to destroy an image handle and
1072       * free the memory associated with the handle
1073       *
1074       * @return  boolean  True on success, false on failure or if no image is loaded
1075       *
1076       * @since   2.5.0
1077       */
1078      public function destroy()
1079      {
1080          if ($this->isLoaded()) {
1081              return imagedestroy($this->getHandle());
1082          }
1083  
1084          return false;
1085      }
1086  
1087      /**
1088       * Method to call the destroy() method one last time
1089       * to free any memory when the object is unset
1090       *
1091       * @see    Image::destroy()
1092       * @since  2.5.0
1093       */
1094      public function __destruct()
1095      {
1096          $this->destroy();
1097      }
1098  
1099      /**
1100       * Method for set option of generate thumbnail method
1101       *
1102       * @param   boolean  $quality  True for best quality. False for best speed.
1103       *
1104       * @return  void
1105       *
1106       * @since   3.7.0
1107       */
1108      public function setThumbnailGenerate($quality = true)
1109      {
1110          $this->generateBestQuality = (bool) $quality;
1111      }
1112  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer