1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11:
12: require_once 'geometry/Rectangle.php';
13: require_once 'geometry/exceptions.php';
14: require_once 'AcColor.php';
15: require_once 'AcImageJPG.php';
16: require_once 'AcImageGIF.php';
17: require_once 'AcImagePNG.php';
18:
19: 20: 21:
22:
23: class AcImage
24: {
25: const PNG = 'image/png';
26: const JPEG = 'image/jpeg';
27: const GIF = 'image/gif';
28:
29: const PROPORTION = 'pr';
30: const PIXELS = 'px';
31: const PERCENT = '%';
32:
33: const TOP_LEFT = 0;
34: const TOP_RIGHT = 1;
35: const BOTTOM_RIGHT = 2;
36: const BOTTOM_LEFT = 3;
37:
38: private static $correctCorners = array (0, 1, 2, 3);
39: private static $cornerLogo = 2;
40:
41: 42: 43: 44: 45:
46:
47: private $filePath;
48:
49: 50: 51: 52: 53:
54:
55: private $size;
56:
57: 58: 59: 60: 61: 62:
63:
64: private $imageInfo;
65:
66: 67: 68:
69:
70: private $resource;
71:
72: 73: 74: 75:
76:
77: private static $backgroundColor = AcColor::WHITE;
78:
79: 80: 81: 82: 83:
84:
85: private static $quality = 85;
86:
87: 88: 89: 90:
91:
92: private static $transparency = true;
93:
94: 95: 96: 97: 98:
99:
100: private static $gdInfo;
101:
102: 103: 104: 105:
106:
107: private static $rewrite = false;
108:
109: 110: 111: 112:
113:
114: private static $maxProportionLogo = 0.1;
115: private static $paddingProportionLogo = 0.02;
116:
117: 118: 119:
120:
121: protected function __construct($filePath)
122: {
123: if (!self::isFileExists($filePath))
124: throw new FileNotFoundException();
125:
126: $this->filePath = $filePath;
127:
128: $imageInfo = $this->getImageInfo();
129: if (!is_array($imageInfo))
130: throw new InvalidFileException($filePath);
131:
132: $this->setSize(new Size($imageInfo[0], $imageInfo[1]));
133: }
134:
135: 136: 137: 138: 139: 140: 141:
142:
143: public static function createImage($filePath)
144: {
145: $image = new AcImage($filePath);
146:
147: if (!self::isSupportedGD())
148: throw new GDnotInstalledException();
149:
150: $imageInfo = $image->getImageInfo();
151: if (!is_array($imageInfo))
152: throw new InvalidFileException($filePath);
153:
154: $mimeType = $imageInfo['mime'];
155:
156: switch ($mimeType)
157: {
158: case self::JPEG :
159: return new AcImageJPG($filePath);
160: case self::PNG :
161: return new AcImagePNG($filePath);
162: case self::GIF :
163: return new AcImageGIF($filePath);
164: default:
165: throw new InvalidFileException($filePath);
166: }
167: }
168:
169: 170: 171: 172: 173: 174: 175: 176: 177: 178:
179:
180: public function saveAsJPG($path)
181: {
182: if(!AcImageJPG::isSupport())
183: throw new UnsupportedFormatException();
184:
185: if (!self::getRewrite() && self::isFileExists($path))
186: throw new FileAlreadyExistsException($path);
187:
188: $this->putBackground(self::$backgroundColor);
189: if(!imagejpeg(self::getResource(), $path, self::getQuality()))
190: throw new FileNotSaveException($path);
191:
192: return $this;
193: }
194:
195: 196: 197: 198: 199: 200: 201: 202: 203: 204:
205:
206: public function saveAsPNG($path)
207: {
208: if(!AcImagePNG::isSupport())
209: throw new UnsupportedFormatException('png');
210:
211: if (!self::getRewrite() && self::isFileExists($path))
212: throw new FileAlreadyExistsException($path);
213:
214: if (!self::getTransparency())
215: $this->putBackground(self::$backgroundColor);
216:
217: if(!imagePng(self::getResource(), $path, AcImagePNG::getQuality()))
218: throw new FileNotSaveException($path);
219:
220: return $this;
221: }
222:
223: 224: 225: 226: 227: 228: 229: 230: 231: 232:
233:
234: public function saveAsGIF($path)
235: {
236: if(!AcImageGIF::isSupportedGD())
237: throw new UnsupportedFormatException();
238:
239: if (!self::getRewrite() && self::isFileExists($path))
240: throw new FileAlreadyExistsException($path);;
241:
242: if(!self::getTransparency())
243: $this->putBackground(self::$backgroundColor);
244:
245: if(!imagegif(self::getResource(), $path))
246: throw new FileNotSaveException($path);
247:
248: return $this;
249: }
250:
251: 252: 253: 254: 255: 256: 257: 258:
259:
260: public function resize()
261: {
262: $args = func_get_args();
263: if (count($args) == 2)
264: {
265: return $this->resize(new Size($args[0], $args[1]));
266: }
267: else if (count($args) == 1 && $args[0] instanceof Size)
268: {
269: $size = $args[0];
270: $imageSize = $this->getSize()->getByFrame($size);
271:
272: $newImageResource = imagecreatetruecolor($imageSize->getWidth(), $imageSize->getHeight());
273: imageAlphaBlending($newImageResource, false);
274: imageSaveAlpha($newImageResource, true);
275: imagecopyresampled($newImageResource, $this->getResource(), 0, 0, 0, 0, $imageSize->getWidth(),
276: $imageSize->getHeight(), $this->getWidth(), $this->getHeight());
277: $this->setResource($newImageResource);
278: $this->setSize($imageSize);
279: return $this;
280: }
281: throw new IllegalArgumentException();
282: }
283:
284: 285: 286: 287: 288: 289: 290:
291:
292: public function resizeByWidth($width)
293: {
294: if (!is_int($width) || $width <= 0)
295: throw new IllegalArgumentException();
296:
297: return $this->resize($width, $this->getHeight());
298: }
299:
300: 301: 302: 303: 304: 305: 306:
307:
308: public function resizeByHeight($height)
309: {
310: if (!is_int($height) || $height <= 0)
311: throw new IllegalArgumentException();
312:
313: return $this->resize($this->getWidth(), $height);
314: }
315:
316: 317: 318: 319: 320: 321: 322: 323:
324:
325: public function crop()
326: {
327: $a = func_get_args();
328:
329: if (count($a) == 4)
330: $rect = new Rectangle($a[0], $a[1], $a[2], $a[3]);
331: else if (count($a) == 1 && $a[0] instanceof Rectangle)
332: $rect = $a[0];
333: $rect = $rect->getIntersectsWith(new Rectangle(new Point(0, 0), $this->getSize()));
334:
335: if (!$rect)
336: throw new IllegalArgumentException();
337:
338: $width = $rect->getWidth();
339: $height = $rect->getHeight();
340: $x = $rect->getLeft();
341: $y = $rect->getTop();
342:
343: if ($width == 0 || $height == 0)
344: throw new IllegalArgumentException();
345:
346: $newImageResource = imagecreatetruecolor($width, $height);
347: imageAlphaBlending($newImageResource, false);
348: imageSaveAlpha($newImageResource, true);
349: imagecopyresampled($newImageResource, $this->getResource(), 0, 0, $x, $y, $width, $height, $width, $height);
350: $this->setResource($newImageResource);
351: $this->setSize($rect->getSize());
352: return $this;
353: }
354:
355: 356: 357: 358: 359: 360: 361: 362:
363:
364: public function cropSquare()
365: {
366: $a = func_get_args();
367: if (count($a) == 1 && $a[0] instanceof Rectangle && $a[0]->isSquare())
368: {
369: $square = $a[0];
370: }
371: else if (count($a) == 2 && $a[0] instanceof Point && is_int($a[1]))
372: {
373: $square = new Rectangle($a[0], new Size($a[1], $a[1]));
374: }
375: else if(count($a) == 3)
376: {
377: $square = new Rectangle(new Point($a[0], $a[1]), new Size($a[2], $a[2]));
378: }
379: else
380: {
381: throw new IllegalArgumentException();
382: }
383:
384: if (!$square->isInner(new Rectangle(new Point(0, 0), $this->getSize())))
385: throw new IllegalArgumentException();
386:
387: return $this->crop($square);
388: }
389:
390: 391: 392: 393: 394: 395: 396: 397: 398: 399:
400:
401: public function cropCenter($width, $height)
402: {
403: $result = self::parseCropCenterArg($width);
404: $widthUnits = $result['units'];
405: $width = $result['value'];
406:
407: $result = self::parseCropCenterArg($height);
408: $heightUnits = $result['units'];
409: $height = $result['value'];
410:
411:
412: if ($widthUnits == self::PROPORTION xor $heightUnits == self::PROPORTION)
413: throw new IllegalArgumentException();
414:
415: if ($widthUnits == self::PERCENT)
416: $width = self::percentToPixels($width, $this->getWidth());
417:
418: if ($heightUnits == self::PERCENT)
419: $height = self::percentToPixels($height, $this->getHeight());
420:
421: if ($widthUnits == self::PROPORTION)
422: {
423: $size = $this->getSizeByProportion($width, $height);
424:
425: $width = $size->getWidth();
426: $height = $size->getHeight();
427: }
428:
429: $width = (int)min($width, $this->getWidth());
430: $height = (int)min($height, $this->getHeight());
431:
432: $imageRect = new Rectangle(0, 0, $this->getWidth(), $this->getHeight());
433: $cropRect = new Rectangle(0, 0, $width, $height);
434: $cropRect = $cropRect->center($imageRect);
435:
436: return $this->crop($cropRect);
437: }
438:
439: 440: 441:
442:
443: private static function percentToPixels($value, $imageSide)
444: {
445: return (int)round(($imageSide / 100 * $value));
446: }
447:
448: 449: 450:
451:
452: private function getSizeByProportion($width, $height)
453: {
454: $imageWidth = $this->getWidth();
455: $imageHeight = $this->getHeight();
456:
457: if($height / $imageHeight > $width / $imageWidth)
458: {
459: return new Size((int)round($imageHeight / $height * $width), $imageHeight);
460: }
461: else
462: {
463: return new Size($imageWidth, (int)round($imageWidth / $width * $height));
464: }
465: }
466:
467: 468: 469:
470:
471: private static function parseCropCenterArg($arg)
472: {
473: $pattern = '/^(\d+(\.\d+)*)(px|\%|pr)$/';
474:
475: $matches = array();
476: if (is_int($arg))
477: {
478: $units = self::PIXELS;
479: $value = $arg;
480: }
481: else if (preg_match($pattern, $arg, $matches))
482: {
483: $units = $matches[3];
484: $value = $matches[1];
485: }
486: else
487: {
488: throw new IllegalArgumentException();
489: }
490: return array (
491: 'units' => $units,
492: 'value' => $value
493: );
494: }
495:
496: 497: 498: 499: 500: 501: 502: 503: 504:
505:
506: public function thumbnail($width, $height, $c = 2)
507: {
508: if ($c <= 1 || !is_finite($c) || $width <= 0 || $height <= 0)
509: throw new IllegalArgumentException();
510:
511: $size = new Size($width, $height);
512:
513: if($this->getSize()->lessThen($size))
514: $size = $this->getSize();
515:
516: $imageSize = $this->getSize();
517:
518: $isRotate = false;
519: if($size->getWidth() / $imageSize->getWidth() <= $size->getHeight() / $imageSize->getHeight())
520: {
521: $size->flip();
522: $imageSize->flip();
523: $isRotate = true;
524: }
525:
526: $width = $size->getWidth();
527: $height = $size->getHeight();
528:
529: $imageWidth = $imageSize->getWidth();
530: $imageHeight = $imageSize->getHeight();
531:
532: $lim = (int)($c * $height);
533: $newHeight = (int)($imageHeight * $width / $imageWidth);
534:
535: if ($imageWidth > $width)
536: {
537: if($newHeight <= $lim)
538: {
539: $size = new Size($width, $newHeight);
540: }
541: else
542: {
543: if($newHeight <= 2 * $lim)
544: {
545: $size = new Size((int)($imageWidth * $lim / $imageHeight), $lim);
546: }
547: else
548: {
549: $size = new Size((int)($width / 2), (int)($imageHeight * $width / $imageWidth));
550: }
551: }
552: }
553: else
554: {
555: if($imageHeight <= $lim)
556: {
557: $size = $this->getSize();
558: }
559: else
560: {
561: if($imageHeight <= 2 * $lim)
562: {
563: if($imageWidth * $lim / $imageHeight >= $width / 2)
564: {
565: $size = new Size((int)($imageWidth * $lim / $imageHeight), $lim);
566: }
567: else
568: {
569: $size = new Size((int)($width / 2), (int)($imageHeight * $width / ($imageWidth * 2)));
570: }
571: }
572: else
573: {
574: $size = new Size((int)($width / 2), (int)($imageHeight * $width / ($imageWidth * 2)));
575: }
576: }
577: }
578: if ($isRotate)
579: {
580: $size->flip();
581: $imageSize->flip();
582: }
583: return $this->resize($size);
584: }
585:
586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601:
602:
603: public function drawLogo($logo, $corner = null)
604: {
605: if (is_null($corner))
606: $corner = self::$cornerLogo;
607:
608: if (!AcImage::isCorrectCorner($corner))
609: throw new IllegalArgumentException();
610:
611: if (is_string($logo))
612: $logo = AcImage::createImage($logo);
613:
614: if (!($logo instanceof AcImage))
615: throw new IllegalArgumentException();
616:
617: $maxWidthLogo = (int)($this->getWidth() * self::$maxProportionLogo);
618: $maxHeightLogo = (int)($this->getHeight() * self::$maxProportionLogo);
619:
620: $logo->resize($maxWidthLogo, $maxHeightLogo);
621:
622: if (!self::getTransparency())
623: $logo->putBackground(self::$backgroundColor);
624:
625: imagealphablending($this->getResource(), true);
626: $logoSize = $logo->getSize();
627:
628: $location = $this->getLogoPosition($corner, $logoSize->getWidth(), $logoSize->getHeight());
629: imagecopy($this->getResource(), $logo->getResource(), $location->getX(), $location->getY(), 0, 0,
630: $logoSize->getWidth(), $logoSize->getHeight());
631:
632: return $this;
633: }
634:
635: 636: 637:
638:
639: private function getLogoPosition($corner, $width, $height)
640: {
641: $paddingX = $this->getWidth() * self::$paddingProportionLogo;
642: $paddingY = $this->getHeight() * self::$paddingProportionLogo;
643:
644:
645: if ($corner == self::BOTTOM_RIGHT || $corner == self::BOTTOM_LEFT)
646: $y = $this->getHeight() - $paddingY - $height;
647: else
648: $y = $paddingY;
649:
650: if ($corner == self::BOTTOM_RIGHT || $corner == self::TOP_RIGHT)
651: $x = $this->getWidth() - $paddingX - $width;
652: else
653: $x = $paddingX;
654:
655: return new Point((int)$x, (int)$y);
656: }
657:
658: 659: 660:
661:
662: private static function isCorrectCorner($corner)
663: {
664: return in_array($corner, self::$correctCorners);
665: }
666:
667: 668: 669: 670: 671: 672:
673:
674: public static function isFileExists($filePath)
675: {
676: if (@file_exists($filePath))
677: return true;
678:
679: if(!preg_match("|^http(s)?|", $filePath))
680: return false;
681:
682: $headers = @get_headers($filePath);
683: if(preg_match("|200|", $headers[0]))
684: return true;
685:
686: return false;
687: }
688:
689: 690: 691: 692: 693: 694:
695:
696: public static function isFileImage($filePath)
697: {
698: if (!self::isFileExists($filePath))
699: return false;
700:
701: $imageInfo = @getimagesize($filePath);
702: return is_array($imageInfo);
703: }
704:
705: 706: 707:
708:
709: protected function putBackground()
710: {
711: $newImageResource = imagecreatetruecolor($this->getWidth(), $this->getHeight());
712: imagefill($newImageResource , 0, 0, self::getBackgroundColor()->getCode());
713: imagecopyresampled($newImageResource , $this->getResource(), 0, 0, 0, 0,
714: $this->getWidth(), $this->getHeight(), $this->getWidth(), $this->getHeight());
715: $this->setResource($newImageResource);
716: }
717:
718: 719: 720: 721:
722:
723: public static function setRewrite($mode)
724: {
725: if (!is_bool($mode))
726: throw new IllegalArgumentException();
727:
728: self::$rewrite = $mode;
729: }
730:
731: public static function getRewrite()
732: {
733: return self::$rewrite;
734: }
735:
736: public function getImageInfo()
737: {
738: if (!isset($this->imageInfo))
739: $this->imageInfo = @getimagesize($this->getFilePath());
740:
741: return $this->imageInfo;
742: }
743:
744: 745: 746: 747: 748: 749: 750: 751: 752:
753:
754: public static function getShortPHPVersion()
755: {
756: $matches = array();
757: preg_match("@^\d\.\d@", phpversion(), $matches);
758: return $matches[0];
759: }
760:
761: public static function isSupportedGD()
762: {
763: return function_exists('gd_info');
764: }
765:
766: 767: 768: 769: 770: 771: 772:
773:
774: public static function getGDinfo()
775: {
776: if (!self::isSupportedGD())
777: return false;
778:
779: if (!isset(self::$gdInfo))
780: self::$gdInfo = gd_info();
781:
782: return self::$gdInfo;
783: }
784:
785: public function getFilePath()
786: {
787: return $this->filePath;
788: }
789:
790: private function setSize(Size $s)
791: {
792: $this->size = $s;
793: }
794:
795: public function getSourceImage()
796: {
797: return $this->sourceImage;
798: }
799:
800: public function getMimeType()
801: {
802: $imageInfo = $this->getImageInfo();
803: return $imageInfo['mime'];
804: }
805:
806: public function getSize()
807: {
808: return clone $this->size;
809: }
810:
811: public function getWidth()
812: {
813: return $this->getSize()->getWidth();
814: }
815:
816: public function getHeight()
817: {
818: return $this->getSize()->getHeight();
819: }
820:
821: public function getResource()
822: {
823: return $this->resource;
824: }
825:
826: 827: 828:
829:
830: protected function setResource($resource)
831: {
832: return $this->resource = $resource;
833: }
834:
835:
836:
837: 838: 839: 840: 841:
842:
843: public static function setQuality($q)
844: {
845: $q = (int)$q;
846: if (!is_integer($q) || $q <= 0 || $q > 100)
847: throw new IllegalArgumentException();
848:
849: self::$quality = $q;
850: }
851:
852: public static function getQuality()
853: {
854: return self::$quality;
855: }
856:
857: 858: 859: 860: 861:
862:
863: public static function setTransparency($mode)
864: {
865: if(!is_bool($mode))
866: throw new IllegalArgumentException();
867:
868: self::$transparency = $mode;
869: }
870:
871: public static function getTransparency()
872: {
873: return self::$transparency;
874: }
875:
876: public static function setBackgroundColor()
877: {
878: $a = func_get_args();
879: if (count($a) == 1)
880: {
881: if ($a[0] instanceof AcColor)
882: {
883: self::$backgroundColor = $a[0];
884: }
885: else
886: {
887: self::$backgroundColor = new AcColor($a[0]);
888: }
889: }
890: else if (count($a) == 3)
891: {
892: self::$backgroundColor = new AcColor($a[0], $a[1], $a[2]);
893: }
894: else
895: {
896: throw new IllegalArgumentException();
897: }
898: }
899:
900: public static function getBackgroundColor()
901: {
902: if (is_integer(self::$backgroundColor))
903: self::$backgroundColor = new AcColor(self::$backgroundColor);
904:
905: return self::$backgroundColor;
906: }
907:
908: public function getCornerLogo()
909: {
910: return self::$cornerLogo;
911: }
912:
913: 914: 915: 916: 917: 918: 919: 920: 921: 922: 923: 924: 925:
926:
927: public function setCornerLogo($corner)
928: {
929: if(!self::isCorrectCorner($corner))
930: throw new IllegalArgumentException();
931:
932: self::$cornerLogo = $corner;
933: }
934:
935: 936: 937: 938: 939:
940:
941: public static function setMaxProportionLogo($maxPropotionsLogo)
942: {
943: if (!is_float($maxPropotionsLogo) || $maxPropotionsLogo > 1 ||
944: $maxPropotionsLogo <= 0)
945: throw new IllegalArgumentException();
946:
947: self::$maxProportionLogo = $maxPropotionsLogo;
948: }
949:
950: public static function getMaxProportionLogo()
951: {
952: return self::$maxProportionLogo;
953: }
954:
955: 956: 957: 958: 959:
960:
961: public static function setPaddingProportionLogo($paddingProportionLogo)
962: {
963: if (!is_float($paddingProportionLogo) || $paddingProportionLogo > 1 ||
964: $paddingProportionLogo <= 0)
965: throw new IllegalArgumentException();
966:
967: self::$paddingProportionLogo = $paddingProportionLogo;
968: }
969:
970: public static function getPaddingProportionLogo()
971: {
972: return self::$paddingProportionLogo;
973: }
974: }
975: ?>