Completed
Push — stable8 ( 420dab...a42074 )
by
unknown
10:01
created

OC_Image::imagecreatefrombmp()   F

Complexity

Conditions 32
Paths 1590

Size

Total Lines 143
Code Lines 112

Duplication

Lines 23
Ratio 16.08 %

Importance

Changes 0
Metric Value
cc 32
eloc 112
nc 1590
nop 1
dl 23
loc 143
rs 2
c 0
b 0
f 0

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