PageRenderTime 57ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

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

https://github.com/swenson/projectpier
PHP | 537 lines | 261 code | 56 blank | 220 comment | 59 complexity | 8e55d8cdf160e47e60d406035797f49d MD5 | raw file
  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 resouce
  28. *
  29. * @var resouce
  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($load_from_file = null) {
  63. if (!is_null($load_from_file)) {
  64. $this->loadFromFile($load_from_file);
  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 loadFromFile($file_path) {
  88. if (!is_readable($file_path)) {
  89. throw new FileDnxError($file_path);
  90. }
  91. $image_type = false;
  92. if (function_exists('exif_imagetype')) {
  93. $image_type = exif_imagetype($file_path);
  94. } else {
  95. $image_size = getimagesize($file_path);
  96. if (is_array($image_size) && isset($image_size[2])) {
  97. $image_type = $image_size[2];
  98. }
  99. } // if
  100. if ($image_type === false) {
  101. throw new FileNotImageError($file_path);
  102. }
  103. switch ($image_type) {
  104. case IMAGETYPE_PNG:
  105. $this->resource = imagecreatefrompng($file_path);
  106. break;
  107. case IMAGETYPE_JPEG:
  108. $this->resource = imagecreatefromjpeg($file_path);
  109. break;
  110. case IMAGETYPE_GIF:
  111. $this->resource = imagecreatefromgif($file_path);
  112. break;
  113. default:
  114. throw new ImageTypeNotSupportedError($file_path, $image_type);
  115. } // switch
  116. if (!is_resource($this->resource)) {
  117. $this->resource = null;
  118. throw new FailedToLoadImageError($file_path);
  119. } // if
  120. $this->setIsLoaded();
  121. $this->setSource($file_path);
  122. $this->setImageType($image_type);
  123. } // loadFromFile
  124. /**
  125. * This function is used for saving files that are loaded to file in case
  126. * you transformed the resource and want to save it back to the file. If
  127. * this file is new or you want to change its type use saveAs() function
  128. *
  129. * @param void
  130. * @return boolean
  131. * @throws ImageTypeNotSupportedError
  132. * @throws FileNotWriableError
  133. */
  134. function save() {
  135. if (!$this->isLoaded() || !is_file($this->getSource())) {
  136. 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');
  137. } // if
  138. if (!file_is_writable($this->getSource())) {
  139. throw new FileNotWriableError($this->getSource());
  140. } // if
  141. switch ($this->getImageType()) {
  142. case IMAGETYPE_PNG:
  143. imagepng($this->resource, $this->getSource());
  144. break;
  145. case IMAGETYPE_JPG:
  146. imagejpeg($this->resource, $this->getSource(), 80);
  147. break;
  148. case IMAGETYPE_GIF:
  149. imagegif($this->resource, $this->getSource());
  150. break;
  151. default:
  152. throw new ImageTypeNotSupportedError(null, $this->getImageType());
  153. } // switch
  154. return true;
  155. } // save
  156. /**
  157. * Save image into the file
  158. *
  159. * @param string $file_path
  160. * @param integer $as_type Save image as type. Default type is PNG
  161. * @return null
  162. * @throws ImageTypeNotSupportedError
  163. * @throws FailedToWriteFileError
  164. */
  165. function saveAs($file_path, $as_type = null) {
  166. // Use internal value if we called convertType with new object
  167. if (is_null($as_type)) {
  168. $as_type = $this->getImageType();
  169. }
  170. $as_type = (integer) $as_type;
  171. if (($as_type < IMAGETYPE_GIF) || ($as_type > IMAGETYPE_PNG)) {
  172. $as_type = IMAGETYPE_PNG;
  173. }
  174. switch ($as_type) {
  175. case IMAGETYPE_PNG:
  176. $write = imagepng($this->resource, $file_path);
  177. break;
  178. case IMAGETYPE_JPEG:
  179. $write = imagejpeg($this->resource, $file_path, 80);
  180. break;
  181. case IMAGETYPE_GIF:
  182. $write = imagegif($this->resource, $file_path);
  183. break;
  184. default:
  185. throw new ImageTypeNotSupportedError(null, $this->getImageType());
  186. } // switch
  187. if (!$write) {
  188. throw new FailedToWriteFileError($file_path);
  189. }
  190. return true;
  191. } // saveAs
  192. /**
  193. * User image resource to populate this object
  194. *
  195. * @param resource $resource
  196. * @return null
  197. */
  198. function createFromResource($resource) {
  199. if (is_resource($resource) && (get_resource_type($resource) == 'gd')) {
  200. $this->reset();
  201. $this->resource = $resource;
  202. } else {
  203. throw new InvalidInstanceError('resource', $resource, 'resource');
  204. } // if
  205. } // createFromResource
  206. /**
  207. * Reset all fields
  208. *
  209. * @param void
  210. * @return null
  211. */
  212. protected function reset() {
  213. $this->is_new = true;
  214. $this->is_loaded = false;
  215. $this->resource = null;
  216. $this->source = null;
  217. $this->image_type = null;
  218. $this->width = null;
  219. $this->height = null;
  220. } // reset
  221. /**
  222. * Return MIME type based on image type
  223. *
  224. * @param void
  225. * @return string
  226. */
  227. function getMimeType() {
  228. return image_type_to_mime_type($this->getImageType());
  229. } // getMimeType
  230. /**
  231. * Return extension based on the source file or type if we have new image
  232. *
  233. * @param void
  234. * @return string
  235. */
  236. function getExtension() {
  237. if ($this->isLoaded()) {
  238. return get_file_extension($this->getSource());
  239. } else {
  240. return image_type_to_extension($this->getImageType());
  241. } // if
  242. } // getExtension
  243. // ---------------------------------------------------
  244. // Transformations
  245. // ---------------------------------------------------
  246. /**
  247. * Resize image to given dimensions. This transformation will not keep
  248. * the ratio of the image
  249. *
  250. * @param integer $width New width
  251. * @param integer $height
  252. * @param boolean $mutate If true transofrmation will be done with the internal
  253. * resource. If false new resouce will be created, new SimpleGdImage will be
  254. * created from it and returned
  255. * @return boolean
  256. */
  257. function resize($width, $height, $mutate = true) {
  258. if (!is_resource($this->resource) || (get_resource_type($this->resource) <> 'gd')) {
  259. return false;
  260. }
  261. $width = (integer) $width > 0 ? (integer) $width : 1;
  262. $height = (integer) $height > 0 ? (integer) $height : 1;
  263. if ($this->getImageType() == IMAGETYPE_GIF) {
  264. $new_resource = imagecreate($width, $height);
  265. } else {
  266. $new_resource = imagecreatetruecolor($width, $height);
  267. } // if
  268. imagecopyresampled($new_resource, $this->resource, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
  269. if ($mutate) {
  270. // Destroy old resrouce, set new one and reset cached values
  271. imagedestroy($this->resource);
  272. $this->resource = $new_resource;
  273. $this->width = $width;
  274. $this->height = $height;
  275. return true;
  276. } else {
  277. // Create new image from the resouce and return
  278. $new_image = new SimpleGdImage();
  279. $new_image->createFromResource($new_resource);
  280. return $new_image;
  281. } // if
  282. } // resize
  283. /**
  284. * Resize to $width x $height but keep the image proportion
  285. *
  286. * @param integer $width Max width
  287. * @param integer $height Max height
  288. * @param string $boundary Limit the scale action: drecrease or increase
  289. * only. If $boundary is NULL than don't limit
  290. * @param boolean $mutate Save the transformation in internal resource or
  291. * create new image and keep internal as is?
  292. * @return SimpleGdImage
  293. */
  294. function scale($width, $height, $boundary = null, $mutate = true) {
  295. if (!is_resource($this->resource) || (get_resource_type($this->resource) <> 'gd')) {
  296. return false;
  297. }
  298. $width = (integer) $width > 0 ? (integer) $width : 1;
  299. $height = (integer) $height > 0 ? (integer) $height : 1;
  300. $scale = min($width / $this->getWidth(), $height / $this->getHeight());
  301. if ($boundary == self::BOUNDARY_DECREASE_ONLY) {
  302. if ($scale > 1) {
  303. if ($mutate) {
  304. return;
  305. } else {
  306. $new_image = new SimpleGdImage();
  307. $new_image->createFromResource($this->resource);
  308. return $new_image;
  309. } // if
  310. } // if
  311. } elseif ($boundary == self::BOUNDARY_INCREASE_ONLY) {
  312. if ($scale < 1) {
  313. if ($mutate) {
  314. return;
  315. } else {
  316. $new_image = new SimpleGdImage();
  317. $new_image->createFromResource($this->resource);
  318. return $new_image;
  319. } // if
  320. } // if
  321. } // if
  322. $new_width = floor($scale * $this->getWidth());
  323. $new_height = floor($scale * $this->getHeight());
  324. if ($this->getImageType() == IMAGETYPE_GIF) {
  325. $new_resource = imagecreate($new_width, $new_height);
  326. } else {
  327. $new_resource = imagecreatetruecolor($new_width, $new_height);
  328. } // if
  329. imagecopyresampled($new_resource, $this->resource, 0, 0, 0, 0, $new_width, $new_height, $this->getWidth(), $this->getHeight());
  330. if ($mutate) {
  331. imagedestroy($this->resource);
  332. $this->resource = $new_resource;
  333. $this->width = $new_width;
  334. $this->height = $new_height;
  335. return true;
  336. } else {
  337. $new_image = new SimpleGdImage();
  338. $new_image->createFromResource($new_resource);
  339. return $new_image;
  340. } // if
  341. } // scale
  342. /**
  343. * Change internat type value
  344. *
  345. * @param integer $to_type
  346. * @return null
  347. */
  348. function convertType($to_type) {
  349. if ($this->getImageType() == $to_type) {
  350. return;
  351. }
  352. if ($to_type == IMAGETYPE_PNG || $to_type == IMAGETYPE_JPEG || $to_type == IMAGETYPE_GIF) {
  353. $this->setImageType($to_type);
  354. } else {
  355. throw new ImageTypeNotSupportedError(null, $to_type);
  356. } // if
  357. } // convertType
  358. // ---------------------------------------------------
  359. // Flags
  360. // ---------------------------------------------------
  361. /**
  362. * Returns true if this image is new (no file, just resource that we can manipulate)
  363. *
  364. * @param void
  365. * @return boolean
  366. */
  367. function isNew() {
  368. return $this->is_new;
  369. } // isNew
  370. /**
  371. * Returns true if this image is loaded
  372. *
  373. * @param void
  374. * @return boolean
  375. */
  376. function isLoaded() {
  377. return $this->is_loaded;
  378. } // isLoaded
  379. // ---------------------------------------------------
  380. // Getters and setters
  381. // ---------------------------------------------------
  382. /**
  383. * Mark this object as new
  384. *
  385. * @param void
  386. * @return null
  387. */
  388. function setIsNew() {
  389. $this->is_new = true;
  390. $this->is_loaded = false;
  391. } // setIsNew
  392. /**
  393. * Mark this object as loaded
  394. *
  395. * @param void
  396. * @return null
  397. */
  398. function setIsLoaded() {
  399. $this->is_new = false;
  400. $this->is_loaded = true;
  401. } // setIsLoaded
  402. /**
  403. * Get source
  404. *
  405. * @param null
  406. * @return string
  407. */
  408. function getSource() {
  409. return $this->source;
  410. } // getSource
  411. /**
  412. * Set source value
  413. *
  414. * @param string $value
  415. * @return null
  416. */
  417. private function setSource($value) {
  418. $this->source = $value;
  419. } // setSource
  420. /**
  421. * Get image_type
  422. *
  423. * @param null
  424. * @return integer
  425. */
  426. function getImageType() {
  427. return $this->image_type;
  428. } // getImageType
  429. /**
  430. * Set image_type value
  431. *
  432. * @param integer $value
  433. * @return null
  434. */
  435. private function setImageType($value) {
  436. $this->image_type = $value;
  437. } // setImageType
  438. /**
  439. * Get width
  440. *
  441. * @param null
  442. * @return integer
  443. */
  444. function getWidth() {
  445. if (is_null($this->width)) {
  446. $this->width = imagesx($this->resource);
  447. }
  448. return $this->width;
  449. } // getWidth
  450. /**
  451. * Set width value
  452. *
  453. * @param integer $value
  454. * @return null
  455. */
  456. protected function setWidth($value) {
  457. $this->width = $value;
  458. } // setWidth
  459. /**
  460. * Get height
  461. *
  462. * @param null
  463. * @return integer
  464. */
  465. function getHeight() {
  466. if (is_null($this->height)) {
  467. $this->height = imagesy($this->resource);
  468. }
  469. return $this->height;
  470. } // getHeight
  471. /**
  472. * Set height value
  473. *
  474. * @param integer $value
  475. * @return null
  476. */
  477. protected function setHeight($value) {
  478. $this->height = $value;
  479. } // setHeight
  480. } // SimpleGdImage
  481. ?>