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