image.php 28 KB

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