Image::__sleep()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
	/**
3
##DOC-SIGNATURE##
4
5
    This file is part of WideImage.
6
		
7
    WideImage is free software; you can redistribute it and/or modify
8
    it under the terms of the GNU Lesser General Public License as published by
9
    the Free Software Foundation; either version 2.1 of the License, or
10
    (at your option) any later version.
11
		
12
    WideImage is distributed in the hope that it will be useful,
13
    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
    GNU Lesser General Public License for more details.
16
		
17
    You should have received a copy of the GNU Lesser General Public License
18
    along with WideImage; if not, write to the Free Software
19
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21
	* @package WideImage
22
  **/
23
24
namespace WideImage;
25
26
use WideImage\Exception\UnknownErrorWhileMappingException;
27
use WideImage\Exception\GDFunctionResultException;
28
29
/**
30
 * Base class for images
31
 * 
32
 * @package WideImage
33
 */
34
abstract class Image
35
{
36
	/**
37
	 * Holds the image resource
38
	 * @var resource
39
	 */
40
	protected $handle = null;
41
	
42
	/**
43
	 * Flag that determines if WideImage should call imagedestroy() upon object destruction
44
	 * @var bool
45
	 */
46
	protected $handleReleased = false;
47
	
48
	/**
49
	 * Canvas object
50
	 * @var \WideImage\Canvas
51
	 */
52
	protected $canvas = null;
53
	
54
	/**
55
	 * @var string
56
	 */
57
	protected $sdata = null;
58
	
59
	/**
60
	 * The base class constructor
61
	 *
62
	 * @param resource $handle Image handle (GD2 resource)
63
	 */
64
	public function __construct($handle)
65
	{
66
		WideImage::assertValidImageHandle($handle);
67
		$this->handle = $handle;
68
	}
69
	
70
	/**
71
	 * Cleanup
72
	 * 
73
	 * Destroys the handle via \WideImage\Image::destroy() when called by the GC.
74
	 */
75
	public function __destruct()
76
	{
77
		$this->destroy();
78
	}
79
	
80
	/**
81
	 * This method destroy the image handle, and releases the image resource.
82
	 * 
83
	 * After this is called, the object doesn't hold a valid image any more.
84
	 * No operation should be called after that.
85
	 */
86
	public function destroy()
87
	{
88
		if ($this->isValid() && !$this->handleReleased) {
89
			imagedestroy($this->handle);
90
		}
91
		
92
		$this->handle = null;
93
	}
94
	
95
	/**
96
	 * Returns the GD image resource
97
	 * 
98
	 * @return resource GD image resource
99
	 */
100
	public function getHandle()
101
	{
102
		return $this->handle;
103
	}
104
	
105
	/**
106
	 * @return bool True, if the image object holds a valid GD image, false otherwise
107
	 */
108
	public function isValid()
109
	{
110
		return WideImage::isValidImageHandle($this->handle);
111
	}
112
	
113
	/**
114
	 * Releases the handle
115
	 */
116
	public function releaseHandle()
117
	{
118
		$this->handleReleased = true;
119
	}
120
	
121
	/**
122
	 * Saves an image to a file
123
	 * 
124
	 * The file type is recognized from the $uri. If you save to a GIF8, truecolor images
125
	 * are automatically converted to palette.
126
	 * 
127
	 * This method supports additional parameters: quality (for jpeg images) and 
128
	 * compression quality and filters (for png images). See http://www.php.net/imagejpeg and
129
	 * http://www.php.net/imagepng for details.
130
	 * 
131
	 * Examples:
132
	 * <code>
133
	 * // save to a GIF
134
	 * $image->saveToFile('image.gif');
135
	 * 
136
	 * // save to a PNG with compression=7 and no filters
137
	 * $image->saveToFile('image.png', 7, PNG_NO_FILTER);
138
	 * 
139
	 * // save to a JPEG with quality=80
140
	 * $image->saveToFile('image.jpg', 80);
141
	 * 
142
	 * // save to a JPEG with default quality=100
143
	 * $image->saveToFile('image.jpg');
144
	 * </code>
145
	 * 
146
	 * @param string $uri File location
147
	 */
148
	public function saveToFile($uri)
149
	{
150
		$mapper = MapperFactory::selectMapper($uri, null);
151
		$args = func_get_args();
152
		array_unshift($args, $this->getHandle());
153
		$res = call_user_func_array(array($mapper, 'save'), $args);
154
		
155
		if (!$res) {
156
			throw new UnknownErrorWhileMappingException(get_class($mapper) . " returned an invalid result while saving to $uri");
0 ignored issues
show
Bug introduced by
It seems like $mapper can also be of type false and null; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

156
			throw new UnknownErrorWhileMappingException(get_class(/** @scrutinizer ignore-type */ $mapper) . " returned an invalid result while saving to $uri");
Loading history...
157
		}
158
	}
159
	
160
	/**
161
	 * Returns binary string with image data in format specified by $format
162
	 * 
163
	 * Additional parameters may be passed to the function. See \WideImage\Image::saveToFile() for more details.
164
	 * 
165
	 * @param string $format The format of the image
166
	 * @return string The binary image data in specified format
167
	 */
168
	public function asString($format)
169
	{
170
		ob_start();
171
		$args = func_get_args();
172
		$args[0] = null;
173
		array_unshift($args, $this->getHandle());
174
		
175
		$mapper = MapperFactory::selectMapper(null, $format);
176
		$res    = call_user_func_array(array($mapper, 'save'), $args);
177
		
178
		if (!$res) {
179
			throw new UnknownErrorWhileMappingException(get_class($mapper) . " returned an invalid result while writing the image data");
0 ignored issues
show
Bug introduced by
It seems like $mapper can also be of type false and null; however, parameter $object of get_class() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

179
			throw new UnknownErrorWhileMappingException(get_class(/** @scrutinizer ignore-type */ $mapper) . " returned an invalid result while writing the image data");
Loading history...
180
		}
181
		
182
		return ob_get_clean();
183
	}
184
	
185
	/**
186
	 * Output a header to browser.
187
	 * 
188
	 * @param $name Name of the header
0 ignored issues
show
Bug introduced by
The type WideImage\Name was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
189
	 * @param $data Data
0 ignored issues
show
Bug introduced by
The type WideImage\Data was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
190
	 */
191
	protected function writeHeader($name, $data)
192
	{
193
		header($name . ": " . $data);
194
	}
195
	
196
	/**
197
	 * Outputs the image to browser
198
	 * 
199
	 * Sets headers Content-length and Content-type, and echoes the image in the specified format.
200
	 * All other headers (such as Content-disposition) must be added manually. 
201
	 * 
202
	 * Example:
203
	 * <code>
204
	 * WideImage::load('image1.png')->resize(100, 100)->output('gif');
205
	 * </code>
206
	 * 
207
	 * @param string $format Image format
208
	 */
209
	public function output($format)
210
	{
211
		$args = func_get_args();
212
		$data = call_user_func_array(array($this, 'asString'), $args);
213
		
214
		$this->writeHeader('Content-length', strlen($data));
215
		$this->writeHeader('Content-type', MapperFactory::mimeType($format));
216
		echo $data;
217
	}
218
	
219
	/**
220
	 * @return int Image width
221
	 */
222
	public function getWidth()
223
	{
224
		return imagesx($this->handle);
225
	}
226
	
227
	/**
228
	 * @return int Image height
229
	 */
230
	public function getHeight()
231
	{
232
		return imagesy($this->handle);
233
	}
234
	
235
	/**
236
	 * Allocate a color by RGB values.
237
	 * 
238
	 * @param mixed $R Red-component value or an RGB array (with red, green, blue keys)
239
	 * @param int $G If $R is int, this is the green component
240
	 * @param int $B If $R is int, this is the blue component
241
	 * @return int Image color index
242
	 */
243
	public function allocateColor($R, $G = null, $B = null)
244
	{
245
		if (is_array($R)) {
246
			return imageColorAllocate($this->handle, $R['red'], $R['green'], $R['blue']);
247
		}
248
		
249
		return imageColorAllocate($this->handle, $R, $G, $B);
250
	}
251
	
252
	/**
253
	 * @return bool True if the image is transparent, false otherwise
254
	 */
255
	public function isTransparent()
256
	{
257
		return $this->getTransparentColor() >= 0;
258
	}
259
	
260
	/**
261
	 * @return int Transparent color index
262
	 */
263
	public function getTransparentColor()
264
	{
265
		return imagecolortransparent($this->handle);
266
	}
267
	
268
	/**
269
	 * Sets the current transparent color index. Only makes sense for palette images (8-bit).
270
	 * 
271
	 * @param int $color Transparent color index
272
	 */
273
	public function setTransparentColor($color)
274
	{
275
		return imagecolortransparent($this->handle, $color);
276
	}
277
	
278
	/**
279
	 * Returns a RGB array of the transparent color or null if none.
280
	 * 
281
	 * @return mixed Transparent color RGBA array
282
	 */
283
	public function getTransparentColorRGB()
284
	{
285
		$total = imagecolorstotal($this->handle);
286
		$tc    = $this->getTransparentColor();
287
		
288
		if ($tc >= $total && $total > 0) {
289
			return null;
290
		}
291
		
292
		return $this->getColorRGB($tc);
293
	}
294
	
295
	/**
296
	 * Returns a RGBA array for pixel at $x, $y
297
	 * 
298
	 * @param int $x
299
	 * @param int $y
300
	 * @return array RGB array 
301
	 */
302
	public function getRGBAt($x, $y)
303
	{
304
		return $this->getColorRGB($this->getColorAt($x, $y));
305
	}
306
	
307
	/**
308
	 * Writes a pixel at the designated coordinates
309
	 * 
310
	 * Takes an associative array of colours and uses getExactColor() to
311
	 * retrieve the exact index color to write to the image with.
312
	 *
313
	 * @param int $x
314
	 * @param int $y
315
	 * @param array $color
316
	 */
317
	public function setRGBAt($x, $y, $color)
318
	{
319
		$this->setColorAt($x, $y, $this->getExactColor($color));
320
	}
321
	
322
	/**
323
	 * Returns a color's RGB
324
	 * 
325
	 * @param int $colorIndex Color index
326
	 * @return mixed RGBA array for a color with index $colorIndex
327
	 */
328
	public function getColorRGB($colorIndex)
329
	{
330
		return imageColorsForIndex($this->handle, $colorIndex);
331
	}
332
	
333
	/**
334
	 * Returns an index of the color at $x, $y
335
	 * 
336
	 * @param int $x
337
	 * @param int $y
338
	 * @return int Color index for a pixel at $x, $y
339
	 */
340
	public function getColorAt($x, $y)
341
	{
342
		return imagecolorat($this->handle, $x, $y);
343
	}
344
	
345
	/**
346
	 * Set the color index $color to a pixel at $x, $y
347
	 * 
348
	 * @param int $x
349
	 * @param int $y
350
	 * @param int $color Color index
351
	 */
352
	public function setColorAt($x, $y, $color)
353
	{
354
		return imagesetpixel($this->handle, $x, $y, $color);
355
	}
356
	
357
	/**
358
	 * Returns closest color index that matches the given RGB value. Uses
359
	 * PHP's imagecolorclosest()
360
	 * 
361
	 * @param mixed $R Red or RGBA array
362
	 * @param int $G Green component (or null if $R is an RGB array)
363
	 * @param int $B Blue component (or null if $R is an RGB array)
364
	 * @return int Color index
365
	 */
366
	public function getClosestColor($R, $G = null, $B = null)
367
	{
368
		if (is_array($R)) {
369
			return imagecolorclosest($this->handle, $R['red'], $R['green'], $R['blue']);
370
		}
371
		
372
		return imagecolorclosest($this->handle, $R, $G, $B);
373
	}
374
	
375
	/**
376
	 * Returns the color index that exactly matches the given RGB value. Uses
377
	 * PHP's imagecolorexact()
378
	 * 
379
	 * @param mixed $R Red or RGBA array
380
	 * @param int $G Green component (or null if $R is an RGB array)
381
	 * @param int $B Blue component (or null if $R is an RGB array)
382
	 * @return int Color index
383
	 */
384
	public function getExactColor($R, $G = null, $B = null)
385
	{
386
		if (is_array($R)) {
387
			return imagecolorexact($this->handle, $R['red'], $R['green'], $R['blue']);
388
		}
389
		
390
		return imagecolorexact($this->handle, $R, $G, $B);
391
	}
392
	
393
	/**
394
	 * Copies transparency information from $sourceImage. Optionally fills
395
	 * the image with the transparent color at (0, 0).
396
	 * 
397
	 * @param object $sourceImage
398
	 * @param bool $fill True if you want to fill the image with transparent color
399
	 */
400
	public function copyTransparencyFrom($sourceImage, $fill = true)
401
	{
402
		if ($sourceImage->isTransparent()) {
403
			$rgba = $sourceImage->getTransparentColorRGB();
404
			
405
			if ($rgba === null) {
406
				return;
407
			}
408
			
409
			if ($this->isTrueColor()) {
410
				$rgba['alpha'] = 127;
411
				$color = $this->allocateColorAlpha($rgba);
0 ignored issues
show
Bug introduced by
The method allocateColorAlpha() does not exist on WideImage\Image. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

411
				/** @scrutinizer ignore-call */ 
412
    $color = $this->allocateColorAlpha($rgba);
Loading history...
412
			} else {
413
				$color = $this->allocateColor($rgba);
414
			}
415
			
416
			$this->setTransparentColor($color);
0 ignored issues
show
Bug introduced by
It seems like $color can also be of type WideImage\Image; however, parameter $color of WideImage\Image::setTransparentColor() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

416
			$this->setTransparentColor(/** @scrutinizer ignore-type */ $color);
Loading history...
417
			
418
			if ($fill) {
419
				$this->fill(0, 0, $color);
0 ignored issues
show
Bug introduced by
It seems like $color can also be of type WideImage\Image; however, parameter $color of WideImage\Image::fill() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

419
				$this->fill(0, 0, /** @scrutinizer ignore-type */ $color);
Loading history...
420
			}
421
		}
422
	}
423
	
424
	/**
425
	 * Fill the image at ($x, $y) with color index $color
426
	 * 
427
	 * @param int $x
428
	 * @param int $y
429
	 * @param int $color
430
	 */
431
	public function fill($x, $y, $color)
432
	{
433
		return imagefill($this->handle, $x, $y, $color);
434
	}
435
	
436
	/**
437
	 * Used internally to create Operation objects
438
	 *
439
	 * @param string $name
440
	 * @return object
441
	 */
442
	protected function getOperation($name)
443
	{
444
		return OperationFactory::get($name);
445
	}
446
	
447
	/**
448
	 * Returns the image's mask
449
	 * 
450
	 * Mask is a greyscale image where the shade defines the alpha channel (black = transparent, white = opaque).
451
	 * 
452
	 * For opaque images (JPEG), the result will be white. For images with single-color transparency (GIF, 8-bit PNG), 
453
	 * the areas with the transparent color will be black. For images with alpha channel transparenct, 
454
	 * the result will be alpha channel.
455
	 * 
456
	 * @return \WideImage\Image An image mask
457
	 **/
458
	public function getMask()
459
	{
460
		return $this->getOperation('GetMask')->execute($this);
461
	}
462
	
463
	/**
464
	 * Resize the image to given dimensions.
465
	 * 
466
	 * $width and $height are both smart coordinates. This means that you can pass any of these values in:
467
	 *   - positive or negative integer (100, -20, ...)
468
	 *   - positive or negative percent string (30%, -15%, ...)
469
	 *   - complex coordinate (50% - 20, 15 + 30%, ...)
470
	 *   - null: if one dimension is null, it's calculated proportionally from the other.
471
	 * 
472
	 * $fit parameter can be set to one of these three values:
473
	 *   - 'inside': resize proportionally and fit the resulting image tightly in the $width x $height box
474
	 *   - 'outside': resize proportionally and fit the resulting image tighly outside the box
475
	 *   - 'fill': resize the image to fill the $width x $height box exactly
476
	 *
477
	 * $scale parameter can be:
478
	 *   - 'down': only resize the image if it's larger than the $width x $height box
479
	 *   - 'up': only resize the image if it's smaller than the $width x $height box
480
	 *   - 'any': resize the image
481
	 *
482
	 * Example (resize to half-size):
483
	 * <code>
484
	 * $smaller = $image->resize('50%');
485
	 * 
486
	 * $smaller = $image->resize('100', '100', 'inside', 'down');
487
	 * is the same as
488
	 * $smaller = $image->resizeDown(100, 100, 'inside');
489
	 * </code>
490
	 * 
491
	 * @param mixed $width The new width (smart coordinate), or null.
492
	 * @param mixed $height The new height (smart coordinate), or null.
493
	 * @param string $fit 'inside', 'outside', 'fill'
494
	 * @param string $scale 'down', 'up', 'any'
495
	 * @return \WideImage\Image The resized image
496
	 */
497
	public function resize($width = null, $height = null, $fit = 'inside', $scale = 'any')
498
	{
499
		return $this->getOperation('Resize')->execute($this, $width, $height, $fit, $scale);
500
	}
501
	
502
	/**
503
	 * Same as \WideImage\Image::resize(), but the image is only applied if it is larger then the given dimensions.
504
	 * Otherwise, the resulting image retains the source's dimensions.
505
	 * 
506
	 * @param int $width New width, smart coordinate
507
	 * @param int $height New height, smart coordinate
508
	 * @param string $fit 'inside', 'outside', 'fill'
509
	 * @return \WideImage\Image resized image
510
	 */
511
	public function resizeDown($width = null, $height = null, $fit = 'inside')
512
	{
513
		return $this->resize($width, $height, $fit, 'down');
514
	}
515
	
516
	/**
517
	 * Same as \WideImage\Image::resize(), but the image is only applied if it is smaller then the given dimensions.
518
	 * Otherwise, the resulting image retains the source's dimensions.
519
	 * 
520
	 * @param int $width New width, smart coordinate
521
	 * @param int $height New height, smart coordinate
522
	 * @param string $fit 'inside', 'outside', 'fill'
523
	 * @return \WideImage\Image resized image
524
	 */
525
	public function resizeUp($width = null, $height = null, $fit = 'inside')
526
	{
527
		return $this->resize($width, $height, $fit, 'up');
528
	}
529
	
530
	/**
531
	 * Rotate the image for angle $angle clockwise.
532
	 * 
533
	 * Preserves transparency. Has issues when saving to a BMP.
534
	 * 
535
	 * @param int $angle Angle in degrees, clock-wise
536
	 * @param int $bgColor color of the new background
537
	 * @param bool $ignoreTransparent
538
	 * @return \WideImage\Image The rotated image
539
	 */
540
	public function rotate($angle, $bgColor = null, $ignoreTransparent = true)
541
	{
542
		return $this->getOperation('Rotate')->execute($this, $angle, $bgColor, $ignoreTransparent);
543
	}
544
	
545
	/**
546
	 * This method lays the overlay (watermark) on the image.
547
	 * 
548
	 * Hint: if the overlay is a truecolor image with alpha channel, you should leave $pct at 100.
549
	 * 
550
	 * This operation supports alignment notation in coordinates:
551
	 * <code>
552
	 * $watermark = WideImage::load('logo.gif');
553
	 * $base = WideImage::load('picture.jpg');
554
	 * $result = $base->merge($watermark, "right - 10", "bottom - 10", 50);
555
	 * // applies a logo aligned to bottom-right corner with a 10 pixel margin
556
	 * </code>
557
	 * 
558
	 * @param \WideImage\Image $overlay The overlay image
559
	 * @param mixed $left Left position of the overlay, smart coordinate
560
	 * @param mixed $top Top position of the overlay, smart coordinate
561
	 * @param int $pct The opacity of the overlay
562
	 * @return \WideImage\Image The merged image
563
	 */
564
	public function merge($overlay, $left = 0, $top = 0, $pct = 100)
565
	{
566
		return $this->getOperation('Merge')->execute($this, $overlay, $left, $top, $pct);
567
	}
568
	
569
	/**
570
	 * Resizes the canvas of the image, but doesn't scale the content of the image
571
	 * 
572
	 * This operation creates an empty canvas with dimensions $width x $height, filled with 
573
	 * background color $bg_color and draws the original image onto it at position [$pos_x, $pos_y].
574
	 * 
575
	 * Arguments $width, $height, $pos_x and $pos_y are all smart coordinates. $width and $height are 
576
	 * relative to the current image size, $pos_x and $pos_y are relative to the newly calculated
577
	 * canvas size. This can be confusing, but it makes sense. See the example below.
578
	 * 
579
	 * The example below loads a 100x150 image and then resizes its canvas to 200% x 100%+20 
580
	 * (which evaluates to 200x170). The image is placed at position [10, center+20], which evaluates to [10, 30].
581
	 * <code>
582
	 * $image = WideImage::load('someimage.jpg'); // 100x150
583
	 * $white = $image->allocateColor(255, 255, 255);
584
	 * $image->resizeCanvas('200%', '100% + 20', 10, 'center+20', $white);
585
	 * </code>
586
	 * 
587
	 * The parameter $merge defines whether the original image should be merged onto the new canvas.
588
	 * This means it blends transparent color and alpha colors into the background color. If set to false,
589
	 * the original image is just copied over, preserving the transparency/alpha information.
590
	 * 
591
	 * You can set the $scale parameter to limit when to resize the canvas. For example, if you want 
592
	 * to resize the canvas only if the image is smaller than the new size, but leave the image intact 
593
	 * if it's larger, set it to 'up'. Likewise, if you want to shrink the canvas, but don't want to 
594
	 * change images that are already smaller, set it to 'down'. 
595
	 * 
596
	 * @param mixed $width Width of the new canvas (smart coordinate, relative to current image width)
597
	 * @param mixed $height Height of the new canvas (smart coordinate, relative to current image height)
598
	 * @param mixed $pos_x x-position of the image (smart coordinate, relative to the new width)
599
	 * @param mixed $pos_y y-position of the image (smart coordinate, relative to the new height)
600
	 * @param int $bg_color Background color (created with allocateColor or allocateColorAlpha), defaults to null (tries to use a transparent color)
601
	 * @param string $scale Possible values: 'up' (enlarge only), 'down' (downsize only), 'any' (resize precisely to $width x $height). Defaults to 'any'.
602
	 * @param bool $merge Merge the original image (flatten alpha channel and transparency) or copy it over (preserve). Defaults to false.
603
	 * @return \WideImage\Image The resulting image with resized canvas
604
	 */
605
	public function resizeCanvas($width, $height, $pos_x, $pos_y, $bg_color = null, $scale = 'any', $merge = false)
606
	{
607
		return $this->getOperation('ResizeCanvas')->execute($this, $width, $height, $pos_x, $pos_y, $bg_color, $scale, $merge);
608
	}
609
	
610
	/**
611
	 * Returns an image with round corners
612
	 * 
613
	 * You can either set the corners' color or set them transparent.
614
	 * 
615
	 * Note on $smoothness: 1 means jagged edges, 2 is much better, more than 4 doesn't noticeably improve the quality.
616
	 * Rendering becomes increasingly slower if you increase smoothness.
617
	 * 
618
	 * Example:
619
	 * <code>
620
	 * $nice = $ugly->roundCorners(20, $ugly->allocateColor(255, 0, 0), 2);
621
	 * </code>
622
	 * 
623
	 * Use $corners parameter to specify which corners to draw rounded. Possible values are
624
	 * WideImage::SIDE_TOP_LEFT, WideImage::SIDE_TOP,
625
	 * WideImage::SIDE_TOP_RIGHT, WideImage::SIDE_RIGHT,
626
	 * WideImage::SIDE_BOTTOM_RIGHT, WideImage::SIDE_BOTTOM, 
627
	 * WideImage::SIDE_BOTTOM_LEFT, WideImage::SIDE_LEFT, and WideImage::SIDE_ALL.
628
	 * You can specify any combination of corners with a + operation, see example below.
629
	 * 
630
	 * Example:
631
	 * <code>
632
	 * $white = $image->allocateColor(255, 255, 255);
633
	 * $diagonal_corners = $image->roundCorners(15, $white, 2, WideImage::SIDE_TOP_LEFT + WideImage::SIDE_BOTTOM_RIGHT);
634
	 * $right_corners = $image->roundCorners(15, $white, 2, WideImage::SIDE_RIGHT);
635
	 * </code>
636
	 * 
637
	 * @param int $radius Radius of the corners
638
	 * @param int $color The color of corners. If null, corners are rendered transparent (slower than using a solid color).
639
	 * @param int $smoothness Specify the level of smoothness. Suggested values from 1 to 4.
640
	 * @param int $corners Specify which corners to draw (defaults to WideImage::SIDE_ALL = all corners)
641
	 * @return \WideImage\Image The resulting image with round corners
642
	 */
643
	public function roundCorners($radius, $color = null, $smoothness = 2, $corners = 255)
644
	{
645
		return $this->getOperation('RoundCorners')->execute($this, $radius, $color, $smoothness, $corners);
646
	}
647
	
648
	/**
649
	 * Returns an image with applied mask
650
	 * 
651
	 * A mask is a grayscale image, where the shade determines the alpha channel. Black is fully transparent
652
	 * and white is fully opaque.
653
	 * 
654
	 * @param \WideImage\Image $mask The mask image, greyscale
655
	 * @param mixed $left Left coordinate, smart coordinate
656
	 * @param mixed $top Top coordinate, smart coordinate
657
	 * @return \WideImage\Image The resulting image
658
	 **/
659
	public function applyMask($mask, $left = 0, $top = 0)
660
	{
661
		return $this->getOperation('ApplyMask')->execute($this, $mask, $left, $top);
662
	}
663
	
664
	/**
665
	 * Applies a filter
666
	 *
667
	 * @param int $filter One of the IMG_FILTER_* constants
668
	 * @param int $arg1
669
	 * @param int $arg2
670
	 * @param int $arg3
671
	 * @param int $arg4
672
	 * @return \WideImage\Image
673
	 */
674
	public function applyFilter($filter, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null)
675
	{
676
		return $this->getOperation('ApplyFilter')->execute($this, $filter, $arg1, $arg2, $arg3, $arg4);
677
	}
678
	
679
	/**
680
	 * Applies convolution matrix with imageconvolution()
681
	 *
682
	 * @param array $matrix
683
	 * @param float $div
684
	 * @param float $offset
685
	 * @return \WideImage\Image
686
	 */
687
	public function applyConvolution($matrix, $div, $offset)
688
	{
689
		return $this->getOperation('ApplyConvolution')->execute($this, $matrix, $div, $offset);
690
	}
691
	
692
	/**
693
	 * Returns a cropped rectangular portion of the image
694
	 * 
695
	 * If the rectangle specifies area that is out of bounds, it's limited to the current image bounds.
696
	 * 
697
	 * Examples:
698
	 * <code>
699
	 * $cropped = $img->crop(10, 10, 150, 200); // crops a 150x200 rect at (10, 10)
700
	 * $cropped = $img->crop(-100, -50, 100, 50); // crops a 100x50 rect at the right-bottom of the image
701
	 * $cropped = $img->crop('25%', '25%', '50%', '50%'); // crops a 50%x50% rect from the center of the image
702
	 * </code>
703
	 * 
704
	 * This operation supports alignment notation in left/top coordinates.
705
	 * Example:
706
	 * <code>
707
	 * $cropped = $img->crop("right", "bottom", 100, 200); // crops a 100x200 rect from right bottom
708
	 * $cropped = $img->crop("center", "middle", 50, 30); // crops a 50x30 from the center of the image
709
	 * </code>
710
	 * 
711
	 * @param mixed $left Left-coordinate of the crop rect, smart coordinate
712
	 * @param mixed $top Top-coordinate of the crop rect, smart coordinate
713
	 * @param mixed $width Width of the crop rect, smart coordinate
714
	 * @param mixed $height Height of the crop rect, smart coordinate
715
	 * @return \WideImage\Image The cropped image
716
	 **/
717
	public function crop($left = 0, $top = 0, $width = '100%', $height = '100%')
718
	{
719
		return $this->getOperation('Crop')->execute($this, $left, $top, $width, $height);
720
	}
721
	
722
	/**
723
	 * Performs an auto-crop on the image
724
	 *
725
	 * The image is auto-cropped from each of four sides. All sides are 
726
	 * scanned for pixels that differ from $base_color for more than 
727
	 * $rgb_threshold in absolute RGB difference. If more than $pixel_cutoff 
728
	 * differentiating pixels are found, that line is considered to be the crop line for the side.
729
	 * If the line isn't different enough, the algorithm procedes to the next line 
730
	 * towards the other edge of the image.
731
	 * 
732
	 * When the crop rectangle is found, it's enlarged by the $margin value on each of the four sides.
733
	 *
734
	 * @param int $margin Margin for the crop rectangle, can be negative.
735
	 * @param int $rgb_threshold RGB difference which still counts as "same color".
736
	 * @param int $pixel_cutoff How many pixels need to be different to mark a cut line.
737
	 * @param int $base_color The base color index. If none specified (or null given), left-top pixel is used.
738
	 * @return \WideImage\Image The cropped image
739
	 */
740
	public function autoCrop($margin = 0, $rgb_threshold = 0, $pixel_cutoff = 1, $base_color = null)
741
	{
742
		return $this->getOperation('AutoCrop')->execute($this, $margin, $rgb_threshold, $pixel_cutoff, $base_color);
743
	}
744
	
745
	/**
746
	 * Returns a negative of the image
747
	 *
748
	 * This operation differs from calling \WideImage\Image::applyFilter(IMG_FILTER_NEGATIVE), because it's 8-bit and transparency safe.
749
	 * This means it will return an 8-bit image, if the source image is 8-bit. If that 8-bit image has a palette transparency,
750
	 * the resulting image will keep transparency.
751
	 *
752
	 * @return \WideImage\Image negative of the image
753
	 */
754
	public function asNegative()
755
	{
756
		return $this->getOperation('AsNegative')->execute($this);
757
	}
758
	
759
	/**
760
	 * Returns a grayscale copy of the image
761
	 * 
762
	 * @return \WideImage\Image grayscale copy
763
	 **/
764
	public function asGrayscale()
765
	{
766
		return $this->getOperation('AsGrayscale')->execute($this);
767
	}
768
	
769
	/**
770
	 * Returns a mirrored copy of the image
771
	 * 
772
	 * @return \WideImage\Image Mirrored copy
773
	 **/
774
	public function mirror()
775
	{
776
		return $this->getOperation('Mirror')->execute($this);
777
	}
778
	
779
	/**
780
	 * Applies the unsharp filter
781
	 * 
782
	 * @param float $amount
783
	 * @param float $radius
784
	 * @param float $threshold
785
	 * @return \WideImage\Image Unsharpened copy of the image
786
	 **/
787
	public function unsharp($amount, $radius, $threshold)
788
	{
789
		return $this->getOperation('Unsharp')->execute($this, $amount, $radius, $threshold);
790
	}
791
	
792
	/**
793
	 * Returns a flipped (mirrored over horizontal line) copy of the image
794
	 * 
795
	 * @return \WideImage\Image Flipped copy
796
	 **/
797
	public function flip()
798
	{
799
		return $this->getOperation('Flip')->execute($this);
800
	}
801
	
802
	/**
803
	 * Corrects gamma on the image
804
	 * 
805
	 * @param float $inputGamma
806
	 * @param float $outputGamma
807
	 * @return \WideImage\Image Image with corrected gamma
808
	 **/
809
	public function correctGamma($inputGamma, $outputGamma)
810
	{
811
		return $this->getOperation('CorrectGamma')->execute($this, $inputGamma, $outputGamma);
812
	}
813
	
814
	/**
815
	 * Adds noise to the image
816
	 * 
817
	 * @author Tomasz Kapusta
818
	 * 
819
	 * @param int $amount Number of noise pixels to add
820
	 * @param string $type Type of noise 'salt&pepper', 'color' or 'mono'
821
	 * @return \WideImage\Image Image with noise added
822
	 **/
823
	public function addNoise($amount, $type)
824
	{
825
		return $this->getOperation('AddNoise')->execute($this, $amount, $type);
826
	}
827
	
828
	/**
829
	 * Used internally to execute operations
830
	 *
831
	 * @param string $name
832
	 * @param array $args
833
	 * @return \WideImage\Image
834
	 */
835
	public function __call($name, $args)
836
	{
837
		$op = $this->getOperation($name);
838
		array_unshift($args, $this);
839
		return call_user_func_array(array($op, 'execute'), $args);
840
	}
841
	
842
	/**
843
	 * Returns an image in GIF or PNG format
844
	 *
845
	 * @return string
846
	 */
847
	public function __toString()
848
	{
849
		if ($this->isTransparent()) {
850
			return $this->asString('gif');
851
		}
852
		
853
		return $this->asString('png');
854
	}
855
	
856
	/**
857
	 * Returns a copy of the image object
858
	 * 
859
	 * @return \WideImage\Image The copy
860
	 **/
861
	public function copy()
862
	{
863
		$dest = $this->doCreate($this->getWidth(), $this->getHeight());
0 ignored issues
show
Bug introduced by
The method doCreate() does not exist on WideImage\Image. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

863
		/** @scrutinizer ignore-call */ 
864
  $dest = $this->doCreate($this->getWidth(), $this->getHeight());
Loading history...
864
		$dest->copyTransparencyFrom($this, true);
865
		$this->copyTo($dest, 0, 0);
866
		return $dest;
867
	}
868
	
869
	/**
870
	 * Copies this image onto another image
871
	 * 
872
	 * @param \WideImage\Image $dest
873
	 * @param int $left
874
	 * @param int $top
875
	 **/
876
	public function copyTo($dest, $left = 0, $top = 0)
877
	{
878
		if (!imagecopy($dest->getHandle(), $this->handle, $left, $top, 0, 0, $this->getWidth(), $this->getHeight())) {
879
			throw new GDFunctionResultException("imagecopy() returned false");
880
		}
881
	}
882
	
883
	/**
884
	 * Returns the canvas object
885
	 * 
886
	 * The Canvas object can be used to draw text and shapes on the image
887
	 * 
888
	 * Examples:
889
	 * <code>
890
	 * $img = WideImage::load('pic.jpg);
891
	 * $canvas = $img->getCanvas();
892
	 * $canvas->useFont('arial.ttf', 15, $img->allocateColor(200, 220, 255));
893
	 * $canvas->writeText(10, 50, "Hello world!");
894
	 * 
895
	 * $canvas->filledRectangle(10, 10, 80, 40, $img->allocateColor(255, 127, 255));
896
	 * $canvas->line(60, 80, 30, 100, $img->allocateColor(255, 0, 0));
897
	 * $img->saveToFile('new.png');
898
	 * </code>
899
	 * 
900
	 * @return \WideImage\Canvas The Canvas object
901
	 **/
902
	function getCanvas()
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
903
	{
904
		if ($this->canvas == null) {
905
			$this->canvas = new Canvas($this);
906
		}
907
		
908
		return $this->canvas;
909
	}
910
	
911
	/**
912
	 * Returns true if the image is true-color, false otherwise
913
	 * 
914
	 * @return bool
915
	 **/
916
	abstract public function isTrueColor();
917
	
918
	/**
919
	 * Returns a true-color copy of the image
920
	 * 
921
	 * @return \WideImage\TrueColorImage
922
	 **/
923
	abstract public function asTrueColor();
924
	
925
	/**
926
	 * Returns a palette copy (8bit) of the image
927
	 *
928
	 * @param int $nColors Number of colors in the resulting image, more than 0, less or equal to 255
929
	 * @param bool $dither Use dithering or not
930
	 * @param bool $matchPalette Set to true to use imagecolormatch() to match the resulting palette more closely to the original image 
931
	 * @return \WideImage\Image
932
	 **/
933
	abstract public function asPalette($nColors = 255, $dither = null, $matchPalette = true);
934
	
935
	/**
936
	 * Retrieve an image with selected channels
937
	 * 
938
	 * Examples:
939
	 * <code>
940
	 * $channels = $img->getChannels('red', 'blue');
941
	 * $channels = $img->getChannels('alpha', 'green');
942
	 * $channels = $img->getChannels(array('green', 'blue'));
943
	 * </code>
944
	 * 
945
	 * @return \WideImage\Image
946
	 **/
947
	abstract public function getChannels();
948
	
949
	/**
950
	 * Returns an image without an alpha channel
951
	 * 
952
	 * @return \WideImage\Image
953
	 **/
954
	abstract public function copyNoAlpha();
955
	
956
	/**
957
	 * Returns an array of serializable protected variables. Called automatically upon serialize().
958
	 * 
959
	 * @return array
960
	 */
961
	public function __sleep()
962
	{
963
		$this->sdata = $this->asString('png');
964
		
965
		return array('sdata', 'handleReleased');
966
	}
967
	
968
	/**
969
	 * Restores an image from serialization. Called automatically upon unserialize().
970
	 */
971
	public function __wakeup()
972
	{
973
		$temp_image = WideImage::loadFromString($this->sdata);
974
		$temp_image->releaseHandle();
975
		$this->handle = $temp_image->handle;
976
		$temp_image = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $temp_image is dead and can be removed.
Loading history...
977
		$this->sdata = null;
978
	}
979
}
980