Completed
Push — stable7 ( 35746e...825360 )
by
unknown
29:41
created

image.php ➔ imagebmp()   D

Complexity

Conditions 24
Paths 60

Size

Total Lines 105
Code Lines 81

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 24
eloc 81
nc 60
nop 4
dl 0
loc 105
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
/**
4
* ownCloud
5
*
6
* @author Thomas Tanghus
7
* @copyright 2011 Thomas Tanghus <[email protected]>
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
 */
26
class OC_Image {
27
	protected $resource = false; // tmp resource.
28
	protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
29
	protected $mimeType = "image/png"; // Default to png
30
	protected $bitDepth = 24;
31
	protected $filePath = null;
32
33
	private $fileInfo;
34
35
	/**
36
	* Get mime type for an image file.
37
	* @param string|null $filePath The path to a local image file.
38
	* @return string The mime type if the it could be determined, otherwise an empty string.
39
	*/
40
	static public function getMimeTypeForFile($filePath) {
41
		// exif_imagetype throws "read error!" if file is less than 12 byte
42
		if (filesize($filePath) > 11) {
43
			$imageType = exif_imagetype($filePath);
44
		} else {
45
			$imageType = false;
46
		}
47
		return $imageType ? image_type_to_mime_type($imageType) : '';
48
	}
49
50
	/**
51
	 * Constructor.
52
	 * @param resource|string $imageRef The path to a local file, a base64 encoded string or a resource created by
53
	 * an imagecreate* function.
54
	 * @return \OC_Image False on error
0 ignored issues
show
Comprehensibility Best Practice introduced by
Adding a @return annotation to constructors is generally not recommended as a constructor does not have a meaningful return value.

Adding a @return annotation to a constructor is not recommended, since a constructor does not have a meaningful return value.

Please refer to the PHP core documentation on constructors.

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