image.php 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021
  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. /**
  23. * Class for basic image manipulation
  24. */
  25. class OC_Image {
  26. protected $resource = false; // tmp resource.
  27. protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
  28. protected $mimeType = "image/png"; // Default to png
  29. protected $bitDepth = 24;
  30. protected $filePath = null;
  31. private $fileInfo;
  32. /**
  33. * @brief Get mime type for an image file.
  34. * @param $filepath The path to a local image file.
  35. * @returns string The mime type if the it could be determined, otherwise an empty string.
  36. */
  37. static public function getMimeTypeForFile($filePath) {
  38. // exif_imagetype throws "read error!" if file is less than 12 byte
  39. if (filesize($filePath) > 11) {
  40. $imageType = exif_imagetype($filePath);
  41. }
  42. else {
  43. $imageType = false;
  44. }
  45. return $imageType ? image_type_to_mime_type($imageType) : '';
  46. }
  47. /**
  48. * @brief Constructor.
  49. * @param $imageref The path to a local file, a base64 encoded string or a resource created by an imagecreate* function.
  50. * @returns bool False on error
  51. */
  52. public function __construct($imageRef = null) {
  53. //OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG);
  54. if(!extension_loaded('gd') || !function_exists('gd_info')) {
  55. OC_Log::write('core', __METHOD__.'(): GD module not installed', OC_Log::ERROR);
  56. return false;
  57. }
  58. if (\OC_Util::fileInfoLoaded()) {
  59. $this->fileInfo = new finfo(FILEINFO_MIME_TYPE);
  60. }
  61. if(!is_null($imageRef)) {
  62. $this->load($imageRef);
  63. }
  64. }
  65. /**
  66. * @brief Determine whether the object contains an image resource.
  67. * @returns bool
  68. */
  69. public function valid() { // apparently you can't name a method 'empty'...
  70. return is_resource($this->resource);
  71. }
  72. /**
  73. * @brief Returns the MIME type of the image or an empty string if no image is loaded.
  74. * @returns int
  75. */
  76. public function mimeType() {
  77. return $this->valid() ? $this->mimeType : '';
  78. }
  79. /**
  80. * @brief Returns the width of the image or -1 if no image is loaded.
  81. * @returns int
  82. */
  83. public function width() {
  84. return $this->valid() ? imagesx($this->resource) : -1;
  85. }
  86. /**
  87. * @brief Returns the height of the image or -1 if no image is loaded.
  88. * @returns int
  89. */
  90. public function height() {
  91. return $this->valid() ? imagesy($this->resource) : -1;
  92. }
  93. /**
  94. * @brief Returns the width when the image orientation is top-left.
  95. * @returns int
  96. */
  97. public function widthTopLeft() {
  98. $o = $this->getOrientation();
  99. OC_Log::write('core', 'OC_Image->widthTopLeft() Orientation: '.$o, OC_Log::DEBUG);
  100. switch($o) {
  101. case -1:
  102. case 1:
  103. case 2: // Not tested
  104. case 3:
  105. case 4: // Not tested
  106. return $this->width();
  107. break;
  108. case 5: // Not tested
  109. case 6:
  110. case 7: // Not tested
  111. case 8:
  112. return $this->height();
  113. break;
  114. }
  115. return $this->width();
  116. }
  117. /**
  118. * @brief Returns the height when the image orientation is top-left.
  119. * @returns int
  120. */
  121. public function heightTopLeft() {
  122. $o = $this->getOrientation();
  123. OC_Log::write('core', 'OC_Image->heightTopLeft() Orientation: '.$o, OC_Log::DEBUG);
  124. switch($o) {
  125. case -1:
  126. case 1:
  127. case 2: // Not tested
  128. case 3:
  129. case 4: // Not tested
  130. return $this->height();
  131. break;
  132. case 5: // Not tested
  133. case 6:
  134. case 7: // Not tested
  135. case 8:
  136. return $this->width();
  137. break;
  138. }
  139. return $this->height();
  140. }
  141. /**
  142. * @brief Outputs the image.
  143. * @returns bool
  144. */
  145. public function show() {
  146. header('Content-Type: '.$this->mimeType());
  147. return $this->_output();
  148. }
  149. /**
  150. * @brief Saves the image.
  151. * @returns bool
  152. */
  153. public function save($filePath=null) {
  154. if($filePath === null && $this->filePath === null) {
  155. OC_Log::write('core', __METHOD__.'(): called with no path.', OC_Log::ERROR);
  156. return false;
  157. } elseif($filePath === null && $this->filePath !== null) {
  158. $filePath = $this->filePath;
  159. }
  160. return $this->_output($filePath);
  161. }
  162. /**
  163. * @brief Outputs/saves the image.
  164. */
  165. private function _output($filePath=null) {
  166. if($filePath) {
  167. if (!file_exists(dirname($filePath)))
  168. mkdir(dirname($filePath), 0777, true);
  169. if(!is_writable(dirname($filePath))) {
  170. OC_Log::write('core',
  171. __METHOD__.'(): Directory \''.dirname($filePath).'\' is not writable.',
  172. OC_Log::ERROR);
  173. return false;
  174. } elseif(is_writable(dirname($filePath)) && file_exists($filePath) && !is_writable($filePath)) {
  175. OC_Log::write('core', __METHOD__.'(): File \''.$filePath.'\' is not writable.', OC_Log::ERROR);
  176. return false;
  177. }
  178. }
  179. if (!$this->valid()) {
  180. return false;
  181. }
  182. $retVal = false;
  183. switch($this->imageType) {
  184. case IMAGETYPE_GIF:
  185. $retVal = imagegif($this->resource, $filePath);
  186. break;
  187. case IMAGETYPE_JPEG:
  188. $retVal = imagejpeg($this->resource, $filePath);
  189. break;
  190. case IMAGETYPE_PNG:
  191. $retVal = imagepng($this->resource, $filePath);
  192. break;
  193. case IMAGETYPE_XBM:
  194. $retVal = imagexbm($this->resource, $filePath);
  195. break;
  196. case IMAGETYPE_WBMP:
  197. $retVal = imagewbmp($this->resource, $filePath);
  198. break;
  199. case IMAGETYPE_BMP:
  200. $retVal = imagebmp($this->resource, $filePath, $this->bitDepth);
  201. break;
  202. default:
  203. $retVal = imagepng($this->resource, $filePath);
  204. }
  205. return $retVal;
  206. }
  207. /**
  208. * @brief Prints the image when called as $image().
  209. */
  210. public function __invoke() {
  211. return $this->show();
  212. }
  213. /**
  214. * @returns Returns the image resource in any.
  215. */
  216. public function resource() {
  217. return $this->resource;
  218. }
  219. /**
  220. * @returns Returns the raw image data.
  221. */
  222. function data() {
  223. ob_start();
  224. switch ($this->mimeType) {
  225. case "image/png":
  226. $res = imagepng($this->resource);
  227. break;
  228. case "image/jpeg":
  229. $res = imagejpeg($this->resource);
  230. break;
  231. case "image/gif":
  232. $res = imagegif($this->resource);
  233. break;
  234. default:
  235. $res = imagepng($this->resource);
  236. OC_Log::write('core', 'OC_Image->data. Couldn\'t guess mimetype, defaulting to png', OC_Log::INFO);
  237. break;
  238. }
  239. if (!$res) {
  240. OC_Log::write('core', 'OC_Image->data. Error getting image data.', OC_Log::ERROR);
  241. }
  242. return ob_get_clean();
  243. }
  244. /**
  245. * @returns Returns a base64 encoded string suitable for embedding in a VCard.
  246. */
  247. function __toString() {
  248. return base64_encode($this->data());
  249. }
  250. /**
  251. * (I'm open for suggestions on better method name ;)
  252. * @brief Get the orientation based on EXIF data.
  253. * @returns The orientation or -1 if no EXIF data is available.
  254. */
  255. public function getOrientation() {
  256. if(!is_callable('exif_read_data')) {
  257. OC_Log::write('core', 'OC_Image->fixOrientation() Exif module not enabled.', OC_Log::DEBUG);
  258. return -1;
  259. }
  260. if(!$this->valid()) {
  261. OC_Log::write('core', 'OC_Image->fixOrientation() No image loaded.', OC_Log::DEBUG);
  262. return -1;
  263. }
  264. if(is_null($this->filePath) || !is_readable($this->filePath)) {
  265. OC_Log::write('core', 'OC_Image->fixOrientation() No readable file path set.', OC_Log::DEBUG);
  266. return -1;
  267. }
  268. $exif = @exif_read_data($this->filePath, 'IFD0');
  269. if(!$exif) {
  270. return -1;
  271. }
  272. if(!isset($exif['Orientation'])) {
  273. return -1;
  274. }
  275. return $exif['Orientation'];
  276. }
  277. /**
  278. * (I'm open for suggestions on better method name ;)
  279. * @brief Fixes orientation based on EXIF data.
  280. * @returns bool.
  281. */
  282. public function fixOrientation() {
  283. $o = $this->getOrientation();
  284. OC_Log::write('core', 'OC_Image->fixOrientation() Orientation: '.$o, OC_Log::DEBUG);
  285. $rotate = 0;
  286. $flip = false;
  287. switch($o) {
  288. case -1:
  289. return false; //Nothing to fix
  290. break;
  291. case 1:
  292. $rotate = 0;
  293. $flip = false;
  294. break;
  295. case 2: // Not tested
  296. $rotate = 0;
  297. $flip = true;
  298. break;
  299. case 3:
  300. $rotate = 180;
  301. $flip = false;
  302. break;
  303. case 4: // Not tested
  304. $rotate = 180;
  305. $flip = true;
  306. break;
  307. case 5: // Not tested
  308. $rotate = 90;
  309. $flip = true;
  310. break;
  311. case 6:
  312. //$rotate = 90;
  313. $rotate = 270;
  314. $flip = false;
  315. break;
  316. case 7: // Not tested
  317. $rotate = 270;
  318. $flip = true;
  319. break;
  320. case 8:
  321. $rotate = 90;
  322. $flip = false;
  323. break;
  324. }
  325. if($rotate) {
  326. $res = imagerotate($this->resource, $rotate, -1);
  327. if($res) {
  328. if(imagealphablending($res, true)) {
  329. if(imagesavealpha($res, true)) {
  330. imagedestroy($this->resource);
  331. $this->resource = $res;
  332. return true;
  333. } else {
  334. OC_Log::write('core', 'OC_Image->fixOrientation() Error during alphasaving.', OC_Log::DEBUG);
  335. return false;
  336. }
  337. } else {
  338. OC_Log::write('core', 'OC_Image->fixOrientation() Error during alphablending.', OC_Log::DEBUG);
  339. return false;
  340. }
  341. } else {
  342. OC_Log::write('core', 'OC_Image->fixOrientation() Error during oriention fixing.', OC_Log::DEBUG);
  343. return false;
  344. }
  345. }
  346. }
  347. /**
  348. * @brief Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
  349. * @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 ).
  350. * @returns An image resource or false on error
  351. */
  352. public function load($imageRef) {
  353. if(is_resource($imageRef)) {
  354. if(get_resource_type($imageRef) == 'gd') {
  355. $this->resource = $imageRef;
  356. return $this->resource;
  357. } elseif(in_array(get_resource_type($imageRef), array('file', 'stream'))) {
  358. return $this->loadFromFileHandle($imageRef);
  359. }
  360. } elseif($this->loadFromFile($imageRef) !== false) {
  361. return $this->resource;
  362. } elseif($this->loadFromBase64($imageRef) !== false) {
  363. return $this->resource;
  364. } elseif($this->loadFromData($imageRef) !== false) {
  365. return $this->resource;
  366. } else {
  367. OC_Log::write('core', __METHOD__.'(): couldn\'t load anything. Giving up!', OC_Log::DEBUG);
  368. return false;
  369. }
  370. }
  371. /**
  372. * @brief Loads an image from an open file handle.
  373. * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
  374. * @param $handle
  375. * @returns An image resource or false on error
  376. */
  377. public function loadFromFileHandle($handle) {
  378. OC_Log::write('core', __METHOD__.'(): Trying', OC_Log::DEBUG);
  379. $contents = stream_get_contents($handle);
  380. if($this->loadFromData($contents)) {
  381. return $this->resource;
  382. }
  383. }
  384. /**
  385. * @brief Loads an image from a local file.
  386. * @param $imageref The path to a local file.
  387. * @returns An image resource or false on error
  388. */
  389. public function loadFromFile($imagePath=false) {
  390. // exif_imagetype throws "read error!" if file is less than 12 byte
  391. if(!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
  392. // Debug output disabled because this method is tried before loadFromBase64?
  393. OC_Log::write('core', 'OC_Image->loadFromFile, couldn\'t load: '.$imagePath, OC_Log::DEBUG);
  394. return false;
  395. }
  396. $iType = exif_imagetype($imagePath);
  397. switch ($iType) {
  398. case IMAGETYPE_GIF:
  399. if (imagetypes() & IMG_GIF) {
  400. $this->resource = imagecreatefromgif($imagePath);
  401. } else {
  402. OC_Log::write('core',
  403. 'OC_Image->loadFromFile, GIF images not supported: '.$imagePath,
  404. OC_Log::DEBUG);
  405. }
  406. break;
  407. case IMAGETYPE_JPEG:
  408. if (imagetypes() & IMG_JPG) {
  409. $this->resource = imagecreatefromjpeg($imagePath);
  410. } else {
  411. OC_Log::write('core',
  412. 'OC_Image->loadFromFile, JPG images not supported: '.$imagePath,
  413. OC_Log::DEBUG);
  414. }
  415. break;
  416. case IMAGETYPE_PNG:
  417. if (imagetypes() & IMG_PNG) {
  418. $this->resource = imagecreatefrompng($imagePath);
  419. } else {
  420. OC_Log::write('core',
  421. 'OC_Image->loadFromFile, PNG images not supported: '.$imagePath,
  422. OC_Log::DEBUG);
  423. }
  424. break;
  425. case IMAGETYPE_XBM:
  426. if (imagetypes() & IMG_XPM) {
  427. $this->resource = imagecreatefromxbm($imagePath);
  428. } else {
  429. OC_Log::write('core',
  430. 'OC_Image->loadFromFile, XBM/XPM images not supported: '.$imagePath,
  431. OC_Log::DEBUG);
  432. }
  433. break;
  434. case IMAGETYPE_WBMP:
  435. if (imagetypes() & IMG_WBMP) {
  436. $this->resource = imagecreatefromwbmp($imagePath);
  437. } else {
  438. OC_Log::write('core',
  439. 'OC_Image->loadFromFile, WBMP images not supported: '.$imagePath,
  440. OC_Log::DEBUG);
  441. }
  442. break;
  443. case IMAGETYPE_BMP:
  444. $this->resource = $this->imagecreatefrombmp($imagePath);
  445. break;
  446. /*
  447. case IMAGETYPE_TIFF_II: // (intel byte order)
  448. break;
  449. case IMAGETYPE_TIFF_MM: // (motorola byte order)
  450. break;
  451. case IMAGETYPE_JPC:
  452. break;
  453. case IMAGETYPE_JP2:
  454. break;
  455. case IMAGETYPE_JPX:
  456. break;
  457. case IMAGETYPE_JB2:
  458. break;
  459. case IMAGETYPE_SWC:
  460. break;
  461. case IMAGETYPE_IFF:
  462. break;
  463. case IMAGETYPE_ICO:
  464. break;
  465. case IMAGETYPE_SWF:
  466. break;
  467. case IMAGETYPE_PSD:
  468. break;
  469. */
  470. default:
  471. // this is mostly file created from encrypted file
  472. $this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath)));
  473. $iType = IMAGETYPE_PNG;
  474. OC_Log::write('core', 'OC_Image->loadFromFile, Default', OC_Log::DEBUG);
  475. break;
  476. }
  477. if($this->valid()) {
  478. $this->imageType = $iType;
  479. $this->mimeType = image_type_to_mime_type($iType);
  480. $this->filePath = $imagePath;
  481. }
  482. return $this->resource;
  483. }
  484. /**
  485. * @brief Loads an image from a string of data.
  486. * @param $str A string of image data as read from a file.
  487. * @returns An image resource or false on error
  488. */
  489. public function loadFromData($str) {
  490. if(is_resource($str)) {
  491. return false;
  492. }
  493. $this->resource = @imagecreatefromstring($str);
  494. if ($this->fileInfo) {
  495. $this->mimeType = $this->fileInfo->buffer($str);
  496. }
  497. if(is_resource($this->resource)) {
  498. imagealphablending($this->resource, false);
  499. imagesavealpha($this->resource, true);
  500. }
  501. if(!$this->resource) {
  502. OC_Log::write('core', 'OC_Image->loadFromData, couldn\'t load', OC_Log::DEBUG);
  503. return false;
  504. }
  505. return $this->resource;
  506. }
  507. /**
  508. * @brief Loads an image from a base64 encoded string.
  509. * @param $str A string base64 encoded string of image data.
  510. * @returns An image resource or false on error
  511. */
  512. public function loadFromBase64($str) {
  513. if(!is_string($str)) {
  514. return false;
  515. }
  516. $data = base64_decode($str);
  517. if($data) { // try to load from string data
  518. $this->resource = @imagecreatefromstring($data);
  519. if ($this->fileInfo) {
  520. $this->mimeType = $this->fileInfo->buffer($data);
  521. }
  522. if(!$this->resource) {
  523. OC_Log::write('core', 'OC_Image->loadFromBase64, couldn\'t load', OC_Log::DEBUG);
  524. return false;
  525. }
  526. return $this->resource;
  527. } else {
  528. return false;
  529. }
  530. }
  531. /**
  532. * Create a new image from file or URL
  533. * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm
  534. * @version 1.00
  535. * @param string $filename <p>
  536. * Path to the BMP image.
  537. * </p>
  538. * @return resource an image resource identifier on success, <b>FALSE</b> on errors.
  539. */
  540. private function imagecreatefrombmp($fileName) {
  541. if (!($fh = fopen($fileName, 'rb'))) {
  542. trigger_error('imagecreatefrombmp: Can not open ' . $fileName, E_USER_WARNING);
  543. return false;
  544. }
  545. // read file header
  546. $meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
  547. // check for bitmap
  548. if ($meta['type'] != 19778) {
  549. trigger_error('imagecreatefrombmp: ' . $fileName . ' is not a bitmap!', E_USER_WARNING);
  550. return false;
  551. }
  552. // read image header
  553. $meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
  554. // read additional 16bit header
  555. if ($meta['bits'] == 16) {
  556. $meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
  557. }
  558. // set bytes and padding
  559. $meta['bytes'] = $meta['bits'] / 8;
  560. $this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call
  561. $meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4)- floor($meta['width'] * $meta['bytes'] / 4)));
  562. if ($meta['decal'] == 4) {
  563. $meta['decal'] = 0;
  564. }
  565. // obtain imagesize
  566. if ($meta['imagesize'] < 1) {
  567. $meta['imagesize'] = $meta['filesize'] - $meta['offset'];
  568. // in rare cases filesize is equal to offset so we need to read physical size
  569. if ($meta['imagesize'] < 1) {
  570. $meta['imagesize'] = @filesize($filename) - $meta['offset'];
  571. if ($meta['imagesize'] < 1) {
  572. trigger_error('imagecreatefrombmp: Can not obtain filesize of ' . $filename . '!', E_USER_WARNING);
  573. return false;
  574. }
  575. }
  576. }
  577. // calculate colors
  578. $meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
  579. // read color palette
  580. $palette = array();
  581. if ($meta['bits'] < 16) {
  582. $palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
  583. // in rare cases the color value is signed
  584. if ($palette[1] < 0) {
  585. foreach ($palette as $i => $color) {
  586. $palette[$i] = $color + 16777216;
  587. }
  588. }
  589. }
  590. // create gd image
  591. $im = imagecreatetruecolor($meta['width'], $meta['height']);
  592. $data = fread($fh, $meta['imagesize']);
  593. $p = 0;
  594. $vide = chr(0);
  595. $y = $meta['height'] - 1;
  596. $error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
  597. // loop through the image data beginning with the lower left corner
  598. while ($y >= 0) {
  599. $x = 0;
  600. while ($x < $meta['width']) {
  601. switch ($meta['bits']) {
  602. case 32:
  603. case 24:
  604. if (!($part = substr($data, $p, 3))) {
  605. trigger_error($error, E_USER_WARNING);
  606. return $im;
  607. }
  608. $color = unpack('V', $part . $vide);
  609. break;
  610. case 16:
  611. if (!($part = substr($data, $p, 2))) {
  612. trigger_error($error, E_USER_WARNING);
  613. return $im;
  614. }
  615. $color = unpack('v', $part);
  616. $color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
  617. break;
  618. case 8:
  619. $color = unpack('n', $vide . substr($data, $p, 1));
  620. $color[1] = $palette[ $color[1] + 1 ];
  621. break;
  622. case 4:
  623. $color = unpack('n', $vide . substr($data, floor($p), 1));
  624. $color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
  625. $color[1] = $palette[ $color[1] + 1 ];
  626. break;
  627. case 1:
  628. $color = unpack('n', $vide . substr($data, floor($p), 1));
  629. switch (($p * 8) % 8) {
  630. case 0:
  631. $color[1] = $color[1] >> 7;
  632. break;
  633. case 1:
  634. $color[1] = ($color[1] & 0x40) >> 6;
  635. break;
  636. case 2:
  637. $color[1] = ($color[1] & 0x20) >> 5;
  638. break;
  639. case 3:
  640. $color[1] = ($color[1] & 0x10) >> 4;
  641. break;
  642. case 4:
  643. $color[1] = ($color[1] & 0x8) >> 3;
  644. break;
  645. case 5:
  646. $color[1] = ($color[1] & 0x4) >> 2;
  647. break;
  648. case 6:
  649. $color[1] = ($color[1] & 0x2) >> 1;
  650. break;
  651. case 7:
  652. $color[1] = ($color[1] & 0x1);
  653. break;
  654. }
  655. $color[1] = $palette[ $color[1] + 1 ];
  656. break;
  657. default:
  658. trigger_error('imagecreatefrombmp: '
  659. . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!',
  660. E_USER_WARNING);
  661. return false;
  662. }
  663. imagesetpixel($im, $x, $y, $color[1]);
  664. $x++;
  665. $p += $meta['bytes'];
  666. }
  667. $y--;
  668. $p += $meta['decal'];
  669. }
  670. fclose($fh);
  671. return $im;
  672. }
  673. /**
  674. * @brief Resizes the image preserving ratio.
  675. * @param $maxsize The maximum size of either the width or height.
  676. * @returns bool
  677. */
  678. public function resize($maxSize) {
  679. if(!$this->valid()) {
  680. OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR);
  681. return false;
  682. }
  683. $widthOrig=imageSX($this->resource);
  684. $heightOrig=imageSY($this->resource);
  685. $ratioOrig = $widthOrig/$heightOrig;
  686. if ($ratioOrig > 1) {
  687. $newHeight = round($maxSize/$ratioOrig);
  688. $newWidth = $maxSize;
  689. } else {
  690. $newWidth = round($maxSize*$ratioOrig);
  691. $newHeight = $maxSize;
  692. }
  693. $this->preciseResize(round($newWidth), round($newHeight));
  694. return true;
  695. }
  696. public function preciseResize($width, $height) {
  697. if (!$this->valid()) {
  698. OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR);
  699. return false;
  700. }
  701. $widthOrig=imageSX($this->resource);
  702. $heightOrig=imageSY($this->resource);
  703. $process = imagecreatetruecolor($width, $height);
  704. if ($process == false) {
  705. OC_Log::write('core', __METHOD__.'(): Error creating true color image', OC_Log::ERROR);
  706. imagedestroy($process);
  707. return false;
  708. }
  709. // preserve transparency
  710. if($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
  711. imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
  712. imagealphablending($process, false);
  713. imagesavealpha($process, true);
  714. }
  715. imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
  716. if ($process == false) {
  717. OC_Log::write('core', __METHOD__.'(): Error resampling process image '.$width.'x'.$height, OC_Log::ERROR);
  718. imagedestroy($process);
  719. return false;
  720. }
  721. imagedestroy($this->resource);
  722. $this->resource = $process;
  723. return true;
  724. }
  725. /**
  726. * @brief Crops the image to the middle square. If the image is already square it just returns.
  727. * @param int maximum size for the result (optional)
  728. * @returns bool for success or failure
  729. */
  730. public function centerCrop($size=0) {
  731. if(!$this->valid()) {
  732. OC_Log::write('core', 'OC_Image->centerCrop, No image loaded', OC_Log::ERROR);
  733. return false;
  734. }
  735. $widthOrig=imageSX($this->resource);
  736. $heightOrig=imageSY($this->resource);
  737. if($widthOrig === $heightOrig and $size==0) {
  738. return true;
  739. }
  740. $ratioOrig = $widthOrig/$heightOrig;
  741. $width = $height = min($widthOrig, $heightOrig);
  742. if ($ratioOrig > 1) {
  743. $x = ($widthOrig/2) - ($width/2);
  744. $y = 0;
  745. } else {
  746. $y = ($heightOrig/2) - ($height/2);
  747. $x = 0;
  748. }
  749. if($size>0) {
  750. $targetWidth=$size;
  751. $targetHeight=$size;
  752. }else{
  753. $targetWidth=$width;
  754. $targetHeight=$height;
  755. }
  756. $process = imagecreatetruecolor($targetWidth, $targetHeight);
  757. if ($process == false) {
  758. OC_Log::write('core', 'OC_Image->centerCrop. Error creating true color image', OC_Log::ERROR);
  759. imagedestroy($process);
  760. return false;
  761. }
  762. // preserve transparency
  763. if($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
  764. imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
  765. imagealphablending($process, false);
  766. imagesavealpha($process, true);
  767. }
  768. imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
  769. if ($process == false) {
  770. OC_Log::write('core',
  771. 'OC_Image->centerCrop. Error resampling process image '.$width.'x'.$height,
  772. OC_Log::ERROR);
  773. imagedestroy($process);
  774. return false;
  775. }
  776. imagedestroy($this->resource);
  777. $this->resource = $process;
  778. return true;
  779. }
  780. /**
  781. * @brief Crops the image from point $x$y with dimension $wx$h.
  782. * @param $x Horizontal position
  783. * @param $y Vertical position
  784. * @param $w Width
  785. * @param $h Height
  786. * @returns bool for success or failure
  787. */
  788. public function crop($x, $y, $w, $h) {
  789. if(!$this->valid()) {
  790. OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR);
  791. return false;
  792. }
  793. $process = imagecreatetruecolor($w, $h);
  794. if ($process == false) {
  795. OC_Log::write('core', __METHOD__.'(): Error creating true color image', OC_Log::ERROR);
  796. imagedestroy($process);
  797. return false;
  798. }
  799. imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
  800. if ($process == false) {
  801. OC_Log::write('core', __METHOD__.'(): Error resampling process image '.$w.'x'.$h, OC_Log::ERROR);
  802. imagedestroy($process);
  803. return false;
  804. }
  805. imagedestroy($this->resource);
  806. $this->resource = $process;
  807. return true;
  808. }
  809. /**
  810. * @brief Resizes the image to fit within a boundry while preserving ratio.
  811. * @param $maxWidth
  812. * @param $maxHeight
  813. * @returns bool
  814. */
  815. public function fitIn($maxWidth, $maxHeight) {
  816. if(!$this->valid()) {
  817. OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR);
  818. return false;
  819. }
  820. $widthOrig=imageSX($this->resource);
  821. $heightOrig=imageSY($this->resource);
  822. $ratio = $widthOrig/$heightOrig;
  823. $newWidth = min($maxWidth, $ratio*$maxHeight);
  824. $newHeight = min($maxHeight, $maxWidth/$ratio);
  825. $this->preciseResize(round($newWidth), round($newHeight));
  826. return true;
  827. }
  828. public function destroy() {
  829. if($this->valid()) {
  830. imagedestroy($this->resource);
  831. }
  832. $this->resource=null;
  833. }
  834. public function __destruct() {
  835. $this->destroy();
  836. }
  837. }
  838. if ( ! function_exists( 'imagebmp') ) {
  839. /**
  840. * Output a BMP image to either the browser or a file
  841. * @link http://www.ugia.cn/wp-data/imagebmp.php
  842. * @author legend <legendsky@hotmail.com>
  843. * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
  844. * @author mgutt <marc@gutt.it>
  845. * @version 1.00
  846. * @param resource $image
  847. * @param string $filename [optional] <p>The path to save the file to.</p>
  848. * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
  849. * @param int $compression [optional]
  850. * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
  851. */
  852. function imagebmp($im, $fileName='', $bit=24, $compression=0) {
  853. if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
  854. $bit = 24;
  855. }
  856. else if ($bit == 32) {
  857. $bit = 24;
  858. }
  859. $bits = pow(2, $bit);
  860. imagetruecolortopalette($im, true, $bits);
  861. $width = imagesx($im);
  862. $height = imagesy($im);
  863. $colorsNum = imagecolorstotal($im);
  864. $rgbQuad = '';
  865. if ($bit <= 8) {
  866. for ($i = 0; $i < $colorsNum; $i++) {
  867. $colors = imagecolorsforindex($im, $i);
  868. $rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
  869. }
  870. $bmpData = '';
  871. if ($compression == 0 || $bit < 8) {
  872. $compression = 0;
  873. $extra = '';
  874. $padding = 4 - ceil($width / (8 / $bit)) % 4;
  875. if ($padding % 4 != 0) {
  876. $extra = str_repeat("\0", $padding);
  877. }
  878. for ($j = $height - 1; $j >= 0; $j --) {
  879. $i = 0;
  880. while ($i < $width) {
  881. $bin = 0;
  882. $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
  883. for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
  884. $index = imagecolorat($im, $i, $j);
  885. $bin |= $index << $k;
  886. $i++;
  887. }
  888. $bmpData .= chr($bin);
  889. }
  890. $bmpData .= $extra;
  891. }
  892. }
  893. // RLE8
  894. else if ($compression == 1 && $bit == 8) {
  895. for ($j = $height - 1; $j >= 0; $j--) {
  896. $lastIndex = "\0";
  897. $sameNum = 0;
  898. for ($i = 0; $i <= $width; $i++) {
  899. $index = imagecolorat($im, $i, $j);
  900. if ($index !== $lastIndex || $sameNum > 255) {
  901. if ($sameNum != 0) {
  902. $bmpData .= chr($same_num) . chr($lastIndex);
  903. }
  904. $lastIndex = $index;
  905. $sameNum = 1;
  906. }
  907. else {
  908. $sameNum++;
  909. }
  910. }
  911. $bmpData .= "\0\0";
  912. }
  913. $bmpData .= "\0\1";
  914. }
  915. $sizeQuad = strlen($rgbQuad);
  916. $sizeData = strlen($bmpData);
  917. }
  918. else {
  919. $extra = '';
  920. $padding = 4 - ($width * ($bit / 8)) % 4;
  921. if ($padding % 4 != 0) {
  922. $extra = str_repeat("\0", $padding);
  923. }
  924. $bmpData = '';
  925. for ($j = $height - 1; $j >= 0; $j--) {
  926. for ($i = 0; $i < $width; $i++) {
  927. $index = imagecolorat($im, $i, $j);
  928. $colors = imagecolorsforindex($im, $index);
  929. if ($bit == 16) {
  930. $bin = 0 << $bit;
  931. $bin |= ($colors['red'] >> 3) << 10;
  932. $bin |= ($colors['green'] >> 3) << 5;
  933. $bin |= $colors['blue'] >> 3;
  934. $bmpData .= pack("v", $bin);
  935. }
  936. else {
  937. $bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
  938. }
  939. }
  940. $bmpData .= $extra;
  941. }
  942. $sizeQuad = 0;
  943. $sizeData = strlen($bmpData);
  944. $colorsNum = 0;
  945. }
  946. $fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
  947. $infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
  948. if ($fileName != '') {
  949. $fp = fopen($fileName, 'wb');
  950. fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
  951. fclose($fp);
  952. return true;
  953. }
  954. echo $fileHeader . $infoHeader. $rgbQuad . $bmpData;
  955. return true;
  956. }
  957. }
  958. if ( ! function_exists( 'exif_imagetype' ) ) {
  959. /**
  960. * Workaround if exif_imagetype does not exist
  961. * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
  962. * @param string $filename
  963. * @return string|boolean
  964. */
  965. function exif_imagetype ( $fileName ) {
  966. if ( ( $info = getimagesize( $fileName ) ) !== false ) {
  967. return $info[2];
  968. }
  969. return false;
  970. }
  971. }