Completed
Push — stable8.1 ( 7a1b8f...6080a3 )
by
unknown
107:52
created

image.php ➔ imagebmp()   D

Complexity

Conditions 24
Paths 60

Size

Total Lines 100
Code Lines 81

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 600
Metric Value
cc 24
eloc 81
nc 60
nop 4
dl 0
loc 100
ccs 0
cts 94
cp 0
crap 600
rs 4.5989

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @author Andreas Fischer <[email protected]>
4
 * @author Bartek Przybylski <[email protected]>
5
 * @author Bart Visscher <[email protected]>
6
 * @author Björn Schießle <[email protected]>
7
 * @author Byron Marohn <[email protected]>
8
 * @author Christopher Schäpers <[email protected]>
9
 * @author Georg Ehrke <[email protected]>
10
 * @author j-ed <[email protected]>
11
 * @author Joas Schilling <[email protected]>
12
 * @author Johannes Willnecker <[email protected]>
13
 * @author Jörn Friedrich Dreyer <[email protected]>
14
 * @author Morris Jobke <[email protected]>
15
 * @author Olivier Paroz <[email protected]>
16
 * @author Robin Appelman <[email protected]>
17
 * @author Scrutinizer Auto-Fixer <[email protected]>
18
 * @author Thomas Müller <[email protected]>
19
 * @author Thomas Tanghus <[email protected]>
20
 *
21
 * @copyright Copyright (c) 2015, ownCloud, Inc.
22
 * @license AGPL-3.0
23
 *
24
 * This code is free software: you can redistribute it and/or modify
25
 * it under the terms of the GNU Affero General Public License, version 3,
26
 * as published by the Free Software Foundation.
27
 *
28
 * This program is distributed in the hope that it will be useful,
29
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31
 * GNU Affero General Public License for more details.
32
 *
33
 * You should have received a copy of the GNU Affero General Public License, version 3,
34
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
35
 *
36
 */
37
38
/**
39
 * Class for basic image manipulation
40
 */
41
class OC_Image implements \OCP\IImage {
42
	protected $resource = false; // tmp resource.
43
	protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
44
	protected $mimeType = "image/png"; // Default to png
45
	protected $bitDepth = 24;
46
	protected $filePath = null;
47
48
	private $fileInfo;
49
50
	/**
51
	 * @var \OCP\ILogger
52
	 */
53
	private $logger;
54
55
	/**
56
	 * Get mime type for an image file.
57
	 *
58
	 * @param string|null $filePath The path to a local image file.
59
	 * @return string The mime type if the it could be determined, otherwise an empty string.
60
	 */
61 4
	static public function getMimeTypeForFile($filePath) {
62
		// exif_imagetype throws "read error!" if file is less than 12 byte
63 4
		if ($filePath !== null && filesize($filePath) > 11) {
64 4
			$imageType = exif_imagetype($filePath);
65 4
		} else {
66 1
			$imageType = false;
67
		}
68 4
		return $imageType ? image_type_to_mime_type($imageType) : '';
69
	}
70
71
	/**
72
	 * Constructor.
73
	 *
74
	 * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by
75
	 * an imagecreate* function.
76
	 * @param \OCP\ILogger $logger
77
	 */
78 301
	public function __construct($imageRef = null, $logger = null) {
79 301
		$this->logger = $logger;
80 301
		if (is_null($logger)) {
81 301
			$this->logger = \OC::$server->getLogger();
82 301
		}
83
84 301
		if (!extension_loaded('gd') || !function_exists('gd_info')) {
85
			$this->logger->error(__METHOD__ . '(): GD module not installed', array('app' => 'core'));
86
			return false;
0 ignored issues
show
Bug introduced by
Constructors do not have meaningful return values, anything that is returned from here is discarded. Are you sure this is correct?
Loading history...
87
		}
88
89 301
		if (\OC_Util::fileInfoLoaded()) {
90 301
			$this->fileInfo = new finfo(FILEINFO_MIME_TYPE);
91 301
		}
92
93 301
		if (!is_null($imageRef)) {
94 66
			$this->load($imageRef);
95 66
		}
96 301
	}
97
98
	/**
99
	 * Determine whether the object contains an image resource.
100
	 *
101
	 * @return bool
102
	 */
103 220
	public function valid() { // apparently you can't name a method 'empty'...
104 220
		return is_resource($this->resource);
105
	}
106
107
	/**
108
	 * Returns the MIME type of the image or an empty string if no image is loaded.
109
	 *
110
	 * @return string
111
	 */
112 9
	public function mimeType() {
113 9
		return $this->valid() ? $this->mimeType : '';
114
	}
115
116
	/**
117
	 * Returns the width of the image or -1 if no image is loaded.
118
	 *
119
	 * @return int
120
	 */
121 199
	public function width() {
122 199
		return $this->valid() ? imagesx($this->resource) : -1;
123
	}
124
125
	/**
126
	 * Returns the height of the image or -1 if no image is loaded.
127
	 *
128
	 * @return int
129
	 */
130 199
	public function height() {
131 199
		return $this->valid() ? imagesy($this->resource) : -1;
132
	}
133
134
	/**
135
	 * Returns the width when the image orientation is top-left.
136
	 *
137
	 * @return int
138
	 */
139 View Code Duplication
	public function widthTopLeft() {
140
		$o = $this->getOrientation();
141
		$this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, array('app' => 'core'));
142
		switch ($o) {
143
			case -1:
144
			case 1:
145
			case 2: // Not tested
146
			case 3:
147
			case 4: // Not tested
148
				return $this->width();
149
			case 5: // Not tested
150
			case 6:
151
			case 7: // Not tested
152
			case 8:
153
				return $this->height();
154
		}
155
		return $this->width();
156
	}
157
158
	/**
159
	 * Returns the height when the image orientation is top-left.
160
	 *
161
	 * @return int
162
	 */
163 View Code Duplication
	public function heightTopLeft() {
164
		$o = $this->getOrientation();
165
		$this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, array('app' => 'core'));
166
		switch ($o) {
167
			case -1:
168
			case 1:
169
			case 2: // Not tested
170
			case 3:
171
			case 4: // Not tested
172
				return $this->height();
173
			case 5: // Not tested
174
			case 6:
175
			case 7: // Not tested
176
			case 8:
177
				return $this->width();
178
		}
179
		return $this->height();
180
	}
181
182
	/**
183
	 * Outputs the image.
184
	 *
185
	 * @param string $mimeType
186
	 * @return bool
187
	 */
188
	public function show($mimeType = null) {
189
		if ($mimeType === null) {
190
			$mimeType = $this->mimeType();
191
		}
192
		header('Content-Type: ' . $mimeType);
193
		return $this->_output(null, $mimeType);
194
	}
195
196
	/**
197
	 * Saves the image.
198
	 *
199
	 * @param string $filePath
200
	 * @param string $mimeType
201
	 * @return bool
202
	 */
203
204 4
	public function save($filePath = null, $mimeType = null) {
205 4
		if ($mimeType === null) {
206 1
			$mimeType = $this->mimeType();
207 1
		}
208 4
		if ($filePath === null && $this->filePath === null) {
209
			$this->logger->error(__METHOD__ . '(): called with no path.', array('app' => 'core'));
210
			return false;
211 4
		} elseif ($filePath === null && $this->filePath !== null) {
212
			$filePath = $this->filePath;
213
		}
214 4
		return $this->_output($filePath, $mimeType);
215
	}
216
217
	/**
218
	 * Outputs/saves the image.
219
	 *
220
	 * @param string $filePath
221
	 * @param string $mimeType
222
	 * @return bool
223
	 * @throws Exception
224
	 */
225 4
	private function _output($filePath = null, $mimeType = null) {
226 4
		if ($filePath) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $filePath of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
227 4
			if (!file_exists(dirname($filePath)))
228 4
				mkdir(dirname($filePath), 0777, true);
229 4
			if (!is_writable(dirname($filePath))) {
230
				$this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', array('app' => 'core'));
231
				return false;
232 4
			} elseif (is_writable(dirname($filePath)) && file_exists($filePath) && !is_writable($filePath)) {
233
				$this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', array('app' => 'core'));
234
				return false;
235
			}
236 4
		}
237 4
		if (!$this->valid()) {
238
			return false;
239
		}
240
241 4
		$imageType = $this->imageType;
242 4
		if ($mimeType !== null) {
243
			switch ($mimeType) {
244 4
				case 'image/gif':
245 1
					$imageType = IMAGETYPE_GIF;
246 1
					break;
247 3
				case 'image/jpeg':
248 2
					$imageType = IMAGETYPE_JPEG;
249 2
					break;
250 2
				case 'image/png':
251 2
					$imageType = IMAGETYPE_PNG;
252 2
					break;
253
				case 'image/x-xbitmap':
254
					$imageType = IMAGETYPE_XBM;
255
					break;
256
				case 'image/bmp':
257
				case 'image/x-ms-bmp':
258
					$imageType = IMAGETYPE_BMP;
259
					break;
260
				default:
261
					throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format');
262
			}
263 4
		}
264
265
		switch ($imageType) {
266 4
			case IMAGETYPE_GIF:
267 1
				$retVal = imagegif($this->resource, $filePath);
268 1
				break;
269 3
			case IMAGETYPE_JPEG:
270 2
				$retVal = imagejpeg($this->resource, $filePath);
271 2
				break;
272 2
			case IMAGETYPE_PNG:
273 2
				$retVal = imagepng($this->resource, $filePath);
274 2
				break;
275
			case IMAGETYPE_XBM:
276
				if (function_exists('imagexbm')) {
277
					$retVal = imagexbm($this->resource, $filePath);
278
				} else {
279
					throw new Exception('\OC_Image::_output(): imagexbm() is not supported.');
280
				}
281
282
				break;
283
			case IMAGETYPE_WBMP:
284
				$retVal = imagewbmp($this->resource, $filePath);
285
				break;
286
			case IMAGETYPE_BMP:
287
				$retVal = imagebmp($this->resource, $filePath, $this->bitDepth);
288
				break;
289
			default:
290
				$retVal = imagepng($this->resource, $filePath);
291
		}
292 4
		return $retVal;
293
	}
294
295
	/**
296
	 * Prints the image when called as $image().
297
	 */
298
	public function __invoke() {
299
		return $this->show();
300
	}
301
302
	/**
303
	 * @return resource Returns the image resource in any.
304
	 */
305 30
	public function resource() {
306 30
		return $this->resource;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->resource; (boolean) is incompatible with the return type declared by the interface OCP\IImage::resource of type resource.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
307
	}
308
309
	/**
310
	 * @return null|string Returns the raw image data.
311
	 */
312 175
	public function data() {
313 175
		if (!$this->valid()) {
314 1
			return null;
315
		}
316 174
		ob_start();
317 174
		switch ($this->mimeType) {
318 174
			case "image/png":
319 103
				$res = imagepng($this->resource);
320 103
				break;
321 89
			case "image/jpeg":
322 49
				$res = imagejpeg($this->resource);
323 49
				break;
324 42
			case "image/gif":
325 42
				$res = imagegif($this->resource);
326 42
				break;
327
			default:
328
				$res = imagepng($this->resource);
329
				$this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', array('app' => 'core'));
330
				break;
331 174
		}
332 174
		if (!$res) {
333
			$this->logger->error('OC_Image->data. Error getting image data.', array('app' => 'core'));
334
		}
335 174
		return ob_get_clean();
336
	}
337
338
	/**
339
	 * @return string - base64 encoded, which is suitable for embedding in a VCard.
340
	 */
341 1
	function __toString() {
342 1
		return base64_encode($this->data());
343
	}
344
345
	/**
346
	 * (I'm open for suggestions on better method name ;)
347
	 * Get the orientation based on EXIF data.
348
	 *
349
	 * @return int The orientation or -1 if no EXIF data is available.
350
	 */
351 128
	public function getOrientation() {
352 128
		if ($this->imageType !== IMAGETYPE_JPEG) {
353 81
			$this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', array('app' => 'core'));
354 81
			return -1;
355
		}
356 47
		if (!is_callable('exif_read_data')) {
357
			$this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', array('app' => 'core'));
358
			return -1;
359
		}
360 47
		if (!$this->valid()) {
361
			$this->logger->debug('OC_Image->fixOrientation() No image loaded.', array('app' => 'core'));
362
			return -1;
363
		}
364 47
		if (is_null($this->filePath) || !is_readable($this->filePath)) {
365
			$this->logger->debug('OC_Image->fixOrientation() No readable file path set.', array('app' => 'core'));
366
			return -1;
367
		}
368 47
		$exif = @exif_read_data($this->filePath, 'IFD0');
369 47
		if (!$exif) {
370 47
			return -1;
371
		}
372
		if (!isset($exif['Orientation'])) {
373
			return -1;
374
		}
375
		return $exif['Orientation'];
376
	}
377
378
	/**
379
	 * (I'm open for suggestions on better method name ;)
380
	 * Fixes orientation based on EXIF data.
381
	 *
382
	 * @return bool.
0 ignored issues
show
Documentation introduced by
The doc-type bool. could not be parsed: Unknown type name "bool." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
383
	 */
384 128
	public function fixOrientation() {
385 128
		$o = $this->getOrientation();
386 128
		$this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, array('app' => 'core'));
387 128
		$rotate = 0;
388 128
		$flip = false;
389
		switch ($o) {
390 128
			case -1:
391 128
				return false; //Nothing to fix
392
			case 1:
393
				$rotate = 0;
394
				break;
395
			case 2:
396
				$rotate = 0;
397
				$flip = true;
398
				break;
399
			case 3:
400
				$rotate = 180;
401
				break;
402
			case 4:
403
				$rotate = 180;
404
				$flip = true;
405
				break;
406
			case 5:
407
				$rotate = 90;
408
				$flip = true;
409
				break;
410
			case 6:
411
				$rotate = 270;
412
				break;
413
			case 7:
414
				$rotate = 270;
415
				$flip = true;
416
				break;
417
			case 8:
418
				$rotate = 90;
419
				break;
420
		}
421
		if($flip && function_exists('imageflip')) {
422
			imageflip($this->resource, IMG_FLIP_HORIZONTAL);
423
		}
424
		if ($rotate) {
425
			$res = imagerotate($this->resource, $rotate, 0);
426
			if ($res) {
427
				if (imagealphablending($res, true)) {
428
					if (imagesavealpha($res, true)) {
429
						imagedestroy($this->resource);
430
						$this->resource = $res;
431
						return true;
432
					} else {
433
						$this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', array('app' => 'core'));
434
						return false;
435
					}
436
				} else {
437
					$this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', array('app' => 'core'));
438
					return false;
439
				}
440
			} else {
441
				$this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', array('app' => 'core'));
442
				return false;
443
			}
444
		}
445
		return false;
446
	}
447
448
	/**
449
	 * Loads an image from a local file, a base64 encoded string or a resource created by an imagecreate* function.
450
	 *
451
	 * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by an imagecreate* function or a file resource (file handle    ).
452
	 * @return resource|false An image resource or false on error
453
	 */
454 66
	public function load($imageRef) {
455 66
		if (is_resource($imageRef)) {
456 37
			if (get_resource_type($imageRef) == 'gd') {
457 37
				$this->resource = $imageRef;
0 ignored issues
show
Documentation Bug introduced by
It seems like $imageRef of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
458 37
				return $this->resource;
459
			} elseif (in_array(get_resource_type($imageRef), array('file', 'stream'))) {
460
				return $this->loadFromFileHandle($imageRef);
461
			}
462 30
		} elseif ($this->loadFromBase64($imageRef) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $imageRef defined by parameter $imageRef on line 454 can also be of type resource; however, OC_Image::loadFromBase64() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
463 8
			return $this->resource;
464 30
		} elseif ($this->loadFromFile($imageRef) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $imageRef defined by parameter $imageRef on line 454 can also be of type resource; however, OC_Image::loadFromFile() does only seem to accept boolean|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
465 27
			return $this->resource;
466 15
		} elseif ($this->loadFromData($imageRef) !== false) {
0 ignored issues
show
Bug introduced by
It seems like $imageRef defined by parameter $imageRef on line 454 can also be of type resource; however, OC_Image::loadFromData() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
467 14
			return $this->resource;
468
		}
469 1
		$this->logger->debug(__METHOD__ . '(): could not load anything. Giving up!', array('app' => 'core'));
470 1
		return false;
471
	}
472
473
	/**
474
	 * Loads an image from an open file handle.
475
	 * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again.
476
	 *
477
	 * @param resource $handle
478
	 * @return resource|false An image resource or false on error
479
	 */
480 32
	public function loadFromFileHandle($handle) {
481 32
		$contents = stream_get_contents($handle);
482 32
		if ($this->loadFromData($contents)) {
483 32
			return $this->resource;
484
		}
485
		return false;
486
	}
487
488
	/**
489
	 * Loads an image from a local file.
490
	 *
491
	 * @param bool|string $imagePath The path to a local file.
492
	 * @return bool|resource An image resource or false on error
493
	 */
494 158
	public function loadFromFile($imagePath = false) {
495
		// exif_imagetype throws "read error!" if file is less than 12 byte
496 158
		if (!@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) {
497 15
			return false;
498
		}
499 155
		$iType = exif_imagetype($imagePath);
0 ignored issues
show
Bug introduced by
It seems like $imagePath defined by parameter $imagePath on line 494 can also be of type boolean; however, exif_imagetype() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
500
		switch ($iType) {
501 155 View Code Duplication
			case IMAGETYPE_GIF:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
502 45
				if (imagetypes() & IMG_GIF) {
503 45
					$this->resource = imagecreatefromgif($imagePath);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatefromgif($imagePath) of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
504
					// Preserve transparency
505 45
					imagealphablending($this->resource, true);
506 45
					imagesavealpha($this->resource, true);
507 45
				} else {
508
					$this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, array('app' => 'core'));
509
				}
510 45
				break;
511 112
			case IMAGETYPE_JPEG:
512 54 View Code Duplication
				if (imagetypes() & IMG_JPG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
513 54
					$this->resource = imagecreatefromjpeg($imagePath);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatefromjpeg($imagePath) of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
514 54
				} else {
515
					$this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, array('app' => 'core'));
516
				}
517 54
				break;
518 60 View Code Duplication
			case IMAGETYPE_PNG:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
519 60
				if (imagetypes() & IMG_PNG) {
520 60
					$this->resource = imagecreatefrompng($imagePath);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatefrompng($imagePath) of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
521
					// Preserve transparency
522 60
					imagealphablending($this->resource, true);
523 60
					imagesavealpha($this->resource, true);
524 60
				} else {
525
					$this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, array('app' => 'core'));
526
				}
527 60
				break;
528
			case IMAGETYPE_XBM:
529 View Code Duplication
				if (imagetypes() & IMG_XPM) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
530
					$this->resource = imagecreatefromxbm($imagePath);
531
				} else {
532
					$this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, array('app' => 'core'));
533
				}
534
				break;
535
			case IMAGETYPE_WBMP:
536 View Code Duplication
				if (imagetypes() & IMG_WBMP) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
537
					$this->resource = imagecreatefromwbmp($imagePath);
0 ignored issues
show
Documentation Bug introduced by
It seems like imagecreatefromwbmp($imagePath) of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
538
				} else {
539
					$this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, array('app' => 'core'));
540
				}
541
				break;
542
			case IMAGETYPE_BMP:
543
				$this->resource = $this->imagecreatefrombmp($imagePath);
0 ignored issues
show
Bug introduced by
It seems like $imagePath defined by parameter $imagePath on line 494 can also be of type boolean; however, OC_Image::imagecreatefrombmp() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Documentation Bug introduced by
It seems like $this->imagecreatefrombmp($imagePath) can also be of type resource. However, the property $resource is declared as type boolean. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
544
				break;
545
			/*
546
			case IMAGETYPE_TIFF_II: // (intel byte order)
547
				break;
548
			case IMAGETYPE_TIFF_MM: // (motorola byte order)
549
				break;
550
			case IMAGETYPE_JPC:
551
				break;
552
			case IMAGETYPE_JP2:
553
				break;
554
			case IMAGETYPE_JPX:
555
				break;
556
			case IMAGETYPE_JB2:
557
				break;
558
			case IMAGETYPE_SWC:
559
				break;
560
			case IMAGETYPE_IFF:
561
				break;
562
			case IMAGETYPE_ICO:
563
				break;
564
			case IMAGETYPE_SWF:
565
				break;
566
			case IMAGETYPE_PSD:
567
				break;
568
			*/
569
			default:
570
571
				// this is mostly file created from encrypted file
572
				$this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagePath)));
0 ignored issues
show
Bug introduced by
It seems like $imagePath defined by parameter $imagePath on line 494 can also be of type boolean; however, OC\Files\Filesystem::getLocalPath() does only seem to accept string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Documentation Bug introduced by
It seems like imagecreatefromstring(\O...LocalPath($imagePath))) of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
573
				$iType = IMAGETYPE_PNG;
574
				$this->logger->debug('OC_Image->loadFromFile, Default', array('app' => 'core'));
575
				break;
576
		}
577 155
		if ($this->valid()) {
578 155
			$this->imageType = $iType;
579 155
			$this->mimeType = image_type_to_mime_type($iType);
580 155
			$this->filePath = $imagePath;
581 155
		}
582 155
		return $this->resource;
583
	}
584
585
	/**
586
	 * Loads an image from a string of data.
587
	 *
588
	 * @param string $str A string of image data as read from a file.
589
	 * @return bool|resource An image resource or false on error
590
	 */
591 91
	public function loadFromData($str) {
592 91
		if (is_resource($str)) {
593
			return false;
594
		}
595 91
		$this->resource = @imagecreatefromstring($str);
596 91
		if ($this->fileInfo) {
597 91
			$this->mimeType = $this->fileInfo->buffer($str);
598 91
		}
599 91
		if (is_resource($this->resource)) {
600 90
			imagealphablending($this->resource, false);
601 90
			imagesavealpha($this->resource, true);
602 90
		}
603
604 91
		if (!$this->resource) {
605 1
			$this->logger->debug('OC_Image->loadFromFile, could not load', array('app' => 'core'));
606 1
			return false;
607
		}
608 90
		return $this->resource;
609
	}
610
611
	/**
612
	 * Loads an image from a base64 encoded string.
613
	 *
614
	 * @param string $str A string base64 encoded string of image data.
615
	 * @return bool|resource An image resource or false on error
616
	 */
617 30
	public function loadFromBase64($str) {
618 30
		if (!is_string($str)) {
619
			return false;
620
		}
621 30
		$data = base64_decode($str);
622 30
		if ($data) { // try to load from string data
623 27
			$this->resource = @imagecreatefromstring($data);
624 27
			if ($this->fileInfo) {
625 27
				$this->mimeType = $this->fileInfo->buffer($data);
626 27
			}
627 27
			if (!$this->resource) {
628 27
				$this->logger->debug('OC_Image->loadFromBase64, could not load', array('app' => 'core'));
629 27
				return false;
630
			}
631 8
			return $this->resource;
632
		} else {
633 12
			return false;
634
		}
635
	}
636
637
	/**
638
	 * Create a new image from file or URL
639
	 *
640
	 * @link http://www.programmierer-forum.de/function-imagecreatefrombmp-laeuft-mit-allen-bitraten-t143137.htm
641
	 * @version 1.00
642
	 * @param string $fileName <p>
643
	 * Path to the BMP image.
644
	 * </p>
645
	 * @return bool|resource an image resource identifier on success, <b>FALSE</b> on errors.
646
	 */
647
	private function imagecreatefrombmp($fileName) {
648 View Code Duplication
		if (!($fh = fopen($fileName, 'rb'))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
649
			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName, array('app' => 'core'));
650
			return false;
651
		}
652
		// read file header
653
		$meta = unpack('vtype/Vfilesize/Vreserved/Voffset', fread($fh, 14));
654
		// check for bitmap
655 View Code Duplication
		if ($meta['type'] != 19778) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
656
			fclose($fh);
657
			$this->logger->warning('imagecreatefrombmp: Can not open ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
658
			return false;
659
		}
660
		// read image header
661
		$meta += unpack('Vheadersize/Vwidth/Vheight/vplanes/vbits/Vcompression/Vimagesize/Vxres/Vyres/Vcolors/Vimportant', fread($fh, 40));
662
		// read additional 16bit header
663
		if ($meta['bits'] == 16) {
664
			$meta += unpack('VrMask/VgMask/VbMask', fread($fh, 12));
665
		}
666
		// set bytes and padding
667
		$meta['bytes'] = $meta['bits'] / 8;
668
		$this->bitDepth = $meta['bits']; //remember the bit depth for the imagebmp call
669
		$meta['decal'] = 4 - (4 * (($meta['width'] * $meta['bytes'] / 4) - floor($meta['width'] * $meta['bytes'] / 4)));
670
		if ($meta['decal'] == 4) {
671
			$meta['decal'] = 0;
672
		}
673
		// obtain imagesize
674
		if ($meta['imagesize'] < 1) {
675
			$meta['imagesize'] = $meta['filesize'] - $meta['offset'];
676
			// in rare cases filesize is equal to offset so we need to read physical size
677
			if ($meta['imagesize'] < 1) {
678
				$meta['imagesize'] = @filesize($fileName) - $meta['offset'];
679 View Code Duplication
				if ($meta['imagesize'] < 1) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
680
					fclose($fh);
681
					$this->logger->warning('imagecreatefrombmp: Can not obtain file size of ' . $fileName . ' is not a bitmap!', array('app' => 'core'));
682
					return false;
683
				}
684
			}
685
		}
686
		// calculate colors
687
		$meta['colors'] = !$meta['colors'] ? pow(2, $meta['bits']) : $meta['colors'];
688
		// read color palette
689
		$palette = array();
690
		if ($meta['bits'] < 16) {
691
			$palette = unpack('l' . $meta['colors'], fread($fh, $meta['colors'] * 4));
692
			// in rare cases the color value is signed
693
			if ($palette[1] < 0) {
694
				foreach ($palette as $i => $color) {
695
					$palette[$i] = $color + 16777216;
696
				}
697
			}
698
		}
699
		// create gd image
700
		$im = imagecreatetruecolor($meta['width'], $meta['height']);
701
		if ($im == false) {
702
			fclose($fh);
703
			$this->logger->warning(
704
				'imagecreatefrombmp: imagecreatetruecolor failed for file "' . $fileName . '" with dimensions ' . $meta['width'] . 'x' . $meta['height'],
705
				array('app' => 'core'));
706
			return false;
707
		}
708
709
		$data = fread($fh, $meta['imagesize']);
710
		$p = 0;
711
		$vide = chr(0);
712
		$y = $meta['height'] - 1;
713
		$error = 'imagecreatefrombmp: ' . $fileName . ' has not enough data!';
714
		// loop through the image data beginning with the lower left corner
715
		while ($y >= 0) {
716
			$x = 0;
717
			while ($x < $meta['width']) {
718
				switch ($meta['bits']) {
719
					case 32:
720
					case 24:
721 View Code Duplication
						if (!($part = substr($data, $p, 3))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
722
							$this->logger->warning($error, array('app' => 'core'));
723
							return $im;
724
						}
725
						$color = unpack('V', $part . $vide);
726
						break;
727
					case 16:
728 View Code Duplication
						if (!($part = substr($data, $p, 2))) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
729
							fclose($fh);
730
							$this->logger->warning($error, array('app' => 'core'));
731
							return $im;
732
						}
733
						$color = unpack('v', $part);
734
						$color[1] = (($color[1] & 0xf800) >> 8) * 65536 + (($color[1] & 0x07e0) >> 3) * 256 + (($color[1] & 0x001f) << 3);
735
						break;
736
					case 8:
737
						$color = unpack('n', $vide . substr($data, $p, 1));
738
						$color[1] = $palette[$color[1] + 1];
739
						break;
740
					case 4:
741
						$color = unpack('n', $vide . substr($data, floor($p), 1));
742
						$color[1] = ($p * 2) % 2 == 0 ? $color[1] >> 4 : $color[1] & 0x0F;
743
						$color[1] = $palette[$color[1] + 1];
744
						break;
745
					case 1:
746
						$color = unpack('n', $vide . substr($data, floor($p), 1));
747
						switch (($p * 8) % 8) {
748
							case 0:
749
								$color[1] = $color[1] >> 7;
750
								break;
751
							case 1:
752
								$color[1] = ($color[1] & 0x40) >> 6;
753
								break;
754
							case 2:
755
								$color[1] = ($color[1] & 0x20) >> 5;
756
								break;
757
							case 3:
758
								$color[1] = ($color[1] & 0x10) >> 4;
759
								break;
760
							case 4:
761
								$color[1] = ($color[1] & 0x8) >> 3;
762
								break;
763
							case 5:
764
								$color[1] = ($color[1] & 0x4) >> 2;
765
								break;
766
							case 6:
767
								$color[1] = ($color[1] & 0x2) >> 1;
768
								break;
769
							case 7:
770
								$color[1] = ($color[1] & 0x1);
771
								break;
772
						}
773
						$color[1] = $palette[$color[1] + 1];
774
						break;
775
					default:
776
						fclose($fh);
777
						$this->logger->warning('imagecreatefrombmp: ' . $fileName . ' has ' . $meta['bits'] . ' bits and this is not supported!', array('app' => 'core'));
778
						return false;
779
				}
780
				imagesetpixel($im, $x, $y, $color[1]);
781
				$x++;
782
				$p += $meta['bytes'];
783
			}
784
			$y--;
785
			$p += $meta['decal'];
786
		}
787
		fclose($fh);
788
		return $im;
789
	}
790
791
	/**
792
	 * Resizes the image preserving ratio.
793
	 *
794
	 * @param integer $maxSize The maximum size of either the width or height.
795
	 * @return bool
796
	 */
797 4
	public function resize($maxSize) {
798 4 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
799
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
800
			return false;
801
		}
802 4
		$widthOrig = imageSX($this->resource);
803 4
		$heightOrig = imageSY($this->resource);
804 4
		$ratioOrig = $widthOrig / $heightOrig;
805
806 4
		if ($ratioOrig > 1) {
807 2
			$newHeight = round($maxSize / $ratioOrig);
808 2
			$newWidth = $maxSize;
809 2
		} else {
810 4
			$newWidth = round($maxSize * $ratioOrig);
811 4
			$newHeight = $maxSize;
812
		}
813
814 4
		$this->preciseResize(round($newWidth), round($newHeight));
815 4
		return true;
816
	}
817
818
	/**
819
	 * @param int $width
820
	 * @param int $height
821
	 * @return bool
822
	 */
823 183
	public function preciseResize($width, $height) {
824 183 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
825
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
826
			return false;
827
		}
828 183
		$widthOrig = imageSX($this->resource);
829 183
		$heightOrig = imageSY($this->resource);
830 183
		$process = imagecreatetruecolor($width, $height);
831
832 183
		if ($process == false) {
833
			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
834
			imagedestroy($process);
835
			return false;
836
		}
837
838
		// preserve transparency
839 183 View Code Duplication
		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
840 143
			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
841 143
			imagealphablending($process, false);
842 143
			imagesavealpha($process, true);
843 143
		}
844
845 183
		imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig);
846 183
		if ($process == false) {
847
			$this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core'));
848
			imagedestroy($process);
849
			return false;
850
		}
851 183
		imagedestroy($this->resource);
852 183
		$this->resource = $process;
0 ignored issues
show
Documentation Bug introduced by
It seems like $process of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
853 183
		return true;
854
	}
855
856
	/**
857
	 * Crops the image to the middle square. If the image is already square it just returns.
858
	 *
859
	 * @param int $size maximum size for the result (optional)
860
	 * @return bool for success or failure
861
	 */
862 1
	public function centerCrop($size = 0) {
863 1
		if (!$this->valid()) {
864
			$this->logger->error('OC_Image->centerCrop, No image loaded', array('app' => 'core'));
865
			return false;
866
		}
867 1
		$widthOrig = imageSX($this->resource);
868 1
		$heightOrig = imageSY($this->resource);
869 1
		if ($widthOrig === $heightOrig and $size == 0) {
870 1
			return true;
871
		}
872 1
		$ratioOrig = $widthOrig / $heightOrig;
873 1
		$width = $height = min($widthOrig, $heightOrig);
874
875 1
		if ($ratioOrig > 1) {
876 1
			$x = ($widthOrig / 2) - ($width / 2);
877 1
			$y = 0;
878 1
		} else {
879 1
			$y = ($heightOrig / 2) - ($height / 2);
880 1
			$x = 0;
881
		}
882 1
		if ($size > 0) {
883 1
			$targetWidth = $size;
884 1
			$targetHeight = $size;
885 1
		} else {
886 1
			$targetWidth = $width;
887 1
			$targetHeight = $height;
888
		}
889 1
		$process = imagecreatetruecolor($targetWidth, $targetHeight);
890 1
		if ($process == false) {
891
			$this->logger->error('OC_Image->centerCrop, Error creating true color image', array('app' => 'core'));
892
			imagedestroy($process);
893
			return false;
894
		}
895
896
		// preserve transparency
897 1 View Code Duplication
		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
898 1
			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
899 1
			imagealphablending($process, false);
900 1
			imagesavealpha($process, true);
901 1
		}
902
903 1
		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height);
904 1 View Code Duplication
		if ($process == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
905
			$this->logger->error('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, array('app' => 'core'));
906
			imagedestroy($process);
907
			return false;
908
		}
909 1
		imagedestroy($this->resource);
910 1
		$this->resource = $process;
0 ignored issues
show
Documentation Bug introduced by
It seems like $process of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
911 1
		return true;
912
	}
913
914
	/**
915
	 * Crops the image from point $x$y with dimension $wx$h.
916
	 *
917
	 * @param int $x Horizontal position
918
	 * @param int $y Vertical position
919
	 * @param int $w Width
920
	 * @param int $h Height
921
	 * @return bool for success or failure
922
	 */
923 155
	public function crop($x, $y, $w, $h) {
924 155 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
925
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
926
			return false;
927
		}
928 155
		$process = imagecreatetruecolor($w, $h);
929 155
		if ($process == false) {
930
			$this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core'));
931
			imagedestroy($process);
932
			return false;
933
		}
934
935
		// preserve transparency
936 155 View Code Duplication
		if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
937 123
			imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127));
938 123
			imagealphablending($process, false);
939 123
			imagesavealpha($process, true);
940 123
		}
941
942 155
		imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h);
943 155 View Code Duplication
		if ($process == false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
944
			$this->logger->error(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, array('app' => 'core'));
945
			imagedestroy($process);
946
			return false;
947
		}
948 155
		imagedestroy($this->resource);
949 155
		$this->resource = $process;
0 ignored issues
show
Documentation Bug introduced by
It seems like $process of type resource is incompatible with the declared type boolean of property $resource.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
950 155
		return true;
951
	}
952
953
	/**
954
	 * Resizes the image to fit within a boundary while preserving ratio.
955
	 *
956
	 * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up
957
	 *
958
	 * @param integer $maxWidth
959
	 * @param integer $maxHeight
960
	 * @return bool
961
	 */
962 95
	public function fitIn($maxWidth, $maxHeight) {
963 95 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
964
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
965
			return false;
966
		}
967 95
		$widthOrig = imageSX($this->resource);
968 95
		$heightOrig = imageSY($this->resource);
969 95
		$ratio = $widthOrig / $heightOrig;
970
971 95
		$newWidth = min($maxWidth, $ratio * $maxHeight);
972 95
		$newHeight = min($maxHeight, $maxWidth / $ratio);
973
974 95
		$this->preciseResize(round($newWidth), round($newHeight));
975 95
		return true;
976
	}
977
978
	/**
979
	 * Shrinks larger images to fit within specified boundaries while preserving ratio.
980
	 *
981
	 * @param integer $maxWidth
982
	 * @param integer $maxHeight
983
	 * @return bool
984
	 */
985 138
	public function scaleDownToFit($maxWidth, $maxHeight) {
986 138 View Code Duplication
		if (!$this->valid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
987
			$this->logger->error(__METHOD__ . '(): No image loaded', array('app' => 'core'));
988
			return false;
989
		}
990 138
		$widthOrig = imageSX($this->resource);
991 138
		$heightOrig = imageSY($this->resource);
992
993 138
		if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) {
994 92
			return $this->fitIn($maxWidth, $maxHeight);
995
		}
996
997 46
		return false;
998
	}
999
1000
	/**
1001
	 * Destroys the current image and resets the object
1002
	 */
1003 220
	public function destroy() {
1004 220
		if ($this->valid()) {
1005 217
			imagedestroy($this->resource);
1006 217
		}
1007 220
		$this->resource = null;
1008 220
	}
1009
1010 220
	public function __destruct() {
1011 220
		$this->destroy();
1012 220
	}
1013
}
1014
1015 1
if (!function_exists('imagebmp')) {
1016
	/**
1017
	 * Output a BMP image to either the browser or a file
1018
	 *
1019
	 * @link http://www.ugia.cn/wp-data/imagebmp.php
1020
	 * @author legend <[email protected]>
1021
	 * @link http://www.programmierer-forum.de/imagebmp-gute-funktion-gefunden-t143716.htm
1022
	 * @author mgutt <[email protected]>
1023
	 * @version 1.00
1024
	 * @param string $fileName [optional] <p>The path to save the file to.</p>
1025
	 * @param int $bit [optional] <p>Bit depth, (default is 24).</p>
1026
	 * @param int $compression [optional]
1027
	 * @return bool <b>TRUE</b> on success or <b>FALSE</b> on failure.
1028
	 */
1029
	function imagebmp($im, $fileName = '', $bit = 24, $compression = 0) {
1030
		if (!in_array($bit, array(1, 4, 8, 16, 24, 32))) {
1031
			$bit = 24;
1032
		} else if ($bit == 32) {
1033
			$bit = 24;
1034
		}
1035
		$bits = pow(2, $bit);
1036
		imagetruecolortopalette($im, true, $bits);
1037
		$width = imagesx($im);
1038
		$height = imagesy($im);
1039
		$colorsNum = imagecolorstotal($im);
1040
		$rgbQuad = '';
1041
		if ($bit <= 8) {
1042
			for ($i = 0; $i < $colorsNum; $i++) {
1043
				$colors = imagecolorsforindex($im, $i);
1044
				$rgbQuad .= chr($colors['blue']) . chr($colors['green']) . chr($colors['red']) . "\0";
1045
			}
1046
			$bmpData = '';
1047
			if ($compression == 0 || $bit < 8) {
1048
				$compression = 0;
1049
				$extra = '';
1050
				$padding = 4 - ceil($width / (8 / $bit)) % 4;
1051
				if ($padding % 4 != 0) {
1052
					$extra = str_repeat("\0", $padding);
1053
				}
1054
				for ($j = $height - 1; $j >= 0; $j--) {
1055
					$i = 0;
1056
					while ($i < $width) {
1057
						$bin = 0;
1058
						$limit = $width - $i < 8 / $bit ? (8 / $bit - $width + $i) * $bit : 0;
1059
						for ($k = 8 - $bit; $k >= $limit; $k -= $bit) {
1060
							$index = imagecolorat($im, $i, $j);
1061
							$bin |= $index << $k;
1062
							$i++;
1063
						}
1064
						$bmpData .= chr($bin);
1065
					}
1066
					$bmpData .= $extra;
1067
				}
1068
			} // RLE8
1069
			else if ($compression == 1 && $bit == 8) {
1070
				for ($j = $height - 1; $j >= 0; $j--) {
1071
					$lastIndex = "\0";
1072
					$sameNum = 0;
1073
					for ($i = 0; $i <= $width; $i++) {
1074
						$index = imagecolorat($im, $i, $j);
1075
						if ($index !== $lastIndex || $sameNum > 255) {
1076
							if ($sameNum != 0) {
1077
								$bmpData .= chr($sameNum) . chr($lastIndex);
1078
							}
1079
							$lastIndex = $index;
1080
							$sameNum = 1;
1081
						} else {
1082
							$sameNum++;
1083
						}
1084
					}
1085
					$bmpData .= "\0\0";
1086
				}
1087
				$bmpData .= "\0\1";
1088
			}
1089
			$sizeQuad = strlen($rgbQuad);
1090
			$sizeData = strlen($bmpData);
1091
		} else {
1092
			$extra = '';
1093
			$padding = 4 - ($width * ($bit / 8)) % 4;
1094
			if ($padding % 4 != 0) {
1095
				$extra = str_repeat("\0", $padding);
1096
			}
1097
			$bmpData = '';
1098
			for ($j = $height - 1; $j >= 0; $j--) {
1099
				for ($i = 0; $i < $width; $i++) {
1100
					$index = imagecolorat($im, $i, $j);
1101
					$colors = imagecolorsforindex($im, $index);
1102
					if ($bit == 16) {
1103
						$bin = 0 << $bit;
1104
						$bin |= ($colors['red'] >> 3) << 10;
1105
						$bin |= ($colors['green'] >> 3) << 5;
1106
						$bin |= $colors['blue'] >> 3;
1107
						$bmpData .= pack("v", $bin);
1108
					} else {
1109
						$bmpData .= pack("c*", $colors['blue'], $colors['green'], $colors['red']);
1110
					}
1111
				}
1112
				$bmpData .= $extra;
1113
			}
1114
			$sizeQuad = 0;
1115
			$sizeData = strlen($bmpData);
1116
			$colorsNum = 0;
1117
		}
1118
		$fileHeader = 'BM' . pack('V3', 54 + $sizeQuad + $sizeData, 0, 54 + $sizeQuad);
1119
		$infoHeader = pack('V3v2V*', 0x28, $width, $height, 1, $bit, $compression, $sizeData, 0, 0, $colorsNum, 0);
1120
		if ($fileName != '') {
1121
			$fp = fopen($fileName, 'wb');
1122
			fwrite($fp, $fileHeader . $infoHeader . $rgbQuad . $bmpData);
1123
			fclose($fp);
1124
			return true;
1125
		}
1126
		echo $fileHeader . $infoHeader . $rgbQuad . $bmpData;
1127
		return true;
1128
	}
1129 1
}
1130
1131 1
if (!function_exists('exif_imagetype')) {
1132
	/**
1133
	 * Workaround if exif_imagetype does not exist
1134
	 *
1135
	 * @link http://www.php.net/manual/en/function.exif-imagetype.php#80383
1136
	 * @param string $fileName
1137
	 * @return string|boolean
1138
	 */
1139
	function exif_imagetype($fileName) {
1140
		if (($info = getimagesize($fileName)) !== false) {
1141
			return $info[2];
1142
		}
1143
		return false;
1144
	}
1145
}
1146