PageRenderTime 63ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/_app/vendor/Intervention/Image/Image.php

https://bitbucket.org/sirestudios/fortis-wellness
PHP | 1537 lines | 767 code | 254 blank | 516 comment | 105 complexity | 5ef06ea2cf6f471c312f0175f2c1ff25 MD5 | raw file
Possible License(s): JSON
  1. <?php
  2. namespace Intervention\Image;
  3. use Exception;
  4. use Closure;
  5. class Image
  6. {
  7. /**
  8. * The image resource identifier of current image
  9. *
  10. * @var resource
  11. */
  12. public $resource;
  13. /**
  14. * Type of current image
  15. *
  16. * @var string
  17. */
  18. public $type;
  19. /**
  20. * Width of current image
  21. *
  22. * @var integer
  23. */
  24. public $width;
  25. /**
  26. * Height of current image
  27. *
  28. * @var integer
  29. */
  30. public $height;
  31. /**
  32. * Directory path of current image
  33. *
  34. * @var string
  35. */
  36. public $dirname;
  37. /**
  38. * Trailing name component of current image filename
  39. *
  40. * @var string
  41. */
  42. public $basename;
  43. /**
  44. * File extension of current image filename
  45. *
  46. * @var string
  47. */
  48. public $extension;
  49. /**
  50. * Combined filename (basename and extension)
  51. *
  52. * @var string
  53. */
  54. public $filename;
  55. /**
  56. * MIME type of image
  57. *
  58. * @var string
  59. */
  60. public $mime;
  61. /**
  62. * Attributes of the original created image
  63. *
  64. * @var Array
  65. */
  66. protected $original;
  67. /**
  68. * Identifier for cached images
  69. *
  70. * @var boolean
  71. */
  72. public $cached = false;
  73. /**
  74. * Create a new instance of Image class
  75. *
  76. * @param string $source
  77. * @param integer $width
  78. * @param integer $height
  79. * @param mixed $bgcolor
  80. */
  81. public function __construct($source = null, $width = null, $height = null, $bgcolor = null)
  82. {
  83. // set image properties
  84. if ( ! is_null($source)) {
  85. if ($this->isImageResource($source)) {
  86. // image properties come from gd image resource
  87. $this->initFromResource($source);
  88. } elseif ($this->isBinary($source)) {
  89. // image properties come from binary image string
  90. $this->initFromString($source);
  91. } else {
  92. // image properties come from image file
  93. $this->initFromPath($source);
  94. }
  95. } else {
  96. // new empty resource
  97. $this->initEmpty($width, $height, $bgcolor);
  98. }
  99. }
  100. /**
  101. * Open a new image resource from image file
  102. *
  103. * @param mixed $source
  104. * @return Image
  105. */
  106. public static function make($source)
  107. {
  108. return new Image($source);
  109. }
  110. /**
  111. * Create a new empty image resource
  112. *
  113. * @param int $width
  114. * @param int $height
  115. * @param mixed $bgcolor
  116. * @return Image
  117. */
  118. public static function canvas($width, $height, $bgcolor = null)
  119. {
  120. return new Image(null, $width, $height, $bgcolor);
  121. }
  122. /**
  123. * Create a new image resource with image data from string
  124. *
  125. * @param string $data
  126. * @return Image
  127. */
  128. public static function raw($string)
  129. {
  130. return new Image($string);
  131. }
  132. /**
  133. * Create new cached image and run callback
  134. * (requires additional package intervention/imagecache)
  135. *
  136. * @param Closure $callback
  137. * @param integer $lifetime
  138. * @param boolean $returnObj
  139. * @return Image
  140. */
  141. public static function cache(Closure $callback = null, $lifetime = null, $returnObj = false)
  142. {
  143. if ( ! class_exists('\Intervention\Image\ImageCache')) {
  144. throw new Exception('Please install package intervention/imagecache before running this function.');
  145. }
  146. // Create image and run callback
  147. $image = new \Intervention\Image\ImageCache;
  148. $image = is_callable($callback) ? $callback($image) : $image;
  149. return $image->get($lifetime, $returnObj);
  150. }
  151. /**
  152. * Set properties for image resource from image file
  153. *
  154. * @param string $path
  155. * @return void
  156. */
  157. private function initFromPath($path)
  158. {
  159. if ( ! file_exists($path)) {
  160. throw new Exception("Image file ({$path}) not found");
  161. }
  162. // set file info
  163. $this->setFileInfoFromPath($path);
  164. // set image info
  165. $this->setImageInfoFromPath($path);
  166. }
  167. /**
  168. * Set properties for image resource from string
  169. *
  170. * @param string $string
  171. * @return void
  172. */
  173. private function initFromString($string)
  174. {
  175. $this->setImageInfoFromString($string);
  176. }
  177. /**
  178. * Set image properties from GD image resource
  179. *
  180. * @param resource $resource
  181. */
  182. private function initFromResource($resource)
  183. {
  184. if ( ! $this->isImageResource($resource)) {
  185. throw new Exception("initFromResource expects parameter to be resource.");
  186. }
  187. $this->setImageInfoFromResource($resource);
  188. }
  189. /**
  190. * Set properties for empty image resource
  191. *
  192. * @param int $width
  193. * @param int $height
  194. * @param mixed $bgcolor
  195. * @return void
  196. */
  197. private function initEmpty($width, $height, $bgcolor = null)
  198. {
  199. $this->width = is_numeric($width) ? intval($width) : 1;
  200. $this->height = is_numeric($height) ? intval($height) : 1;
  201. $this->original['width'] = $this->width;
  202. $this->original['height'] = $this->height;
  203. // create empty image
  204. $this->resource = imagecreatetruecolor($this->width, $this->height);
  205. // set background color
  206. if (is_null($bgcolor)) {
  207. // fill with transparent background instead of black
  208. $bgcolor = imagecolorallocatealpha($this->resource, 0, 0, 0, 127);
  209. } else {
  210. $bgcolor = $this->parseColor($bgcolor);
  211. }
  212. imagefill($this->resource, 0, 0, $bgcolor);
  213. $this->original['bgcolor'] = $bgcolor;
  214. }
  215. /**
  216. * Modify wrapper function used by resize and grab
  217. *
  218. * @param integer $dst_x
  219. * @param integer $dst_y
  220. * @param integer $src_x
  221. * @param integer $src_y
  222. * @param integer $dst_w
  223. * @param integer $dst_h
  224. * @param integer $src_w
  225. * @param integer $src_h
  226. * @return Image
  227. */
  228. private function modify($dst_x , $dst_y , $src_x , $src_y , $dst_w , $dst_h , $src_w , $src_h)
  229. {
  230. // create new image
  231. $image = imagecreatetruecolor($dst_w, $dst_h);
  232. // preserve transparency
  233. imagealphablending($image, false);
  234. imagesavealpha($image, true);
  235. // copy content from resource
  236. imagecopyresampled($image, $this->resource, $dst_x , $dst_y , $src_x , $src_y , $dst_w , $dst_h , $src_w , $src_h);
  237. // set new content as recource
  238. $this->resource = $image;
  239. // set new dimensions
  240. $this->width = $dst_w;
  241. $this->height = $dst_h;
  242. return $this;
  243. }
  244. /**
  245. * Open a new image resource from image file
  246. *
  247. * @param string $path
  248. * @return Image
  249. */
  250. public function open($path)
  251. {
  252. $this->initFromPath($path);
  253. return $this;
  254. }
  255. /**
  256. * Resize current image based on given width/height
  257. *
  258. * Width and height are optional, the not given parameter is calculated
  259. * based on the given. The ratio boolean decides whether the resizing
  260. * should keep the image ratio. You can also pass along a boolean to
  261. * prevent the image from being upsized.
  262. *
  263. * @param integer $width The target width for the image
  264. * @param integer $height The target height for the image
  265. * @param boolean $ratio Determines if the image ratio should be preserved
  266. * @param boolean $upsize Determines whether the image can be upsized
  267. *
  268. * @return Image
  269. */
  270. public function resize($width = null, $height = null, $ratio = false, $upsize = true)
  271. {
  272. // catch legacy call
  273. if (is_array($width)) {
  274. $dimensions = $width;
  275. return $this->legacyResize($dimensions);
  276. }
  277. // Evaluate passed parameters.
  278. $width = isset($width) ? intval($width) : null;
  279. $height = $max_height = isset($height) ? intval($height) : null;
  280. $ratio = $ratio ? true : false;
  281. $upsize = $upsize ? true : false;
  282. // If the ratio needs to be kept.
  283. if ($ratio) {
  284. // If both width and hight have been passed along, the width and
  285. // height parameters are maximum values.
  286. if ( ! is_null($width) && ! is_null($height)) {
  287. // First, calculate the height.
  288. $height = intval($width / $this->width * $this->height);
  289. // If the height is too large, set it to the maximum
  290. // height and calculate the width.
  291. if ($height > $max_height) {
  292. $height = $max_height;
  293. $width = intval($height / $this->height * $this->width);
  294. }
  295. } elseif ($ratio && ( ! is_null($width) OR ! is_null($height))) { // If only one of width or height has been provided.
  296. $width = is_null($width) ? intval($height / $this->height * $this->width) : $width;
  297. $height = is_null($height) ? intval($width / $this->width * $this->height) : $height;
  298. }
  299. }
  300. // If the image can't be upsized, check if the given width and/or
  301. // height are too large.
  302. if ( ! $upsize) {
  303. // If the given width is larger then the image width,
  304. // then don't resize it.
  305. if ( ! is_null($width) && $width > $this->width) {
  306. $width = $this->width;
  307. // If ratio needs to be kept, height is recalculated.
  308. if ($ratio) {
  309. $height = intval($width / $this->width * $this->height);
  310. }
  311. }
  312. // If the given height is larger then the image height,
  313. // then don't resize it.
  314. if ( ! is_null($height) && $height > $this->height) {
  315. $height = $this->height;
  316. // If ratio needs to be kept, width is recalculated.
  317. if ($ratio) {
  318. $width = intval($height / $this->height * $this->width);
  319. }
  320. }
  321. }
  322. // If both the width and height haven't been passed along,
  323. // throw an exception.
  324. if (is_null($width) && is_null($height)) {
  325. throw new Exception('width or height needs to be defined');
  326. } elseif (is_null($width)) { // If only the width hasn't been set, keep the current width.
  327. $width = $this->width;
  328. } elseif (is_null($height)) { // If only the height hasn't been set, keep the current height.
  329. $height = $this->height;
  330. }
  331. // Create new image in new dimensions.
  332. return $this->modify(0, 0, 0, 0, $width, $height, $this->width, $this->height);
  333. }
  334. /**
  335. * Legacy method to support old resizing calls
  336. *
  337. * @param array $dimensions
  338. * @return Image
  339. */
  340. public function legacyResize($dimensions = array())
  341. {
  342. $width = array_key_exists('width', $dimensions) ? intval($dimensions['width']) : null;
  343. $height = array_key_exists('height', $dimensions) ? intval($dimensions['height']) : null;
  344. return $this->resize($width, $height, true);
  345. }
  346. /**
  347. * Resize image canvas
  348. *
  349. * @param int $width
  350. * @param int $height
  351. * @param string $anchor
  352. * @param boolean $relative
  353. * @param mixed $bgcolor
  354. * @return Image
  355. */
  356. public function resizeCanvas($width, $height, $anchor = null, $relative = false, $bgcolor = null)
  357. {
  358. // check of only width or height is set
  359. $width = is_null($width) ? $this->width : intval($width);
  360. $height = is_null($height) ? $this->height : intval($height);
  361. // check on relative width/height
  362. if ($relative) {
  363. $width = $this->width + $width;
  364. $height = $this->height + $height;
  365. }
  366. // check for negative width
  367. if ($width <= 0) {
  368. $width = $this->width + $width;
  369. }
  370. // check for negative height
  371. if ($height <= 0) {
  372. $height = $this->height + $height;
  373. }
  374. // create new canvas
  375. $image = imagecreatetruecolor($width, $height);
  376. if ($width > $this->width || $height > $this->height) {
  377. $bgcolor = is_null($bgcolor) ? '000000' : $bgcolor;
  378. imagefill($image, 0, 0, $this->parseColor($bgcolor));
  379. }
  380. if ($width >= $this->width) {
  381. $src_w = $this->width;
  382. } else {
  383. $src_w = $width;
  384. }
  385. if ($height >= $this->height) {
  386. $src_h = $this->height;
  387. } else {
  388. $src_h = $height;
  389. }
  390. // define anchor
  391. switch ($anchor) {
  392. case 'top-left':
  393. case 'left-top':
  394. $src_x = 0;
  395. $src_y = 0;
  396. break;
  397. case 'top':
  398. case 'top-center':
  399. case 'top-middle':
  400. case 'center-top':
  401. case 'middle-top':
  402. $src_x = ($width < $this->width) ? intval(($this->width - $width) / 2) : 0;
  403. $src_y = 0;
  404. break;
  405. case 'top-right':
  406. case 'right-top':
  407. $src_x = ($width < $this->width) ? intval($this->width - $width) : 0;
  408. $src_y = 0;
  409. break;
  410. case 'left':
  411. case 'left-center':
  412. case 'left-middle':
  413. case 'center-left':
  414. case 'middle-left':
  415. $src_x = 0;
  416. $src_y = ($height < $this->height) ? intval(($this->height - $height) / 2) : 0;
  417. break;
  418. case 'right':
  419. case 'right-center':
  420. case 'right-middle':
  421. case 'center-right':
  422. case 'middle-right':
  423. $src_x = ($width < $this->width) ? intval($this->width - $width) : 0;
  424. $src_y = ($height < $this->height) ? intval(($this->height - $height) / 2) : 0;
  425. break;
  426. case 'bottom-left':
  427. case 'left-bottom':
  428. $src_x = 0;
  429. $src_y = ($height < $this->height) ? intval($this->height - $height) : 0;
  430. break;
  431. case 'bottom':
  432. case 'bottom-center':
  433. case 'bottom-middle':
  434. case 'center-bottom':
  435. case 'middle-bottom':
  436. $src_x = ($width < $this->width) ? intval(($this->width - $width) / 2) : 0;
  437. $src_y = ($height < $this->height) ? intval($this->height - $height) : 0;
  438. break;
  439. case 'bottom-right':
  440. case 'right-bottom':
  441. $src_x = ($width < $this->width) ? intval($this->width - $width) : 0;
  442. $src_y = ($height < $this->height) ? intval($this->height - $height) : 0;
  443. break;
  444. default:
  445. case 'center':
  446. case 'middle':
  447. case 'center-center':
  448. case 'middle-middle':
  449. $src_x = ($width < $this->width) ? intval(($this->width - $width) / 2) : 0;
  450. $src_y = ($height < $this->height) ? intval(($this->height - $height) / 2) : 0;
  451. break;
  452. }
  453. // define dest. pos
  454. $dst_x = ($width <= $this->width) ? 0 : intval(($width - $this->width) / 2);
  455. $dst_y = ($height <= $this->height) ? 0 : intval(($height - $this->height) / 2);
  456. // copy content from resource
  457. imagecopy($image, $this->resource, $dst_x , $dst_y , $src_x , $src_y , $src_w , $src_h);
  458. // set new content as recource
  459. $this->resource = $image;
  460. // set new dimensions
  461. $this->width = $width;
  462. $this->height = $height;
  463. return $this;
  464. }
  465. /**
  466. * Crop the current image
  467. *
  468. * @param integer $width
  469. * @param integer $height
  470. * @param integer $pos_x
  471. * @param integer $pos_y
  472. *
  473. * @return Image
  474. */
  475. public function crop($width, $height, $pos_x = null, $pos_y = null)
  476. {
  477. $width = is_numeric($width) ? intval($width) : null;
  478. $height = is_numeric($height) ? intval($height) : null;
  479. $pos_x = is_numeric($pos_x) ? intval($pos_x) : null;
  480. $pos_y = is_numeric($pos_y) ? intval($pos_y) : null;
  481. if (is_null($pos_x) && is_null($pos_y)) {
  482. // center position of width/height rectangle
  483. $pos_x = floor(($this->width - intval($width)) / 2);
  484. $pos_y = floor(($this->height - intval($height)) / 2);
  485. }
  486. if (is_null($width) || is_null($height)) {
  487. throw new Exception('width and height of cutout needs to be defined');
  488. }
  489. return $this->modify(0, 0, $pos_x , $pos_y, $width, $height, $width, $height);
  490. }
  491. /**
  492. * Cut out a detail of the image in given ratio and resize to output size
  493. *
  494. * @param integer $width
  495. * @param integer $height
  496. *
  497. * @return Image
  498. */
  499. public function grab($width = null, $height = null)
  500. {
  501. // catch legacy call
  502. if (is_array($width)) {
  503. $dimensions = $width;
  504. return $this->legacyGrab($dimensions);
  505. }
  506. $width = is_numeric($width) ? intval($width) : null;
  507. $height = is_numeric($height) ? intval($height) : null;
  508. if ( ! is_null($width) OR ! is_null($height)) {
  509. // if width or height are not set, define values automatically
  510. $width = is_null($width) ? $height : $width;
  511. $height = is_null($height) ? $width : $height;
  512. } else {
  513. // width or height not defined (resume with original values)
  514. throw new Exception('width or height needs to be defined');
  515. }
  516. // ausschnitt berechnen
  517. $grab_width = $this->width;
  518. $ratio = $grab_width / $width;
  519. if($height * $ratio <= $this->height) {
  520. $grab_height = round($height * $ratio);
  521. $src_x = 0;
  522. $src_y = round(($this->height - $grab_height) / 2);
  523. } else {
  524. $grab_height = $this->height;
  525. $ratio = $grab_height / $height;
  526. $grab_width = round($width * $ratio);
  527. $src_x = round(($this->width - $grab_width) / 2);
  528. $src_y = 0;
  529. }
  530. return $this->modify(0, 0, $src_x, $src_y, $width, $height, $grab_width, $grab_height);
  531. }
  532. /**
  533. * Legacy Method to support older grab calls
  534. *
  535. * @param array $dimensions
  536. * @return Image
  537. */
  538. public function legacyGrab($dimensions = array())
  539. {
  540. $width = array_key_exists('width', $dimensions) ? intval($dimensions['width']) : null;
  541. $height = array_key_exists('height', $dimensions) ? intval($dimensions['height']) : null;
  542. return $this->grab($width, $height);
  543. }
  544. /**
  545. * Mirror image horizontally or vertically
  546. *
  547. * @param mixed $mode
  548. * @return Image
  549. */
  550. public function flip($mode = null)
  551. {
  552. $x = 0;
  553. $y = 0;
  554. $width = $this->width;
  555. $height = $this->height;
  556. switch (strtolower($mode)) {
  557. case 2:
  558. case 'v':
  559. case 'vert':
  560. case 'vertical':
  561. $y = $height - 1;
  562. $height = $height * (-1);
  563. break;
  564. default:
  565. $x = $width - 1;
  566. $width = $width * (-1);
  567. break;
  568. }
  569. return $this->modify(0, 0, $x, $y, $this->width, $this->height, $width, $height);
  570. }
  571. /**
  572. * Insert another image on top of the current image
  573. *
  574. * @param mixed $source
  575. * @param integer $pos_x
  576. * @param integer $pos_y
  577. * @param string $anchor
  578. * @return Image
  579. */
  580. public function insert($source, $pos_x = 0, $pos_y = 0, $anchor = null)
  581. {
  582. $obj = is_a($source, 'Intervention\Image\Image') ? $source : (new Image($source));
  583. // define anchor
  584. switch ($anchor) {
  585. case 'top':
  586. case 'top-center':
  587. case 'top-middle':
  588. case 'center-top':
  589. case 'middle-top':
  590. $pos_x = intval((($this->width - $obj->width) / 2) + $pos_x);
  591. $pos_y = $pos_y;
  592. break;
  593. case 'top-right':
  594. case 'right-top':
  595. $pos_x = intval($this->width - $obj->width - $pos_x);
  596. $pos_y = $pos_y;
  597. break;
  598. case 'left':
  599. case 'left-center':
  600. case 'left-middle':
  601. case 'center-left':
  602. case 'middle-left':
  603. $pos_x = $pos_x;
  604. $pos_y = intval((($this->height - $obj->height) / 2) + $pos_y);
  605. break;
  606. case 'right':
  607. case 'right-center':
  608. case 'right-middle':
  609. case 'center-right':
  610. case 'middle-right':
  611. $pos_x = intval($this->width - $obj->width - $pos_x);
  612. $pos_y = intval((($this->height - $obj->height) / 2) + $pos_y);
  613. break;
  614. case 'bottom-left':
  615. case 'left-bottom':
  616. $pos_x = $pos_x;
  617. $pos_y = intval($this->height - $obj->height - $pos_y);
  618. break;
  619. case 'bottom':
  620. case 'bottom-center':
  621. case 'bottom-middle':
  622. case 'center-bottom':
  623. case 'middle-bottom':
  624. $pos_x = intval((($this->width - $obj->width) / 2) + $pos_x);
  625. $pos_y = intval($this->height - $obj->height - $pos_y);
  626. break;
  627. case 'bottom-right':
  628. case 'right-bottom':
  629. $pos_x = intval($this->width - $obj->width - $pos_x);
  630. $pos_y = intval($this->height - $obj->height - $pos_y);
  631. break;
  632. case 'center':
  633. case 'middle':
  634. case 'center-center':
  635. case 'middle-middle':
  636. $pos_x = intval((($this->width - $obj->width) / 2) + $pos_x);
  637. $pos_y = intval((($this->height - $obj->height) / 2) + $pos_y);
  638. break;
  639. default:
  640. case 'top-left':
  641. case 'left-top':
  642. $pos_x = intval($pos_x);
  643. $pos_y = intval($pos_y);
  644. break;
  645. }
  646. imagecopy($this->resource, $obj->resource, $pos_x, $pos_y, 0, 0, $obj->width, $obj->height);
  647. return $this;
  648. }
  649. /**
  650. * Set opacity of current image
  651. *
  652. * @param integer $transparency
  653. * @return Image
  654. */
  655. public function opacity($transparency)
  656. {
  657. if ($transparency >= 0 && $transparency <= 100) {
  658. $transparency = intval($transparency) / 100;
  659. } else {
  660. throw new Exception('Opacity must be between 0 and 100');
  661. }
  662. // create alpha mask
  663. $alpha = new self(null, $this->width, $this->height);
  664. $alpha->fill(sprintf('rgba(0, 0, 0, %.1f)', $transparency));
  665. // apply alpha mask
  666. $this->mask($alpha, true);
  667. return $this;
  668. }
  669. /**
  670. * Apply given image as alpha mask on current image
  671. *
  672. * @param mixed $source
  673. * @param boolean $mask_with_alpha
  674. * @return Image
  675. */
  676. public function mask($source, $mask_with_alpha = false)
  677. {
  678. // create new empty image
  679. $maskedImage = new Image(null, $this->width, $this->height);
  680. // create mask
  681. $mask = is_a($source, 'Intervention\Image\Image') ? $source : (new Image($source));
  682. // resize mask to size of current image (if necessary)
  683. if ($mask->width != $this->width || $mask->height != $this->height) {
  684. $mask->resize($this->width, $this->height);
  685. }
  686. // redraw old image pixel by pixel considering alpha map
  687. for ($x=0; $x < $this->width; $x++) {
  688. for ($y=0; $y < $this->height; $y++) {
  689. $color = $this->pickColor($x, $y, 'array');
  690. $alpha = $mask->pickColor($x, $y, 'array');
  691. if ($mask_with_alpha) {
  692. $alpha = $alpha['a']; // use alpha channel as mask
  693. } else {
  694. $alpha = floatval(round($alpha['r'] / 255, 2)); // use red channel as mask
  695. }
  696. // preserve alpha of original image...
  697. if ($color['a'] < $alpha) {
  698. $alpha = $color['a'];
  699. }
  700. $pixelColor = array($color['r'], $color['g'], $color['b'], $alpha);
  701. $maskedImage->pixel($pixelColor, $x, $y);
  702. }
  703. }
  704. // apply masked image to current instance
  705. $this->resource = $maskedImage->resource;
  706. $this->width = $maskedImage->width;
  707. $this->height = $maskedImage->height;
  708. return $this;
  709. }
  710. /**
  711. * Rotate image with given angle
  712. *
  713. * @param float $angle
  714. * @param string $color
  715. * @param int $ignore_transparent
  716. * @return Image
  717. */
  718. public function rotate($angle = 0, $bgcolor = '#000000', $ignore_transparent = 0)
  719. {
  720. // rotate image
  721. $this->resource = imagerotate($this->resource, $angle, $this->parseColor($bgcolor), $ignore_transparent);
  722. // re-read width/height
  723. $this->width = imagesx($this->resource);
  724. $this->height = imagesy($this->resource);
  725. return $this;
  726. }
  727. /**
  728. * Fill image with given color or image source at position x,y
  729. *
  730. * @param mixed $source
  731. * @param integer $pos_x
  732. * @param integer $pos_y
  733. * @return Image
  734. */
  735. public function fill($source, $pos_x = 0, $pos_y = 0)
  736. {
  737. if (is_a($source, 'Intervention\Image\Image')) {
  738. // fill with image
  739. imagesettile($this->resource, $source->resource);
  740. $source = IMG_COLOR_TILED;
  741. } elseif ($this->isImageResource($source)) {
  742. // fill with image resource
  743. imagesettile($this->resource, $source);
  744. $source = IMG_COLOR_TILED;
  745. } elseif (is_string($source) && $this->isBinary($source)) {
  746. // fill with image from binary string
  747. $img = new self($source);
  748. imagesettile($this->resource, $img->resource);
  749. $source = IMG_COLOR_TILED;
  750. } elseif (is_string($source) && file_exists(realpath($source))) {
  751. $img = new self($source);
  752. imagesettile($this->resource, $img->resource);
  753. $source = IMG_COLOR_TILED;
  754. } else {
  755. // fill with color
  756. $source = $this->parseColor($source);
  757. }
  758. imagefill($this->resource, $pos_x, $pos_y, $source);
  759. return $this;
  760. }
  761. /**
  762. * Set single pixel
  763. *
  764. * @param string $color
  765. * @param integer $pos_x
  766. * @param integer $pos_y
  767. * @return Image
  768. */
  769. public function pixel($color, $pos_x = 0, $pos_y = 0)
  770. {
  771. imagesetpixel($this->resource, $pos_x, $pos_y, $this->parseColor($color));
  772. return $this;
  773. }
  774. /**
  775. * Draw rectangle in current image starting at point 1 and ending at point 2
  776. *
  777. * @param string $color
  778. * @param integer $x1
  779. * @param integer $y1
  780. * @param integer $x2
  781. * @param integer $y2
  782. * @param boolean $filled
  783. * @return Image
  784. */
  785. public function rectangle($color, $x1 = 0, $y1 = 0, $x2 = 10, $y2 = 10, $filled = true)
  786. {
  787. $callback = $filled ? 'imagefilledrectangle' : 'imagerectangle';
  788. call_user_func($callback, $this->resource, $x1, $y1, $x2, $y2, $this->parseColor($color));
  789. return $this;
  790. }
  791. /**
  792. * Draw a line in current image starting at point 1 and ending at point 2
  793. *
  794. * @param string $color
  795. * @param integer $x1
  796. * @param integer $y1
  797. * @param integer $x2
  798. * @param integer $y2
  799. * @return Image
  800. */
  801. public function line($color, $x1 = 0, $y1 = 0, $x2 = 10, $y2 = 10)
  802. {
  803. imageline($this->resource, $x1, $y1, $x2, $y2, $this->parseColor($color));
  804. return $this;
  805. }
  806. /**
  807. * Draw an ellipse centered at given coordinates.
  808. *
  809. * @param string $color
  810. * @param integer $pos_x
  811. * @param integer $pos_y
  812. * @param integer $width
  813. * @param integer $height
  814. * @return Image
  815. */
  816. public function ellipse($color, $pos_x = 0, $pos_y = 0, $width = 10, $height = 10, $filled = true)
  817. {
  818. $callback = $filled ? 'imagefilledellipse' : 'imageellipse';
  819. call_user_func($callback, $this->resource, $pos_x, $pos_y, $width, $height, $this->parseColor($color));
  820. return $this;
  821. }
  822. /**
  823. * Draw a circle centered at given coordinates
  824. *
  825. * @param string $color
  826. * @param integer $x
  827. * @param integer $y
  828. * @param integer $radius
  829. * @param boolean $filled
  830. * @return Image
  831. */
  832. public function circle($color, $x = 0, $y = 0, $radius = 10, $filled = true)
  833. {
  834. return $this->ellipse($color, $x, $y, $radius * 2, $radius * 2, $filled);
  835. }
  836. /**
  837. * Write text in current image
  838. *
  839. * @param string $text
  840. * @param integer $pos_x
  841. * @param integer $pos_y
  842. * @param integer $angle
  843. * @param integer $size
  844. * @param string $color
  845. * @param string $fontfile
  846. * @return Image
  847. */
  848. public function text($text, $pos_x = 0, $pos_y = 0, $size = 16, $color = '000000', $angle = 0, $fontfile = null)
  849. {
  850. if (is_null($fontfile)) {
  851. imagestring($this->resource, $size, $pos_x, $pos_y, $text, $this->parseColor($color));
  852. } else {
  853. imagettftext($this->resource, $size, $angle, $pos_x, $pos_y, $this->parseColor($color), $fontfile, $text);
  854. }
  855. return $this;
  856. }
  857. /**
  858. * Changes the brightness of the current image
  859. *
  860. * @param int $level [description]
  861. * @return Image
  862. */
  863. public function brightness($level)
  864. {
  865. // normalize level
  866. if ($level >= -100 && $level <= 100) {
  867. $level = $level * 2.55;
  868. } else {
  869. throw new Exception('Brightness level must be between -100 and +100');
  870. }
  871. imagefilter($this->resource, IMG_FILTER_BRIGHTNESS, $level);
  872. return $this;
  873. }
  874. /**
  875. * Changes the contrast of the current image
  876. *
  877. * @param int $level
  878. * @return Image
  879. */
  880. public function contrast($level)
  881. {
  882. // normalize level
  883. if ($level >= -100 && $level <= 100) {
  884. $level = $level * (-1);
  885. } else {
  886. throw new Exception('Contrast level must be between -100 and +100');
  887. }
  888. imagefilter($this->resource, IMG_FILTER_CONTRAST, $level);
  889. return $this;
  890. }
  891. /**
  892. * Pixelate current image
  893. *
  894. * @param integer $size
  895. * @param boolean $advanced
  896. * @return Image
  897. */
  898. public function pixelate($size = 10, $advanced = true)
  899. {
  900. imagefilter($this->resource, IMG_FILTER_PIXELATE, $size, $advanced);
  901. return $this;
  902. }
  903. /**
  904. * Turn current image into a greyscale verision
  905. *
  906. * @return Image
  907. */
  908. public function grayscale()
  909. {
  910. imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
  911. return $this;
  912. }
  913. /**
  914. * Alias of greyscale
  915. *
  916. * @return Image
  917. */
  918. public function greyscale()
  919. {
  920. $this->grayscale();
  921. return $this;
  922. }
  923. /**
  924. * Invert colors of current image
  925. *
  926. * @return Image
  927. */
  928. public function invert()
  929. {
  930. imagefilter($this->resource, IMG_FILTER_NEGATE);
  931. return $this;
  932. }
  933. /**
  934. * Apply blur filter on the current image
  935. *
  936. * @param integer $amount
  937. * @return Image
  938. */
  939. public function blur($amount = 1)
  940. {
  941. for ($i=0; $i < intval($amount); $i++) {
  942. imagefilter($this->resource, IMG_FILTER_GAUSSIAN_BLUR);
  943. }
  944. return $this;
  945. }
  946. /**
  947. * Set a maximum number of colors for the current image
  948. *
  949. * @param integer $count
  950. * @param mixed $matte
  951. * @return Image
  952. */
  953. public function limitColors($count = null, $matte = null)
  954. {
  955. // create empty canvas
  956. $resource = imagecreatetruecolor($this->width, $this->height);
  957. // define matte
  958. $matte = is_null($matte) ? imagecolorallocatealpha($resource, 0, 0, 0, 127) : $this->parseColor($matte);
  959. // fill with matte and copy original image
  960. imagefill($resource, 0, 0, $matte);
  961. // set transparency
  962. imagecolortransparent($resource, $matte);
  963. // copy original image
  964. imagecopy($resource, $this->resource, 0, 0, 0, 0, $this->width, $this->height);
  965. if (is_numeric($count) && $count <= 256) {
  966. // decrease colors
  967. imagetruecolortopalette($resource, true, intval($count));
  968. }
  969. // set new resource
  970. $this->resource = $resource;
  971. return $this;
  972. }
  973. /**
  974. * Reset to original image resource
  975. *
  976. * @return void
  977. */
  978. public function reset()
  979. {
  980. if (is_null($this->dirname) && is_null($this->basename)) {
  981. $this->initEmpty($this->original['width'], $this->original['height'], $this->original['bgcolor']);
  982. } else {
  983. $this->initFromPath($this->dirname .'/'. $this->basename);
  984. }
  985. return $this;
  986. }
  987. /**
  988. * Encode image in different formats
  989. *
  990. * @param string $format
  991. * @param integer $quality
  992. * @return string
  993. */
  994. public function encode($format = null, $quality = 90)
  995. {
  996. $format = is_null($format) ? $this->type : $format;
  997. if ($quality < 0 || $quality > 100) {
  998. throw new Exception('Quality of image must range from 0 to 100.');
  999. }
  1000. ob_start();
  1001. switch (strtolower($format)) {
  1002. case 'data-url':
  1003. echo sprintf('data:%s;base64,%s', $this->mime, base64_encode($this->encode($this->type, $quality)));
  1004. break;
  1005. case 'gif':
  1006. case 1:
  1007. imagegif($this->resource);
  1008. break;
  1009. case 'png':
  1010. case 3:
  1011. $quality = round($quality / 11.11111111111); // transform quality to png setting
  1012. imagealphablending($this->resource, false);
  1013. imagesavealpha($this->resource, true);
  1014. imagepng($this->resource, null, $quality);
  1015. break;
  1016. default:
  1017. case 'jpg':
  1018. case 'jpeg':
  1019. case 2:
  1020. imagejpeg($this->resource, null, $quality);
  1021. break;
  1022. }
  1023. $data = ob_get_contents();
  1024. ob_end_clean();
  1025. return $data;
  1026. }
  1027. /**
  1028. * Picks and formats color at position
  1029. *
  1030. * @param int $x
  1031. * @param int $y
  1032. * @param string $format
  1033. * @return mixed
  1034. */
  1035. public function pickColor($x, $y, $format = null)
  1036. {
  1037. // pick color at postion
  1038. $color = imagecolorat($this->resource, $x, $y);
  1039. // format color
  1040. switch (strtolower($format)) {
  1041. case 'rgb':
  1042. $color = imagecolorsforindex($this->resource, $color);
  1043. $color = sprintf('rgb(%d, %d, %d)', $color['red'], $color['green'], $color['blue']);
  1044. break;
  1045. case 'rgba':
  1046. $color = imagecolorsforindex($this->resource, $color);
  1047. $color = sprintf('rgba(%d, %d, %d, %.2f)', $color['red'], $color['green'], $color['blue'], $this->alpha2rgba($color['alpha']));
  1048. break;
  1049. case 'hex':
  1050. $color = imagecolorsforindex($this->resource, $color);
  1051. $color = sprintf('#%02x%02x%02x', $color['red'], $color['green'], $color['blue']);
  1052. break;
  1053. case 'int':
  1054. case 'integer':
  1055. # in gd2 library color already is int...
  1056. break;
  1057. default:
  1058. case 'array':
  1059. $color = imagecolorsforindex($this->resource, $color);
  1060. $color = array(
  1061. 'r' => $color['red'],
  1062. 'g' => $color['green'],
  1063. 'b' => $color['blue'],
  1064. 'a' => $this->alpha2rgba($color['alpha'])
  1065. );
  1066. break;
  1067. }
  1068. return $color;
  1069. }
  1070. /**
  1071. * Allocate color from given string
  1072. *
  1073. * @param string $value
  1074. * @return int
  1075. */
  1076. public function parseColor($value)
  1077. {
  1078. $a = 0; // alpha value
  1079. if (is_int($value)) {
  1080. // color is alread allocated
  1081. $allocatedColor = $value;
  1082. } elseif(is_array($value)) {
  1083. // parse color array like: array(155, 155, 155)
  1084. if (count($value) == 4) {
  1085. // color array with alpha value
  1086. list($r, $g, $b, $a) = $value;
  1087. $a = $this->alpha2gd($a);
  1088. } elseif (count($value) == 3) {
  1089. // color array without alpha value
  1090. list($r, $g, $b) = $value;
  1091. }
  1092. } elseif(is_string($value)) {
  1093. // parse color string in hexidecimal format like #cccccc or cccccc or ccc
  1094. if (preg_match('/^#?([a-f0-9]{1,2})([a-f0-9]{1,2})([a-f0-9]{1,2})$/i', $value, $matches)) {
  1095. $r = strlen($matches[1]) == '1' ? '0x'.$matches[1].$matches[1] : '0x'.$matches[1];
  1096. $g = strlen($matches[2]) == '1' ? '0x'.$matches[2].$matches[2] : '0x'.$matches[2];
  1097. $b = strlen($matches[3]) == '1' ? '0x'.$matches[3].$matches[3] : '0x'.$matches[3];
  1098. // parse color string in format rgb(140, 140, 140)
  1099. } elseif (preg_match('/^rgb ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3})\)$/i', $value, $matches)) {
  1100. $r = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0;
  1101. $g = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0;
  1102. $b = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0;
  1103. // parse color string in format rgba(255, 0, 0, 0.5)
  1104. } elseif (preg_match('/^rgba ?\(([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9]{1,3}), ?([0-9.]{1,4})\)$/i', $value, $matches)) {
  1105. $r = ($matches[1] >= 0 && $matches[1] <= 255) ? intval($matches[1]) : 0;
  1106. $g = ($matches[2] >= 0 && $matches[2] <= 255) ? intval($matches[2]) : 0;
  1107. $b = ($matches[3] >= 0 && $matches[3] <= 255) ? intval($matches[3]) : 0;
  1108. $a = $this->alpha2gd($matches[4]);
  1109. }
  1110. }
  1111. if (isset($allocatedColor)) {
  1112. return $allocatedColor;
  1113. } elseif (isset($r) && isset($g) && isset($b)) {
  1114. return imagecolorallocatealpha($this->resource, $r, $g, $b, $a);
  1115. } else {
  1116. throw new Exception("Error parsing color [{$value}]");
  1117. }
  1118. }
  1119. /**
  1120. * Save image in filesystem
  1121. *
  1122. * @param string $path
  1123. * @param integer $quality
  1124. * @return Image
  1125. */
  1126. public function save($path = null, $quality = 90)
  1127. {
  1128. $path = is_null($path) ? ($this->dirname .'/'. $this->basename) : $path;
  1129. file_put_contents($path, $this->encode(pathinfo($path, PATHINFO_EXTENSION), $quality));
  1130. return $this;
  1131. }
  1132. /**
  1133. * Convert rgba alpha (0-1) value to gd value (0-127)
  1134. *
  1135. * @param float $input
  1136. * @return int
  1137. */
  1138. private function alpha2gd($input)
  1139. {
  1140. $range_input = range(1, 0, 1/127);
  1141. $range_output = range(0, 127);
  1142. foreach ($range_input as $key => $value) {
  1143. if ($value <= $input) {
  1144. return $range_output[$key];
  1145. }
  1146. }
  1147. return 127;
  1148. }
  1149. /**
  1150. * Convert gd alpha (0-127) value to rgba alpha value (0-1)
  1151. *
  1152. * @param int $input
  1153. * @return float
  1154. */
  1155. private function alpha2rgba($input)
  1156. {
  1157. $range_input = range(0, 127);
  1158. $range_output = range(1, 0, 1/127);
  1159. foreach ($range_input as $key => $value) {
  1160. if ($value >= $input) {
  1161. return round($range_output[$key], 2);
  1162. }
  1163. }
  1164. return 1;
  1165. }
  1166. /**
  1167. * Checks if string contains printable characters
  1168. *
  1169. * @param mixed $input
  1170. * @return boolean
  1171. */
  1172. private function isBinary($input)
  1173. {
  1174. return ( ! ctype_print($input));
  1175. }
  1176. /**
  1177. * Checks if the input object is image resource
  1178. *
  1179. * @param mixed $input
  1180. * @return boolean
  1181. */
  1182. private function isImageResource($input)
  1183. {
  1184. return (is_resource($input) && get_resource_type($input) == 'gd');
  1185. }
  1186. /**
  1187. * Checks if the current image has (half) transparent pixels
  1188. *
  1189. * @return boolean
  1190. */
  1191. private function hasTransparency()
  1192. {
  1193. $step_x = min(max(floor($this->width/50), 1), 10);
  1194. $step_y = min(max(floor($this->height/50), 1), 10);
  1195. for ($x=0; $x<$this->width; $x=$x+$step_x) {
  1196. for ($y=0; $y<$this->height; $y=$y+$step_y) {
  1197. $color = $this->pickColor($x, $y);
  1198. if ($color['a'] < 1) return true;
  1199. }
  1200. }
  1201. return false;
  1202. }
  1203. /**
  1204. * Set file info from image path in filesystem
  1205. *
  1206. * @param string $path
  1207. */
  1208. private function setFileInfoFromPath($path)
  1209. {
  1210. // set file info
  1211. $info = pathinfo($path);
  1212. $this->dirname = array_key_exists('dirname', $info) ? $info['dirname'] : null;
  1213. $this->basename = array_key_exists('basename', $info) ? $info['basename'] : null;
  1214. $this->extension = array_key_exists('extension', $info) ? $info['extension'] : null;
  1215. $this->filename = array_key_exists('filename', $info) ? $info['filename'] : null;
  1216. }
  1217. /**
  1218. * Set image info from image path in filesystem
  1219. *
  1220. * @param string $path
  1221. */
  1222. private function setImageInfoFromPath($path)
  1223. {
  1224. $info = getimagesize($path);
  1225. $this->width = $info[0];
  1226. $this->height = $info[1];
  1227. $this->type = $info[2];
  1228. $this->mime = $info['mime'];
  1229. // set resource
  1230. switch ($this->type) {
  1231. case IMG_PNG:
  1232. case 3:
  1233. $this->resource = imagecreatefrompng($path);
  1234. break;
  1235. case IMG_JPG:
  1236. case 2:
  1237. $this->resource = imagecreatefromjpeg($path);
  1238. break;
  1239. case IMG_GIF:
  1240. case 1:
  1241. $this->resource = imagecreatefromgif($path);
  1242. break;
  1243. default:
  1244. throw new Exception("Wrong image type ({$this->type}) only use JPG, PNG or GIF images.");
  1245. break;
  1246. }
  1247. }
  1248. /**
  1249. * Set local image info from GD resource
  1250. *
  1251. * @param resource $resource
  1252. */
  1253. private function setImageInfoFromResource($resource)
  1254. {
  1255. $this->resource = $resource;
  1256. $this->width = imagesx($this->resource);
  1257. $this->height = imagesy($this->resource);
  1258. $this->original['width'] = $this->width;
  1259. $this->original['height'] = $this->height;
  1260. }
  1261. /**
  1262. * Set local image information from image string
  1263. *
  1264. * @param string $string
  1265. */
  1266. private function setImageInfoFromString($string)
  1267. {
  1268. $this->resource = imagecreatefromstring($string);
  1269. $this->width = imagesx($this->resource);
  1270. $this->height = imagesy($this->resource);
  1271. $this->original['width'] = $this->width;
  1272. $this->original['height'] = $this->height;
  1273. }
  1274. /**
  1275. * Returns image stream
  1276. *
  1277. * @return string
  1278. */
  1279. public function __toString()
  1280. {
  1281. return $this->encode();
  1282. }
  1283. }