image.php 30 KB

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