Image.php 24KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. <?php
  2. namespace Gregwar\ImageBundle;
  3. require_once (__DIR__.'/ImageColor.php');
  4. /**
  5. * Images handling class
  6. *
  7. * @author Gregwar <g.passault@gmail.com>
  8. */
  9. class Image
  10. {
  11. /**
  12. * Direcory to use for file caching
  13. */
  14. protected $cacheDir = 'cache/images';
  15. /**
  16. * The actual cache dir
  17. */
  18. protected $actualCacheDir = null;
  19. /**
  20. * GD Rssource
  21. */
  22. protected $gd = null;
  23. /**
  24. * User-defined resource
  25. */
  26. protected $resource = null;
  27. /**
  28. * Type name
  29. */
  30. protected $type = 'jpeg';
  31. /**
  32. * Transformations hash
  33. */
  34. protected $hash = null;
  35. /**
  36. * File
  37. */
  38. protected $file = null;
  39. /**
  40. * Image data
  41. */
  42. protected $data = null;
  43. /**
  44. * Dimensions for new resources
  45. */
  46. protected $width = null;
  47. protected $height = null;
  48. /**
  49. * Supported types
  50. */
  51. public static $types = array(
  52. 'jpg' => 'jpeg',
  53. 'jpeg' => 'jpeg',
  54. 'png' => 'png',
  55. 'gif' => 'gif',
  56. );
  57. public static $gdTypes = array(
  58. 'jpeg' => IMG_JPG,
  59. 'gif' => IMG_GIF,
  60. 'png' => IMG_PNG,
  61. );
  62. /**
  63. * Change the caching directory
  64. */
  65. public function setCacheDir($cacheDir)
  66. {
  67. $this->cacheDir = $cacheDir;
  68. }
  69. /**
  70. * The actual cache dir
  71. */
  72. public function setActualCacheDir($actualCacheDir)
  73. {
  74. $this->actualCacheDir = $actualCacheDir;
  75. }
  76. /**
  77. * Operations array
  78. */
  79. protected $operations = array();
  80. public function __construct($originalFile = null, $width = null, $height = null)
  81. {
  82. $this->file = $originalFile;
  83. $this->width = $width;
  84. $this->height = $height;
  85. if (!(extension_loaded('gd') && function_exists('gd_info')))
  86. {
  87. throw new \RuntimeException('You need to install GD PHP Extension to use this library');
  88. }
  89. }
  90. /**
  91. * Sets the image data
  92. */
  93. public function setData($data)
  94. {
  95. $this->data = $data;
  96. }
  97. /**
  98. * Sets the resource
  99. */
  100. public function setResource($resource)
  101. {
  102. $this->resource = $resource;
  103. }
  104. /**
  105. * Create and returns the absolute directory for a hash
  106. *
  107. * @param string $hash the hash
  108. *
  109. * @return string the full file name
  110. */
  111. public function generateFileFromHash($hash)
  112. {
  113. $directory = $this->cacheDir;
  114. if ($this->actualCacheDir === null) {
  115. $actualDirectory = $directory;
  116. } else {
  117. $actualDirectory = $this->actualCacheDir;
  118. }
  119. for ($i=0; $i<5; $i++)
  120. {
  121. $c = $hash[$i];
  122. $directory .= '/' . $c;
  123. $actualDirectory .= '/' . $c;
  124. }
  125. $file = $directory . '/' . substr($hash, 5);
  126. $actualFile = $actualDirectory . '/' . substr($hash, 5);
  127. return array($actualFile, $file);
  128. }
  129. /**
  130. * Defines the file only after instantiation
  131. *
  132. * @param string $originalFile the file path
  133. */
  134. public function fromFile($originalFile)
  135. {
  136. $this->file = $originalFile;
  137. return $this;
  138. }
  139. /**
  140. * Tells if the image is correct
  141. */
  142. public function correct()
  143. {
  144. return (false !== @exif_imagetype($this->file));
  145. }
  146. /**
  147. * Guess the file type
  148. */
  149. public function guessType()
  150. {
  151. if (function_exists('exif_imagetype'))
  152. {
  153. $type = @exif_imagetype($this->file);
  154. if (false !== $type) {
  155. if ($type == IMAGETYPE_JPEG)
  156. {
  157. return 'jpeg';
  158. }
  159. if ($type == IMAGETYPE_GIF)
  160. {
  161. return 'gif';
  162. }
  163. if ($type == IMAGETYPE_PNG)
  164. {
  165. return 'png';
  166. }
  167. }
  168. }
  169. $parts = explode('.', $this->file);
  170. $ext = strtolower($parts[count($parts)-1]);
  171. if (isset(self::$types[$ext]))
  172. {
  173. return self::$types[$ext];
  174. }
  175. return 'jpeg';
  176. }
  177. /**
  178. * Converts the image to true color
  179. */
  180. protected function convertToTrueColor()
  181. {
  182. if (!imageistruecolor($this->gd))
  183. {
  184. $transparentIndex = imagecolortransparent($this->gd);
  185. $w = imagesx($this->gd);
  186. $h = imagesy($this->gd);
  187. $img = imagecreatetruecolor($w, $h);
  188. imagecopy($img, $this->gd, 0, 0, 0, 0, $w, $h);
  189. if ($transparentIndex != -1)
  190. {
  191. $width = imagesx($this->gd);
  192. $height = imagesy($this->gd);
  193. imagealphablending($img, false);
  194. imagesavealpha($img, true);
  195. for ($x=0; $x<$width; $x++)
  196. {
  197. for ($y=0; $y<$height; $y++)
  198. {
  199. if (imagecolorat($this->gd, $x, $y) == $transparentIndex)
  200. {
  201. imagesetpixel($img, $x, $y, 127 << 24);
  202. }
  203. }
  204. }
  205. }
  206. $this->gd = $img;
  207. }
  208. }
  209. /**
  210. * Try to open the file
  211. */
  212. public function initGd()
  213. {
  214. if (null === $this->file)
  215. {
  216. if (null === $this->data)
  217. {
  218. if (null === $this->resource)
  219. {
  220. $this->gd = imagecreatetruecolor($this->width, $this->height);
  221. } else {
  222. $this->gd = $this->resource;
  223. }
  224. }
  225. else
  226. {
  227. $this->gd = imagecreatefromstring($this->data);
  228. }
  229. }
  230. else
  231. {
  232. if (null === $this->gd)
  233. {
  234. $this->type = $this->guessType();
  235. if (!(imagetypes() & self::$gdTypes[$this->type]))
  236. {
  237. throw new \RuntimeException('Type '.$this->type.' is not supported by GD');
  238. }
  239. if ($this->type == 'jpeg')
  240. {
  241. $this->openJpeg();
  242. }
  243. if ($this->type == 'gif')
  244. {
  245. $this->openGif();
  246. }
  247. if ($this->type == 'png')
  248. {
  249. $this->openPng();
  250. }
  251. if (null === $this->gd)
  252. {
  253. throw new \UnexpectedValueException('Unable to open file ('.$this->file.')');
  254. }
  255. else
  256. {
  257. $this->convertToTrueColor();
  258. }
  259. }
  260. }
  261. if ($this->gd)
  262. {
  263. imagesavealpha($this->gd, true);
  264. }
  265. return $this;
  266. }
  267. /**
  268. * Try to open the file using jpeg
  269. *
  270. */
  271. public function openJpeg()
  272. {
  273. $this->gd = imagecreatefromjpeg($this->file);
  274. }
  275. /**
  276. * Try to open the file using gif
  277. */
  278. public function openGif()
  279. {
  280. $this->gd = imagecreatefromgif($this->file);
  281. }
  282. /**
  283. * Try to open the file using PNG
  284. */
  285. public function openPng()
  286. {
  287. $this->gd = imagecreatefrompng($this->file);
  288. }
  289. /**
  290. * Adds an operation
  291. */
  292. protected function addOperation($method, $args)
  293. {
  294. $this->operations[] = array($method, $args);
  295. }
  296. /**
  297. * Generic function
  298. */
  299. public function __call($func, $args)
  300. {
  301. $reflection = new \ReflectionClass(get_class($this));
  302. $methodName = '_'.$func;
  303. if ($reflection->hasMethod($methodName))
  304. {
  305. $method = $reflection->getMethod($methodName);
  306. if ($method->getNumberOfRequiredParameters() > count($args))
  307. {
  308. throw new \InvalidArgumentException('Not enough arguments given for '.$func);
  309. }
  310. $this->addOperation($methodName, $args);
  311. return $this;
  312. }
  313. throw new \BadFunctionCallException('Invalid method: '.$func);
  314. }
  315. /**
  316. * Perform a zoom crop of the image to desired width and height
  317. *
  318. * @param integer $width Desired width
  319. * @param integer $height Desired height
  320. *
  321. * @return void
  322. */
  323. private function _zoomCrop($width, $height, $bg = 0xffffff)
  324. {
  325. // Calculate the different ratios
  326. $originalRatio = imagesx($this->gd) / imagesy($this->gd);
  327. $newRatio = $width / $height;
  328. // Compare ratios
  329. if ($originalRatio > $newRatio) {
  330. // Original image is wider
  331. $newHeight = $height;
  332. $newWidth = (int) $height * $originalRatio;
  333. } else {
  334. // Equal width or smaller
  335. $newHeight = (int) $width / $originalRatio;
  336. $newWidth = $width;
  337. }
  338. // Perform resize
  339. $this->_resize($newWidth, $newHeight, $bg, true);
  340. // Calculate cropping area
  341. $xPos = (int) ($newWidth - $width) / 2;
  342. $yPos = (int) ($newHeight - $height) / 2;
  343. // Crop image to reach desired size
  344. $this->_crop($xPos, $yPos, $width, $height);
  345. }
  346. /**
  347. * Resizes the image. It will never be enlarged.
  348. *
  349. * @param int $w the width
  350. * @param int $h the height
  351. * @param int $bg the background
  352. */
  353. protected function _resize($w = null, $h = null, $bg = 0xffffff, $force = false, $rescale = false, $crop = false)
  354. {
  355. $width = imagesx($this->gd);
  356. $height = imagesy($this->gd);
  357. $scale = 1.0;
  358. if ($h === null && preg_match('#^(.+)%$#mUsi', $w, $matches))
  359. {
  360. $w = (int)($width * ((float)$matches[1]/100.0));
  361. $h = (int)($height * ((float)$matches[1]/100.0));
  362. }
  363. if (!$force || $crop)
  364. {
  365. if ($w!=null && $width>$w)
  366. {
  367. $scale = $width/$w;
  368. }
  369. if ($h!=null && $height>$h)
  370. {
  371. if ($height/$h > $scale)
  372. $scale = $height/$h;
  373. }
  374. }
  375. else
  376. {
  377. if ($w!=null)
  378. {
  379. $scale = $width/$w;
  380. $new_width = $w;
  381. }
  382. if ($h!=null)
  383. {
  384. if ($w!=null && $rescale)
  385. {
  386. $scale = max($scale,$height/$h);
  387. }
  388. else
  389. {
  390. $scale = $height/$h;
  391. }
  392. $new_height = $h;
  393. }
  394. }
  395. if (!$force || $w==null || $rescale)
  396. {
  397. $new_width = (int)($width/$scale);
  398. }
  399. if (!$force || $h==null || $rescale)
  400. {
  401. $new_height = (int)($height/$scale);
  402. }
  403. if ($w == null || $crop)
  404. {
  405. $w = $new_width;
  406. }
  407. if ($h == null || $crop)
  408. {
  409. $h = $new_height;
  410. }
  411. $n = imagecreatetruecolor($w, $h);
  412. if ($bg != 'transparent')
  413. {
  414. imagefill($n, 0, 0, ImageColor::parse($bg));
  415. }
  416. else
  417. {
  418. imagealphablending($n, false);
  419. $color = imagecolorallocatealpha($n, 0, 0, 0, 127);
  420. imagefill($n, 0, 0, $color);
  421. imagesavealpha($n, true);
  422. }
  423. imagecopyresampled($n, $this->gd, ($w-$new_width)/2, ($h-$new_height)/2, 0, 0, $new_width, $new_height, $width, $height);
  424. imagedestroy($this->gd);
  425. $this->gd = $n;
  426. }
  427. /**
  428. * Resizes the image forcing the destination to have exactly the
  429. * given width and the height
  430. *
  431. * @param int $w the width
  432. * @param int $h the height
  433. * @param int $bg the background
  434. */
  435. protected function _forceResize($width = null, $height = null, $background = 0xffffff)
  436. {
  437. $this->_resize($width, $height, $background, true);
  438. }
  439. /**
  440. * Resizes the image preserving scale. Can enlarge it.
  441. *
  442. * @param int $w the width
  443. * @param int $h the height
  444. * @param int $bg the background
  445. */
  446. protected function _scaleResize($width, $height, $background=0xffffff)
  447. {
  448. $this->_resize($width, $height, $background, false, true);
  449. }
  450. /**
  451. * Works as resize() excepts that the layout will be cropped
  452. *
  453. * @param int $w the width
  454. * @param int $h the height
  455. * @param int $bg the background
  456. */
  457. protected function _cropResize($width, $height, $background=0xffffff)
  458. {
  459. $this->_resize($width, $height, $background, false, false, true);
  460. }
  461. /**
  462. * Crops the image
  463. *
  464. * @param int $x the top-left x position of the crop box
  465. * @param int $y the top-left y position of the crop box
  466. * @param int $w the width of the crop box
  467. * @param int $h the height of the crop box
  468. */
  469. public function _crop($x, $y, $w, $h) {
  470. $dst = imagecreatetruecolor($w, $h);
  471. imagecopy($dst, $this->gd, 0, 0, $x, $y, imagesx($this->gd), imagesy($this->gd));
  472. imagedestroy($this->gd);
  473. $this->gd = $dst;
  474. }
  475. /**
  476. * Negates the image
  477. */
  478. public function _negate()
  479. {
  480. imagefilter($this->gd, IMG_FILTER_NEGATE);
  481. }
  482. /**
  483. * Changes the brightness of the image
  484. *
  485. * @param int $brightness the brightness
  486. */
  487. protected function _brightness($b)
  488. {
  489. imagefilter($this->gd, IMG_FILTER_BRIGHTNESS, $b);
  490. }
  491. /**
  492. * Contrasts the image
  493. *
  494. * @param int $c the contrast
  495. */
  496. protected function _contrast($c)
  497. {
  498. imagefilter($this->gd, IMG_FILTER_CONTRAST, $c);
  499. }
  500. /**
  501. * Apply a grayscale level effect on the image
  502. */
  503. protected function _grayscale()
  504. {
  505. imagefilter($this->gd, IMG_FILTER_GRAYSCALE);
  506. }
  507. /**
  508. * Emboss the image
  509. */
  510. protected function _emboss()
  511. {
  512. imagefilter($this->gd, IMG_FILTER_EMBOSS);
  513. }
  514. /**
  515. * Smooth the image
  516. */
  517. protected function _smooth($p)
  518. {
  519. imagefilter($this->gd, IMG_FILTER_SMOOTH, $p);
  520. }
  521. /**
  522. * Sharps the image
  523. */
  524. protected function _sharp()
  525. {
  526. imagefilter($this->gd, IMG_FILTER_MEAN_REMOVAL);
  527. }
  528. /**
  529. * Edges the image
  530. */
  531. protected function _edge()
  532. {
  533. imagefilter($this->gd, IMG_FILTER_EDGEDETECT);
  534. }
  535. /**
  536. * Colorize the image
  537. */
  538. protected function _colorize($red, $green, $blue)
  539. {
  540. imagefilter($this->gd, IMG_FILTER_COLORIZE, $red, $green, $blue);
  541. }
  542. /**
  543. * Sepias the image
  544. */
  545. protected function _sepia()
  546. {
  547. imagefilter($this->gd, IMG_FILTER_GRAYSCALE);
  548. imagefilter($this->gd, IMG_FILTER_COLORIZE, 100, 50, 0);
  549. }
  550. /**
  551. * Merge with another image
  552. */
  553. protected function _merge(Image $other, $x = 0, $y = 0, $w = null, $h = null)
  554. {
  555. $other = clone $other;
  556. $other->initGd();
  557. $other->applyOperations();
  558. imagealphablending($this->gd, true);
  559. if (null == $w)
  560. {
  561. $w = $other->width();
  562. }
  563. if (null == $h)
  564. {
  565. $h = $other->height();
  566. }
  567. imagecopyresampled($this->gd, $other->gd, $x, $y, 0, 0, $w, $h, $w, $h);
  568. }
  569. /**
  570. * Rotate the image
  571. */
  572. protected function _rotate($angle, $background = 0xffffff)
  573. {
  574. $this->gd = imagerotate($this->gd, $angle, ImageColor::parse($background));
  575. imagealphablending($this->gd, true);
  576. imagesavealpha($this->gd, true);
  577. }
  578. /**
  579. * Fills the image
  580. */
  581. protected function _fill($color = 0xffffff, $x = 0, $y = 0)
  582. {
  583. imagealphablending($this->gd, false);
  584. imagefilledrectangle($this->gd, $x, $y, imagesx($this->gd), imagesy($this->gd), ImageColor::parse($color));
  585. }
  586. /**
  587. * Writes some text
  588. */
  589. protected function _write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $pos = 'left')
  590. {
  591. imagealphablending($this->gd, true);
  592. if ($pos != 'left')
  593. {
  594. $sim_size = self::TTFBox($font, $text, $size, $angle);
  595. if ($pos == 'center')
  596. {
  597. $x -= $sim_size['width'] / 2;
  598. }
  599. if ($pos == 'right')
  600. {
  601. $x -= $sim_size['width'];
  602. }
  603. }
  604. imagettftext($this->gd, $size, $angle, $x, $y, ImageColor::parse($color), $font, $text);
  605. }
  606. /**
  607. * Gets the width and the height for writing some text
  608. */
  609. public static function TTFBox($font, $text, $size, $angle = 0)
  610. {
  611. $box = imagettfbbox($size, $angle, $font, $text);
  612. return array(
  613. 'width' => abs($box[2] - $box[0]),
  614. 'height' => abs($box[3] - $box[5])
  615. );
  616. }
  617. /**
  618. * Draws a rectangle
  619. */
  620. protected function _rectangle($x1, $y1, $x2, $y2, $color, $filled = false)
  621. {
  622. if ($filled)
  623. {
  624. imagefilledrectangle($this->gd, $x1, $y1, $x2, $y2, ImageColor::parse($color));
  625. }
  626. else
  627. {
  628. imagerectangle($this->gd, $x1, $y1, $x2, $y2, ImageColor::parse($color));
  629. }
  630. }
  631. /**
  632. * Draws a line
  633. */
  634. protected function _line($x1, $y1, $x2, $y2, $color = 0x000000)
  635. {
  636. imageline($this->gd, $x1, $y1, $x2, $y2, ImageColor::parse($color));
  637. }
  638. /**
  639. * Draws an ellipse
  640. */
  641. protected function _ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false)
  642. {
  643. if ($filled)
  644. {
  645. imagefilledellipse($this->gd, $cx, $cy, $width, $height, ImageColor::parse($color));
  646. }
  647. else
  648. {
  649. imageellipse($this->gd, $cx, $cy, $width, $height, ImageColor::parse($color));
  650. }
  651. }
  652. /**
  653. * Draws a circle
  654. */
  655. protected function _circle($cx, $cy, $r, $color = 0x000000, $filled)
  656. {
  657. $this->_ellipse($cx, $cy, $r, $r, ImageColor::parse($color), $filled);
  658. }
  659. /**
  660. * Draws a polygon
  661. */
  662. protected function _polygon(array $points, $color, $filled = false)
  663. {
  664. if ($filled)
  665. {
  666. imagefilledpolygon($this->gd, $points, count($points)/2, ImageColor::parse($color));
  667. }
  668. else
  669. {
  670. imagepolygon($this->gd, $points, count($points)/2, ImageColor::parse($color));
  671. }
  672. }
  673. /**
  674. * Serialization of operations
  675. */
  676. public function serializeOperations()
  677. {
  678. $datas = array();
  679. foreach ($this->operations as $operation)
  680. {
  681. $method = $operation[0];
  682. $args = $operation[1];
  683. foreach ($args as &$arg)
  684. {
  685. if ($arg instanceof Image)
  686. {
  687. $arg = $arg->getHash();
  688. }
  689. }
  690. $datas[] = array($method, $args);
  691. }
  692. return serialize($datas);
  693. }
  694. /**
  695. * Generates the hash
  696. */
  697. public function generateHash($type = 'jpeg', $quality = 80)
  698. {
  699. $ctime = 0;
  700. try {
  701. $ctime = filectime($this->file);
  702. }
  703. catch (\Exception $e)
  704. {
  705. }
  706. $datas = array(
  707. $this->file,
  708. $ctime,
  709. $this->serializeOperations(),
  710. $type,
  711. $quality
  712. );
  713. $this->hash = sha1(serialize($datas));
  714. }
  715. /**
  716. * Gets the hash
  717. */
  718. public function getHash($type = 'jpeg', $quality = 80)
  719. {
  720. if (null === $this->hash)
  721. {
  722. $this->generateHash();
  723. }
  724. return $this->hash;
  725. }
  726. /**
  727. * Gets the cache file name and generate it if it does not exists.
  728. * Note that if it exists, all the image computation process will
  729. * not be done.
  730. */
  731. public function cacheFile($type = 'jpg', $quality = 80)
  732. {
  733. if ($type == 'guess')
  734. {
  735. $type = $this->type;
  736. }
  737. if (!count($this->operations) && $type == $this->guessType())
  738. {
  739. return $this->getFilename($this->file);
  740. }
  741. // Computes the hash
  742. $this->hash = $this->getHash($type, $quality);
  743. // Generates the cache file
  744. list($actualFile, $file) = $this->generateFileFromHash($this->hash.'.'.$type);
  745. // If the files does not exists, save it
  746. if (!file_exists($actualFile))
  747. {
  748. $this->save($actualFile, $type, $quality);
  749. }
  750. return $this->getFilename($file);
  751. }
  752. /**
  753. * Hook to helps to extends and enhance this class
  754. */
  755. protected function getFilename($filename)
  756. {
  757. return $filename;
  758. }
  759. /**
  760. * Generates and output a jpeg cached file
  761. */
  762. public function jpeg($quality = 80)
  763. {
  764. return $this->cacheFile('jpg', $quality);
  765. }
  766. /**
  767. * Generates and output a gif cached file
  768. */
  769. public function gif()
  770. {
  771. return $this->cacheFile('gif');
  772. }
  773. /**
  774. * Generates and output a png cached file
  775. */
  776. public function png()
  777. {
  778. return $this->cacheFile('png');
  779. }
  780. /**
  781. * Generates and output an image using the same type as input
  782. */
  783. public function guess($quality = 80)
  784. {
  785. return $this->cacheFile('guess', $quality);
  786. }
  787. /**
  788. * Applies the operations
  789. */
  790. public function applyOperations()
  791. {
  792. // Renders the effects
  793. foreach ($this->operations as $operation)
  794. {
  795. call_user_func_array(array($this, $operation[0]), $operation[1]);
  796. }
  797. }
  798. /**
  799. * Save the file to a given output
  800. */
  801. public function save($file, $type = 'jpeg', $quality = 80)
  802. {
  803. if ($file) {
  804. $directory = dirname($file);
  805. if (!is_dir($directory)) {
  806. @mkdir($directory, 0777, true);
  807. }
  808. }
  809. if (is_int($type))
  810. {
  811. $quality = $type;
  812. $type = 'jpeg';
  813. }
  814. if ($type == 'guess')
  815. {
  816. $type = $this->type;
  817. }
  818. if (!isset(self::$types[$type]))
  819. {
  820. throw new \InvalidArgumentException('Given type ('.$type.') is not valid');
  821. }
  822. $type = self::$types[$type];
  823. $this->initGd();
  824. $this->applyOperations();
  825. $success = false;
  826. if (null == $file)
  827. {
  828. ob_start();
  829. }
  830. if ($type == 'jpeg')
  831. {
  832. $success = imagejpeg($this->gd, $file, $quality);
  833. }
  834. if ($type == 'gif')
  835. {
  836. $transColor = imagecolorallocatealpha($this->gd, 0, 0, 0, 127);
  837. imagecolortransparent($this->gd, $transColor);
  838. $success = imagegif($this->gd, $file);
  839. }
  840. if ($type == 'png')
  841. {
  842. $success = imagepng($this->gd, $file);
  843. }
  844. if (!$success)
  845. {
  846. return false;
  847. }
  848. return (null === $file ? ob_get_clean() : $file);
  849. }
  850. /**
  851. * Get the contents of the image
  852. */
  853. public function get($type = 'jpeg', $quality = 80)
  854. {
  855. return $this->save(null, $type, $quality);
  856. }
  857. /* Image API */
  858. /**
  859. * Gets the width
  860. */
  861. public function width()
  862. {
  863. if (null === $this->gd)
  864. {
  865. $this->initGd();
  866. }
  867. return imagesx($this->gd);
  868. }
  869. /**
  870. * Gets the height
  871. */
  872. public function height()
  873. {
  874. if (null === $this->gd)
  875. {
  876. $this->initGd();
  877. }
  878. return imagesy($this->gd);
  879. }
  880. /**
  881. * Tostring defaults to jpeg
  882. */
  883. public function __toString()
  884. {
  885. return $this->jpeg();
  886. }
  887. /**
  888. * Returning basic html code for this image
  889. */
  890. public function html($title = '')
  891. {
  892. return '<img title="' . $title . '" src="' . $this->jpeg() . '" />';
  893. }
  894. /**
  895. * Creates an instance, usefull for one-line chaining
  896. */
  897. public static function open($file = '')
  898. {
  899. return new Image($file);
  900. }
  901. /**
  902. * Creates an instance of a new resource
  903. */
  904. public static function create($width, $height)
  905. {
  906. return new Image(null, $width, $height);
  907. }
  908. /**
  909. * Creates an instance of image from its data
  910. */
  911. public static function fromData($data)
  912. {
  913. $image = new Image();
  914. $image->setData($data);
  915. return $image;
  916. }
  917. /**
  918. * Creates an instance of image from resource
  919. */
  920. public static function fromResource($resource)
  921. {
  922. $image = new Image();
  923. $image->setResource($resource);
  924. return $image;
  925. }
  926. }