GD.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  1. <?php
  2. namespace Gregwar\Image\Adapter;
  3. use Gregwar\Image\Image;
  4. use Gregwar\Image\ImageColor;
  5. class GD extends Common
  6. {
  7. public static $gdTypes = array(
  8. 'jpeg' => \IMG_JPG,
  9. 'gif' => \IMG_GIF,
  10. 'png' => \IMG_PNG,
  11. 'webp' => \IMG_WEBP
  12. );
  13. protected function loadResource($resource)
  14. {
  15. parent::loadResource($resource);
  16. imagesavealpha($this->resource, true);
  17. }
  18. /**
  19. * Gets the width and the height for writing some text.
  20. */
  21. public static function TTFBox($font, $text, $size, $angle = 0)
  22. {
  23. $box = imagettfbbox($size, $angle, $font, $text);
  24. return array(
  25. 'width' => abs($box[2] - $box[0]),
  26. 'height' => abs($box[3] - $box[5]),
  27. );
  28. }
  29. public function __construct()
  30. {
  31. parent::__construct();
  32. if (!(extension_loaded('gd') && function_exists('gd_info'))) {
  33. throw new \RuntimeException('You need to install GD PHP Extension to use this library');
  34. }
  35. }
  36. /**
  37. * {@inheritdoc}
  38. */
  39. public function getName()
  40. {
  41. return 'GD';
  42. }
  43. /**
  44. * {@inheritdoc}
  45. */
  46. public function fillBackground($background = 0xffffff)
  47. {
  48. $w = $this->width();
  49. $h = $this->height();
  50. $n = imagecreatetruecolor($w, $h);
  51. imagefill($n, 0, 0, ImageColor::gdAllocate($this->resource, $background));
  52. imagecopyresampled($n, $this->resource, 0, 0, 0, 0, $w, $h, $w, $h);
  53. imagedestroy($this->resource);
  54. $this->resource = $n;
  55. return $this;
  56. }
  57. /**
  58. * Do the image resize.
  59. *
  60. * @return $this
  61. */
  62. protected function doResize($bg, $target_width, $target_height, $new_width, $new_height)
  63. {
  64. $width = $this->width();
  65. $height = $this->height();
  66. $n = imagecreatetruecolor($target_width, $target_height);
  67. if ($bg != 'transparent') {
  68. imagefill($n, 0, 0, ImageColor::gdAllocate($this->resource, $bg));
  69. } else {
  70. imagealphablending($n, false);
  71. $color = ImageColor::gdAllocate($this->resource, 'transparent');
  72. imagefill($n, 0, 0, $color);
  73. imagesavealpha($n, true);
  74. }
  75. imagecopyresampled($n, $this->resource, ($target_width - $new_width) / 2, ($target_height - $new_height) / 2, 0, 0, $new_width, $new_height, $width, $height);
  76. imagedestroy($this->resource);
  77. $this->resource = $n;
  78. return $this;
  79. }
  80. /**
  81. * {@inheritdoc}
  82. */
  83. public function crop($x, $y, $width, $height)
  84. {
  85. $destination = imagecreatetruecolor($width, $height);
  86. imagealphablending($destination, false);
  87. imagesavealpha($destination, true);
  88. imagecopy($destination, $this->resource, 0, 0, $x, $y, $this->width(), $this->height());
  89. imagedestroy($this->resource);
  90. $this->resource = $destination;
  91. return $this;
  92. }
  93. /**
  94. * {@inheritdoc}
  95. */
  96. public function negate()
  97. {
  98. imagefilter($this->resource, IMG_FILTER_NEGATE);
  99. return $this;
  100. }
  101. /**
  102. * {@inheritdoc}
  103. */
  104. public function brightness($brightness)
  105. {
  106. imagefilter($this->resource, IMG_FILTER_BRIGHTNESS, $brightness);
  107. return $this;
  108. }
  109. /**
  110. * {@inheritdoc}
  111. */
  112. public function contrast($contrast)
  113. {
  114. imagefilter($this->resource, IMG_FILTER_CONTRAST, $contrast);
  115. return $this;
  116. }
  117. /**
  118. * {@inheritdoc}
  119. */
  120. public function grayscale()
  121. {
  122. imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
  123. return $this;
  124. }
  125. /**
  126. * {@inheritdoc}
  127. */
  128. public function emboss()
  129. {
  130. imagefilter($this->resource, IMG_FILTER_EMBOSS);
  131. return $this;
  132. }
  133. /**
  134. * {@inheritdoc}
  135. */
  136. public function smooth($p)
  137. {
  138. imagefilter($this->resource, IMG_FILTER_SMOOTH, $p);
  139. return $this;
  140. }
  141. /**
  142. * {@inheritdoc}
  143. */
  144. public function sharp()
  145. {
  146. imagefilter($this->resource, IMG_FILTER_MEAN_REMOVAL);
  147. return $this;
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. public function edge()
  153. {
  154. imagefilter($this->resource, IMG_FILTER_EDGEDETECT);
  155. return $this;
  156. }
  157. /**
  158. * {@inheritdoc}
  159. */
  160. public function colorize($red, $green, $blue)
  161. {
  162. imagefilter($this->resource, IMG_FILTER_COLORIZE, $red, $green, $blue);
  163. return $this;
  164. }
  165. /**
  166. * {@inheritdoc}
  167. */
  168. public function sepia()
  169. {
  170. imagefilter($this->resource, IMG_FILTER_GRAYSCALE);
  171. imagefilter($this->resource, IMG_FILTER_COLORIZE, 100, 50, 0);
  172. return $this;
  173. }
  174. /**
  175. * {@inheritdoc}
  176. */
  177. public function gaussianBlur($blurFactor = 1)
  178. {
  179. $blurFactor = round($blurFactor); // blurFactor has to be an integer
  180. $originalWidth = $this->width();
  181. $originalHeight = $this->height();
  182. $smallestWidth = ceil($originalWidth * pow(0.5, $blurFactor));
  183. $smallestHeight = ceil($originalHeight * pow(0.5, $blurFactor));
  184. // for the first run, the previous image is the original input
  185. $prevImage = $this->resource;
  186. $prevWidth = $originalWidth;
  187. $prevHeight = $originalHeight;
  188. // scale way down and gradually scale back up, blurring all the way
  189. for ($i = 0; $i < $blurFactor; ++$i) {
  190. // determine dimensions of next image
  191. $nextWidth = $smallestWidth * pow(2, $i);
  192. $nextHeight = $smallestHeight * pow(2, $i);
  193. // resize previous image to next size
  194. $nextImage = imagecreatetruecolor($nextWidth, $nextHeight);
  195. imagecopyresized($nextImage, $prevImage, 0, 0, 0, 0,
  196. $nextWidth, $nextHeight, $prevWidth, $prevHeight);
  197. // apply blur filter
  198. imagefilter($nextImage, IMG_FILTER_GAUSSIAN_BLUR);
  199. // now the new image becomes the previous image for the next step
  200. $prevImage = $nextImage;
  201. $prevWidth = $nextWidth;
  202. $prevHeight = $nextHeight;
  203. }
  204. // scale back to original size and blur one more time
  205. imagecopyresized($this->resource, $nextImage,
  206. 0, 0, 0, 0, $originalWidth, $originalHeight, $nextWidth, $nextHeight);
  207. imagefilter($this->resource, IMG_FILTER_GAUSSIAN_BLUR);
  208. // clean up
  209. imagedestroy($prevImage);
  210. return $this;
  211. }
  212. /**
  213. * {@inheritdoc}
  214. */
  215. public function merge(Image $other, $x = 0, $y = 0, $width = null, $height = null)
  216. {
  217. $other = clone $other;
  218. $other->init();
  219. $other->applyOperations();
  220. imagealphablending($this->resource, true);
  221. if (null == $width) {
  222. $width = $other->width();
  223. }
  224. if (null == $height) {
  225. $height = $other->height();
  226. }
  227. imagecopyresampled($this->resource, $other->getAdapter()->getResource(), $x, $y, 0, 0, $width, $height, $width, $height);
  228. return $this;
  229. }
  230. /**
  231. * {@inheritdoc}
  232. */
  233. public function rotate($angle, $background = 0xffffff)
  234. {
  235. $this->resource = imagerotate($this->resource, $angle, ImageColor::gdAllocate($this->resource, $background));
  236. imagealphablending($this->resource, true);
  237. imagesavealpha($this->resource, true);
  238. return $this;
  239. }
  240. /**
  241. * {@inheritdoc}
  242. */
  243. public function fill($color = 0xffffff, $x = 0, $y = 0)
  244. {
  245. imagealphablending($this->resource, false);
  246. imagefill($this->resource, $x, $y, ImageColor::gdAllocate($this->resource, $color));
  247. return $this;
  248. }
  249. /**
  250. * {@inheritdoc}
  251. */
  252. public function write($font, $text, $x = 0, $y = 0, $size = 12, $angle = 0, $color = 0x000000, $align = 'left')
  253. {
  254. imagealphablending($this->resource, true);
  255. if ($align != 'left') {
  256. $sim_size = self::TTFBox($font, $text, $size, $angle);
  257. if ($align == 'center') {
  258. $x -= $sim_size['width'] / 2;
  259. }
  260. if ($align == 'right') {
  261. $x -= $sim_size['width'];
  262. }
  263. }
  264. imagettftext($this->resource, $size, $angle, $x, $y, ImageColor::gdAllocate($this->resource, $color), $font, $text);
  265. return $this;
  266. }
  267. /**
  268. * {@inheritdoc}
  269. */
  270. public function rectangle($x1, $y1, $x2, $y2, $color, $filled = false)
  271. {
  272. if ($filled) {
  273. imagefilledrectangle($this->resource, $x1, $y1, $x2, $y2, ImageColor::gdAllocate($this->resource, $color));
  274. } else {
  275. imagerectangle($this->resource, $x1, $y1, $x2, $y2, ImageColor::gdAllocate($this->resource, $color));
  276. }
  277. return $this;
  278. }
  279. /**
  280. * {@inheritdoc}
  281. */
  282. public function roundedRectangle($x1, $y1, $x2, $y2, $radius, $color, $filled = false)
  283. {
  284. if ($color) {
  285. $color = ImageColor::gdAllocate($this->resource, $color);
  286. }
  287. if ($filled == true) {
  288. imagefilledrectangle($this->resource, $x1 + $radius, $y1, $x2 - $radius, $y2, $color);
  289. imagefilledrectangle($this->resource, $x1, $y1 + $radius, $x1 + $radius - 1, $y2 - $radius, $color);
  290. imagefilledrectangle($this->resource, $x2 - $radius + 1, $y1 + $radius, $x2, $y2 - $radius, $color);
  291. imagefilledarc($this->resource, $x1 + $radius, $y1 + $radius, $radius * 2, $radius * 2, 180, 270, $color, IMG_ARC_PIE);
  292. imagefilledarc($this->resource, $x2 - $radius, $y1 + $radius, $radius * 2, $radius * 2, 270, 360, $color, IMG_ARC_PIE);
  293. imagefilledarc($this->resource, $x1 + $radius, $y2 - $radius, $radius * 2, $radius * 2, 90, 180, $color, IMG_ARC_PIE);
  294. imagefilledarc($this->resource, $x2 - $radius, $y2 - $radius, $radius * 2, $radius * 2, 360, 90, $color, IMG_ARC_PIE);
  295. } else {
  296. imageline($this->resource, $x1 + $radius, $y1, $x2 - $radius, $y1, $color);
  297. imageline($this->resource, $x1 + $radius, $y2, $x2 - $radius, $y2, $color);
  298. imageline($this->resource, $x1, $y1 + $radius, $x1, $y2 - $radius, $color);
  299. imageline($this->resource, $x2, $y1 + $radius, $x2, $y2 - $radius, $color);
  300. imagearc($this->resource, $x1 + $radius, $y1 + $radius, $radius * 2, $radius * 2, 180, 270, $color);
  301. imagearc($this->resource, $x2 - $radius, $y1 + $radius, $radius * 2, $radius * 2, 270, 360, $color);
  302. imagearc($this->resource, $x1 + $radius, $y2 - $radius, $radius * 2, $radius * 2, 90, 180, $color);
  303. imagearc($this->resource, $x2 - $radius, $y2 - $radius, $radius * 2, $radius * 2, 360, 90, $color);
  304. }
  305. return $this;
  306. }
  307. /**
  308. * {@inheritdoc}
  309. */
  310. public function line($x1, $y1, $x2, $y2, $color = 0x000000)
  311. {
  312. imageline($this->resource, $x1, $y1, $x2, $y2, ImageColor::gdAllocate($this->resource, $color));
  313. return $this;
  314. }
  315. /**
  316. * {@inheritdoc}
  317. */
  318. public function ellipse($cx, $cy, $width, $height, $color = 0x000000, $filled = false)
  319. {
  320. if ($filled) {
  321. imagefilledellipse($this->resource, $cx, $cy, $width, $height, ImageColor::gdAllocate($this->resource, $color));
  322. } else {
  323. imageellipse($this->resource, $cx, $cy, $width, $height, ImageColor::gdAllocate($this->resource, $color));
  324. }
  325. return $this;
  326. }
  327. /**
  328. * {@inheritdoc}
  329. */
  330. public function circle($cx, $cy, $r, $color = 0x000000, $filled = false)
  331. {
  332. return $this->ellipse($cx, $cy, $r, $r, ImageColor::gdAllocate($this->resource, $color), $filled);
  333. }
  334. /**
  335. * {@inheritdoc}
  336. */
  337. public function polygon(array $points, $color, $filled = false)
  338. {
  339. if ($filled) {
  340. imagefilledpolygon($this->resource, $points, count($points) / 2, ImageColor::gdAllocate($this->resource, $color));
  341. } else {
  342. imagepolygon($this->resource, $points, count($points) / 2, ImageColor::gdAllocate($this->resource, $color));
  343. }
  344. return $this;
  345. }
  346. /**
  347. * {@inheritdoc}
  348. */
  349. public function flip($flipVertical, $flipHorizontal)
  350. {
  351. if (!$flipVertical && !$flipHorizontal) {
  352. return $this;
  353. }
  354. if (function_exists('imageflip')) {
  355. if ($flipVertical && $flipHorizontal) {
  356. $flipMode = \IMG_FLIP_BOTH;
  357. } elseif ($flipVertical && !$flipHorizontal) {
  358. $flipMode = \IMG_FLIP_VERTICAL;
  359. } elseif (!$flipVertical && $flipHorizontal) {
  360. $flipMode = \IMG_FLIP_HORIZONTAL;
  361. }
  362. imageflip($this->resource, $flipMode);
  363. } else {
  364. $width = $this->width();
  365. $height = $this->height();
  366. $src_x = 0;
  367. $src_y = 0;
  368. $src_width = $width;
  369. $src_height = $height;
  370. if ($flipVertical) {
  371. $src_y = $height - 1;
  372. $src_height = -$height;
  373. }
  374. if ($flipHorizontal) {
  375. $src_x = $width - 1;
  376. $src_width = -$width;
  377. }
  378. $imgdest = imagecreatetruecolor($width, $height);
  379. imagealphablending($imgdest, false);
  380. imagesavealpha($imgdest, true);
  381. if (imagecopyresampled($imgdest, $this->resource, 0, 0, $src_x, $src_y, $width, $height, $src_width, $src_height)) {
  382. imagedestroy($this->resource);
  383. $this->resource = $imgdest;
  384. }
  385. }
  386. return $this;
  387. }
  388. /**
  389. * {@inheritdoc}
  390. */
  391. public function width()
  392. {
  393. if (null === $this->resource) {
  394. $this->init();
  395. }
  396. return imagesx($this->resource);
  397. }
  398. /**
  399. * {@inheritdoc}
  400. */
  401. public function height()
  402. {
  403. if (null === $this->resource) {
  404. $this->init();
  405. }
  406. return imagesy($this->resource);
  407. }
  408. protected function createImage($width, $height)
  409. {
  410. $this->resource = imagecreatetruecolor($width, $height);
  411. }
  412. protected function createImageFromData($data)
  413. {
  414. $this->resource = @imagecreatefromstring($data);
  415. }
  416. /**
  417. * Converts the image to true color.
  418. */
  419. protected function convertToTrueColor()
  420. {
  421. if (!imageistruecolor($this->resource)) {
  422. if (function_exists('imagepalettetotruecolor')) {
  423. // Available in PHP 5.5
  424. imagepalettetotruecolor($this->resource);
  425. } else {
  426. $transparentIndex = imagecolortransparent($this->resource);
  427. $w = $this->width();
  428. $h = $this->height();
  429. $img = imagecreatetruecolor($w, $h);
  430. imagecopy($img, $this->resource, 0, 0, 0, 0, $w, $h);
  431. if ($transparentIndex != -1) {
  432. $width = $this->width();
  433. $height = $this->height();
  434. imagealphablending($img, false);
  435. imagesavealpha($img, true);
  436. for ($x = 0; $x < $width; ++$x) {
  437. for ($y = 0; $y < $height; ++$y) {
  438. if (imagecolorat($this->resource, $x, $y) == $transparentIndex) {
  439. imagesetpixel($img, $x, $y, 127 << 24);
  440. }
  441. }
  442. }
  443. }
  444. $this->resource = $img;
  445. }
  446. }
  447. imagesavealpha($this->resource, true);
  448. }
  449. /**
  450. * {@inheritdoc}
  451. */
  452. public function saveGif($file)
  453. {
  454. $transColor = imagecolorallocatealpha($this->resource, 255, 255, 255, 127);
  455. imagecolortransparent($this->resource, $transColor);
  456. imagegif($this->resource, $file);
  457. return $this;
  458. }
  459. /**
  460. * {@inheritdoc}
  461. */
  462. public function savePng($file)
  463. {
  464. imagepng($this->resource, $file);
  465. return $this;
  466. }
  467. /**
  468. * {@inheritdoc}
  469. */
  470. public function saveWebp($file, $quality)
  471. {
  472. imagewebp($this->resource, $file, $quality);
  473. return $this;
  474. }
  475. /**
  476. * {@inheritdoc}
  477. */
  478. public function saveJpeg($file, $quality)
  479. {
  480. imagejpeg($this->resource, $file, $quality);
  481. return $this;
  482. }
  483. /**
  484. * Try to open the file using jpeg.
  485. */
  486. protected function openJpeg($file)
  487. {
  488. if (file_exists($file) && filesize($file)) {
  489. $this->resource = @imagecreatefromjpeg($file);
  490. } else {
  491. $this->resource = false;
  492. }
  493. }
  494. /**
  495. * Try to open the file using gif.
  496. */
  497. protected function openGif($file)
  498. {
  499. if (file_exists($file) && filesize($file)) {
  500. $this->resource = @imagecreatefromgif($file);
  501. } else {
  502. $this->resource = false;
  503. }
  504. }
  505. /**
  506. * Try to open the file using PNG.
  507. */
  508. protected function openPng($file)
  509. {
  510. if (file_exists($file) && filesize($file)) {
  511. $this->resource = @imagecreatefrompng($file);
  512. } else {
  513. $this->resource = false;
  514. }
  515. }
  516. /**
  517. * Try to open the file using WEBP.
  518. */
  519. protected function openWebp($file)
  520. {
  521. if (file_exists($file) && filesize($file)) {
  522. $this->resource = @imagecreatefromwebp($file);
  523. } else {
  524. $this->resource = false;
  525. }
  526. }
  527. /**
  528. * Does this adapter supports type ?
  529. */
  530. protected function supports($type)
  531. {
  532. return imagetypes() & self::$gdTypes[$type];
  533. }
  534. protected function getColor($x, $y)
  535. {
  536. return imagecolorat($this->resource, $x, $y);
  537. }
  538. /**
  539. * {@inheritdoc}
  540. */
  541. public function enableProgressive()
  542. {
  543. imageinterlace($this->resource, 1);
  544. return $this;
  545. }
  546. }