image.php 27 KB

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