PageRenderTime 172ms CodeModel.GetById 81ms app.highlight 49ms RepoModel.GetById 32ms app.codeStats 0ms

/lib/image.php

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