image.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. <?php
  2. /**
  3. * ownCloud
  4. *
  5. * @author Thomas Tanghus
  6. * @copyright 2011 Thomas Tanghus <thomas@tanghus.net>
  7. *
  8. * This library is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  10. * License as published by the Free Software Foundation; either
  11. * version 3 of the License, or any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  17. *
  18. * You should have received a copy of the GNU Affero General Public
  19. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. //From user comments at http://dk2.php.net/manual/en/function.exif-imagetype.php
  23. if ( ! function_exists( 'exif_imagetype' ) ) {
  24. function exif_imagetype ( $filename ) {
  25. if ( ( $info = getimagesize( $filename ) ) !== false ) {
  26. return $info[2];
  27. }
  28. return false;
  29. }
  30. }
  31. function ellipsis($str, $maxlen) {
  32. if (strlen($str) > $maxlen) {
  33. $characters = floor($maxlen / 2);
  34. return substr($str, 0, $characters) . '...' . substr($str, -1 * $characters);
  35. }
  36. return $str;
  37. }
  38. /**
  39. * Class for basic image manipulation
  40. *
  41. */
  42. class OC_Image {
  43. protected $resource = false; // tmp resource.
  44. protected $imagetype = IMAGETYPE_PNG; // Default to png if file type isn't evident.
  45. protected $filepath = null;
  46. /**
  47. * @brief Get mime type for an image file.
  48. * @param $filepath The path to a local image file.
  49. * @returns string The mime type if the it could be determined, otherwise an empty string.
  50. */
  51. static public function getMimeTypeForFile($filepath) {
  52. $imagetype = exif_imagetype($filepath);
  53. return $imagetype ? image_type_to_mime_type($imagetype) : '';
  54. }
  55. /**
  56. * @brief Constructor.
  57. * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function.
  58. * @returns bool False on error
  59. */
  60. public function __construct($imageref = null) {
  61. //OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG);
  62. if(!extension_loaded('gd') || !function_exists('gd_info')) {
  63. OC_Log::write('core',__METHOD__.'(): GD module not installed', OC_Log::ERROR);
  64. return false;
  65. }
  66. if(!is_null($imageref)) {
  67. $this->load($imageref);
  68. }
  69. }
  70. /**
  71. * @brief Determine whether the object contains an image resource.
  72. * @returns bool
  73. */
  74. public function valid() { // apparently you can't name a method 'empty'...
  75. return is_resource($this->resource);
  76. }
  77. /**
  78. * @brief Returns the MIME type of the image or an empty string if no image is loaded.
  79. * @returns int
  80. */
  81. public function mimeType() {
  82. return $this->valid() ? image_type_to_mime_type($this->imagetype) : '';
  83. }
  84. /**
  85. * @brief Returns the width of the image or -1 if no image is loaded.
  86. * @returns int
  87. */
  88. public function width() {
  89. return $this->valid() ? imagesx($this->resource) : -1;
  90. }
  91. /**
  92. * @brief Returns the height of the image or -1 if no image is loaded.
  93. * @returns int
  94. */
  95. public function height() {
  96. return $this->valid() ? imagesy($this->resource) : -1;
  97. }
  98. /**
  99. * @brief Returns the width when the image orientation is top-left.
  100. * @returns int
  101. */
  102. public function widthTopLeft() {
  103. $o = $this->getOrientation();
  104. OC_Log::write('core','OC_Image->widthTopLeft() Orientation: '.$o, OC_Log::DEBUG);
  105. switch($o) {
  106. case -1:
  107. case 1:
  108. case 2: // Not tested
  109. case 3:
  110. case 4: // Not tested
  111. return $this->width();
  112. break;
  113. case 5: // Not tested
  114. case 6:
  115. case 7: // Not tested
  116. case 8:
  117. return $this->height();
  118. break;
  119. }
  120. return $this->width();
  121. }
  122. /**
  123. * @brief Returns the height when the image orientation is top-left.
  124. * @returns int
  125. */
  126. public function heightTopLeft() {
  127. $o = $this->getOrientation();
  128. OC_Log::write('core','OC_Image->heightTopLeft() Orientation: '.$o, OC_Log::DEBUG);
  129. switch($o) {
  130. case -1:
  131. case 1:
  132. case 2: // Not tested
  133. case 3:
  134. case 4: // Not tested
  135. return $this->height();
  136. break;
  137. case 5: // Not tested
  138. case 6:
  139. case 7: // Not tested
  140. case 8:
  141. return $this->width();
  142. break;
  143. }
  144. return $this->height();
  145. }
  146. /**
  147. * @brief Outputs the image.
  148. * @returns bool
  149. */
  150. public function show() {
  151. header('Content-Type: '.$this->mimeType());
  152. return $this->_output();
  153. }
  154. /**
  155. * @brief Saves the image.
  156. * @returns bool
  157. */
  158. public function save($filepath=null) {
  159. if($filepath === null && $this->filepath === null) {
  160. OC_Log::write('core',__METHOD__.'(): called with no path.', OC_Log::ERROR);
  161. return false;
  162. } elseif($filepath === null && $this->filepath !== null) {
  163. $filepath = $this->filepath;
  164. }
  165. return $this->_output($filepath);
  166. }
  167. /**
  168. * @brief Outputs/saves the image.
  169. */
  170. private function _output($filepath=null) {
  171. if($filepath) {
  172. if (!file_exists(dirname($filepath)))
  173. mkdir(dirname($filepath), 0777, true);
  174. if(!is_writable(dirname($filepath))) {
  175. OC_Log::write('core',__METHOD__.'(): Directory \''.dirname($filepath).'\' is not writable.', OC_Log::ERROR);
  176. return false;
  177. } elseif(is_writable(dirname($filepath)) && file_exists($filepath) && !is_writable($filepath)) {
  178. OC_Log::write('core',__METHOD__.'(): File \''.$filepath.'\' is not writable.', OC_Log::ERROR);
  179. return false;
  180. }
  181. }
  182. if (!$this->valid()) {
  183. return false;
  184. }
  185. $retval = false;
  186. switch($this->imagetype) {
  187. case IMAGETYPE_GIF:
  188. $retval = imagegif($this->resource, $filepath);
  189. break;
  190. case IMAGETYPE_JPEG:
  191. $retval = imagejpeg($this->resource, $filepath);
  192. break;
  193. case IMAGETYPE_PNG:
  194. $retval = imagepng($this->resource, $filepath);
  195. break;
  196. case IMAGETYPE_XBM:
  197. $retval = imagexbm($this->resource, $filepath);
  198. break;
  199. case IMAGETYPE_WBMP:
  200. case IMAGETYPE_BMP:
  201. $retval = imagewbmp($this->resource, $filepath);
  202. break;
  203. default:
  204. $retval = imagepng($this->resource, $filepath);
  205. }
  206. return $retval;
  207. }
  208. /**
  209. * @brief Prints the image when called as $image().
  210. */
  211. public function __invoke() {
  212. return $this->show();
  213. }
  214. /**
  215. * @returns Returns the image resource in any.
  216. */
  217. public function resource() {
  218. return $this->resource;
  219. }
  220. /**
  221. * @returns Returns the raw image data.
  222. */
  223. function data() {
  224. ob_start();
  225. $res = imagepng($this->resource);
  226. if (!$res) {
  227. OC_Log::write('core','OC_Image->data. Error getting image data.',OC_Log::ERROR);
  228. }
  229. return ob_get_clean();
  230. }
  231. /**
  232. * @returns Returns a base64 encoded string suitable for embedding in a VCard.
  233. */
  234. function __toString() {
  235. return base64_encode($this->data());
  236. }
  237. /**
  238. * (I'm open for suggestions on better method name ;)
  239. * @brief Get the orientation based on EXIF data.
  240. * @returns The orientation or -1 if no EXIF data is available.
  241. */
  242. public function getOrientation() {
  243. if(!is_callable('exif_read_data')){
  244. OC_Log::write('core','OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG);
  245. return -1;
  246. }
  247. if(!$this->valid()) {
  248. OC_Log::write('core','OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG);
  249. return -1;
  250. }
  251. if(is_null($this->filepath) || !is_readable($this->filepath)) {
  252. OC_Log::write('core','OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG);
  253. return -1;
  254. }
  255. $exif = @exif_read_data($this->filepath, 'IFD0');
  256. if(!$exif) {
  257. return -1;
  258. }
  259. if(!isset($exif['Orientation'])) {
  260. return -1;
  261. }
  262. return $exif['Orientation'];
  263. }
  264. /**
  265. * (I'm open for suggestions on better method name ;)
  266. * @brief Fixes orientation based on EXIF data.
  267. * @returns bool.
  268. */
  269. public function fixOrientation() {
  270. $o = $this->getOrientation();
  271. OC_Log::write('core','OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG);
  272. $rotate = 0;
  273. $flip = false;
  274. switch($o) {
  275. case -1:
  276. return false; //Nothing to fix
  277. break;
  278. case 1:
  279. $rotate = 0;
  280. $flip = false;
  281. break;
  282. case 2: // Not tested
  283. $rotate = 0;
  284. $flip = true;
  285. break;
  286. case 3:
  287. $rotate = 180;
  288. $flip = false;
  289. break;
  290. case 4: // Not tested
  291. $rotate = 180;
  292. $flip = true;
  293. break;
  294. case 5: // Not tested
  295. $rotate = 90;
  296. $flip = true;
  297. break;
  298. case 6:
  299. //$rotate = 90;
  300. $rotate = 270;
  301. $flip = false;
  302. break;
  303. case 7: // Not tested
  304. $rotate = 270;
  305. $flip = true;
  306. break;
  307. case 8:
  308. $rotate = 90;
  309. $flip = false;
  310. break;
  311. }
  312. if($rotate) {
  313. $res = imagerotate($this->resource, $rotate, -1);
  314. if($res) {
  315. if(imagealphablending($res, true)) {
  316. if(imagesavealpha($res, true)) {
  317. imagedestroy($this->resource);
  318. $this->resource = $res;
  319. return true;
  320. } else {
  321. OC_Log::write('core','OC_Image->fixOrientation() Error during alphasaving.', OC_Log::DEBUG);
  322. return false;
  323. }
  324. } else {
  325. OC_Log::write('core','OC_Image->fixOrientation() Error during alphablending.', OC_Log::DEBUG);
  326. return false;
  327. }
  328. } else {
  329. OC_Log::write('core','OC_Image->fixOrientation() Error during oriention fixing.', OC_Log::DEBUG);
  330. return false;
  331. }
  332. }
  333. }
  334. /**
  335. * @brief Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
  336. * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle ).
  337. * @returns An image resource or false on error
  338. */
  339. public function load($imageref) {
  340. if(is_resource($imageref)) {
  341. if(get_resource_type($imageref) == 'gd') {
  342. $this->resource = $imageref;
  343. return $this->resource;
  344. } elseif(in_array(get_resource_type($imageref), array('file','stream'))) {
  345. return $this->loadFromFileHandle($imageref);
  346. }
  347. } elseif($this->loadFromFile($imageref) !== false) {
  348. return $this->resource;
  349. } elseif($this->loadFromBase64($imageref) !== false) {
  350. return $this->resource;
  351. } elseif($this->loadFromData($imageref) !== false) {
  352. return $this->resource;
  353. } else {
  354. OC_Log::write('core',__METHOD__.'(): couldn\'t load anything. Giving up!', OC_Log::DEBUG);
  355. return false;
  356. }
  357. }
  358. /**
  359. * @brief Loads an image from an open file handle.
  360. * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
  361. * @param $handle
  362. * @returns An image resource or false on error
  363. */
  364. public function loadFromFileHandle($handle) {
  365. OC_Log::write('core',__METHOD__.'(): Trying', OC_Log::DEBUG);
  366. $contents = stream_get_contents($handle);
  367. if($this->loadFromData($contents)) {
  368. return $this->resource;
  369. }
  370. }
  371. /**
  372. * @brief Loads an image from a local file.
  373. * @param $imageref The path to a local file.
  374. * @returns An image resource or false on error
  375. */
  376. public function loadFromFile($imagepath=false) {
  377. if(!is_file($imagepath) || !file_exists($imagepath) || !is_readable($imagepath)) {
  378. // Debug output disabled because this method is tried before loadFromBase64?
  379. OC_Log::write('core','OC_Image->loadFromFile, couldn\'t load: '.ellipsis($imagepath, 50), OC_Log::DEBUG);
  380. return false;
  381. }
  382. $itype = exif_imagetype($imagepath);
  383. switch($itype) {
  384. case IMAGETYPE_GIF:
  385. if (imagetypes() & IMG_GIF) {
  386. $this->resource = imagecreatefromgif($imagepath);
  387. } else {
  388. OC_Log::write('core','OC_Image->loadFromFile, GIF images not supported: '.$imagepath, OC_Log::DEBUG);
  389. }
  390. break;
  391. case IMAGETYPE_JPEG:
  392. if (imagetypes() & IMG_JPG) {
  393. $this->resource = imagecreatefromjpeg($imagepath);
  394. } else {
  395. OC_Log::write('core','OC_Image->loadFromFile, JPG images not supported: '.$imagepath, OC_Log::DEBUG);
  396. }
  397. break;
  398. case IMAGETYPE_PNG:
  399. if (imagetypes() & IMG_PNG) {
  400. $this->resource = imagecreatefrompng($imagepath);
  401. } else {
  402. OC_Log::write('core','OC_Image->loadFromFile, PNG images not supported: '.$imagepath, OC_Log::DEBUG);
  403. }
  404. break;
  405. case IMAGETYPE_XBM:
  406. if (imagetypes() & IMG_XPM) {
  407. $this->resource = imagecreatefromxbm($imagepath);
  408. } else {
  409. OC_Log::write('core','OC_Image->loadFromFile, XBM/XPM images not supported: '.$imagepath, OC_Log::DEBUG);
  410. }
  411. break;
  412. case IMAGETYPE_WBMP:
  413. case IMAGETYPE_BMP:
  414. if (imagetypes() & IMG_WBMP) {
  415. $this->resource = imagecreatefromwbmp($imagepath);
  416. } else {
  417. OC_Log::write('core','OC_Image->loadFromFile, (W)BMP images not supported: '.$imagepath, OC_Log::DEBUG);
  418. }
  419. break;
  420. /*
  421. case IMAGETYPE_TIFF_II: // (intel byte order)
  422. break;
  423. case IMAGETYPE_TIFF_MM: // (motorola byte order)
  424. break;
  425. case IMAGETYPE_JPC:
  426. break;
  427. case IMAGETYPE_JP2:
  428. break;
  429. case IMAGETYPE_JPX:
  430. break;
  431. case IMAGETYPE_JB2:
  432. break;
  433. case IMAGETYPE_SWC:
  434. break;
  435. case IMAGETYPE_IFF:
  436. break;
  437. case IMAGETYPE_ICO:
  438. break;
  439. case IMAGETYPE_SWF:
  440. break;
  441. case IMAGETYPE_PSD:
  442. break;
  443. */
  444. default:
  445. // this is mostly file created from encrypted file
  446. $this->resource = imagecreatefromstring(\OC_Filesystem::file_get_contents(\OC_Filesystem::getLocalPath($imagepath)));
  447. $itype = IMAGETYPE_PNG;
  448. OC_Log::write('core','OC_Image->loadFromFile, Default', OC_Log::DEBUG);
  449. break;
  450. }
  451. if($this->valid()) {
  452. $this->imagetype = $itype;
  453. $this->filepath = $imagepath;
  454. }
  455. return $this->resource;
  456. }
  457. /**
  458. * @brief Loads an image from a string of data.
  459. * @param $str A string of image data as read from a file.
  460. * @returns An image resource or false on error
  461. */
  462. public function loadFromData($str) {
  463. if(is_resource($str)) {
  464. return false;
  465. }
  466. $this->resource = @imagecreatefromstring($str);
  467. if(!$this->resource) {
  468. OC_Log::write('core','OC_Image->loadFromData, couldn\'t load', OC_Log::DEBUG);
  469. return false;
  470. }
  471. return $this->resource;
  472. }
  473. /**
  474. * @brief Loads an image from a base64 encoded string.
  475. * @param $str A string base64 encoded string of image data.
  476. * @returns An image resource or false on error
  477. */
  478. public function loadFromBase64($str) {
  479. if(!is_string($str)) {
  480. return false;
  481. }
  482. $data = base64_decode($str);
  483. if($data) { // try to load from string data
  484. $this->resource = @imagecreatefromstring($data);
  485. if(!$this->resource) {
  486. OC_Log::write('core','OC_Image->loadFromBase64, couldn\'t load', OC_Log::DEBUG);
  487. return false;
  488. }
  489. return $this->resource;
  490. } else {
  491. return false;
  492. }
  493. }
  494. /**
  495. * @brief Resizes the image preserving ratio.
  496. * @param $maxsize The maximum size of either the width or height.
  497. * @returns bool
  498. */
  499. public function resize($maxsize) {
  500. if(!$this->valid()) {
  501. OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
  502. return false;
  503. }
  504. $width_orig=imageSX($this->resource);
  505. $height_orig=imageSY($this->resource);
  506. $ratio_orig = $width_orig/$height_orig;
  507. if ($ratio_orig > 1) {
  508. $new_height = round($maxsize/$ratio_orig);
  509. $new_width = $maxsize;
  510. } else {
  511. $new_width = round($maxsize*$ratio_orig);
  512. $new_height = $maxsize;
  513. }
  514. $process = imagecreatetruecolor(round($new_width), round($new_height));
  515. if ($process == false) {
  516. OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
  517. imagedestroy($process);
  518. return false;
  519. }
  520. imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $new_width, $new_height, $width_orig, $height_orig);
  521. if ($process == false) {
  522. OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$new_width.'x'.$new_height,OC_Log::ERROR);
  523. imagedestroy($process);
  524. return false;
  525. }
  526. imagedestroy($this->resource);
  527. $this->resource = $process;
  528. return true;
  529. }
  530. public function preciseResize($width, $height) {
  531. if (!$this->valid()) {
  532. OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
  533. return false;
  534. }
  535. $width_orig=imageSX($this->resource);
  536. $height_orig=imageSY($this->resource);
  537. $process = imagecreatetruecolor($width, $height);
  538. if ($process == false) {
  539. OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
  540. imagedestroy($process);
  541. return false;
  542. }
  543. imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);
  544. if ($process == false) {
  545. OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$width.'x'.$height,OC_Log::ERROR);
  546. imagedestroy($process);
  547. return false;
  548. }
  549. imagedestroy($this->resource);
  550. $this->resource = $process;
  551. return true;
  552. }
  553. /**
  554. * @brief Crops the image to the middle square. If the image is already square it just returns.
  555. * @param int maximum size for the result (optional)
  556. * @returns bool for success or failure
  557. */
  558. public function centerCrop($size=0) {
  559. if(!$this->valid()) {
  560. OC_Log::write('core','OC_Image->centerCrop, No image loaded', OC_Log::ERROR);
  561. return false;
  562. }
  563. $width_orig=imageSX($this->resource);
  564. $height_orig=imageSY($this->resource);
  565. if($width_orig === $height_orig and $size==0) {
  566. return true;
  567. }
  568. $ratio_orig = $width_orig/$height_orig;
  569. $width = $height = min($width_orig, $height_orig);
  570. if ($ratio_orig > 1) {
  571. $x = ($width_orig/2) - ($width/2);
  572. $y = 0;
  573. } else {
  574. $y = ($height_orig/2) - ($height/2);
  575. $x = 0;
  576. }
  577. if($size>0){
  578. $targetWidth=$size;
  579. $targetHeight=$size;
  580. }else{
  581. $targetWidth=$width;
  582. $targetHeight=$height;
  583. }
  584. $process = imagecreatetruecolor($targetWidth, $targetHeight);
  585. if ($process == false) {
  586. OC_Log::write('core','OC_Image->centerCrop. Error creating true color image',OC_Log::ERROR);
  587. imagedestroy($process);
  588. return false;
  589. }
  590. imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
  591. if ($process == false) {
  592. OC_Log::write('core','OC_Image->centerCrop. Error resampling process image '.$width.'x'.$height,OC_Log::ERROR);
  593. imagedestroy($process);
  594. return false;
  595. }
  596. imagedestroy($this->resource);
  597. $this->resource = $process;
  598. return true;
  599. }
  600. /**
  601. * @brief Crops the image from point $x$y with dimension $wx$h.
  602. * @param $x Horizontal position
  603. * @param $y Vertical position
  604. * @param $w Width
  605. * @param $h Hight
  606. * @returns bool for success or failure
  607. */
  608. public function crop($x, $y, $w, $h) {
  609. if(!$this->valid()) {
  610. OC_Log::write('core',__METHOD__.'(): No image loaded', OC_Log::ERROR);
  611. return false;
  612. }
  613. $process = imagecreatetruecolor($w, $h);
  614. if ($process == false) {
  615. OC_Log::write('core',__METHOD__.'(): Error creating true color image',OC_Log::ERROR);
  616. imagedestroy($process);
  617. return false;
  618. }
  619. imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
  620. if ($process == false) {
  621. OC_Log::write('core',__METHOD__.'(): Error resampling process image '.$w.'x'.$h,OC_Log::ERROR);
  622. imagedestroy($process);
  623. return false;
  624. }
  625. imagedestroy($this->resource);
  626. $this->resource = $process;
  627. return true;
  628. }
  629. public function destroy(){
  630. if($this->valid()){
  631. imagedestroy($this->resource);
  632. }
  633. $this->resource=null;
  634. }
  635. public function __destruct(){
  636. $this->destroy();
  637. }
  638. }