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