[ Index ] |
PHP Cross Reference of Joomla 4.2.2 documentation |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Wed Sep 7 05:41:13 2022 | Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer |