1<?php 2 # ========================================================================# 3 # 4 # This work is licensed under the Creative Commons Attribution 3.0 Unported 5 # License. To view a copy of this license, 6 # visit http://creativecommons.org/licenses/by/3.0/ or send a letter to 7 # Creative Commons, 444 Castro Street, Suite 900, Mountain View, California, 8 # 94041, USA. 9 # 10 # All rights reserved. 11 # 12 # Author: Jarrod Oberto 13 # Version: 1.5.1 14 # Date: 10-05-11 15 # Purpose: Provide tools for image manipulation using GD 16 # Param In: See functions. 17 # Param Out: Produces a resized image 18 # Requires : Requires PHP GD library. 19 # Usage Example: 20 # include("lib/php_image_magician.php"); 21 # $magicianObj = new resize('images/car.jpg'); 22 # $magicianObj -> resizeImage(150, 100, 0); 23 # $magicianObj -> saveImage('images/car_small.jpg', 100); 24 # 25 # - See end of doc for more examples - 26 # 27 # Supported file types include: jpg, png, gif, bmp, psd (read) 28 # 29 # 30 # 31 # The following functions are taken from phpThumb() [available from 32 # http://phpthumb.sourceforge.net], and are used with written permission 33 # from James Heinrich. 34 # - GD2BMPstring 35 # - GetPixelColor 36 # - LittleEndian2String 37 # 38 # The following functions are from Marc Hibbins and are used with written 39 # permission (are also under the Attribution-ShareAlike 40 # [http://creativecommons.org/licenses/by-sa/3.0/] license. 41 # - 42 # 43 # PhpPsdReader is used with written permission from Tim de Koning. 44 # [http://www.kingsquare.nl/phppsdreader] 45 # 46 # 47 # 48 # Modificatoin history 49 # Date Initials Ver Description 50 # 10-05-11 J.C.O 0.0 Initial build 51 # 01-06-11 J.C.O 0.1.1 * Added reflections 52 # * Added Rounded corners 53 # * You can now use PNG interlacing 54 # * Added shadow 55 # * Added caption box 56 # * Added vintage filter 57 # * Added dynamic image resizing (resize on the fly) 58 # * minor bug fixes 59 # 05-06-11 J.C.O 0.1.1.1 * Fixed undefined variables 60 # 17-06-11 J.C.O 0.1.2 * Added image_batch_class.php class 61 # * Minor bug fixes 62 # 26-07-11 J.C.O 0.1.4 * Added support for external images 63 # * Can now set the crop poisition 64 # 03-08-11 J.C.O 0.1.5 * Added reset() method to reset resource to 65 # original input file. 66 # * Added method addTextToCaptionBox() to 67 # simplify adding text to a caption box. 68 # * Added experimental writeIPTC. (not finished) 69 # * Added experimental readIPTC. (not finished) 70 # 11-08-11 J.C.O * Added initial border presets. 71 # 30-08-11 J.C.O * Added 'auto' crop option to crop portrait 72 # images near the top. 73 # 08-09-11 J.C.O * Added cropImage() method to allow standalone 74 # cropping. 75 # 17-09-11 J.C.O * Added setCropFromTop() set method - set the 76 # percentage to crop from the top when using 77 # crop 'auto' option. 78 # * Added setTransparency() set method - allows you 79 # to turn transparency off (like when saving 80 # as a jpg). 81 # * Added setFillColor() set method - set the 82 # background color to use instead of transparency. 83 # 05-11-11 J.C.O 0.1.5.1 * Fixed interlacing option 84 # 0-07-12 J.C.O 1.0 85 # 86 # Known issues & Limitations: 87 # ------------------------------- 88 # Not so much an issue, the image is destroyed on the deconstruct rather than 89 # when we have finished with it. The reason for this is that we don't know 90 # when we're finished with it as you can both save the image and display 91 # it directly to the screen (imagedestroy($this->imageResized)) 92 # 93 # Opening BMP files is slow. A test with 884 bmp files processed in a loop 94 # takes forever - over 5 min. This test inlcuded opening the file, then 95 # getting and displaying its width and height. 96 # 97 # $forceStretch: 98 # ------------------------------- 99 # On by default. 100 # $forceStretch can be disabled by calling method setForceStretch with false 101 # parameter. If disabled, if an images original size is smaller than the size 102 # specified by the user, the original size will be used. This is useful when 103 # dealing with small images. 104 # 105 # If enabled, images smaller than the size specified will be stretched to 106 # that size. 107 # 108 # Tips: 109 # ------------------------------- 110 # * If you're resizing a transparent png and saving it as a jpg, set 111 # $keepTransparency to false with: $magicianObj->setTransparency(false); 112 # 113 # FEATURES: 114 # * EASY TO USE 115 # * BMP SUPPORT (read & write) 116 # * PSD (photoshop) support (read) 117 # * RESIZE IMAGES 118 # - Preserve transparency (png, gif) 119 # - Apply sharpening (jpg) (requires PHP >= 5.1.0) 120 # - Set image quality (jpg, png) 121 # - Resize modes: 122 # - exact size 123 # - resize by width (auto height) 124 # - resize by height (auto width) 125 # - auto (automatically determine the best of the above modes to use) 126 # - crop - resize as best as it can then crop the rest 127 # - Force stretching of smaller images (upscale) 128 # * APPLY FILTERS 129 # - Convert to grey scale 130 # - Convert to black and white 131 # - Convert to sepia 132 # - Convert to negative 133 # * ROTATE IMAGES 134 # - Rotate using predefined "left", "right", or "180"; or any custom degree amount 135 # * EXTRACT EXIF DATA (requires exif module) 136 # - make 137 # - model 138 # - date 139 # - exposure 140 # - aperture 141 # - f-stop 142 # - iso 143 # - focal length 144 # - exposure program 145 # - metering mode 146 # - flash status 147 # - creator 148 # - copyright 149 # * ADD WATERMARK 150 # - Specify exact x, y placement 151 # - Or, specify using one of the 9 pre-defined placements such as "tl" 152 # (for top left), "m" (for middle), "br" (for bottom right) 153 # - also specify padding from edge amount (optional). 154 # - Set opacity of watermark (png). 155 # * ADD BORDER 156 # * USE HEX WHEN SPECIFYING COLORS (eg: #ffffff) 157 # * SAVE IMAGE OR OUTPUT TO SCREEN 158 # 159 # 160 # ========================================================================# 161 162 163class imageLib 164{ 165 private $fileName; 166 private $image; 167 protected $imageResized; 168 private $widthOriginal; # Always be the original width 169 private $heightOriginal; 170 private $width; # Current width (width after resize) 171 private $height; 172 private $imageSize; 173 private $fileExtension; 174 175 private $debug = true; 176 private $errorArray = []; 177 178 private $forceStretch = true; 179 private $aggresiveSharpening = false; 180 181 private $transparentArray = ['.png', '.gif']; 182 private $keepTransparency = true; 183 private $fillColorArray = ['r'=>255, 'g'=>255, 'b'=>255]; 184 185 private $sharpenArray = ['jpg']; 186 187 private $psdReaderPath; 188 private $filterOverlayPath; 189 190 private $isInterlace; 191 192 private $captionBoxPositionArray = []; 193 194 private $fontDir = 'fonts'; 195 196 private $cropFromTopPercent = 10; 197 198 199## -------------------------------------------------------- 200 201 function __construct($fileName) 202 # Author: Jarrod Oberto 203 # Date: 27-02-08 204 # Purpose: Constructor 205 # Param in: $fileName: File name and path. 206 # Param out: n/a 207 # Reference: 208 # Notes: 209 # 210 { 211 if (!$this->testGDInstalled()) { 212 if ($this->debug) { 213 throw new Exception('The GD Library is not installed.'); 214 } else { 215 throw new Exception(); 216 } 217 }; 218 219 $this->initialise(); 220 221 // *** Save the image file name. Only store this incase you want to display it 222 $this->fileName = $fileName; 223 $this->fileExtension = fix_strtolower(strrchr($fileName, '.')); 224 225 // *** Open up the file 226 $this->image = $this->openImage($fileName); 227 228 229 // *** Assign here so we don't modify the original 230 $this->imageResized = $this->image; 231 232 // *** If file is an image 233 if ($this->testIsImage($this->image)) { 234 // *** Get width and height 235 $this->width = imagesx($this->image); 236 $this->widthOriginal = imagesx($this->image); 237 $this->height = imagesy($this->image); 238 $this->heightOriginal = imagesy($this->image); 239 240 241 /* Added 15-09-08 242 * Get the filesize using this build in method. 243 * Stores an array of size 244 * 245 * $this->imageSize[1] = width 246 * $this->imageSize[2] = height 247 * $this->imageSize[3] = width x height 248 * 249 */ 250 $this->imageSize = getimagesize($this->fileName); 251 } else { 252 $this->errorArray[] = 'File is not an image'; 253 } 254 } 255 256## -------------------------------------------------------- 257 258 private function initialise() 259 { 260 $this->psdReaderPath = dirname(__FILE__) . '/classPhpPsdReader.php'; 261 $this->filterOverlayPath = dirname(__FILE__) . '/filters'; 262 263 // *** Set if image should be interlaced or not. 264 $this->isInterlace = false; 265 } 266 267 268 269/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 270 Resize 271*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 272 273 274 public function resizeImage($newWidth, $newHeight, $option = 0, $sharpen = false, $autoRotate = false) 275 # Author: Jarrod Oberto 276 # Date: 27-02-08 277 # Purpose: Resizes the image 278 # Param in: $newWidth: 279 # $newHeight: 280 # $option: 0 / exact = defined size; 281 # 1 / portrait = keep aspect set height; 282 # 2 / landscape = keep aspect set width; 283 # 3 / auto = auto; 284 # 4 / crop= resize and crop; 285 # 286 # $option can also be an array containing options for 287 # cropping. E.G., array('crop', 'r') 288 # 289 # This array only applies to 'crop' and the 'r' refers to 290 # "crop right". Other value include; tl, t, tr, l, m (default), 291 # r, bl, b, br, or you can specify your own co-ords (which 292 # isn't recommended. 293 # 294 # $sharpen: true: sharpen (jpg only); 295 # false: don't sharpen 296 # Param out: n/a 297 # Reference: 298 # Notes: To clarify the $option input: 299 # 0 = The exact height and width dimensions you set. 300 # 1 = Whatever height is passed in will be the height that 301 # is set. The width will be calculated and set automatically 302 # to a the value that keeps the original aspect ratio. 303 # 2 = The same but based on the width. We try make the image the 304 # biggest size we can while stil fitting inside the box size 305 # 3 = Depending whether the image is landscape or portrait, this 306 # will automatically determine whether to resize via 307 # dimension 1,2 or 0 308 # 4 = Will resize and then crop the image for best fit 309 # 310 # forceStretch can be applied to options 1,2,3 and 4 311 # 312 { 313 314 // *** We can pass in an array of options to change the crop position 315 $cropPos = 'm'; 316 if (is_array($option) && fix_strtolower($option[0]) == 'crop') { 317 $cropPos = $option[1]; # get the crop option 318 } elseif (strpos($option, '-') !== false) { 319 // *** Or pass in a hyphen seperated option 320 $optionPiecesArray = explode('-', $option); 321 $cropPos = end($optionPiecesArray); 322 } 323 324 // *** Check the option is valid 325 $option = $this->prepOption($option); 326 327 // *** Make sure the file passed in is valid 328 if (!$this->image) { 329 if ($this->debug) { 330 throw new Exception('file ' . $this->getFileName() .' is missing or invalid'); 331 } else { 332 throw new Exception(); 333 } 334 }; 335 336 // *** Get optimal width and height - based on $option 337 $dimensionsArray = $this->getDimensions($newWidth, $newHeight, $option); 338 339 $optimalWidth = $dimensionsArray['optimalWidth']; 340 $optimalHeight = $dimensionsArray['optimalHeight']; 341 342 // *** Resample - create image canvas of x, y size 343 $this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight); 344 $this->keepTransparancy($optimalWidth, $optimalHeight, $this->imageResized); 345 imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height); 346 347 348 // *** If '4', then crop too 349 if ($option == 4 || $option == 'crop') { 350 if (($optimalWidth >= $newWidth && $optimalHeight >= $newHeight)) { 351 $this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos); 352 } 353 } 354 355 // *** If Rotate. 356 if ($autoRotate) { 357 $exifData = $this->getExif(false); 358 if (count($exifData) > 0) { 359 switch ($exifData['orientation']) { 360 case 8: 361 $this->imageResized = imagerotate($this->imageResized, 90, 0); 362 break; 363 case 3: 364 $this->imageResized = imagerotate($this->imageResized, 180, 0); 365 break; 366 case 6: 367 $this->imageResized = imagerotate($this->imageResized, -90, 0); 368 break; 369 } 370 } 371 } 372 373 // *** Sharpen image (if jpg and the user wishes to do so) 374 if ($sharpen && in_array($this->fileExtension, $this->sharpenArray)) { 375 376 // *** Sharpen 377 $this->sharpen(); 378 } 379 } 380 381## -------------------------------------------------------- 382 383 public function cropImage($newWidth, $newHeight, $cropPos = 'm') 384 # Author: Jarrod Oberto 385 # Date: 08-09-11 386 # Purpose: Crops the image 387 # Param in: $newWidth: crop with 388 # $newHeight: crop height 389 # $cropPos: Can be any of the following: 390 # tl, t, tr, l, m, r, bl, b, br, auto 391 # Or: 392 # a custom position such as '30x50' 393 # Param out: n/a 394 # Reference: 395 # Notes: 396 # 397 { 398 399 // *** Make sure the file passed in is valid 400 if (!$this->image) { 401 if ($this->debug) { 402 throw new Exception('file ' . $this->getFileName() .' is missing or invalid'); 403 } else { 404 throw new Exception(); 405 } 406 }; 407 408 $this->imageResized = $this->image; 409 $this->crop($this->width, $this->height, $newWidth, $newHeight, $cropPos); 410 } 411 412## -------------------------------------------------------- 413 414 private function keepTransparancy($width, $height, $im) 415 # Author: Jarrod Oberto 416 # Date: 08-04-11 417 # Purpose: Keep transparency for png and gif image 418 # Param in: 419 # Param out: n/a 420 # Reference: 421 # Notes: 422 # 423 { 424 // *** If PNG, perform some transparency retention actions (gif untested) 425 if (in_array($this->fileExtension, $this->transparentArray) && $this->keepTransparency) { 426 imagealphablending($im, false); 427 imagesavealpha($im, true); 428 $transparent = imagecolorallocatealpha($im, 255, 255, 255, 127); 429 imagefilledrectangle($im, 0, 0, $width, $height, $transparent); 430 } else { 431 $color = imagecolorallocate($im, $this->fillColorArray['r'], $this->fillColorArray['g'], $this->fillColorArray['b']); 432 imagefilledrectangle($im, 0, 0, $width, $height, $color); 433 } 434 } 435 436## -------------------------------------------------------- 437 438 private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos) 439 # Author: Jarrod Oberto 440 # Date: 15-09-08 441 # Purpose: Crops the image 442 # Param in: $newWidth: 443 # $newHeight: 444 # Param out: n/a 445 # Reference: 446 # Notes: 447 # 448 { 449 450 // *** Get cropping co-ordinates 451 $cropArray = $this->getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $cropPos); 452 $cropStartX = $cropArray['x']; 453 $cropStartY = $cropArray['y']; 454 455 // *** Crop this bad boy 456 $crop = imagecreatetruecolor($newWidth, $newHeight); 457 $this->keepTransparancy($optimalWidth, $optimalHeight, $crop); 458 imagecopyresampled($crop, $this->imageResized, 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight, $newWidth, $newHeight); 459 460 $this->imageResized = $crop; 461 462 // *** Set new width and height to our variables 463 $this->width = $newWidth; 464 $this->height = $newHeight; 465 } 466 467## -------------------------------------------------------- 468 469 private function getCropPlacing($optimalWidth, $optimalHeight, $newWidth, $newHeight, $pos='m') 470 # 471 # Author: Jarrod Oberto 472 # Date: July 11 473 # Purpose: Set the cropping area. 474 # Params in: 475 # Params out: (array) the crop x and y co-ordinates. 476 # Notes: When specifying the exact pixel crop position (eg 10x15), be 477 # very careful as it's easy to crop out of the image leaving 478 # black borders. 479 # 480 { 481 $pos = fix_strtolower($pos); 482 483 // *** If co-ords have been entered 484 if (strstr($pos, 'x')) { 485 $pos = str_replace(' ', '', $pos); 486 487 $xyArray = explode('x', $pos); 488 list($cropStartX, $cropStartY) = $xyArray; 489 } else { 490 switch ($pos) { 491 case 'tl': 492 $cropStartX = 0; 493 $cropStartY = 0; 494 break; 495 496 case 't': 497 $cropStartX = ($optimalWidth / 2) - ($newWidth /2); 498 $cropStartY = 0; 499 break; 500 501 case 'tr': 502 $cropStartX = $optimalWidth - $newWidth; 503 $cropStartY = 0; 504 break; 505 506 case 'l': 507 $cropStartX = 0; 508 $cropStartY = ($optimalHeight/ 2) - ($newHeight/2); 509 break; 510 511 case 'm': 512 $cropStartX = ($optimalWidth / 2) - ($newWidth /2); 513 $cropStartY = ($optimalHeight/ 2) - ($newHeight/2); 514 break; 515 516 case 'r': 517 $cropStartX = $optimalWidth - $newWidth; 518 $cropStartY = ($optimalHeight/ 2) - ($newHeight/2); 519 break; 520 521 case 'bl': 522 $cropStartX = 0; 523 $cropStartY = $optimalHeight - $newHeight; 524 break; 525 526 case 'b': 527 $cropStartX = ($optimalWidth / 2) - ($newWidth /2); 528 $cropStartY = $optimalHeight - $newHeight; 529 break; 530 531 case 'br': 532 $cropStartX = $optimalWidth - $newWidth; 533 $cropStartY = $optimalHeight - $newHeight; 534 break; 535 536 case 'auto': 537 // *** If image is a portrait crop from top, not center. v1.5 538 if ($optimalHeight > $optimalWidth) { 539 $cropStartX = ($optimalWidth / 2) - ($newWidth /2); 540 $cropStartY = ($this->cropFromTopPercent /100) * $optimalHeight; 541 } else { 542 543 // *** Else crop from the center 544 $cropStartX = ($optimalWidth / 2) - ($newWidth /2); 545 $cropStartY = ($optimalHeight/ 2) - ($newHeight/2); 546 } 547 break; 548 549 default: 550 // *** Default to center 551 $cropStartX = ($optimalWidth / 2) - ($newWidth /2); 552 $cropStartY = ($optimalHeight/ 2) - ($newHeight/2); 553 break; 554 } 555 } 556 557 return ['x' => $cropStartX, 'y' => $cropStartY]; 558 } 559 560## -------------------------------------------------------- 561 562 private function getDimensions($newWidth, $newHeight, $option) 563 # Author: Jarrod Oberto 564 # Date: 17-11-09 565 # Purpose: Get new image dimensions based on user specificaions 566 # Param in: $newWidth: 567 # $newHeight: 568 # Param out: Array of new width and height values 569 # Reference: 570 # Notes: If $option = 3 then this function is call recursivly 571 # 572 # To clarify the $option input: 573 # 0 = The exact height and width dimensions you set. 574 # 1 = Whatever height is passed in will be the height that 575 # is set. The width will be calculated and set automatically 576 # to a the value that keeps the original aspect ratio. 577 # 2 = The same but based on the width. 578 # 3 = Depending whether the image is landscape or portrait, this 579 # will automatically determine whether to resize via 580 # dimension 1,2 or 0. 581 # 4 = Resize the image as much as possible, then crop the 582 # remainder. 583 { 584 switch (strval($option)) { 585 case '0': 586 case 'exact': 587 $optimalWidth = $newWidth; 588 $optimalHeight= $newHeight; 589 break; 590 case '1': 591 case 'portrait': 592 $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight); 593 $optimalWidth = $dimensionsArray['optimalWidth']; 594 $optimalHeight = $dimensionsArray['optimalHeight']; 595 break; 596 case '2': 597 case 'landscape': 598 $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight); 599 $optimalWidth = $dimensionsArray['optimalWidth']; 600 $optimalHeight = $dimensionsArray['optimalHeight']; 601 break; 602 case '3': 603 case 'auto': 604 $dimensionsArray = $this->getSizeByAuto($newWidth, $newHeight); 605 $optimalWidth = $dimensionsArray['optimalWidth']; 606 $optimalHeight = $dimensionsArray['optimalHeight']; 607 break; 608 case '4': 609 case 'crop': 610 $dimensionsArray = $this->getOptimalCrop($newWidth, $newHeight); 611 $optimalWidth = $dimensionsArray['optimalWidth']; 612 $optimalHeight = $dimensionsArray['optimalHeight']; 613 break; 614 } 615 616 return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight]; 617 } 618 619## -------------------------------------------------------- 620 621 private function getSizeByFixedHeight($newWidth, $newHeight) 622 { 623 // *** If forcing is off... 624 if (!$this->forceStretch) { 625 626 // *** ...check if actual height is less than target height 627 if ($this->height < $newHeight) { 628 return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height]; 629 } 630 } 631 632 $ratio = $this->width / $this->height; 633 634 $newWidth = $newHeight * $ratio; 635 636 //return $newWidth; 637 return ['optimalWidth' => $newWidth, 'optimalHeight' => $newHeight]; 638 } 639 640## -------------------------------------------------------- 641 642 private function getSizeByFixedWidth($newWidth, $newHeight) 643 { 644 // *** If forcing is off... 645 if (!$this->forceStretch) { 646 647 // *** ...check if actual width is less than target width 648 if ($this->width < $newWidth) { 649 return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height]; 650 } 651 } 652 653 $ratio = $this->height / $this->width; 654 655 $newHeight = $newWidth * $ratio; 656 657 //return $newHeight; 658 return ['optimalWidth' => $newWidth, 'optimalHeight' => $newHeight]; 659 } 660 661## -------------------------------------------------------- 662 663 private function getSizeByAuto($newWidth, $newHeight) 664 # Author: Jarrod Oberto 665 # Date: 19-08-08 666 # Purpose: Depending on the height, choose to resize by 0, 1, or 2 667 # Param in: The new height and new width 668 # Notes: 669 # 670 { 671 // *** If forcing is off... 672 if (!$this->forceStretch) { 673 674 // *** ...check if actual size is less than target size 675 if ($this->width < $newWidth && $this->height < $newHeight) { 676 return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height]; 677 } 678 } 679 680 if ($this->height < $this->width) { 681 // *** Image to be resized is wider (landscape) 682 683 //$optimalWidth = $newWidth; 684 //$optimalHeight= $this->getSizeByFixedWidth($newWidth); 685 686 $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight); 687 $optimalWidth = $dimensionsArray['optimalWidth']; 688 $optimalHeight = $dimensionsArray['optimalHeight']; 689 } elseif ($this->height > $this->width) { 690 // *** Image to be resized is taller (portrait) 691 692 //$optimalWidth = $this->getSizeByFixedHeight($newHeight); 693 //$optimalHeight= $newHeight; 694 695 $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight); 696 $optimalWidth = $dimensionsArray['optimalWidth']; 697 $optimalHeight = $dimensionsArray['optimalHeight']; 698 } else { 699 // *** Image to be resizerd is a square 700 701 702 if ($newHeight < $newWidth) { 703 //$optimalWidth = $newWidth; 704 //$optimalHeight= $this->getSizeByFixedWidth($newWidth); 705 $dimensionsArray = $this->getSizeByFixedWidth($newWidth, $newHeight); 706 $optimalWidth = $dimensionsArray['optimalWidth']; 707 $optimalHeight = $dimensionsArray['optimalHeight']; 708 } elseif ($newHeight > $newWidth) { 709 //$optimalWidth = $this->getSizeByFixedHeight($newHeight); 710 //$optimalHeight= $newHeight; 711 $dimensionsArray = $this->getSizeByFixedHeight($newWidth, $newHeight); 712 $optimalWidth = $dimensionsArray['optimalWidth']; 713 $optimalHeight = $dimensionsArray['optimalHeight']; 714 } else { 715 // *** Sqaure being resized to a square 716 $optimalWidth = $newWidth; 717 $optimalHeight= $newHeight; 718 } 719 } 720 721 return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight]; 722 } 723 724## -------------------------------------------------------- 725 726 private function getOptimalCrop($newWidth, $newHeight) 727 # Author: Jarrod Oberto 728 # Date: 17-11-09 729 # Purpose: Get optimal crop dimensions 730 # Param in: width and height as requested by user (fig 3) 731 # Param out: Array of optimal width and height (fig 2) 732 # Reference: 733 # Notes: The optimal width and height return are not the same as the 734 # same as the width and height passed in. For example: 735 # 736 # 737 # |-----------------| |------------| |-------| 738 # | | => |**| |**| => | | 739 # | | |**| |**| | | 740 # | | |------------| |-------| 741 # |-----------------| 742 # original optimal crop 743 # size size size 744 # Fig 1 2 3 745 # 746 # 300 x 250 150 x 125 150 x 100 747 # 748 # The optimal size is the smallest size (that is closest to the crop size) 749 # while retaining proportion/ratio. 750 # 751 # The crop size is the optimal size that has been cropped on one axis to 752 # make the image the exact size specified by the user. 753 # 754 # * represent cropped area 755 # 756 { 757 758 // *** If forcing is off... 759 if (!$this->forceStretch) { 760 761 // *** ...check if actual size is less than target size 762 if ($this->width < $newWidth && $this->height < $newHeight) { 763 return ['optimalWidth' => $this->width, 'optimalHeight' => $this->height]; 764 } 765 } 766 767 $heightRatio = $this->height / $newHeight; 768 $widthRatio = $this->width / $newWidth; 769 770 if ($heightRatio < $widthRatio) { 771 $optimalRatio = $heightRatio; 772 } else { 773 $optimalRatio = $widthRatio; 774 } 775 776 $optimalHeight = round($this->height / $optimalRatio); 777 $optimalWidth = round($this->width / $optimalRatio); 778 779 return ['optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight]; 780 } 781 782## -------------------------------------------------------- 783 784 private function sharpen() 785 # Author: Jarrod Oberto 786 # Date: 08 04 2011 787 # Purpose: Sharpen image 788 # Param in: n/a 789 # Param out: n/a 790 # Reference: 791 # Notes: 792 # Credit: Incorporates Joe Lencioni (August 6, 2008) code 793 { 794 if (version_compare(PHP_VERSION, '5.1.0') >= 0) { 795 796 // *** 797 if ($this->aggresiveSharpening) { # A more aggressive sharpening solution 798 799 $sharpenMatrix = [ 800 [-1, -1, -1], 801 [-1, 16, -1], 802 [-1, -1, -1] 803 ]; 804 $divisor = 8; 805 $offset = 0; 806 807 imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset); 808 } else { 809 # More subtle and personally more desirable 810 811 $sharpness = $this->findSharp($this->widthOriginal, $this->width); 812 813 $sharpenMatrix = [ 814 [-1, -2, -1], 815 [-2, $sharpness + 12, -2], //Lessen the effect of a filter by increasing the value in the center cell 816 [-1, -2, -1] 817 ]; 818 $divisor = $sharpness; // adjusts brightness 819 $offset = 0; 820 imageconvolution($this->imageResized, $sharpenMatrix, $divisor, $offset); 821 } 822 } else { 823 if ($this->debug) { 824 throw new Exception('Sharpening required PHP 5.1.0 or greater.'); 825 } 826 } 827 } 828 829 ## -------------------------------------------------------- 830 831 private function sharpen2($level) 832 { 833 $sharpenMatrix = [ 834 [$level, $level, $level], 835 [$level, (8*$level)+1, $level], //Lessen the effect of a filter by increasing the value in the center cell 836 [$level, $level, $level] 837 ]; 838 } 839 840## -------------------------------------------------------- 841 842 private function findSharp($orig, $final) 843 # Author: Ryan Rud (http://adryrun.com) 844 # Purpose: Find optimal sharpness 845 # Param in: n/a 846 # Param out: n/a 847 # Reference: 848 # Notes: 849 # 850 { 851 $final = $final * (750.0 / $orig); 852 $a = 52; 853 $b = -0.27810650887573124; 854 $c = .00047337278106508946; 855 856 $result = $a + $b * $final + $c * $final * $final; 857 858 return max(round($result), 0); 859 } 860 861## -------------------------------------------------------- 862 863 private function prepOption($option) 864 # Author: Jarrod Oberto 865 # Purpose: Prep option like change the passed in option to lowercase 866 # Param in: (str/int) $option: eg. 'exact', 'crop'. 0, 4 867 # Param out: lowercase string 868 # Reference: 869 # Notes: 870 # 871 { 872 if (is_array($option)) { 873 if (fix_strtolower($option[0]) == 'crop' && count($option) == 2) { 874 return 'crop'; 875 } else { 876 throw new Exception('Crop resize option array is badly formatted.'); 877 } 878 } elseif (strpos($option, 'crop') !== false) { 879 return 'crop'; 880 } 881 882 if (is_string($option)) { 883 return fix_strtolower($option); 884 } 885 886 return $option; 887 } 888 889 890/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 891 Presets 892*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 893 894# 895# Preset are pre-defined templates you can apply to your image. 896# 897# These are inteded to be applied to thumbnail images. 898# 899 900 901 public function borderPreset($preset) 902 { 903 switch ($preset) { 904 905 case 'simple': 906 $this->addBorder(7, '#fff'); 907 $this->addBorder(6, '#f2f1f0'); 908 $this->addBorder(2, '#fff'); 909 $this->addBorder(1, '#ccc'); 910 break; 911 default: 912 break; 913 } 914 } 915 916 917/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 918 Draw border 919*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 920 921 public function addBorder($thickness = 1, $rgbArray = [255, 255, 255]) 922 # Author: Jarrod Oberto 923 # Date: 05-05-11 924 # Purpose: Add a border to the image 925 # Param in: 926 # Param out: 927 # Reference: 928 # Notes: This border is added to the INSIDE of the image 929 # 930 { 931 if ($this->imageResized) { 932 $rgbArray = $this->formatColor($rgbArray); 933 $r = $rgbArray['r']; 934 $g = $rgbArray['g']; 935 $b = $rgbArray['b']; 936 937 938 $x1 = 0; 939 $y1 = 0; 940 $x2 = ImageSX($this->imageResized) - 1; 941 $y2 = ImageSY($this->imageResized) - 1; 942 943 $rgbArray = ImageColorAllocate($this->imageResized, $r, $g, $b); 944 945 946 for ($i = 0; $i < $thickness; $i++) { 947 ImageRectangle($this->imageResized, $x1++, $y1++, $x2--, $y2--, $rgbArray); 948 } 949 } 950 } 951 952 953/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 954 Gray Scale 955*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 956 957 public function greyScale() 958 # Author: Jarrod Oberto 959 # Date: 07-05-2011 960 # Purpose: Make image greyscale 961 # Param in: n/a 962 # Param out: 963 # Reference: 964 # Notes: 965 # 966 { 967 if ($this->imageResized) { 968 imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); 969 } 970 } 971 972 ## -------------------------------------------------------- 973 974 public function greyScaleEnhanced() 975 # Author: Jarrod Oberto 976 # Date: 07-05-2011 977 # Purpose: Make image greyscale 978 # Param in: n/a 979 # Param out: 980 # Reference: 981 # Notes: 982 # 983 { 984 if ($this->imageResized) { 985 imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); 986 imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15); 987 imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 2); 988 $this->sharpen($this->width); 989 } 990 } 991 992 ## -------------------------------------------------------- 993 994 public function greyScaleDramatic() 995 # Alias of gd_filter_monopin 996 { 997 $this->gd_filter_monopin(); 998 } 999 1000 1001/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1002 Black 'n White 1003*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1004 1005 public function blackAndWhite() 1006 # Author: Jarrod Oberto 1007 # Date: 07-05-2011 1008 # Purpose: Make image black and white 1009 # Param in: n/a 1010 # Param out: 1011 # Reference: 1012 # Notes: 1013 # 1014 { 1015 if ($this->imageResized) { 1016 imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); 1017 imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -1000); 1018 } 1019 } 1020 1021 1022/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1023 Negative 1024*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1025 1026 public function negative() 1027 # Author: Jarrod Oberto 1028 # Date: 07-05-2011 1029 # Purpose: Make image negative 1030 # Param in: n/a 1031 # Param out: 1032 # Reference: 1033 # Notes: 1034 # 1035 { 1036 if ($this->imageResized) { 1037 imagefilter($this->imageResized, IMG_FILTER_NEGATE); 1038 } 1039 } 1040 1041 1042/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1043 Sepia 1044*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1045 1046 public function sepia() 1047 # Author: Jarrod Oberto 1048 # Date: 07-05-2011 1049 # Purpose: Make image sepia 1050 # Param in: n/a 1051 # Param out: 1052 # Reference: 1053 # Notes: 1054 # 1055 { 1056 if ($this->imageResized) { 1057 imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); 1058 imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -10); 1059 imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -20); 1060 imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, 30, -15); 1061 } 1062 } 1063 1064 ## -------------------------------------------------------- 1065 1066 public function sepia2() 1067 { 1068 if ($this->imageResized) { 1069 $total = imagecolorstotal($this->imageResized); 1070 for ($i = 0; $i < $total; $i++) { 1071 $index = imagecolorsforindex($this->imageResized, $i); 1072 $red = ($index["red"] * 0.393 + $index["green"] * 0.769 + $index["blue"] * 0.189) / 1.351; 1073 $green = ($index["red"] * 0.349 + $index["green"] * 0.686 + $index["blue"] * 0.168) / 1.203; 1074 $blue = ($index["red"] * 0.272 + $index["green"] * 0.534 + $index["blue"] * 0.131) / 2.140; 1075 imagecolorset($this->imageResized, $i, $red, $green, $blue); 1076 } 1077 } 1078 } 1079 1080 1081/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1082 Vintage 1083*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1084 1085 public function vintage() 1086 # Alias of gd_filter_monopin 1087 { 1088 $this->gd_filter_vintage(); 1089 } 1090 1091/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1092 Presets By Marc Hibbins 1093*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1094 1095 1096 /** Apply 'Monopin' preset */ 1097 public function gd_filter_monopin() 1098 { 1099 if ($this->imageResized) { 1100 imagefilter($this->imageResized, IMG_FILTER_GRAYSCALE); 1101 imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, -15); 1102 imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -15); 1103 $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 100); 1104 } 1105 } 1106 1107 ## -------------------------------------------------------- 1108 1109 public function gd_filter_vintage() 1110 { 1111 if ($this->imageResized) { 1112 $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'vignette', 45); 1113 imagefilter($this->imageResized, IMG_FILTER_BRIGHTNESS, 20); 1114 imagefilter($this->imageResized, IMG_FILTER_CONTRAST, -35); 1115 imagefilter($this->imageResized, IMG_FILTER_COLORIZE, 60, -10, 35); 1116 imagefilter($this->imageResized, IMG_FILTER_SMOOTH, 7); 1117 $this->imageResized = $this->gd_apply_overlay($this->imageResized, 'scratch', 10); 1118 } 1119 } 1120 1121 ## -------------------------------------------------------- 1122 1123 /** Apply a PNG overlay */ 1124 private function gd_apply_overlay($im, $type, $amount) 1125 # 1126 # Original Author: Marc Hibbins 1127 # License: Attribution-ShareAlike 3.0 1128 # Purpose: 1129 # Params in: 1130 # Params out: 1131 # Notes: 1132 # 1133 { 1134 $width = imagesx($im); 1135 $height = imagesy($im); 1136 $filter = imagecreatetruecolor($width, $height); 1137 1138 imagealphablending($filter, false); 1139 imagesavealpha($filter, true); 1140 1141 $transparent = imagecolorallocatealpha($filter, 255, 255, 255, 127); 1142 imagefilledrectangle($filter, 0, 0, $width, $height, $transparent); 1143 1144 // *** Resize overlay 1145 $overlay = $this->filterOverlayPath . '/' . $type . '.png'; 1146 $png = imagecreatefrompng($overlay); 1147 imagecopyresampled($filter, $png, 0, 0, 0, 0, $width, $height, imagesx($png), imagesy($png)); 1148 1149 $comp = imagecreatetruecolor($width, $height); 1150 imagecopy($comp, $im, 0, 0, 0, 0, $width, $height); 1151 imagecopy($comp, $filter, 0, 0, 0, 0, $width, $height); 1152 imagecopymerge($im, $comp, 0, 0, 0, 0, $width, $height, $amount); 1153 1154 imagedestroy($comp); 1155 return $im; 1156 } 1157 1158 1159/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1160 Colorise 1161*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1162 1163 public function image_colorize($rgb) 1164 { 1165 imageTrueColorToPalette($this->imageResized, true, 256); 1166 $numColors = imageColorsTotal($this->imageResized); 1167 1168 for ($x = 0; $x < $numColors; $x++) { 1169 list($r, $g, $b) = array_values(imageColorsForIndex($this->imageResized, $x)); 1170 1171 // calculate grayscale in percent 1172 $grayscale = ($r + $g + $b) / 3 / 0xff; 1173 1174 imageColorSet($this->imageResized, $x, 1175 $grayscale * $rgb[0], 1176 $grayscale * $rgb[1], 1177 $grayscale * $rgb[2] 1178 ); 1179 } 1180 1181 return true; 1182 } 1183 1184 1185/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1186 Reflection 1187*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1188 1189 public function addReflection($reflectionHeight = 50, $startingTransparency = 30, $inside = false, $bgColor = '#fff', $stretch=false, $divider = 0) 1190 { 1191 1192 // *** Convert color 1193 $rgbArray = $this->formatColor($bgColor); 1194 $r = $rgbArray['r']; 1195 $g = $rgbArray['g']; 1196 $b = $rgbArray['b']; 1197 1198 $im = $this->imageResized; 1199 $li = imagecreatetruecolor($this->width, 1); 1200 1201 $bgc = imagecolorallocate($li, $r, $g, $b); 1202 imagefilledrectangle($li, 0, 0, $this->width, 1, $bgc); 1203 1204 $bg = imagecreatetruecolor($this->width, $reflectionHeight); 1205 $wh = imagecolorallocate($im, 255, 255, 255); 1206 1207 $im = imagerotate($im, -180, $wh); 1208 imagecopyresampled($bg, $im, 0, 0, 0, 0, $this->width, $this->height, $this->width, $this->height); 1209 1210 $im = $bg; 1211 1212 $bg = imagecreatetruecolor($this->width, $reflectionHeight); 1213 1214 for ($x = 0; $x < $this->width; $x++) { 1215 imagecopy($bg, $im, $x, 0, $this->width-$x -1, 0, 1, $reflectionHeight); 1216 } 1217 $im = $bg; 1218 1219 $transparencyAmount = $this->invertTransparency($startingTransparency, 100); 1220 1221 1222 // *** Fade 1223 if ($stretch) { 1224 $step = 100/($reflectionHeight + $startingTransparency); 1225 } else { 1226 $step = 100/$reflectionHeight; 1227 } 1228 for ($i=0; $i<=$reflectionHeight; $i++) { 1229 if ($startingTransparency>100) { 1230 $startingTransparency = 100; 1231 } 1232 if ($startingTransparency< 1) { 1233 $startingTransparency = 1; 1234 } 1235 imagecopymerge($bg, $li, 0, $i, 0, 0, $this->width, 1, $startingTransparency); 1236 $startingTransparency+=$step; 1237 } 1238 1239 // *** Apply fade 1240 imagecopymerge($im, $li, 0, 0, 0, 0, $this->width, $divider, 100); // Divider 1241 1242 1243 // *** width, height of reflection. 1244 $x = imagesx($im); 1245 $y = imagesy($im); 1246 1247 1248 // *** Determines if the reflection should be displayed inside or outside the image 1249 if ($inside) { 1250 1251 // Create new blank image with sizes. 1252 $final = imagecreatetruecolor($this->width, $this->height); 1253 1254 imagecopymerge($final, $this->imageResized, 0, 0, 0, $reflectionHeight, $this->width, $this->height - $reflectionHeight, 100); 1255 imagecopymerge($final, $im, 0, $this->height - $reflectionHeight, 0, 0, $x, $y, 100); 1256 } else { 1257 1258 // Create new blank image with sizes. 1259 $final = imagecreatetruecolor($this->width, $this->height + $y); 1260 1261 imagecopymerge($final, $this->imageResized, 0, 0, 0, 0, $this->width, $this->height, 100); 1262 imagecopymerge($final, $im, 0, $this->height, 0, 0, $x, $y, 100); 1263 } 1264 1265 $this->imageResized = $final; 1266 1267 imagedestroy($li); 1268 imagedestroy($im); 1269 } 1270 1271 1272/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1273 Rotate 1274*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1275 1276 public function rotate($value = 90, $bgColor = 'transparent') 1277 # Author: Jarrod Oberto 1278 # Date: 07-05-2011 1279 # Purpose: Rotate image 1280 # Param in: (mixed) $degrees: (int) number of degress to rotate image 1281 # (str) param "left": rotate left 1282 # (str) param "right": rotate right 1283 # (str) param "upside": upside-down image 1284 # Param out: 1285 # Reference: 1286 # Notes: The default direction of imageRotate() is counter clockwise. 1287 # 1288 { 1289 if ($this->imageResized) { 1290 if (is_integer($value)) { 1291 $degrees = $value; 1292 } 1293 1294 // *** Convert color 1295 $rgbArray = $this->formatColor($bgColor); 1296 $r = $rgbArray['r']; 1297 $g = $rgbArray['g']; 1298 $b = $rgbArray['b']; 1299 if (isset($rgbArray['a'])) { 1300 $a = $rgbArray['a']; 1301 } 1302 1303 if (is_string($value)) { 1304 $value = fix_strtolower($value); 1305 1306 switch ($value) { 1307 case 'left': 1308 $degrees = 90; 1309 break; 1310 case 'right': 1311 $degrees = 270; 1312 break; 1313 case 'upside': 1314 $degrees = 180; 1315 break; 1316 default: 1317 break; 1318 } 1319 } 1320 1321 // *** The default direction of imageRotate() is counter clockwise 1322 // * This makes it clockwise 1323 $degrees = 360 - $degrees; 1324 1325 // *** Create background color 1326 $bg = ImageColorAllocateAlpha($this->imageResized, $r, $g, $b, $a); 1327 1328 // *** Fill with background 1329 ImageFill($this->imageResized, 0, 0, $bg); 1330 1331 // *** Rotate 1332 $this->imageResized = imagerotate($this->imageResized, $degrees, $bg); // Rotate 45 degrees and allocated the transparent colour as the one to make transparent (obviously) 1333 1334 // Ensure alpha transparency 1335 ImageSaveAlpha($this->imageResized, true); 1336 } 1337 } 1338 1339 1340/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1341 Round corners 1342*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1343 1344 public function roundCorners($radius = 5, $bgColor = 'transparent') 1345 # Author: Jarrod Oberto 1346 # Date: 19-05-2011 1347 # Purpose: Create rounded corners on your image 1348 # Param in: (int) radius = the amount of curvature 1349 # (mixed) $bgColor = the corner background color 1350 # Param out: n/a 1351 # Reference: 1352 # Notes: 1353 # 1354 { 1355 1356 // *** Check if the user wants transparency 1357 $isTransparent = false; 1358 if (!is_array($bgColor)) { 1359 if (fix_strtolower($bgColor) == 'transparent') { 1360 $isTransparent = true; 1361 } 1362 } 1363 1364 1365 // *** If we use transparency, we need to color our curved mask with a unique color 1366 if ($isTransparent) { 1367 $bgColor = $this->findUnusedGreen(); 1368 } 1369 1370 // *** Convert color 1371 $rgbArray = $this->formatColor($bgColor); 1372 $r = $rgbArray['r']; 1373 $g = $rgbArray['g']; 1374 $b = $rgbArray['b']; 1375 if (isset($rgbArray['a'])) { 1376 $a = $rgbArray['a']; 1377 } 1378 1379 1380 1381 // *** Create top-left corner mask (square) 1382 $cornerImg = imagecreatetruecolor($radius, $radius); 1383 //$cornerImg = imagecreate($radius, $radius); 1384 1385 //imagealphablending($cornerImg, true); 1386 //imagesavealpha($cornerImg, true); 1387 1388 //imagealphablending($this->imageResized, false); 1389 //imagesavealpha($this->imageResized, true); 1390 1391 // *** Give it a color 1392 $maskColor = imagecolorallocate($cornerImg, 0, 0, 0); 1393 1394 1395 1396 // *** Replace the mask color (black) to transparent 1397 imagecolortransparent($cornerImg, $maskColor); 1398 1399 1400 1401 // *** Create the image background color 1402 $imagebgColor = imagecolorallocate($cornerImg, $r, $g, $b); 1403 1404 1405 1406 // *** Fill the corner area to the user defined color 1407 imagefill($cornerImg, 0, 0, $imagebgColor); 1408 1409 1410 imagefilledellipse($cornerImg, $radius, $radius, $radius * 2, $radius * 2, $maskColor); 1411 1412 1413 // *** Map to top left corner 1414 imagecopymerge($this->imageResized, $cornerImg, 0, 0, 0, 0, $radius, $radius, 100); #tl 1415 1416 // *** Map rounded corner to other corners by rotating and applying the mask 1417 $cornerImg = imagerotate($cornerImg, 90, 0); 1418 imagecopymerge($this->imageResized, $cornerImg, 0, $this->height - $radius, 0, 0, $radius, $radius, 100); #bl 1419 1420 $cornerImg = imagerotate($cornerImg, 90, 0); 1421 imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, $this->height - $radius, 0, 0, $radius, $radius, 100); #br 1422 1423 $cornerImg = imagerotate($cornerImg, 90, 0); 1424 imagecopymerge($this->imageResized, $cornerImg, $this->width - $radius, 0, 0, 0, $radius, $radius, 100); #tr 1425 1426 1427 // *** If corners are to be transparent, we fill our chromakey color as transparent. 1428 if ($isTransparent) { 1429 //imagecolortransparent($this->imageResized, $imagebgColor); 1430 $this->imageResized = $this->transparentImage($this->imageResized); 1431 imagesavealpha($this->imageResized, true); 1432 } 1433 } 1434 1435 1436/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1437 Shadow 1438*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1439 1440 public function addShadow($shadowAngle=45, $blur=15, $bgColor='transparent') 1441 # 1442 # Author: Jarrod Oberto (Adapted from Pascal Naidon) 1443 # Ref: http://www.les-stooges.org/pascal/webdesign/vignettes/index.php?la=en 1444 # Purpose: Add a drop shadow to your image 1445 # Params in: (int) $angle: the angle of the shadow 1446 # (int) $blur: the blur distance 1447 # (mixed) $bgColor: the color of the background 1448 # Params out: 1449 # Notes: 1450 # 1451 { 1452 // *** A higher number results in a smoother shadow 1453 define('STEPS', $blur*2); 1454 1455 // *** Set the shadow distance 1456 $shadowDistance = $blur*0.25; 1457 1458 // *** Set blur width and height 1459 $blurWidth = $blurHeight = $blur; 1460 1461 1462 if ($shadowAngle == 0) { 1463 $distWidth = 0; 1464 $distHeight = 0; 1465 } else { 1466 $distWidth = $shadowDistance * cos(deg2rad($shadowAngle)); 1467 $distHeight = $shadowDistance * sin(deg2rad($shadowAngle)); 1468 } 1469 1470 1471 // *** Convert color 1472 if (fix_strtolower($bgColor) != 'transparent') { 1473 $rgbArray = $this->formatColor($bgColor); 1474 $r0 = $rgbArray['r']; 1475 $g0 = $rgbArray['g']; 1476 $b0 = $rgbArray['b']; 1477 } 1478 1479 1480 $image = $this->imageResized; 1481 $width = $this->width; 1482 $height = $this->height; 1483 1484 1485 $newImage = imagecreatetruecolor($width, $height); 1486 imagecopyresampled($newImage, $image, 0, 0, 0, 0, $width, $height, $width, $height); 1487 1488 1489 // *** RGB 1490 $rgb = imagecreatetruecolor($width+$blurWidth, $height+$blurHeight); 1491 $colour = imagecolorallocate($rgb, 0, 0, 0); 1492 imagefilledrectangle($rgb, 0, 0, $width+$blurWidth, $height+$blurHeight, $colour); 1493 $colour = imagecolorallocate($rgb, 255, 255, 255); 1494 //imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour); 1495 imagefilledrectangle($rgb, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-$distWidth, $height+$blurWidth*0.5-$distHeight, $colour); 1496 //imagecopymerge($rgb, $newImage, 1+$blurWidth*0.5-$distWidth, 1+$blurHeight*0.5-$distHeight, 0,0, $width, $height, 100); 1497 imagecopymerge($rgb, $newImage, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, 0, 0, $width+$blurWidth, $height+$blurHeight, 100); 1498 1499 1500 // *** Shadow (alpha) 1501 $shadow = imagecreatetruecolor($width+$blurWidth, $height+$blurHeight); 1502 imagealphablending($shadow, false); 1503 $colour = imagecolorallocate($shadow, 0, 0, 0); 1504 imagefilledrectangle($shadow, 0, 0, $width+$blurWidth, $height+$blurHeight, $colour); 1505 1506 1507 for ($i=0;$i<=STEPS;$i++) { 1508 $t = ((1.0*$i)/STEPS); 1509 $intensity = 255*$t*$t; 1510 1511 $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity); 1512 $points = [ 1513 $blurWidth*$t, $blurHeight, // Point 1 (x, y) 1514 $blurWidth, $blurHeight*$t, // Point 2 (x, y) 1515 $width, $blurHeight*$t, // Point 3 (x, y) 1516 $width+$blurWidth*(1-$t), $blurHeight, // Point 4 (x, y) 1517 $width+$blurWidth*(1-$t), $height, // Point 5 (x, y) 1518 $width, $height+$blurHeight*(1-$t), // Point 6 (x, y) 1519 $blurWidth, $height+$blurHeight*(1-$t), // Point 7 (x, y) 1520 $blurWidth*$t, $height // Point 8 (x, y) 1521 ]; 1522 imagepolygon($shadow, $points, 8, $colour); 1523 } 1524 1525 for ($i=0;$i<=STEPS;$i++) { 1526 $t = ((1.0*$i)/STEPS); 1527 $intensity = 255*$t*$t; 1528 1529 $colour = imagecolorallocate($shadow, $intensity, $intensity, $intensity); 1530 imagefilledarc($shadow, $blurWidth-1, $blurHeight-1, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 180, 268, $colour, IMG_ARC_PIE); 1531 imagefilledarc($shadow, $width, $blurHeight-1, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 270, 358, $colour, IMG_ARC_PIE); 1532 imagefilledarc($shadow, $width, $height, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 0, 90, $colour, IMG_ARC_PIE); 1533 imagefilledarc($shadow, $blurWidth-1, $height, 2*(1-$t)*$blurWidth, 2*(1-$t)*$blurHeight, 90, 180, $colour, IMG_ARC_PIE); 1534 } 1535 1536 1537 $colour = imagecolorallocate($shadow, 255, 255, 255); 1538 imagefilledrectangle($shadow, $blurWidth, $blurHeight, $width, $height, $colour); 1539 imagefilledrectangle($shadow, $blurWidth*0.5-$distWidth, $blurHeight*0.5-$distHeight, $width+$blurWidth*0.5-1-$distWidth, $height+$blurHeight*0.5-1-$distHeight, $colour); 1540 1541 1542 // *** The magic 1543 imagealphablending($rgb, false); 1544 1545 for ($theX=0;$theX<imagesx($rgb);$theX++) { 1546 for ($theY=0;$theY<imagesy($rgb);$theY++) { 1547 1548 // *** Get the RGB values for every pixel of the RGB image 1549 $colArray = imagecolorat($rgb, $theX, $theY); 1550 $r = ($colArray >> 16) & 0xFF; 1551 $g = ($colArray >> 8) & 0xFF; 1552 $b = $colArray & 0xFF; 1553 1554 // *** Get the alpha value for every pixel of the shadow image 1555 $colArray = imagecolorat($shadow, $theX, $theY); 1556 $a = $colArray & 0xFF; 1557 $a = 127-floor($a/2); 1558 $t = $a/128.0; 1559 1560 // *** Create color 1561 if (fix_strtolower($bgColor) == 'transparent') { 1562 $myColour = imagecolorallocatealpha($rgb, $r, $g, $b, $a); 1563 } else { 1564 $myColour = imagecolorallocate($rgb, $r*(1.0-$t)+$r0*$t, $g*(1.0-$t)+$g0*$t, $b*(1.0-$t)+$b0*$t); 1565 } 1566 1567 // *** Add color to new rgb image 1568 imagesetpixel($rgb, $theX, $theY, $myColour); 1569 } 1570 } 1571 1572 imagealphablending($rgb, true); 1573 imagesavealpha($rgb, true); 1574 1575 $this->imageResized = $rgb; 1576 1577 imagedestroy($image); 1578 imagedestroy($newImage); 1579 imagedestroy($shadow); 1580 } 1581 1582 1583/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1584 Add Caption Box 1585*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1586 1587 public function addCaptionBox($side='b', $thickness=50, $padding=0, $bgColor='#000', $transparencyAmount=30) 1588 # 1589 # Author: Jarrod Oberto 1590 # Date: 26 May 2011 1591 # Purpose: Add a caption box 1592 # Params in: (str) $side: the side to add the caption box (t, r, b, or l). 1593 # (int) $thickness: how thick you want the caption box to be. 1594 # (mixed) $bgColor: The color of the caption box. 1595 # (int) $transparencyAmount: The amount of transparency to be 1596 # applied. 1597 # Params out: n/a 1598 # Notes: 1599 # 1600 { 1601 $side = fix_strtolower($side); 1602 1603 // *** Convert color 1604 $rgbArray = $this->formatColor($bgColor); 1605 $r = $rgbArray['r']; 1606 $g = $rgbArray['g']; 1607 $b = $rgbArray['b']; 1608 1609 $positionArray = $this->calculateCaptionBoxPosition($side, $thickness, $padding); 1610 1611 // *** Store incase we want to use method addTextToCaptionBox() 1612 $this->captionBoxPositionArray = $positionArray; 1613 1614 1615 $transparencyAmount = $this->invertTransparency($transparencyAmount, 127, false); 1616 $transparent = imagecolorallocatealpha($this->imageResized, $r, $g, $b, $transparencyAmount); 1617 imagefilledrectangle($this->imageResized, $positionArray['x1'], $positionArray['y1'], $positionArray['x2'], $positionArray['y2'], $transparent); 1618 } 1619 1620 ## -------------------------------------------------------- 1621 1622 public function addTextToCaptionBox($text, $fontColor='#fff', $fontSize = 12, $angle = 0, $font = null) 1623 # 1624 # Author: Jarrod Oberto 1625 # Date: 03 Aug 11 1626 # Purpose: Simplify adding text to a caption box by automatically 1627 # locating the center of the caption box 1628 # Params in: The usually text paams (less a couple) 1629 # Params out: n/a 1630 # Notes: 1631 # 1632 { 1633 1634 // *** Get the caption box measurements 1635 if (count($this->captionBoxPositionArray) == 4) { 1636 $x1 = $this->captionBoxPositionArray['x1']; 1637 $x2 = $this->captionBoxPositionArray['x2']; 1638 $y1 = $this->captionBoxPositionArray['y1']; 1639 $y2 = $this->captionBoxPositionArray['y2']; 1640 } else { 1641 if ($this->debug) { 1642 throw new Exception('No caption box found.'); 1643 } else { 1644 return false; 1645 } 1646 } 1647 1648 1649 // *** Get text font 1650 $font = $this->getTextFont($font); 1651 1652 // *** Get text size 1653 $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text); 1654 $textWidth = $textSizeArray['width']; 1655 $textHeight = $textSizeArray['height']; 1656 1657 // *** Find the width/height middle points 1658 $boxXMiddle = (($x2 - $x1) / 2); 1659 $boxYMiddle = (($y2 - $y1) / 2); 1660 1661 // *** Box middle - half the text width/height 1662 $xPos = ($x1 + $boxXMiddle) - ($textWidth/2); 1663 $yPos = ($y1 + $boxYMiddle) - ($textHeight/2); 1664 1665 $pos = $xPos . 'x' . $yPos; 1666 1667 $this->addText($text, $pos, $padding = 0, $fontColor, $fontSize, $angle, $font); 1668 } 1669 1670 ## -------------------------------------------------------- 1671 1672 private function calculateCaptionBoxPosition($side, $thickness, $padding) 1673 { 1674 $positionArray = []; 1675 1676 switch ($side) { 1677 case 't': 1678 $positionArray['x1'] = 0; 1679 $positionArray['y1'] = $padding; 1680 $positionArray['x2'] = $this->width; 1681 $positionArray['y2'] = $thickness + $padding; 1682 break; 1683 case 'r': 1684 $positionArray['x1'] = $this->width - $thickness - $padding; 1685 $positionArray['y1'] = 0; 1686 $positionArray['x2'] = $this->width - $padding; 1687 $positionArray['y2'] = $this->height; 1688 break; 1689 case 'b': 1690 $positionArray['x1'] = 0; 1691 $positionArray['y1'] = $this->height - $thickness - $padding; 1692 $positionArray['x2'] = $this->width; 1693 $positionArray['y2'] = $this->height - $padding; 1694 break; 1695 case 'l': 1696 $positionArray['x1'] = $padding; 1697 $positionArray['y1'] = 0; 1698 $positionArray['x2'] = $thickness + $padding; 1699 $positionArray['y2'] = $this->height; 1700 break; 1701 1702 default: 1703 break; 1704 } 1705 1706 return $positionArray; 1707 } 1708 1709/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 1710 Get EXIF Data 1711*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 1712 1713 public function getExif($debug=false) 1714 # Author: Jarrod Oberto 1715 # Date: 07-05-2011 1716 # Purpose: Get image EXIF data 1717 # Param in: n/a 1718 # Param out: An associate array of EXIF data 1719 # Reference: 1720 # Notes: 1721 # 23 May 13 : added orientation flag -jco 1722 # 1723 { 1724 if (!$this->debug || !$debug) { 1725 $debug = false; 1726 } 1727 1728 // *** Check all is good - check the EXIF library exists and the file exists, too. 1729 if (!$this->testEXIFInstalled()) { 1730 if ($debug) { 1731 throw new Exception('The EXIF Library is not installed.'); 1732 } else { 1733 return []; 1734 } 1735 }; 1736 if (!file_exists($this->fileName)) { 1737 if ($debug) { 1738 throw new Exception('Image not found.'); 1739 } else { 1740 return []; 1741 } 1742 }; 1743 if ($this->fileExtension != '.jpg') { 1744 if ($debug) { 1745 throw new Exception('Metadata not supported for this image type.'); 1746 } else { 1747 return []; 1748 } 1749 }; 1750 $exifData = exif_read_data($this->fileName, 'IFD0'); 1751 1752 // *** Format the apperture value 1753 $ev = $exifData['ApertureValue']; 1754 $apPeicesArray = explode('/', $ev); 1755 if (count($apPeicesArray) == 2) { 1756 $apertureValue = round($apPeicesArray[0] / $apPeicesArray[1], 2, PHP_ROUND_HALF_DOWN) . ' EV'; 1757 } else { 1758 $apertureValue = ''; 1759 } 1760 1761 // *** Format the focal length 1762 $focalLength = $exifData['FocalLength']; 1763 $flPeicesArray = explode('/', $focalLength); 1764 if (count($flPeicesArray) == 2) { 1765 $focalLength = $flPeicesArray[0] / $flPeicesArray[1] . '.0 mm'; 1766 } else { 1767 $focalLength = ''; 1768 } 1769 1770 // *** Format fNumber 1771 $fNumber = $exifData['FNumber']; 1772 $fnPeicesArray = explode('/', $fNumber); 1773 if (count($fnPeicesArray) == 2) { 1774 $fNumber = $fnPeicesArray[0] / $fnPeicesArray[1]; 1775 } else { 1776 $fNumber = ''; 1777 } 1778 1779 // *** Resolve ExposureProgram 1780 if (isset($exifData['ExposureProgram'])) { 1781 $ep = $exifData['ExposureProgram']; 1782 } 1783 if (isset($ep)) { 1784 $ep = $this->resolveExposureProgram($ep); 1785 } 1786 1787 1788 // *** Resolve MeteringMode 1789 $mm = $exifData['MeteringMode']; 1790 $mm = $this->resolveMeteringMode($mm); 1791 1792 // *** Resolve Flash 1793 $flash = $exifData['Flash']; 1794 $flash = $this->resolveFlash($flash); 1795 1796 1797 if (isset($exifData['Make'])) { 1798 $exifDataArray['make'] = $exifData['Make']; 1799 } else { 1800 $exifDataArray['make'] = ''; 1801 } 1802 1803 if (isset($exifData['Model'])) { 1804 $exifDataArray['model'] = $exifData['Model']; 1805 } else { 1806 $exifDataArray['model'] = ''; 1807 } 1808 1809 if (isset($exifData['DateTime'])) { 1810 $exifDataArray['date'] = $exifData['DateTime']; 1811 } else { 1812 $exifDataArray['date'] = ''; 1813 } 1814 1815 if (isset($exifData['ExposureTime'])) { 1816 $exifDataArray['exposure time'] = $exifData['ExposureTime'] . ' sec.'; 1817 } else { 1818 $exifDataArray['exposure time'] = ''; 1819 } 1820 1821 if ($apertureValue != '') { 1822 $exifDataArray['aperture value'] = $apertureValue; 1823 } else { 1824 $exifDataArray['aperture value'] = ''; 1825 } 1826 1827 if (isset($exifData['COMPUTED']['ApertureFNumber'])) { 1828 $exifDataArray['f-stop'] = $exifData['COMPUTED']['ApertureFNumber']; 1829 } else { 1830 $exifDataArray['f-stop'] = ''; 1831 } 1832 1833 if (isset($exifData['FNumber'])) { 1834 $exifDataArray['fnumber'] = $exifData['FNumber']; 1835 } else { 1836 $exifDataArray['fnumber'] = ''; 1837 } 1838 1839 if ($fNumber != '') { 1840 $exifDataArray['fnumber value'] = $fNumber; 1841 } else { 1842 $exifDataArray['fnumber value'] = ''; 1843 } 1844 1845 if (isset($exifData['ISOSpeedRatings'])) { 1846 $exifDataArray['iso'] = $exifData['ISOSpeedRatings']; 1847 } else { 1848 $exifDataArray['iso'] = ''; 1849 } 1850 1851 if ($focalLength != '') { 1852 $exifDataArray['focal length'] = $focalLength; 1853 } else { 1854 $exifDataArray['focal length'] = ''; 1855 } 1856 1857 if (isset($ep)) { 1858 $exifDataArray['exposure program'] = $ep; 1859 } else { 1860 $exifDataArray['exposure program'] = ''; 1861 } 1862 1863 if ($mm != '') { 1864 $exifDataArray['metering mode'] = $mm; 1865 } else { 1866 $exifDataArray['metering mode'] = ''; 1867 } 1868 1869 if ($flash != '') { 1870 $exifDataArray['flash status'] = $flash; 1871 } else { 1872 $exifDataArray['flash status'] = ''; 1873 } 1874 1875 if (isset($exifData['Artist'])) { 1876 $exifDataArray['creator'] = $exifData['Artist'] ; 1877 } else { 1878 $exifDataArray['creator'] = ''; 1879 } 1880 1881 if (isset($exifData['Copyright'])) { 1882 $exifDataArray['copyright'] = $exifData['Copyright']; 1883 } else { 1884 $exifDataArray['copyright'] = ''; 1885 } 1886 1887 // *** Orientation 1888 if (isset($exifData['Orientation'])) { 1889 $exifDataArray['orientation'] = $exifData['Orientation']; 1890 } else { 1891 $exifDataArray['orientation'] = ''; 1892 } 1893 1894 return $exifDataArray; 1895 } 1896 1897 ## -------------------------------------------------------- 1898 1899 private function resolveExposureProgram($ep) 1900 { 1901 switch ($ep) { 1902 case 0: 1903 $ep = ''; 1904 break; 1905 case 1: 1906 $ep = 'manual'; 1907 break; 1908 case 2: 1909 $ep = 'normal program'; 1910 break; 1911 case 3: 1912 $ep = 'aperture priority'; 1913 break; 1914 case 4: 1915 $ep = 'shutter priority'; 1916 break; 1917 case 5: 1918 $ep = 'creative program'; 1919 break; 1920 case 6: 1921 $ep = 'action program'; 1922 break; 1923 case 7: 1924 $ep = 'portrait mode'; 1925 break; 1926 case 8: 1927 $ep = 'landscape mode'; 1928 break; 1929 1930 default: 1931 break; 1932 } 1933 1934 return $ep; 1935 } 1936 1937 ## -------------------------------------------------------- 1938 1939 private function resolveMeteringMode($mm) 1940 { 1941 switch ($mm) { 1942 case 0: 1943 $mm = 'unknown'; 1944 break; 1945 case 1: 1946 $mm = 'average'; 1947 break; 1948 case 2: 1949 $mm = 'center weighted average'; 1950 break; 1951 case 3: 1952 $mm = 'spot'; 1953 break; 1954 case 4: 1955 $mm = 'multi spot'; 1956 break; 1957 case 5: 1958 $mm = 'pattern'; 1959 break; 1960 case 6: 1961 $mm = 'partial'; 1962 break; 1963 case 255: 1964 $mm = 'other'; 1965 break; 1966 1967 default: 1968 break; 1969 } 1970 1971 return $mm; 1972 } 1973 1974 ## -------------------------------------------------------- 1975 1976 private function resolveFlash($flash) 1977 { 1978 switch ($flash) { 1979 case 0: 1980 $flash = 'flash did not fire'; 1981 break; 1982 case 1: 1983 $flash = 'flash fired'; 1984 break; 1985 case 5: 1986 $flash = 'strobe return light not detected'; 1987 break; 1988 case 7: 1989 $flash = 'strobe return light detected'; 1990 break; 1991 case 9: 1992 $flash = 'flash fired, compulsory flash mode'; 1993 break; 1994 case 13: 1995 $flash = 'flash fired, compulsory flash mode, return light not detected'; 1996 break; 1997 case 15: 1998 $flash = 'flash fired, compulsory flash mode, return light detected'; 1999 break; 2000 case 16: 2001 $flash = 'flash did not fire, compulsory flash mode'; 2002 break; 2003 case 24: 2004 $flash = 'flash did not fire, auto mode'; 2005 break; 2006 case 25: 2007 $flash = 'flash fired, auto mode'; 2008 break; 2009 case 29: 2010 $flash = 'flash fired, auto mode, return light not detected'; 2011 break; 2012 case 31: 2013 $flash = 'flash fired, auto mode, return light detected'; 2014 break; 2015 case 32: 2016 $flash = 'no flash function'; 2017 break; 2018 case 65: 2019 $flash = 'flash fired, red-eye reduction mode'; 2020 break; 2021 case 69: 2022 $flash = 'flash fired, red-eye reduction mode, return light not detected'; 2023 break; 2024 case 71: 2025 $flash = 'flash fired, red-eye reduction mode, return light detected'; 2026 break; 2027 case 73: 2028 $flash = 'flash fired, compulsory flash mode, red-eye reduction mode'; 2029 break; 2030 case 77: 2031 $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light not detected'; 2032 break; 2033 case 79: 2034 $flash = 'flash fired, compulsory flash mode, red-eye reduction mode, return light detected'; 2035 break; 2036 case 89: 2037 $flash = 'flash fired, auto mode, red-eye reduction mode'; 2038 break; 2039 case 93: 2040 $flash = 'flash fired, auto mode, return light not detected, red-eye reduction mode'; 2041 break; 2042 case 95: 2043 $flash = 'flash fired, auto mode, return light detected, red-eye reduction mode'; 2044 break; 2045 2046 default: 2047 break; 2048 } 2049 2050 return $flash; 2051 } 2052 2053 2054/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 2055 Get IPTC Data 2056*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 2057 2058 2059/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 2060 Write IPTC Data 2061*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 2062 2063 public function writeIPTCcaption($value) 2064 # Caption 2065 { 2066 $this->writeIPTC(120, $value); 2067 } 2068 2069 ## -------------------------------------------------------- 2070 2071 public function writeIPTCwriter($value) 2072 { 2073 //$this->writeIPTC(65, $value); 2074 } 2075 2076 ## -------------------------------------------------------- 2077 2078 private function writeIPTC($dat, $value) 2079 { 2080 2081 # LIMIT TO JPG 2082 2083 $caption_block = $this->iptc_maketag(2, $dat, $value); 2084 $image_string = iptcembed($caption_block, $this->fileName); 2085 file_put_contents('iptc.jpg', $image_string); 2086 } 2087 2088## -------------------------------------------------------- 2089 2090 private function iptc_maketag($rec, $dat, $val) 2091 # Author: Thies C. Arntzen 2092 # Purpose: Function to format the new IPTC text 2093 # Param in: $rec: Application record. (We’re working with #2) 2094 # $dat: Index. (120 for caption, 118 for contact. See the IPTC IIM 2095 # specification: 2096 # http://www.iptc.org/std/IIM/4.1/specification/IIMV4.1.pdf 2097 # $val: Value/data/text. Make sure this is within the length 2098 # constraints of the IPTC IIM specification 2099 # Ref: http://blog.peterhaza.no/working-with-image-meta-data-in-exif-and-iptc-headers-from-php/ 2100 # http://php.net/manual/en/function.iptcembed.php 2101 # 2102 { 2103 $len = strlen($val); 2104 if ($len < 0x8000) { 2105 return chr(0x1c).chr($rec).chr($dat). 2106 chr($len >> 8). 2107 chr($len & 0xff). 2108 $val; 2109 } else { 2110 return chr(0x1c).chr($rec).chr($dat). 2111 chr(0x80).chr(0x04). 2112 chr(($len >> 24) & 0xff). 2113 chr(($len >> 16) & 0xff). 2114 chr(($len >> 8) & 0xff). 2115 chr(($len) & 0xff). 2116 $val; 2117 } 2118 } 2119 2120 2121 2122/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 2123 Write XMP Data 2124*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 2125 2126 //http://xmpphptoolkit.sourceforge.net/ 2127 2128 2129/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 2130 Add Text 2131*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 2132 2133 public function addText($text, $pos = '20x20', $padding = 0, $fontColor='#fff', $fontSize = 12, $angle = 0, $font = null) 2134 # Author: Jarrod Oberto 2135 # Date: 18-11-09 2136 # Purpose: Add text to an image 2137 # Param in: 2138 # Param out: 2139 # Reference: http://php.net/manual/en/function.imagettftext.php 2140 # Notes: Make sure you supply the font. 2141 # 2142 { 2143 2144 // *** Convert color 2145 $rgbArray = $this->formatColor($fontColor); 2146 $r = $rgbArray['r']; 2147 $g = $rgbArray['g']; 2148 $b = $rgbArray['b']; 2149 2150 // *** Get text font 2151 $font = $this->getTextFont($font); 2152 2153 // *** Get text size 2154 $textSizeArray = $this->getTextSize($fontSize, $angle, $font, $text); 2155 $textWidth = $textSizeArray['width']; 2156 $textHeight = $textSizeArray['height']; 2157 2158 // *** Find co-ords to place text 2159 $posArray = $this->calculatePosition($pos, $padding, $textWidth, $textHeight, false); 2160 $x = $posArray['width']; 2161 $y = $posArray['height']; 2162 2163 $fontColor = imagecolorallocate($this->imageResized, $r, $g, $b); 2164 2165 // *** Add text 2166 imagettftext($this->imageResized, $fontSize, $angle, $x, $y, $fontColor, $font, $text); 2167 } 2168 2169 ## -------------------------------------------------------- 2170 2171 private function getTextFont($font) 2172 { 2173 // *** Font path (shou 2174 $fontPath = dirname(__FILE__) . '/' . $this->fontDir; 2175 2176 2177 // *** The below is/may be needed depending on your version (see ref) 2178 putenv('GDFONTPATH=' . realpath('.')); 2179 2180 // *** Check if the passed in font exsits... 2181 if ($font == null || !file_exists($font)) { 2182 2183 // *** ...If not, default to this font. 2184 $font = $fontPath . '/arimo.ttf'; 2185 2186 // *** Check our default font exists... 2187 if (!file_exists($font)) { 2188 2189 // *** If not, return false 2190 if ($this->debug) { 2191 throw new Exception('Font not found'); 2192 } else { 2193 return false; 2194 } 2195 } 2196 } 2197 2198 return $font; 2199 } 2200 2201 ## -------------------------------------------------------- 2202 2203 private function getTextSize($fontSize, $angle, $font, $text) 2204 { 2205 2206 // *** Define box (so we can get the width) 2207 $box = @imageTTFBbox($fontSize, $angle, $font, $text); 2208 2209 // *** Get width of text from dimensions 2210 $textWidth = abs($box[4] - $box[0]); 2211 2212 // *** Get height of text from dimensions (should also be same as $fontSize) 2213 $textHeight = abs($box[5] - $box[1]); 2214 2215 return ['height' => $textHeight, 'width' => $textWidth]; 2216 } 2217 2218 2219/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 2220 Add Watermark 2221*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 2222 2223 public function addWatermark($watermarkImage, $pos, $padding = 0, $opacity = 0) 2224 # Author: Jarrod Oberto 2225 # Date: 18-11-09 2226 # Purpose: Add watermark image 2227 # Param in: (str) $watermark: The watermark image 2228 # (str) $pos: Could be a pre-determined position such as: 2229 # tl = top left, 2230 # t = top (middle), 2231 # tr = top right, 2232 # l = left, 2233 # m = middle, 2234 # r = right, 2235 # bl = bottom left, 2236 # b = bottom (middle), 2237 # br = bottom right 2238 # Or, it could be a co-ordinate position such as: 50x100 2239 # 2240 # (int) $padding: If using a pre-determined position you can 2241 # adjust the padding from the edges by passing an amount 2242 # in pixels. If using co-ordinates, this value is ignored. 2243 # Param out: 2244 # Reference: http://www.php.net/manual/en/image.examples-watermark.php 2245 # Notes: Based on example in reference. 2246 # 2247 # 2248 { 2249 2250 // Load the stamp and the photo to apply the watermark to 2251 $stamp = $this->openImage($watermarkImage); # stamp 2252 $im = $this->imageResized; # photo 2253 2254 // *** Get stamps width and height 2255 $sx = imagesx($stamp); 2256 $sy = imagesy($stamp); 2257 2258 // *** Find co-ords to place image 2259 $posArray = $this->calculatePosition($pos, $padding, $sx, $sy); 2260 $x = $posArray['width']; 2261 $y = $posArray['height']; 2262 2263 // *** Set watermark opacity 2264 if (fix_strtolower(strrchr($watermarkImage, '.')) == '.png') { 2265 $opacity = $this->invertTransparency($opacity, 100); 2266 $this->filterOpacity($stamp, $opacity); 2267 } 2268 2269 // Copy the watermark image onto our photo 2270 imagecopy($im, $stamp, $x, $y, 0, 0, imagesx($stamp), imagesy($stamp)); 2271 } 2272 2273 ## -------------------------------------------------------- 2274 2275 private function calculatePosition($pos, $padding, $assetWidth, $assetHeight, $upperLeft = true) 2276 # 2277 # Author: Jarrod Oberto 2278 # Date: 08-05-11 2279 # Purpose: Calculate the x, y pixel cordinates of the asset to place 2280 # Params in: (str) $pos: Either something like: "tl", "l", "br" or an 2281 # exact position like: "100x50" 2282 # (int) $padding: The amount of padding from the edge. Only 2283 # used for the predefined $pos. 2284 # (int) $assetWidth: The width of the asset to add to the image 2285 # (int) $assetHeight: The height of the asset to add to the image 2286 # (bol) $upperLeft: if true, the asset will be positioned based 2287 # on the upper left x, y coords. If false, it means you're 2288 # using the lower left as the basepoint and this will 2289 # convert it to the upper left position 2290 # Params out: 2291 # NOTE: this is done from the UPPER left corner!! But will convert lower 2292 # left basepoints to upper left if $upperleft is set to false 2293 # 2294 # 2295 { 2296 $pos = fix_strtolower($pos); 2297 2298 // *** If co-ords have been entered 2299 if (strstr($pos, 'x')) { 2300 $pos = str_replace(' ', '', $pos); 2301 2302 $xyArray = explode('x', $pos); 2303 list($width, $height) = $xyArray; 2304 } else { 2305 switch ($pos) { 2306 case 'tl': 2307 $width = 0 + $padding; 2308 $height = 0 + $padding; 2309 break; 2310 2311 case 't': 2312 $width = ($this->width / 2) - ($assetWidth / 2); 2313 $height = 0 + $padding; 2314 break; 2315 2316 case 'tr': 2317 $width = $this->width - $assetWidth - $padding; 2318 $height = 0 + $padding;; 2319 break; 2320 2321 case 'l': 2322 $width = 0 + $padding; 2323 $height = ($this->height / 2) - ($assetHeight / 2); 2324 break; 2325 2326 case 'm': 2327 $width = ($this->width / 2) - ($assetWidth / 2); 2328 $height = ($this->height / 2) - ($assetHeight / 2); 2329 break; 2330 2331 case 'r': 2332 $width = $this->width - $assetWidth - $padding; 2333 $height = ($this->height / 2) - ($assetHeight / 2); 2334 break; 2335 2336 case 'bl': 2337 $width = 0 + $padding; 2338 $height = $this->height - $assetHeight - $padding; 2339 break; 2340 2341 case 'b': 2342 $width = ($this->width / 2) - ($assetWidth / 2); 2343 $height = $this->height - $assetHeight - $padding; 2344 break; 2345 2346 case 'br': 2347 $width = $this->width - $assetWidth - $padding; 2348 $height = $this->height - $assetHeight - $padding; 2349 break; 2350 2351 default: 2352 $width = 0; 2353 $height = 0; 2354 break; 2355 } 2356 } 2357 2358 if (!$upperLeft) { 2359 $height = $height + $assetHeight; 2360 } 2361 2362 return ['width' => $width, 'height' => $height]; 2363 } 2364 2365 2366 ## -------------------------------------------------------- 2367 2368 private function filterOpacity(&$img, $opacity = 75) 2369 # 2370 # Author: aiden dot mail at freemail dot hu 2371 # Author date: 29-03-08 08:16 2372 # Date added: 08-05-11 2373 # Purpose: Change opacity of image 2374 # Params in: $img: Image resource id 2375 # (int) $opacity: the opacity amount: 0-100, 100 being not opaque. 2376 # Params out: (bool) true on success, else false 2377 # Ref: http://www.php.net/manual/en/function.imagefilter.php#82162 2378 # Notes: png only 2379 # 2380 { 2381 if (!isset($opacity)) { 2382 return false; 2383 } 2384 2385 if ($opacity == 100) { 2386 return true; 2387 } 2388 2389 $opacity /= 100; 2390 2391 //get image width and height 2392 $w = imagesx($img); 2393 $h = imagesy($img); 2394 2395 //turn alpha blending off 2396 imagealphablending($img, false); 2397 2398 //find the most opaque pixel in the image (the one with the smallest alpha value) 2399 $minalpha = 127; 2400 for ($x = 0; $x < $w; $x++) { 2401 for ($y = 0; $y < $h; $y++) { 2402 $alpha = (imagecolorat($img, $x, $y) >> 24) & 0xFF; 2403 if ($alpha < $minalpha) { 2404 $minalpha = $alpha; 2405 } 2406 } 2407 } 2408 2409 //loop through image pixels and modify alpha for each 2410 for ($x = 0; $x < $w; $x++) { 2411 for ($y = 0; $y < $h; $y++) { 2412 //get current alpha value (represents the TANSPARENCY!) 2413 $colorxy = imagecolorat($img, $x, $y); 2414 $alpha = ($colorxy >> 24) & 0xFF; 2415 //calculate new alpha 2416 if ($minalpha !== 127) { 2417 $alpha = 127 + 127 * $opacity * ($alpha - 127) / (127 - $minalpha); 2418 } else { 2419 $alpha += 127 * $opacity; 2420 } 2421 //get the color index with new alpha 2422 $alphacolorxy = imagecolorallocatealpha($img, ($colorxy >> 16) & 0xFF, ($colorxy >> 8) & 0xFF, $colorxy & 0xFF, $alpha); 2423 //set pixel with the new color + opacity 2424 if (!imagesetpixel($img, $x, $y, $alphacolorxy)) { 2425 return false; 2426 } 2427 } 2428 } 2429 2430 return true; 2431 } 2432 2433## -------------------------------------------------------- 2434 2435 private function openImage($file) 2436 # Author: Jarrod Oberto 2437 # Date: 27-02-08 2438 # Purpose: 2439 # Param in: 2440 # Param out: n/a 2441 # Reference: 2442 # Notes: 2443 # 2444 { 2445 if (!file_exists($file) && !$this->checkStringStartsWith('http://', $file)) { 2446 if ($this->debug) { 2447 throw new Exception('Image not found.'); 2448 } else { 2449 throw new Exception(); 2450 } 2451 }; 2452 2453 // *** Get extension 2454 $extension = strrchr($file, '.'); 2455 $extension = fix_strtolower($extension); 2456 switch ($extension) { 2457 case '.jpg': 2458 case '.jpeg': 2459 $img = @imagecreatefromjpeg($file); 2460 break; 2461 case '.gif': 2462 $img = @imagecreatefromgif($file); 2463 break; 2464 case '.png': 2465 $img = @imagecreatefrompng($file); 2466 break; 2467 case '.bmp': 2468 $img = @$this->imagecreatefrombmp($file); 2469 break; 2470 case '.psd': 2471 $img = @$this->imagecreatefrompsd($file); 2472 break; 2473 2474 2475 // ... etc 2476 2477 default: 2478 $img = false; 2479 break; 2480 } 2481 2482 return $img; 2483 } 2484 2485## -------------------------------------------------------- 2486 2487 public function reset() 2488 # 2489 # Author: Jarrod Oberto 2490 # Date: 30-08-11 2491 # Purpose: Reset the resource (allow further editing) 2492 # Params in: 2493 # Params out: 2494 # Notes: 2495 # 2496 { 2497 $this->__construct($this->fileName); 2498 } 2499 2500## -------------------------------------------------------- 2501 2502 public function saveImage($savePath, $imageQuality="100") 2503 # Author: Jarrod Oberto 2504 # Date: 27-02-08 2505 # Purpose: Saves the image 2506 # Param in: $savePath: Where to save the image including filename: 2507 # $imageQuality: image quality you want the image saved at 0-100 2508 # Param out: n/a 2509 # Reference: 2510 # Notes: * gif doesn't have a quality parameter 2511 # * jpg has a quality setting 0-100 (100 being the best) 2512 # * png has a quality setting 0-9 (0 being the best) 2513 # 2514 # * bmp files have no native support for bmp files. We use a 2515 # third party class to save as bmp. 2516 { 2517 2518 // *** Perform a check or two. 2519 if (!is_resource($this->imageResized)) { 2520 if ($this->debug) { 2521 throw new Exception('saveImage: This is not a resource.'); 2522 } else { 2523 throw new Exception(); 2524 } 2525 } 2526 $fileInfoArray = pathInfo($savePath); 2527 clearstatcache(); 2528 if (!is_writable($fileInfoArray['dirname'])) { 2529 if ($this->debug) { 2530 throw new Exception('The path is not writable. Please check your permissions.'); 2531 } else { 2532 throw new Exception(); 2533 } 2534 } 2535 2536 // *** Get extension 2537 $extension = strrchr($savePath, '.'); 2538 $extension = fix_strtolower($extension); 2539 2540 $error = ''; 2541 2542 switch ($extension) { 2543 case '.jpg': 2544 case '.jpeg': 2545 $this->checkInterlaceImage($this->isInterlace); 2546 if (imagetypes() & IMG_JPG) { 2547 imagejpeg($this->imageResized, $savePath, $imageQuality); 2548 } else { 2549 $error = 'jpg'; 2550 } 2551 break; 2552 2553 case '.gif': 2554 $this->checkInterlaceImage($this->isInterlace); 2555 if (imagetypes() & IMG_GIF) { 2556 imagegif($this->imageResized, $savePath); 2557 } else { 2558 $error = 'gif'; 2559 } 2560 break; 2561 2562 case '.png': 2563 // *** Scale quality from 0-100 to 0-9 2564 $scaleQuality = round(($imageQuality/100) * 9); 2565 2566 // *** Invert qualit setting as 0 is best, not 9 2567 $invertScaleQuality = 9 - $scaleQuality; 2568 2569 $this->checkInterlaceImage($this->isInterlace); 2570 if (imagetypes() & IMG_PNG) { 2571 imagepng($this->imageResized, $savePath, $invertScaleQuality); 2572 } else { 2573 $error = 'png'; 2574 } 2575 break; 2576 2577 case '.bmp': 2578 file_put_contents($savePath, $this->GD2BMPstring($this->imageResized)); 2579 break; 2580 2581 2582 // ... etc 2583 2584 default: 2585 // *** No extension - No save. 2586 $this->errorArray[] = 'This file type (' . $extension . ') is not supported. File not saved.'; 2587 break; 2588 } 2589 2590 //imagedestroy($this->imageResized); 2591 2592 // *** Display error if a file type is not supported. 2593 if ($error != '') { 2594 $this->errorArray[] = $error . ' support is NOT enabled. File not saved.'; 2595 } 2596 } 2597 2598## -------------------------------------------------------- 2599 2600 public function displayImage($fileType = 'jpg', $imageQuality="100") 2601 # Author: Jarrod Oberto 2602 # Date: 18-11-09 2603 # Purpose: Display images directly to the browser 2604 # Param in: The image type you want to display 2605 # Param out: 2606 # Reference: 2607 # Notes: 2608 # 2609 { 2610 if (!is_resource($this->imageResized)) { 2611 if ($this->debug) { 2612 throw new Exception('saveImage: This is not a resource.'); 2613 } else { 2614 throw new Exception(); 2615 } 2616 } 2617 2618 switch ($fileType) { 2619 case 'jpg': 2620 case 'jpeg': 2621 header('Content-type: image/jpeg'); 2622 imagejpeg($this->imageResized, '', $imageQuality); 2623 break; 2624 case 'gif': 2625 header('Content-type: image/gif'); 2626 imagegif($this->imageResized); 2627 break; 2628 case 'png': 2629 header('Content-type: image/png'); 2630 2631 // *** Scale quality from 0-100 to 0-9 2632 $scaleQuality = round(($imageQuality/100) * 9); 2633 2634 // *** Invert qualit setting as 0 is best, not 9 2635 $invertScaleQuality = 9 - $scaleQuality; 2636 2637 imagepng($this->imageResized, '', $invertScaleQuality); 2638 break; 2639 case 'bmp': 2640 echo 'bmp file format is not supported.'; 2641 break; 2642 2643 // ... etc 2644 2645 default: 2646 // *** No extension - No save. 2647 break; 2648 } 2649 2650 2651 //imagedestroy($this->imageResized); 2652 } 2653 2654## -------------------------------------------------------- 2655 2656 public function setTransparency($bool) 2657 # Sep 2011 2658 { 2659 $this->keepTransparency = $bool; 2660 } 2661 2662## -------------------------------------------------------- 2663 2664 public function setFillColor($value) 2665 # Sep 2011 2666 # Param in: (mixed) $value: (array) Could be an array of RGB 2667 # (str) Could be hex #ffffff or #fff, fff, ffffff 2668 # 2669 # If the keepTransparency is set to false, then no transparency is to be used. 2670 # This is ideal when you want to save as jpg. 2671 # 2672 # this method allows you to set the background color to use instead of 2673 # transparency. 2674 # 2675 { 2676 $colorArray = $this->formatColor($value); 2677 $this->fillColorArray = $colorArray; 2678 } 2679 2680## -------------------------------------------------------- 2681 2682 public function setCropFromTop($value) 2683 # Sep 2011 2684 { 2685 $this->cropFromTopPercent = $value; 2686 } 2687 2688## -------------------------------------------------------- 2689 2690 public function testGDInstalled() 2691 # Author: Jarrod Oberto 2692 # Date: 27-02-08 2693 # Purpose: Test to see if GD is installed 2694 # Param in: n/a 2695 # Param out: (bool) True is gd extension loaded otherwise false 2696 # Reference: 2697 # Notes: 2698 # 2699 { 2700 if (extension_loaded('gd') && function_exists('gd_info')) { 2701 $gdInstalled = true; 2702 } else { 2703 $gdInstalled = false; 2704 } 2705 2706 return $gdInstalled; 2707 } 2708 2709## -------------------------------------------------------- 2710 2711 public function testEXIFInstalled() 2712 # Author: Jarrod Oberto 2713 # Date: 08-05-11 2714 # Purpose: Test to see if EXIF is installed 2715 # Param in: n/a 2716 # Param out: (bool) True is exif extension loaded otherwise false 2717 # Reference: 2718 # Notes: 2719 # 2720 { 2721 if (extension_loaded('exif')) { 2722 $exifInstalled = true; 2723 } else { 2724 $exifInstalled = false; 2725 } 2726 2727 return $exifInstalled; 2728 } 2729 2730## -------------------------------------------------------- 2731 2732 public function testIsImage($image) 2733 # Author: Jarrod Oberto 2734 # Date: 27-02-08 2735 # Purpose: Test if file is an image 2736 # Param in: n/a 2737 # Param out: n/a 2738 # Reference: 2739 # Notes: 2740 # 2741 { 2742 if ($image) { 2743 $fileIsImage = true; 2744 } else { 2745 $fileIsImage = false; 2746 } 2747 2748 return $fileIsImage; 2749 } 2750 2751## -------------------------------------------------------- 2752 2753 public function testFunct() 2754 # Author: Jarrod Oberto 2755 # Date: 27-02-08 2756 # Purpose: Test Function 2757 # Param in: n/a 2758 # Param out: n/a 2759 # Reference: 2760 # Notes: 2761 # 2762 { 2763 echo $this->height; 2764 } 2765 2766## -------------------------------------------------------- 2767 2768 public function setForceStretch($value) 2769 # Author: Jarrod Oberto 2770 # Date: 23-12-10 2771 # Purpose: 2772 # Param in: (bool) $value 2773 # Param out: n/a 2774 # Reference: 2775 # Notes: 2776 # 2777 { 2778 $this->forceStretch = $value; 2779 } 2780 2781## -------------------------------------------------------- 2782 2783 public function setFile($fileName) 2784 # Author: Jarrod Oberto 2785 # Date: 28-02-08 2786 # Purpose: 2787 # Param in: n/a 2788 # Param out: n/a 2789 # Reference: 2790 # Notes: 2791 # 2792 { 2793 self::__construct($fileName); 2794 } 2795 2796## -------------------------------------------------------- 2797 2798 public function getFileName() 2799 # Author: Jarrod Oberto 2800 # Date: 10-09-08 2801 # Purpose: 2802 # Param in: n/a 2803 # Param out: n/a 2804 # Reference: 2805 # Notes: 2806 # 2807 { 2808 return $this->fileName; 2809 } 2810 2811## -------------------------------------------------------- 2812 2813 public function getHeight() 2814 { 2815 return $this->height; 2816 } 2817 2818## -------------------------------------------------------- 2819 2820 public function getWidth() 2821 { 2822 return $this->width; 2823 } 2824 2825## -------------------------------------------------------- 2826 2827 public function getOriginalHeight() 2828 { 2829 return $this->heightOriginal; 2830 } 2831 2832## -------------------------------------------------------- 2833 2834 public function getOriginalWidth() 2835 { 2836 return $this->widthOriginal; 2837 } 2838 2839## -------------------------------------------------------- 2840 2841 public function getErrors() 2842 # Author: Jarrod Oberto 2843 # Date: 19-11-09 2844 # Purpose: Returns the error array 2845 # Param in: n/a 2846 # Param out: Array of errors 2847 # Reference: 2848 # Notes: 2849 # 2850 { 2851 return $this->errorArray; 2852 } 2853 2854## -------------------------------------------------------- 2855 2856 private function checkInterlaceImage($isEnabled) 2857 # jpg will use progressive (they don't use interace) 2858 { 2859 if ($isEnabled) { 2860 imageinterlace($this->imageResized, $isEnabled); 2861 } 2862 } 2863 2864## -------------------------------------------------------- 2865 2866 protected function formatColor($value) 2867 # Author: Jarrod Oberto 2868 # Date: 09-05-11 2869 # Purpose: Determine color method passed in and return color as RGB 2870 # Param in: (mixed) $value: (array) Could be an array of RGB 2871 # (str) Could be hex #ffffff or #fff, fff, ffffff 2872 # Param out: 2873 # Reference: 2874 # Notes: 2875 # 2876 { 2877 $rgbArray = []; 2878 2879 // *** If it's an array it should be R, G, B 2880 if (is_array($value)) { 2881 if (key($value) == 0 && count($value) == 3) { 2882 $rgbArray['r'] = $value[0]; 2883 $rgbArray['g'] = $value[1]; 2884 $rgbArray['b'] = $value[2]; 2885 } else { 2886 $rgbArray = $value; 2887 } 2888 } elseif (fix_strtolower($value) == 'transparent') { 2889 $rgbArray = [ 2890 'r' => 255, 2891 'g' => 255, 2892 'b' => 255, 2893 'a' => 127 2894 ]; 2895 } else { 2896 2897 // *** ...Else it should be hex. Let's make it RGB 2898 $rgbArray = $this -> hex2dec($value); 2899 } 2900 2901 return $rgbArray; 2902 } 2903 2904 ## -------------------------------------------------------- 2905 2906 function hex2dec($hex) 2907 # Purpose: Convert #hex color to RGB 2908 { 2909 $color = str_replace('#', '', $hex); 2910 2911 if (strlen($color) == 3) { 2912 $color = $color . $color; 2913 } 2914 2915 $rgb = [ 2916 'r' => hexdec(substr($color, 0, 2)), 2917 'g' => hexdec(substr($color, 2, 2)), 2918 'b' => hexdec(substr($color, 4, 2)), 2919 'a' => 0 2920 ]; 2921 return $rgb; 2922 } 2923 2924 ## -------------------------------------------------------- 2925 2926 private function createImageColor($colorArray) 2927 { 2928 $r = $colorArray['r']; 2929 $g = $colorArray['g']; 2930 $b = $colorArray['b']; 2931 2932 return imagecolorallocate($this->imageResized, $r, $g, $b); 2933 } 2934 2935 ## -------------------------------------------------------- 2936 2937 private function testColorExists($colorArray) 2938 { 2939 $r = $colorArray['r']; 2940 $g = $colorArray['g']; 2941 $b = $colorArray['b']; 2942 2943 if (imagecolorexact($this->imageResized, $r, $g, $b) == -1) { 2944 return false; 2945 } else { 2946 return true; 2947 } 2948 } 2949 2950 ## -------------------------------------------------------- 2951 2952 private function findUnusedGreen() 2953 # Purpose: We find a green color suitable to use like green-screen effect. 2954 # Therefore, the color must not exist in the image. 2955 { 2956 $green = 255; 2957 2958 do { 2959 $greenChroma = [0, $green, 0]; 2960 $colorArray = $this->formatColor($greenChroma); 2961 $match = $this->testColorExists($colorArray); 2962 $green--; 2963 } while ($match == false && $green > 0); 2964 2965 // *** If no match, just bite the bullet and use green value of 255 2966 if (!$match) { 2967 $greenChroma = [0, $green, 0]; 2968 } 2969 2970 return $greenChroma; 2971 } 2972 2973 ## -------------------------------------------------------- 2974 2975 private function findUnusedBlue() 2976 # Purpose: We find a green color suitable to use like green-screen effect. 2977 # Therefore, the color must not exist in the image. 2978 { 2979 $blue = 255; 2980 2981 do { 2982 $blueChroma = [0, 0, $blue]; 2983 $colorArray = $this->formatColor($blueChroma); 2984 $match = $this->testColorExists($colorArray); 2985 $blue--; 2986 } while ($match == false && $blue > 0); 2987 2988 // *** If no match, just bite the bullet and use blue value of 255 2989 if (!$match) { 2990 $blueChroma = [0, 0, $blue]; 2991 } 2992 2993 return $blueChroma; 2994 } 2995 2996 ## -------------------------------------------------------- 2997 2998 private function invertTransparency($value, $originalMax, $invert=true) 2999 # Purpose: This does two things: 3000 # 1) Convert the range from 0-127 to 0-100 3001 # 2) Inverts value to 100 is not transparent while 0 is fully 3002 # transparent (like Photoshop) 3003 { 3004 // *** Test max range 3005 if ($value > $originalMax) { 3006 $value = $originalMax; 3007 } 3008 3009 // *** Test min range 3010 if ($value < 0) { 3011 $value = 0; 3012 } 3013 3014 if ($invert) { 3015 return $originalMax - (($value/100) * $originalMax); 3016 } else { 3017 return ($value/100) * $originalMax; 3018 } 3019 } 3020 3021 ## -------------------------------------------------------- 3022 3023 private function transparentImage($src) 3024 { 3025 // *** making images with white bg transparent 3026 $r1 = 0; 3027 $g1 = 255; 3028 $b1 = 0; 3029 for ($x = 0; $x < imagesx($src); ++$x) { 3030 for ($y = 0; $y < imagesy($src); ++$y) { 3031 $color = imagecolorat($src, $x, $y); 3032 $r = ($color >> 16) & 0xFF; 3033 $g = ($color >> 8) & 0xFF; 3034 $b = $color & 0xFF; 3035 for ($i = 0; $i < 270; $i++) { 3036 //if ($r . $g . $b == ($r1 + $i) . ($g1 + $i) . ($b1 + $i)) { 3037 if ($r == 0 && $g == 255 && $b == 0) { 3038 //if ($g == 255) { 3039 $trans_colour = imagecolorallocatealpha($src, 0, 0, 0, 127); 3040 imagefill($src, $x, $y, $trans_colour); 3041 } 3042 } 3043 } 3044 } 3045 3046 return $src; 3047 } 3048 3049 ## -------------------------------------------------------- 3050 3051 function checkStringStartsWith($needle, $haystack) 3052 # Check if a string starts with a specific pattern 3053 { 3054 return (substr($haystack, 0, strlen($needle))==$needle); 3055 } 3056 3057 3058/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 3059 BMP SUPPORT (SAVING) - James Heinrich 3060*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 3061 3062 private function GD2BMPstring(&$gd_image) 3063 # Author: James Heinrich 3064 # Purpose: Save file as type bmp 3065 # Param in: The image canvas (passed as ref) 3066 # Param out: 3067 # Reference: 3068 # Notes: This code was stripped out of two external files 3069 # (phpthumb.bmp.php,phpthumb.functions.php) and added below to 3070 # avoid dependancies. 3071 # 3072 { 3073 $imageX = ImageSX($gd_image); 3074 $imageY = ImageSY($gd_image); 3075 3076 $BMP = ''; 3077 for ($y = ($imageY - 1); $y >= 0; $y--) { 3078 $thisline = ''; 3079 for ($x = 0; $x < $imageX; $x++) { 3080 $argb = $this->GetPixelColor($gd_image, $x, $y); 3081 $thisline .= chr($argb['blue']).chr($argb['green']).chr($argb['red']); 3082 } 3083 while (strlen($thisline) % 4) { 3084 $thisline .= "\x00"; 3085 } 3086 $BMP .= $thisline; 3087 } 3088 3089 $bmpSize = strlen($BMP) + 14 + 40; 3090 // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp 3091 $BITMAPFILEHEADER = 'BM'; // WORD bfType; 3092 $BITMAPFILEHEADER .= $this->LittleEndian2String($bmpSize, 4); // DWORD bfSize; 3093 $BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD bfReserved1; 3094 $BITMAPFILEHEADER .= $this->LittleEndian2String(0, 2); // WORD bfReserved2; 3095 $BITMAPFILEHEADER .= $this->LittleEndian2String(54, 4); // DWORD bfOffBits; 3096 3097 // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp 3098 $BITMAPINFOHEADER = $this->LittleEndian2String(40, 4); // DWORD biSize; 3099 $BITMAPINFOHEADER .= $this->LittleEndian2String($imageX, 4); // LONG biWidth; 3100 $BITMAPINFOHEADER .= $this->LittleEndian2String($imageY, 4); // LONG biHeight; 3101 $BITMAPINFOHEADER .= $this->LittleEndian2String(1, 2); // WORD biPlanes; 3102 $BITMAPINFOHEADER .= $this->LittleEndian2String(24, 2); // WORD biBitCount; 3103 $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD biCompression; 3104 $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD biSizeImage; 3105 $BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG biXPelsPerMeter; 3106 $BITMAPINFOHEADER .= $this->LittleEndian2String(2835, 4); // LONG biYPelsPerMeter; 3107 $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD biClrUsed; 3108 $BITMAPINFOHEADER .= $this->LittleEndian2String(0, 4); // DWORD biClrImportant; 3109 3110 return $BITMAPFILEHEADER.$BITMAPINFOHEADER.$BMP; 3111 } 3112 3113## -------------------------------------------------------- 3114 3115 private function GetPixelColor(&$img, $x, $y) 3116 # Author: James Heinrich 3117 # Purpose: 3118 # Param in: 3119 # Param out: 3120 # Reference: 3121 # Notes: 3122 # 3123 { 3124 if (!is_resource($img)) { 3125 return false; 3126 } 3127 return @ImageColorsForIndex($img, @ImageColorAt($img, $x, $y)); 3128 } 3129 3130## -------------------------------------------------------- 3131 3132 private function LittleEndian2String($number, $minbytes=1) 3133 # Author: James Heinrich 3134 # Purpose: BMP SUPPORT (SAVING) 3135 # Param in: 3136 # Param out: 3137 # Reference: 3138 # Notes: 3139 # 3140 { 3141 $intstring = ''; 3142 while ($number > 0) { 3143 $intstring = $intstring.chr($number & 255); 3144 $number >>= 8; 3145 } 3146 return str_pad($intstring, $minbytes, "\x00", STR_PAD_RIGHT); 3147 } 3148 3149 3150/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 3151 BMP SUPPORT (READING) 3152*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 3153 3154 private function ImageCreateFromBMP($filename) 3155 # Author: DHKold 3156 # Date: The 15th of June 2005 3157 # Version: 2.0B 3158 # Purpose: To create an image from a BMP file. 3159 # Param in: BMP file to open. 3160 # Param out: Return a resource like the other ImageCreateFrom functions 3161 # Reference: http://us3.php.net/manual/en/function.imagecreate.php#53879 3162 # Bug fix: Author: domelca at terra dot es 3163 # Date: 06 March 2008 3164 # Fix: Correct 16bit BMP support 3165 # Notes: 3166 # 3167 { 3168 3169 //Ouverture du fichier en mode binaire 3170 if (! $f1 = fopen($filename, "rb")) { 3171 return false; 3172 } 3173 3174 //1 : Chargement des ent�tes FICHIER 3175 $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1, 14)); 3176 if ($FILE['file_type'] != 19778) { 3177 return false; 3178 } 3179 3180 //2 : Chargement des ent�tes BMP 3181 $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'. 3182 '/Vcompression/Vsize_bitmap/Vhoriz_resolution'. 3183 '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1, 40)); 3184 $BMP['colors'] = pow(2, $BMP['bits_per_pixel']); 3185 3186 if ($BMP['size_bitmap'] == 0) { 3187 $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset']; 3188 } 3189 3190 $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8; 3191 $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']); 3192 $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4); 3193 $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4); 3194 $BMP['decal'] = 4-(4*$BMP['decal']); 3195 3196 if ($BMP['decal'] == 4) { 3197 $BMP['decal'] = 0; 3198 } 3199 3200 //3 : Chargement des couleurs de la palette 3201 $PALETTE = []; 3202 if ($BMP['colors'] < 16777216) { 3203 $PALETTE = unpack('V'.$BMP['colors'], fread($f1, $BMP['colors']*4)); 3204 } 3205 3206 //4 : Cr�ation de l'image 3207 $IMG = fread($f1, $BMP['size_bitmap']); 3208 $VIDE = chr(0); 3209 3210 $res = imagecreatetruecolor($BMP['width'], $BMP['height']); 3211 $P = 0; 3212 $Y = $BMP['height']-1; 3213 while ($Y >= 0) { 3214 $X=0; 3215 while ($X < $BMP['width']) { 3216 if ($BMP['bits_per_pixel'] == 24) { 3217 $COLOR = unpack("V", substr($IMG, $P, 3).$VIDE); 3218 } elseif ($BMP['bits_per_pixel'] == 16) { 3219 3220 /* 3221 * BMP 16bit fix 3222 * ================= 3223 * 3224 * Ref: http://us3.php.net/manual/en/function.imagecreate.php#81604 3225 * 3226 * Notes: 3227 * "don't work with bmp 16 bits_per_pixel. change pixel 3228 * generator for this." 3229 * 3230 */ 3231 3232 // *** Original code (don't work) 3233 //$COLOR = unpack("n",substr($IMG,$P,2)); 3234 //$COLOR[1] = $PALETTE[$COLOR[1]+1]; 3235 3236 $COLOR = unpack("v", substr($IMG, $P, 2)); 3237 $blue = ($COLOR[1] & 0x001f) << 3; 3238 $green = ($COLOR[1] & 0x07e0) >> 3; 3239 $red = ($COLOR[1] & 0xf800) >> 8; 3240 $COLOR[1] = $red * 65536 + $green * 256 + $blue; 3241 } elseif ($BMP['bits_per_pixel'] == 8) { 3242 $COLOR = unpack("n", $VIDE.substr($IMG, $P, 1)); 3243 $COLOR[1] = $PALETTE[$COLOR[1]+1]; 3244 } elseif ($BMP['bits_per_pixel'] == 4) { 3245 $COLOR = unpack("n", $VIDE.substr($IMG, floor($P), 1)); 3246 if (($P*2)%2 == 0) { 3247 $COLOR[1] = ($COLOR[1] >> 4) ; 3248 } else { 3249 $COLOR[1] = ($COLOR[1] & 0x0F); 3250 } 3251 $COLOR[1] = $PALETTE[$COLOR[1]+1]; 3252 } elseif ($BMP['bits_per_pixel'] == 1) { 3253 $COLOR = unpack("n", $VIDE.substr($IMG, floor($P), 1)); 3254 if (($P*8)%8 == 0) { 3255 $COLOR[1] = $COLOR[1] >>7; 3256 } elseif (($P*8)%8 == 1) { 3257 $COLOR[1] = ($COLOR[1] & 0x40)>>6; 3258 } elseif (($P*8)%8 == 2) { 3259 $COLOR[1] = ($COLOR[1] & 0x20)>>5; 3260 } elseif (($P*8)%8 == 3) { 3261 $COLOR[1] = ($COLOR[1] & 0x10)>>4; 3262 } elseif (($P*8)%8 == 4) { 3263 $COLOR[1] = ($COLOR[1] & 0x8)>>3; 3264 } elseif (($P*8)%8 == 5) { 3265 $COLOR[1] = ($COLOR[1] & 0x4)>>2; 3266 } elseif (($P*8)%8 == 6) { 3267 $COLOR[1] = ($COLOR[1] & 0x2)>>1; 3268 } elseif (($P*8)%8 == 7) { 3269 $COLOR[1] = ($COLOR[1] & 0x1); 3270 } 3271 $COLOR[1] = $PALETTE[$COLOR[1]+1]; 3272 } else { 3273 return false; 3274 } 3275 3276 imagesetpixel($res, $X, $Y, $COLOR[1]); 3277 $X++; 3278 $P += $BMP['bytes_per_pixel']; 3279 } 3280 3281 $Y--; 3282 $P+=$BMP['decal']; 3283 } 3284 //Fermeture du fichier 3285 fclose($f1); 3286 3287 return $res; 3288 } 3289 3290 3291/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*- 3292 PSD SUPPORT (READING) 3293*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-**-*-*-*-*-*-*-*-*-*-*-*-*-*/ 3294 3295 private function imagecreatefrompsd($fileName) 3296 # Author: Tim de Koning 3297 # Version: 1.3 3298 # Purpose: To create an image from a PSD file. 3299 # Param in: PSD file to open. 3300 # Param out: Return a resource like the other ImageCreateFrom functions 3301 # Reference: http://www.kingsquare.nl/phppsdreader 3302 # Notes: 3303 # 3304 { 3305 if (file_exists($this->psdReaderPath)) { 3306 include_once($this->psdReaderPath); 3307 3308 $psdReader = new PhpPsdReader($fileName); 3309 3310 if (isset($psdReader->infoArray['error'])) { 3311 return ''; 3312 } else { 3313 return $psdReader->getImage(); 3314 } 3315 } else { 3316 return false; 3317 } 3318 } 3319 3320## -------------------------------------------------------- 3321 3322 public function __destruct() 3323 { 3324 if (is_resource($this->imageResized)) { 3325 imagedestroy($this->imageResized); 3326 } 3327 } 3328 3329## -------------------------------------------------------- 3330} 3331 3332 3333 3334 3335/* 3336 * Example with some API calls (outdated): 3337 * 3338 * 3339 * =============================== 3340 * Compulsary 3341 * =============================== 3342 * 3343 * include("classes/resize_class.php"); 3344 * 3345 * // *** Initialise object 3346 * $magicianObj = new resize('images/cars/large/a.jpg'); 3347 * 3348 * // *** Turn off stretching (optional) 3349 * $magicianObj -> setForceStretch(false); 3350 * 3351 * // *** Resize object 3352 * $magicianObj -> resizeImage(150, 100, 0); 3353 * 3354 * =============================== 3355 * Image options - can run none, one, or all. 3356 * =============================== 3357 * 3358 * // *** Add watermark 3359 * $magicianObj -> addWatermark('stamp.png'); 3360 * 3361 * // *** Add text 3362 * $magicianObj -> addText('testing...'); 3363 * 3364 * =============================== 3365 * Output options - can run one, or the other, or both. 3366 * =============================== 3367 * 3368 * // *** Save image to disk 3369 * $magicianObj -> saveImage('images/cars/large/b.jpg', 100); 3370 * 3371 * // *** Or output to screen (params in can be jpg, gif, png) 3372 * $magicianObj -> displayImage('png'); 3373 * 3374 * =============================== 3375 * Return options - return errors. nice for debuggin. 3376 * =============================== 3377 * 3378 * // *** Return error array 3379 * $errorArray = $magicianObj -> getErrors(); 3380 * 3381 * 3382 * =============================== 3383 * Cleanup options - not really neccessary, but good practice 3384 * =============================== 3385 * 3386 * // *** Free used memory 3387 * $magicianObj -> __destruct(); 3388 */ 3389