You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

image.php 29KB

10 years ago
10 years ago
10 years ago
11 years ago
12 years ago
10 years ago
12 years ago
10 years ago
10 years ago
12 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
12 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
12 years ago
11 years ago
10 years ago
12 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
12 years ago
11 years ago
12 years ago
12 years ago
11 years ago
12 years ago
12 years ago
11 years ago
12 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  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 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. * @brief 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. imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
  829. if ($process == false) {
  830. OC_Log::write('core', __METHOD__.'(): Error resampling process image '.$w.'x'.$h, OC_Log::ERROR);
  831. imagedestroy($process);
  832. return false;
  833. }
  834. imagedestroy($this->resource);
  835. $this->resource = $process;
  836. return true;
  837. }
  838. /**
  839. * @brief Resizes the image to fit within a boundry while preserving ratio.
  840. * @param integer $maxWidth
  841. * @param integer $maxHeight
  842. * @return bool
  843. */
  844. public function fitIn($maxWidth, $maxHeight) {
  845. if(!$this->valid()) {
  846. OC_Log::write('core', __METHOD__.'(): No image loaded', OC_Log::ERROR);
  847. return false;
  848. }
  849. $widthOrig=imageSX($this->resource);
  850. $heightOrig=imageSY($this->resource);
  851. $ratio = $widthOrig/$heightOrig;
  852. $newWidth = min($maxWidth, $ratio*$maxHeight);
  853. $newHeight = min($maxHeight, $maxWidth/$ratio);
  854. $this->preciseResize(round($newWidth), round($newHeight));
  855. return true;
  856. }
  857. public function destroy() {
  858. if($this->valid()) {
  859. imagedestroy($this->resource);
  860. }
  861. $this->resource=null;
  862. }
  863. public function __destruct() {
  864. $this->destroy();
  865. }
  866. }
  867. if ( ! function_exists( 'imagebmp') ) {
  868. /**
  869. * Output a BMP image to either the browser or a file
  870. * @link http://www.ugia.cn/wp-data/imagebmp.php
  871. * @author legend <legendsky@hotmail.com>
  872. * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
  873. * @author mgutt <marc@gutt.it>
  874. * @version 1.00
  875. * @param string $fileName [optional] <p>The path to save the file to.</p>
  876. * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
  877. * @param int $compression [optional]
  878. * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
  879. */
  880. function imagebmp($im, $fileName='', $bit=24, $compression=0) {
  881. if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
  882. $bit = 24;
  883. }
  884. else if ($bit == 32) {
  885. $bit = 24;
  886. }
  887. $bits = pow(2, $bit);
  888. imagetruecolortopalette($im, true, $bits);
  889. $width = imagesx($im);
  890. $height = imagesy($im);
  891. $colorsNum = imagecolorstotal($im);
  892. $rgbQuad = '';
  893. if ($bit <= 8) {
  894. for ($i = 0; $i < $colorsNum; $i++) {
  895. $colors = imagecolorsforindex($im, $i);
  896. $rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
  897. }
  898. $bmpData = '';
  899. if ($compression == 0 || $bit < 8) {
  900. $compression = 0;
  901. $extra = '';
  902. $padding = 4 - ceil($width / (8 / $bit)) % 4;
  903. if ($padding % 4 != 0) {
  904. $extra = str_repeat("\0", $padding);
  905. }
  906. for ($j = $height - 1; $j >= 0; $j --) {
  907. $i = 0;
  908. while ($i < $width) {
  909. $bin = 0;
  910. $limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
  911. for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
  912. $index = imagecolorat($im, $i, $j);
  913. $bin |= $index << $k;
  914. $i++;
  915. }
  916. $bmpData .= chr($bin);
  917. }
  918. $bmpData .= $extra;
  919. }
  920. }
  921. // RLE8
  922. else if ($compression == 1 && $bit == 8) {
  923. for ($j = $height - 1; $j >= 0; $j--) {
  924. $lastIndex = "\0";
  925. $sameNum = 0;
  926. for ($i = 0; $i <= $width; $i++) {
  927. $index = imagecolorat($im, $i, $j);
  928. if ($index !== $lastIndex || $sameNum > 255) {
  929. if ($sameNum != 0) {
  930. $bmpData .= chr($sameNum) . chr($lastIndex);
  931. }
  932. $lastIndex = $index;
  933. $sameNum = 1;
  934. }
  935. else {
  936. $sameNum++;
  937. }
  938. }
  939. $bmpData .= "\0\0";
  940. }
  941. $bmpData .= "\0\1";
  942. }
  943. $sizeQuad = strlen($rgbQuad);
  944. $sizeData = strlen($bmpData);
  945. }
  946. else {
  947. $extra = '';
  948. $padding = 4 - ($width * ($bit / 8)) % 4;
  949. if ($padding % 4 != 0) {
  950. $extra = str_repeat("\0", $padding);
  951. }
  952. $bmpData = '';
  953. for ($j = $height - 1; $j >= 0; $j--) {
  954. for ($i = 0; $i < $width; $i++) {
  955. $index = imagecolorat($im, $i, $j);
  956. $colors = imagecolorsforindex($im, $index);
  957. if ($bit == 16) {
  958. $bin = 0 << $bit;
  959. $bin |= ($colors['red'] >> 3) << 10;
  960. $bin |= ($colors['green'] >> 3) << 5;
  961. $bin |= $colors['blue'] >> 3;
  962. $bmpData .= pack("v", $bin);
  963. }
  964. else {
  965. $bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
  966. }
  967. }
  968. $bmpData .= $extra;
  969. }
  970. $sizeQuad = 0;
  971. $sizeData = strlen($bmpData);
  972. $colorsNum = 0;
  973. }
  974. $fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
  975. $infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
  976. if ($fileName != '') {
  977. $fp = fopen($fileName, 'wb');
  978. fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
  979. fclose($fp);
  980. return true;
  981. }
  982. echo $fileHeader . $infoHeader. $rgbQuad . $bmpData;
  983. return true;
  984. }
  985. }
  986. if ( ! function_exists( 'exif_imagetype' ) ) {
  987. /**
  988. * Workaround if exif_imagetype does not exist
  989. * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
  990. * @param string $fileName
  991. * @return string|boolean
  992. */
  993. function exif_imagetype ( $fileName ) {
  994. if ( ( $info = getimagesize( $fileName ) ) !== false ) {
  995. return $info[2];
  996. }
  997. return false;
  998. }
  999. }