PageRenderTime 72ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/library/simplegd/classes/SimpleGdImage.class.php

https://github.com/fb83/Project-Pier
PHP | 619 lines | 336 code | 63 blank | 220 comment | 63 complexity | 70dc8ddf911d4199745ef1452cf95591 MD5 | raw file
Possible License(s): AGPL-1.0, GPL-2.0, AGPL-3.0, LGPL-2.1, GPL-3.0
  1. <?php
  2. /**
  3. * SimpleGdImage wraps arround GD image resource and provides methods to work
  4. * with the resource as with a single object
  5. *
  6. * @version 1.0
  7. * @http://www.projectpier.org/
  8. */
  9. class SimpleGdImage {
  10. /** Boundaries **/
  11. const BOUNDARY_INCREASE_ONLY = 'increase';
  12. const BOUNDARY_DECREASE_ONLY = 'decrease';
  13. /**
  14. * Is new flag, true if we created this image from resouce, not from file
  15. * and we didn't save it to the file
  16. *
  17. * @var boolean
  18. */
  19. protected $is_new = true;
  20. /**
  21. * Is loaded flag, true if loaded this file from the file (or saved it to the file)
  22. *
  23. * @var string
  24. */
  25. protected $is_loaded = false;
  26. /**
  27. * Image resource
  28. *
  29. * @var resource
  30. */
  31. protected $resource;
  32. /**
  33. * Original file path
  34. *
  35. * @var string
  36. */
  37. protected $source;
  38. /**
  39. * Image type
  40. *
  41. * @var integer
  42. */
  43. protected $image_type;
  44. /**
  45. * Image width, cached on request
  46. *
  47. * @var integer
  48. */
  49. protected $width = null;
  50. /**
  51. * Image height, cached on request
  52. *
  53. * @var integer
  54. */
  55. protected $height = null;
  56. /**
  57. * Construct new SimpleGdImage object
  58. *
  59. * @param string $load_from_file Load content from image file
  60. * @return SimpleGdImage
  61. */
  62. function __construct($file_path = null) {
  63. if (!is_null($file_path)) {
  64. $this->setSource($file_path);
  65. }
  66. } // __construct
  67. /**
  68. * Destroy this object and free any memory associated with image image
  69. *
  70. * @param void
  71. * @return null
  72. */
  73. function __destruct() {
  74. if (is_resource($this->resource)) {
  75. imagedestroy($this->resource);
  76. }
  77. } // __destruct
  78. // ---------------------------------------------------
  79. // Utils
  80. // ---------------------------------------------------
  81. /**
  82. * Load this image from file
  83. *
  84. * @param string $file_path
  85. * @return null
  86. */
  87. function load() {
  88. switch ($this->image_type) {
  89. case IMAGETYPE_PNG:
  90. $resource = imagecreatefrompng($this->source);
  91. break;
  92. case IMAGETYPE_JPEG:
  93. $resource = imagecreatefromjpeg($this->source);
  94. break;
  95. case IMAGETYPE_GIF:
  96. $resource = imagecreatefromgif($this->source);
  97. break;
  98. default:
  99. throw new ImageTypeNotSupportedError($this->source, $this->image_type);
  100. } // switch
  101. if (!is_resource($resource)) {
  102. throw new FailedToLoadImageError($this->source);
  103. } // if
  104. if (is_resource($this->resource)) {
  105. imagedestroy($this->resource);
  106. } // if
  107. $this->resource = $resource;
  108. $resource = null;
  109. $this->setIsLoaded();
  110. } // load
  111. /**
  112. * This function is used for saving files that are loaded to file in case
  113. * you transformed the resource and want to save it back to the file. If
  114. * this file is new or you want to change its type use saveAs() function
  115. *
  116. * @param void
  117. * @return boolean
  118. * @throws ImageTypeNotSupportedError
  119. * @throws FileNotWritableError
  120. */
  121. function save() {
  122. if (!$this->isLoaded() || !is_file($this->getSource())) {
  123. throw new Error('This image was not loaded from the file. Use saveAs() function instead of save() - there you\'ll be able to specify output file and type');
  124. } // if
  125. if (!file_is_writable($this->getSource())) {
  126. throw new FileNotWritableError($this->getSource());
  127. } // if
  128. switch ($this->getImageType()) {
  129. case IMAGETYPE_PNG:
  130. imagepng($this->resource, $this->getSource());
  131. break;
  132. case IMAGETYPE_JPG:
  133. imagejpeg($this->resource, $this->getSource(), 80);
  134. break;
  135. case IMAGETYPE_GIF:
  136. imagegif($this->resource, $this->getSource());
  137. break;
  138. default:
  139. throw new ImageTypeNotSupportedError(null, $this->getImageType());
  140. } // switch
  141. return true;
  142. } // save
  143. /**
  144. * Save image into the file
  145. *
  146. * @param string $file_path
  147. * @param integer $as_type Save image as type. Default type is PNG
  148. * @return null
  149. * @throws ImageTypeNotSupportedError
  150. * @throws FailedToWriteFileError
  151. */
  152. function saveAs($file_path, $as_type = null) {
  153. // Use internal value if we called convertType with new object
  154. if (is_null($as_type)) {
  155. $as_type = $this->getImageType();
  156. }
  157. $as_type = (integer) $as_type;
  158. if (($as_type < IMAGETYPE_GIF) || ($as_type > IMAGETYPE_PNG)) {
  159. $as_type = IMAGETYPE_PNG;
  160. }
  161. switch ($as_type) {
  162. case IMAGETYPE_PNG:
  163. $write = imagepng($this->resource, $file_path);
  164. break;
  165. case IMAGETYPE_JPEG:
  166. $write = imagejpeg($this->resource, $file_path, 80);
  167. break;
  168. case IMAGETYPE_GIF:
  169. $write = imagegif($this->resource, $file_path);
  170. break;
  171. default:
  172. throw new ImageTypeNotSupportedError(null, $this->getImageType());
  173. } // switch
  174. if (!$write) {
  175. throw new FailedToWriteFileError($file_path);
  176. }
  177. return true;
  178. } // saveAs
  179. /**
  180. * User image resource to populate this object
  181. *
  182. * @param resource $resource
  183. * @return null
  184. */
  185. function createFromResource($resource) {
  186. if (is_resource($resource) && (get_resource_type($resource) == 'gd')) {
  187. $this->reset();
  188. $this->resource = $resource;
  189. } else {
  190. throw new InvalidInstanceError('resource', $resource, 'resource');
  191. } // if
  192. } // createFromResource
  193. /**
  194. * Reset all fields
  195. *
  196. * @param void
  197. * @return null
  198. */
  199. protected function reset() {
  200. $this->is_new = true;
  201. $this->is_loaded = false;
  202. $this->resource = null;
  203. $this->source = null;
  204. $this->image_type = null;
  205. $this->width = null;
  206. $this->height = null;
  207. } // reset
  208. /**
  209. * Return MIME type based on image type
  210. *
  211. * @param void
  212. * @return string
  213. */
  214. function getMimeType() {
  215. return image_type_to_mime_type($this->getImageType());
  216. } // getMimeType
  217. /**
  218. * Return extension based on the source file or type if we have new image
  219. *
  220. * @param void
  221. * @return string
  222. */
  223. function getExtension() {
  224. if ($this->isLoaded()) {
  225. return get_file_extension($this->getSource());
  226. } else {
  227. return image_type_to_extension($this->getImageType());
  228. } // if
  229. } // getExtension
  230. // ---------------------------------------------------
  231. // Transformations
  232. // ---------------------------------------------------
  233. /**
  234. * Resize image to given dimensions. This transformation will not keep
  235. * the ratio of the image
  236. *
  237. * @param integer $width New width
  238. * @param integer $height
  239. * @param boolean $mutate If true transofrmation will be done with the internal
  240. * resource. If false new resouce will be created, new SimpleGdImage will be
  241. * created from it and returned
  242. * @return boolean
  243. */
  244. function resize($width, $height, $mutate = true) {
  245. if (!is_resource($this->resource) || (get_resource_type($this->resource) <> 'gd')) {
  246. return false;
  247. }
  248. $width = (integer) $width > 0 ? (integer) $width : 1;
  249. $height = (integer) $height > 0 ? (integer) $height : 1;
  250. switch($this->getImageType()) {
  251. case IMAGETYPE_GIF:
  252. $new_resource = imagecreatetruecolor($new_width, $new_height);
  253. $colorcount = imagecolorstotal($this->resource);
  254. imagetruecolortopalette($new_resource, true, $colorcount);
  255. imagepalettecopy($new_resource, $this->resource);
  256. $transparentcolor = imagecolortransparent($this->resource);
  257. imagefill($new_resource, 0, 0, $transparentcolor);
  258. imagecolortransparent($new_resource, $transparentcolor);
  259. break;
  260. case IMAGETYPE_PNG:
  261. $new_resource = imagecreatetruecolor($new_width, $new_height);
  262. $transparent_color_index = imagecolortransparent($this->resource);
  263. if ($transparent_color_index>=0) {
  264. $transparent_color = imagecolorsforindex($this->resource, $transparent_color_index);
  265. $$transparent_color_index = imagecolorallocate($new_resource, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
  266. imagefill($new_resource, 0, 0, $transparent_color_index);
  267. imagecolortransparent($new_resource, $transparent_color_index);
  268. } else {
  269. imagealphablending($new_resource, false);
  270. $color = imagecolorallocatealpha($new_resource, 0, 0, 0, 127);
  271. imagefill($new_resource, 0, 0, $color);
  272. imagesavealpha($new_resource, true);
  273. }
  274. break;
  275. default:
  276. $new_resource = imagecreatetruecolor($new_width, $new_height);
  277. break;
  278. } // switch
  279. imagecopyresampled($new_resource, $this->resource, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
  280. if ($mutate) {
  281. // Destroy old resource, set new one and reset cached values
  282. imagedestroy($this->resource);
  283. $this->resource = $new_resource;
  284. $this->width = $width;
  285. $this->height = $height;
  286. return true;
  287. } else {
  288. // Create new image from the resource and return
  289. $new_image = new SimpleGdImage();
  290. $new_image->createFromResource($new_resource);
  291. return $new_image;
  292. } // if
  293. } // resize
  294. /**
  295. * Resize to $width x $height but keep the image proportion
  296. *
  297. * @param integer $width Max width
  298. * @param integer $height Max height
  299. * @param string $boundary Limit the scale action: drecrease or increase
  300. * only. If $boundary is NULL than don't limit
  301. * @param boolean $mutate Save the transformation in internal resource or
  302. * create new image and keep internal as is?
  303. * @return SimpleGdImage
  304. */
  305. function scale($width, $height, $boundary = null, $mutate = true) {
  306. if (!is_resource($this->resource) || (get_resource_type($this->resource) <> 'gd')) {
  307. if (is_null($this->source)) {
  308. return false;
  309. }
  310. }
  311. $width = (integer) $width > 0 ? (integer) $width : 1;
  312. $height = (integer) $height > 0 ? (integer) $height : 1;
  313. $mem_allowed=1024*1024*6; // 6 MB
  314. $image_mem_usage = $this->getWidth() * $this->getHeight() * 4; // 4 bytes per pixel (RGBA)
  315. if ($image_mem_usage>$mem_allowed) {
  316. $im = imagecreatetruecolor($width, $height);
  317. $white = imagecolorallocate($im, 0xFF, 0xFF, 0xFF);
  318. $black = imagecolorallocate($im, 0x00, 0x00, 0xFF);
  319. @imagefill ( $im , 0, 0, $white );
  320. $s = $this->getWidth() . "x" . $this->getHeight();
  321. @imagestring ( $im , 6 , 10 , 30 , $s , $black );
  322. $this->resource = $im;
  323. $scale = 1;
  324. } else {
  325. $this->load();
  326. $scale = min($width / $this->getWidth(), $height / $this->getHeight());
  327. }
  328. if ($boundary == self::BOUNDARY_DECREASE_ONLY) {
  329. if ($scale >= 1) {
  330. if ($mutate) {
  331. return;
  332. } else {
  333. $new_image = new SimpleGdImage();
  334. $new_image->createFromResource($this->resource);
  335. return $new_image;
  336. } // if
  337. } // if
  338. } elseif ($boundary == self::BOUNDARY_INCREASE_ONLY) {
  339. if ($scale <= 1) {
  340. if ($mutate) {
  341. return;
  342. } else {
  343. $new_image = new SimpleGdImage();
  344. $new_image->createFromResource($this->resource);
  345. return $new_image;
  346. } // if
  347. } // if
  348. } // if
  349. $new_width = floor($scale * $this->getWidth());
  350. $new_height = floor($scale * $this->getHeight());
  351. switch($this->getImageType()) {
  352. case IMAGETYPE_GIF:
  353. $new_resource = imagecreatetruecolor($new_width, $new_height);
  354. $colorcount = imagecolorstotal($this->resource);
  355. imagetruecolortopalette($new_resource, true, $colorcount);
  356. imagepalettecopy($new_resource, $this->resource);
  357. $transparentcolor = imagecolortransparent($this->resource);
  358. imagefill($new_resource, 0, 0, $transparentcolor);
  359. imagecolortransparent($new_resource, $transparentcolor);
  360. break;
  361. case IMAGETYPE_PNG:
  362. $new_resource = imagecreatetruecolor($new_width, $new_height);
  363. $transparent_color_index = imagecolortransparent($this->resource);
  364. if ($transparent_color_index>=0) {
  365. $transparent_color = imagecolorsforindex($this->resource, $transparent_color_index);
  366. $$transparent_color_index = imagecolorallocate($new_resource, $transparent_color['red'], $transparent_color['green'], $transparent_color['blue']);
  367. imagefill($new_resource, 0, 0, $transparent_color_index);
  368. imagecolortransparent($new_resource, $transparent_color_index);
  369. } else {
  370. imagealphablending($new_resource, false);
  371. $color = imagecolorallocatealpha($new_resource, 0, 0, 0, 127);
  372. imagefill($new_resource, 0, 0, $color);
  373. imagesavealpha($new_resource, true);
  374. }
  375. break;
  376. default:
  377. $new_resource = imagecreatetruecolor($new_width, $new_height);
  378. break;
  379. } // switch
  380. imagecopyresampled($new_resource, $this->resource, 0, 0, 0, 0, $new_width, $new_height, $this->getWidth(), $this->getHeight());
  381. if ($mutate) {
  382. imagedestroy($this->resource);
  383. $this->resource = $new_resource;
  384. $this->width = $new_width;
  385. $this->height = $new_height;
  386. return true;
  387. } else {
  388. $new_image = new SimpleGdImage();
  389. $new_image->createFromResource($new_resource);
  390. return $new_image;
  391. } // if
  392. } // scale
  393. /**
  394. * Change internal type value
  395. *
  396. * @param integer $to_type
  397. * @return null
  398. */
  399. function convertType($to_type) {
  400. if ($this->getImageType() == $to_type) {
  401. return;
  402. }
  403. if ($to_type == IMAGETYPE_PNG || $to_type == IMAGETYPE_JPEG || $to_type == IMAGETYPE_GIF) {
  404. $this->setImageType($to_type);
  405. } else {
  406. throw new ImageTypeNotSupportedError(null, $to_type);
  407. } // if
  408. } // convertType
  409. // ---------------------------------------------------
  410. // Flags
  411. // ---------------------------------------------------
  412. /**
  413. * Returns true if this image is new (no file, just resource that we can manipulate)
  414. *
  415. * @param void
  416. * @return boolean
  417. */
  418. function isNew() {
  419. return $this->is_new;
  420. } // isNew
  421. /**
  422. * Returns true if this image is loaded
  423. *
  424. * @param void
  425. * @return boolean
  426. */
  427. function isLoaded() {
  428. return $this->is_loaded;
  429. } // isLoaded
  430. // ---------------------------------------------------
  431. // Getters and setters
  432. // ---------------------------------------------------
  433. /**
  434. * Mark this object as new
  435. *
  436. * @param void
  437. * @return null
  438. */
  439. function setIsNew() {
  440. $this->is_new = true;
  441. $this->is_loaded = false;
  442. } // setIsNew
  443. /**
  444. * Mark this object as loaded
  445. *
  446. * @param void
  447. * @return null
  448. */
  449. function setIsLoaded() {
  450. $this->is_new = false;
  451. $this->is_loaded = true;
  452. } // setIsLoaded
  453. /**
  454. * Get source
  455. *
  456. * @param null
  457. * @return string
  458. */
  459. function getSource() {
  460. return $this->source;
  461. } // getSource
  462. /**
  463. * Set source value
  464. *
  465. * @param string $value
  466. * @return null
  467. */
  468. private function setSource($file_path) {
  469. if (!is_readable($file_path)) {
  470. throw new FileDnxError($file_path);
  471. }
  472. $image_size = false;
  473. $image_type = false;
  474. if (function_exists('exif_imagetype')) {
  475. $image_type = exif_imagetype($file_path);
  476. } else {
  477. $image_size = getimagesize($file_path);
  478. if (is_array($image_size) && isset($image_size[2])) {
  479. $image_type = $image_size[2];
  480. }
  481. } // if
  482. if ($image_type === false) {
  483. throw new FileNotImageError($file_path);
  484. }
  485. $this->source = $file_path;
  486. $this->setImageType($image_type);
  487. if ($image_size === false) {
  488. $image_size = getimagesize($file_path);
  489. }
  490. if (is_array($image_size)) {
  491. $this->setWidth($image_size[0]);
  492. $this->setHeight($image_size[1]);
  493. }
  494. } // setSource
  495. /**
  496. * Get image_type
  497. *
  498. * @param null
  499. * @return integer
  500. */
  501. function getImageType() {
  502. return $this->image_type;
  503. } // getImageType
  504. /**
  505. * Set image_type value
  506. *
  507. * @param integer $value
  508. * @return null
  509. */
  510. private function setImageType($value) {
  511. $this->image_type = $value;
  512. } // setImageType
  513. /**
  514. * Get width
  515. *
  516. * @param null
  517. * @return integer
  518. */
  519. function getWidth() {
  520. if (is_null($this->width)) {
  521. $this->width = imagesx($this->resource);
  522. }
  523. return $this->width;
  524. } // getWidth
  525. /**
  526. * Set width value
  527. *
  528. * @param integer $value
  529. * @return null
  530. */
  531. protected function setWidth($value) {
  532. $this->width = $value;
  533. } // setWidth
  534. /**
  535. * Get height
  536. *
  537. * @param null
  538. * @return integer
  539. */
  540. function getHeight() {
  541. if (is_null($this->height)) {
  542. $this->height = imagesy($this->resource);
  543. }
  544. return $this->height;
  545. } // getHeight
  546. /**
  547. * Set height value
  548. *
  549. * @param integer $value
  550. * @return null
  551. */
  552. protected function setHeight($value) {
  553. $this->height = $value;
  554. } // setHeight
  555. } // SimpleGdImage
  556. ?>