1 <?php
2 /**
3 * @author Gasper Kozak
4 * @copyright 2007-2011
5
6 This file is part of WideImage.
7
8 WideImage is free software; you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 WideImage is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with WideImage; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21
22 * @package WideImage
23 **/
24
25 /**
26 * Thrown when an invalid dimension is passed for some operations
27 *
28 * @package Exceptions
29 */
30 class WideImage_InvalidImageDimensionException extends WideImage_Exception {}
31
32 /**
33 * Thrown when an image can't be saved (returns false by the mapper)
34 *
35 * @package Exceptions
36 */
37 class WideImage_UnknownErrorWhileMappingException extends WideImage_Exception {}
38
39 /**
40 * Base class for images
41 *
42 * @package WideImage
43 */
44 abstract class WideImage_Image
45 {
46 /**
47 * Holds the image resource
48 * @var resource
49 */
50 protected $handle = null;
51
52 /**
53 * Flag that determines if WideImage should call imagedestroy() upon object destruction
54 * @var bool
55 */
56 protected $handleReleased = false;
57
58 /**
59 * Canvas object
60 * @var WideImage_Canvas
61 */
62 protected $canvas = null;
63
64 /**
65 * @var string
66 */
67 protected $sdata = null;
68
69 /**
70 * The base class constructor
71 *
72 * @param resource $handle Image handle (GD2 resource)
73 */
74 function __construct($handle)
75 {
76 WideImage::assertValidImageHandle($handle);
77 $this->handle = $handle;
78 }
79
80 /**
81 * Cleanup
82 *
83 * Destroys the handle via WideImage_Image::destroy() when called by the GC.
84 */
85 function __destruct()
86 {
87 $this->destroy();
88 }
89
90 /**
91 * This method destroy the image handle, and releases the image resource.
92 *
93 * After this is called, the object doesn't hold a valid image any more.
94 * No operation should be called after that.
95 */
96 function destroy()
97 {
98 if ($this->isValid() && !$this->handleReleased)
99 imagedestroy($this->handle);
100
101 $this->handle = null;
102 }
103
104 /**
105 * Returns the GD image resource
106 *
107 * @return resource GD image resource
108 */
109 function getHandle()
110 {
111 return $this->handle;
112 }
113
114 /**
115 * @return bool True, if the image object holds a valid GD image, false otherwise
116 */
117 function isValid()
118 {
119 return WideImage::isValidImageHandle($this->handle);
120 }
121
122 /**
123 * Releases the handle
124 */
125 function releaseHandle()
126 {
127 $this->handleReleased = true;
128 }
129
130 /**
131 * Saves an image to a file
132 *
133 * The file type is recognized from the $uri. If you save to a GIF8, truecolor images
134 * are automatically converted to palette.
135 *
136 * This method supports additional parameters: quality (for jpeg images) and
137 * compression quality and filters (for png images). See http://www.php.net/imagejpeg and
138 * http://www.php.net/imagepng for details.
139 *
140 * Examples:
141 * <code>
142 * // save to a GIF
143 * $image->saveToFile('image.gif');
144 *
145 * // save to a PNG with compression=7 and no filters
146 * $image->saveToFile('image.png', 7, PNG_NO_FILTER);
147 *
148 * // save to a JPEG with quality=80
149 * $image->saveToFile('image.jpg', 80);
150 *
151 * // save to a JPEG with default quality=100
152 * $image->saveToFile('image.jpg');
153 * </code>
154 *
155 * @param string $uri File location
156 */
157 function saveToFile($uri)
158 {
159 $mapper = WideImage_MapperFactory::selectMapper($uri, null);
160 $args = func_get_args();
161 array_unshift($args, $this->getHandle());
162 $res = call_user_func_array(array($mapper, 'save'), $args);
163 if (!$res)
164 throw new WideImage_UnknownErrorWhileMappingException(get_class($mapper) . " returned an invalid result while saving to $uri");
165 }
166
167 /**
168 * Returns binary string with image data in format specified by $format
169 *
170 * Additional parameters may be passed to the function. See WideImage_Image::saveToFile() for more details.
171 *
172 * @param string $format The format of the image
173 * @return string The binary image data in specified format
174 */
175 function asString($format)
176 {
177 ob_start();
178 $args = func_get_args();
179 $args[0] = null;
180 array_unshift($args, $this->getHandle());
181
182 $mapper = WideImage_MapperFactory::selectMapper(null, $format);
183 $res = call_user_func_array(array($mapper, 'save'), $args);
184 if (!$res)
185 throw new WideImage_UnknownErrorWhileMappingException(get_class($mapper) . " returned an invalid result while writing the image data");
186
187 return ob_get_clean();
188 }
189
190 /**
191 * Output a header to browser.
192 *
193 * @param $name Name of the header
194 * @param $data Data
195 */
196 protected function writeHeader($name, $data)
197 {
198 header($name . ": " . $data);
199 }
200
201 /**
202 * Outputs the image to browser
203 *
204 * Sets headers Content-length and Content-type, and echoes the image in the specified format.
205 * All other headers (such as Content-disposition) must be added manually.
206 *
207 * Example:
208 * <code>
209 * WideImage::load('image1.png')->resize(100, 100)->output('gif');
210 * </code>
211 *
212 * @param string $format Image format
213 */
214 function output($format)
215 {
216 $args = func_get_args();
217 $data = call_user_func_array(array($this, 'asString'), $args);
218
219 $this->writeHeader('Content-length', strlen($data));
220 $this->writeHeader('Content-type', WideImage_MapperFactory::mimeType($format));
221 echo $data;
222 }
223
224 /**
225 * @return int Image width
226 */
227 function getWidth()
228 {
229 return imagesx($this->handle);
230 }
231
232 /**
233 * @return int Image height
234 */
235 function getHeight()
236 {
237 return imagesy($this->handle);
238 }
239
240 /**
241 * Allocate a color by RGB values.
242 *
243 * @param mixed $R Red-component value or an RGB array (with red, green, blue keys)
244 * @param int $G If $R is int, this is the green component
245 * @param int $B If $R is int, this is the blue component
246 * @return int Image color index
247 */
248 function allocateColor($R, $G = null, $B = null)
249 {
250 if (is_array($R))
251 return imageColorAllocate($this->handle, $R['red'], $R['green'], $R['blue']);
252 else
253 return imageColorAllocate($this->handle, $R, $G, $B);
254 }
255
256 /**
257 * @return bool True if the image is transparent, false otherwise
258 */
259 function isTransparent()
260 {
261 return $this->getTransparentColor() >= 0;
262 }
263
264 /**
265 * @return int Transparent color index
266 */
267 function getTransparentColor()
268 {
269 return imagecolortransparent($this->handle);
270 }
271
272 /**
273 * Sets the current transparent color index. Only makes sense for palette images (8-bit).
274 *
275 * @param int $color Transparent color index
276 */
277 function setTransparentColor($color)
278 {
279 return imagecolortransparent($this->handle, $color);
280 }
281
282 /**
283 * Returns a RGB array of the transparent color or null if none.
284 *
285 * @return mixed Transparent color RGBA array
286 */
287 function getTransparentColorRGB()
288 {
289 $total = imagecolorstotal($this->handle);
290 $tc = $this->getTransparentColor();
291
292 if ($tc >= $total && $total > 0)
293 return null;
294 else
295 return $this->getColorRGB($tc);
296 }
297
298 /**
299 * Returns a RGBA array for pixel at $x, $y
300 *
301 * @param int $x
302 * @param int $y
303 * @return array RGB array
304 */
305 function getRGBAt($x, $y)
306 {
307 return $this->getColorRGB($this->getColorAt($x, $y));
308 }
309
310 /**
311 * Writes a pixel at the designated coordinates
312 *
313 * Takes an associative array of colours and uses getExactColor() to
314 * retrieve the exact index color to write to the image with.
315 *
316 * @param int $x
317 * @param int $y
318 * @param array $color
319 */
320 function setRGBAt($x, $y, $color)
321 {
322 $this->setColorAt($x, $y, $this->getExactColor($color));
323 }
324
325 /**
326 * Returns a color's RGB
327 *
328 * @param int $colorIndex Color index
329 * @return mixed RGBA array for a color with index $colorIndex
330 */
331 function getColorRGB($colorIndex)
332 {
333 return imageColorsForIndex($this->handle, $colorIndex);
334 }
335
336 /**
337 * Returns an index of the color at $x, $y
338 *
339 * @param int $x
340 * @param int $y
341 * @return int Color index for a pixel at $x, $y
342 */
343 function getColorAt($x, $y)
344 {
345 return imagecolorat($this->handle, $x, $y);
346 }
347
348 /**
349 * Set the color index $color to a pixel at $x, $y
350 *
351 * @param int $x
352 * @param int $y
353 * @param int $color Color index
354 */
355 function setColorAt($x, $y, $color)
356 {
357 return imagesetpixel($this->handle, $x, $y, $color);
358 }
359
360 /**
361 * Returns closest color index that matches the given RGB value. Uses
362 * PHP's imagecolorclosest()
363 *
364 * @param mixed $R Red or RGBA array
365 * @param int $G Green component (or null if $R is an RGB array)
366 * @param int $B Blue component (or null if $R is an RGB array)
367 * @return int Color index
368 */
369 function getClosestColor($R, $G = null, $B = null)
370 {
371 if (is_array($R))
372 return imagecolorclosest($this->handle, $R['red'], $R['green'], $R['blue']);
373 else
374 return imagecolorclosest($this->handle, $R, $G, $B);
375 }
376
377 /**
378 * Returns the color index that exactly matches the given RGB value. Uses
379 * PHP's imagecolorexact()
380 *
381 * @param mixed $R Red or RGBA array
382 * @param int $G Green component (or null if $R is an RGB array)
383 * @param int $B Blue component (or null if $R is an RGB array)
384 * @return int Color index
385 */
386 function getExactColor($R, $G = null, $B = null)
387 {
388 if (is_array($R))
389 return imagecolorexact($this->handle, $R['red'], $R['green'], $R['blue']);
390 else
391 return imagecolorexact($this->handle, $R, $G, $B);
392 }
393
394 /**
395 * Copies transparency information from $sourceImage. Optionally fills
396 * the image with the transparent color at (0, 0).
397 *
398 * @param object $sourceImage
399 * @param bool $fill True if you want to fill the image with transparent color
400 */
401 function copyTransparencyFrom($sourceImage, $fill = true)
402 {
403 if ($sourceImage->isTransparent())
404 {
405 $rgba = $sourceImage->getTransparentColorRGB();
406 if ($rgba === null)
407 return;
408
409 if ($this->isTrueColor())
410 {
411 $rgba['alpha'] = 127;
412 $color = $this->allocateColorAlpha($rgba);
413 }
414 else
415 $color = $this->allocateColor($rgba);
416
417 $this->setTransparentColor($color);
418 if ($fill)
419 $this->fill(0, 0, $color);
420 }
421 }
422
423 /**
424 * Fill the image at ($x, $y) with color index $color
425 *
426 * @param int $x
427 * @param int $y
428 * @param int $color
429 */
430 function fill($x, $y, $color)
431 {
432 return imagefill($this->handle, $x, $y, $color);
433 }
434
435 /**
436 * Used internally to create Operation objects
437 *
438 * @param string $name
439 * @return object
440 */
441 protected function getOperation($name)
442 {
443 return WideImage_OperationFactory::get($name);
444 }
445
446 /**
447 * Returns the image's mask
448 *
449 * Mask is a greyscale image where the shade defines the alpha channel (black = transparent, white = opaque).
450 *
451 * For opaque images (JPEG), the result will be white. For images with single-color transparency (GIF, 8-bit PNG),
452 * the areas with the transparent color will be black. For images with alpha channel transparenct,
453 * the result will be alpha channel.
454 *
455 * @return WideImage_Image An image mask
456 **/
457 function getMask()
458 {
459 return $this->getOperation('GetMask')->execute($this);
460 }
461
462 /**
463 * Resize the image to given dimensions.
464 *
465 * $width and $height are both smart coordinates. This means that you can pass any of these values in:
466 * - positive or negative integer (100, -20, ...)
467 * - positive or negative percent string (30%, -15%, ...)
468 * - complex coordinate (50% - 20, 15 + 30%, ...)
469 *
470 * If $width is null, it's calculated proportionally from $height, and vice versa.
471 *
472 * Example (resize to half-size):
473 * <code>
474 * $smaller = $image->resize('50%');
475 *
476 * $smaller = $image->resize('100', '100', 'inside', 'down');
477 * is the same as
478 * $smaller = $image->resizeDown(100, 100, 'inside');
479 * </code>
480 *
481 * @param mixed $width The new width (smart coordinate), or null.
482 * @param mixed $height The new height (smart coordinate), or null.
483 * @param string $fit 'inside', 'outside', 'fill'
484 * @param string $scale 'down', 'up', 'any'
485 * @return WideImage_Image The resized image
486 */
487 function resize($width = null, $height = null, $fit = 'inside', $scale = 'any')
488 {
489 return $this->getOperation('Resize')->execute($this, $width, $height, $fit, $scale);
490 }
491
492 /**
493 * Same as WideImage_Image::resize(), but the image is only applied if it is larger then the given dimensions.
494 * Otherwise, the resulting image retains the source's dimensions.
495 *
496 * @param int $width New width, smart coordinate
497 * @param int $height New height, smart coordinate
498 * @param string $fit 'inside', 'outside', 'fill'
499 * @return WideImage_Image resized image
500 */
501 function resizeDown($width = null, $height = null, $fit = 'inside')
502 {
503 return $this->resize($width, $height, $fit, 'down');
504 }
505
506 /**
507 * Same as WideImage_Image::resize(), but the image is only applied if it is smaller then the given dimensions.
508 * Otherwise, the resulting image retains the source's dimensions.
509 *
510 * @param int $width New width, smart coordinate
511 * @param int $height New height, smart coordinate
512 * @param string $fit 'inside', 'outside', 'fill'
513 * @return WideImage_Image resized image
514 */
515 function resizeUp($width = null, $height = null, $fit = 'inside')
516 {
517 return $this->resize($width, $height, $fit, 'up');
518 }
519
520 /**
521 * Rotate the image for angle $angle clockwise.
522 *
523 * Preserves transparency. Has issues when saving to a BMP.
524 *
525 * @param int $angle Angle in degrees, clock-wise
526 * @param int $bgColor color of the new background
527 * @param bool $ignoreTransparent
528 * @return WideImage_Image The rotated image
529 */
530 function rotate($angle, $bgColor = null, $ignoreTransparent = true)
531 {
532 return $this->getOperation('Rotate')->execute($this, $angle, $bgColor, $ignoreTransparent);
533 }
534
535 /**
536 * This method lays the overlay (watermark) on the image.
537 *
538 * Hint: if the overlay is a truecolor image with alpha channel, you should leave $pct at 100.
539 *
540 * This operation supports alignment notation in coordinates:
541 * <code>
542 * $watermark = WideImage::load('logo.gif');
543 * $base = WideImage::load('picture.jpg');
544 * $result = $base->merge($watermark, "right - 10", "bottom - 10", 50);
545 * // applies a logo aligned to bottom-right corner with a 10 pixel margin
546 * </code>
547 *
548 * @param WideImage_Image $overlay The overlay image
549 * @param mixed $left Left position of the overlay, smart coordinate
550 * @param mixed $top Top position of the overlay, smart coordinate
551 * @param int $pct The opacity of the overlay
552 * @return WideImage_Image The merged image
553 */
554 function merge($overlay, $left = 0, $top = 0, $pct = 100)
555 {
556 return $this->getOperation('Merge')->execute($this, $overlay, $left, $top, $pct);
557 }
558
559 /**
560 * Resizes the canvas of the image, but doesn't scale the content of the image
561 *
562 * This operation creates an empty canvas with dimensions $width x $height, filled with
563 * background color $bg_color and draws the original image onto it at position [$pos_x, $pos_y].
564 *
565 * Arguments $width, $height, $pos_x and $pos_y are all smart coordinates. $width and $height are
566 * relative to the current image size, $pos_x and $pos_y are relative to the newly calculated
567 * canvas size. This can be confusing, but it makes sense. See the example below.
568 *
569 * The example below loads a 100x150 image and then resizes its canvas to 200% x 100%+20
570 * (which evaluates to 200x170). The image is placed at position [10, center+20], which evaluates to [10, 30].
571 * <code>
572 * $image = WideImage::load('someimage.jpg'); // 100x150
573 * $white = $image->allocateColor(255, 255, 255);
574 * $image->resizeCanvas('200%', '100% + 20', 10, 'center+20', $white);
575 * </code>
576 *
577 * The parameter $merge defines whether the original image should be merged onto the new canvas.
578 * This means it blends transparent color and alpha colors into the background color. If set to false,
579 * the original image is just copied over, preserving the transparency/alpha information.
580 *
581 * You can set the $scale parameter to limit when to resize the canvas. For example, if you want
582 * to resize the canvas only if the image is smaller than the new size, but leave the image intact
583 * if it's larger, set it to 'up'. Likewise, if you want to shrink the canvas, but don't want to
584 * change images that are already smaller, set it to 'down'.
585 *
586 * @param mixed $width Width of the new canvas (smart coordinate, relative to current image width)
587 * @param mixed $height Height of the new canvas (smart coordinate, relative to current image height)
588 * @param mixed $pos_x x-position of the image (smart coordinate, relative to the new width)
589 * @param mixed $pos_y y-position of the image (smart coordinate, relative to the new height)
590 * @param int $bg_color Background color (created with allocateColor or allocateColorAlpha), defaults to null (tries to use a transparent color)
591 * @param string $scale Possible values: 'up' (enlarge only), 'down' (downsize only), 'any' (resize precisely to $width x $height). Defaults to 'any'.
592 * @param bool $merge Merge the original image (flatten alpha channel and transparency) or copy it over (preserve). Defaults to false.
593 * @return WideImage_Image The resulting image with resized canvas
594 */
595 function resizeCanvas($width, $height, $pos_x, $pos_y, $bg_color = null, $scale = 'any', $merge = false)
596 {
597 return $this->getOperation('ResizeCanvas')->execute($this, $width, $height, $pos_x, $pos_y, $bg_color, $scale, $merge);
598 }
599
600 /**
601 * Returns an image with round corners
602 *
603 * You can either set the corners' color or set them transparent.
604 *
605 * Note on $smoothness: 1 means jagged edges, 2 is much better, more than 4 doesn't noticeably improve the quality.
606 * Rendering becomes increasingly slower if you increase smoothness.
607 *
608 * Example:
609 * <code>
610 * $nice = $ugly->roundCorners(20, $ugly->allocateColor(255, 0, 0), 2);
611 * </code>
612 *
613 * Use $corners parameter to specify which corners to draw rounded. Possible values are
614 * WideImage::SIDE_TOP_LEFT, WideImage::SIDE_TOP,
615 * WideImage::SIDE_TOP_RIGHT, WideImage::SIDE_RIGHT,
616 * WideImage::SIDE_BOTTOM_RIGHT, WideImage::SIDE_BOTTOM,
617 * WideImage::SIDE_BOTTOM_LEFT, WideImage::SIDE_LEFT, and WideImage::SIDE_ALL.
618 * You can specify any combination of corners with a + operation, see example below.
619 *
620 * Example:
621 * <code>
622 * $white = $image->allocateColor(255, 255, 255);
623 * $diagonal_corners = $image->roundCorners(15, $white, 2, WideImage::SIDE_TOP_LEFT + WideImage::SIDE_BOTTOM_RIGHT);
624 * $right_corners = $image->roundCorners(15, $white, 2, WideImage::SIDE_RIGHT);
625 * </code>
626 *
627 * @param int $radius Radius of the corners
628 * @param int $color The color of corners. If null, corners are rendered transparent (slower than using a solid color).
629 * @param int $smoothness Specify the level of smoothness. Suggested values from 1 to 4.
630 * @param int $corners Specify which corners to draw (defaults to WideImage::SIDE_ALL = all corners)
631 * @return WideImage_Image The resulting image with round corners
632 */
633 function roundCorners($radius, $color = null, $smoothness = 2, $corners = 255)
634 {
635 return $this->getOperation('RoundCorners')->execute($this, $radius, $color, $smoothness, $corners);
636 }
637
638 /**
639 * Returns an image with applied mask
640 *
641 * A mask is a grayscale image, where the shade determines the alpha channel. Black is fully transparent
642 * and white is fully opaque.
643 *
644 * @param WideImage_Image $mask The mask image, greyscale
645 * @param mixed $left Left coordinate, smart coordinate
646 * @param mixed $top Top coordinate, smart coordinate
647 * @return WideImage_Image The resulting image
648 **/
649 function applyMask($mask, $left = 0, $top = 0)
650 {
651 return $this->getOperation('ApplyMask')->execute($this, $mask, $left, $top);
652 }
653
654 /**
655 * Applies a filter
656 *
657 * @param int $filter One of the IMG_FILTER_* constants
658 * @param int $arg1
659 * @param int $arg2
660 * @param int $arg3
661 * @param int $arg4
662 * @return WideImage_Image
663 */
664 function applyFilter($filter, $arg1 = null, $arg2 = null, $arg3 = null, $arg4 = null)
665 {
666 return $this->getOperation('ApplyFilter')->execute($this, $filter, $arg1, $arg2, $arg3, $arg4);
667 }
668
669 /**
670 * Applies convolution matrix with imageconvolution()
671 *
672 * @param array $matrix
673 * @param float $div
674 * @param float $offset
675 * @return WideImage_Image
676 */
677 function applyConvolution($matrix, $div, $offset)
678 {
679 return $this->getOperation('ApplyConvolution')->execute($this, $matrix, $div, $offset);
680 }
681
682 /**
683 * Returns a cropped rectangular portion of the image
684 *
685 * If the rectangle specifies area that is out of bounds, it's limited to the current image bounds.
686 *
687 * Examples:
688 * <code>
689 * $cropped = $img->crop(10, 10, 150, 200); // crops a 150x200 rect at (10, 10)
690 * $cropped = $img->crop(-100, -50, 100, 50); // crops a 100x50 rect at the right-bottom of the image
691 * $cropped = $img->crop('25%', '25%', '50%', '50%'); // crops a 50%x50% rect from the center of the image
692 * </code>
693 *
694 * This operation supports alignment notation in left/top coordinates.
695 * Example:
696 * <code>
697 * $cropped = $img->crop("right", "bottom", 100, 200); // crops a 100x200 rect from right bottom
698 * $cropped = $img->crop("center", "middle", 50, 30); // crops a 50x30 from the center of the image
699 * </code>
700 *
701 * @param mixed $left Left-coordinate of the crop rect, smart coordinate
702 * @param mixed $top Top-coordinate of the crop rect, smart coordinate
703 * @param mixed $width Width of the crop rect, smart coordinate
704 * @param mixed $height Height of the crop rect, smart coordinate
705 * @return WideImage_Image The cropped image
706 **/
707 function crop($left = 0, $top = 0, $width = '100%', $height = '100%')
708 {
709 return $this->getOperation('Crop')->execute($this, $left, $top, $width, $height);
710 }
711
712 /**
713 * Performs an auto-crop on the image
714 *
715 * The image is auto-cropped from each of four sides. All sides are
716 * scanned for pixels that differ from $base_color for more than
717 * $rgb_threshold in absolute RGB difference. If more than $pixel_cutoff
718 * differentiating pixels are found, that line is considered to be the crop line for the side.
719 * If the line isn't different enough, the algorithm procedes to the next line
720 * towards the other edge of the image.
721 *
722 * When the crop rectangle is found, it's enlarged by the $margin value on each of the four sides.
723 *
724 * @param int $margin Margin for the crop rectangle, can be negative.
725 * @param int $rgb_threshold RGB difference which still counts as "same color".
726 * @param int $pixel_cutoff How many pixels need to be different to mark a cut line.
727 * @param int $base_color The base color index. If none specified (or null given), left-top pixel is used.
728 * @return WideImage_Image The cropped image
729 */
730 function autoCrop($margin = 0, $rgb_threshold = 0, $pixel_cutoff = 1, $base_color = null)
731 {
732 return $this->getOperation('AutoCrop')->execute($this, $margin, $rgb_threshold, $pixel_cutoff, $base_color);
733 }
734
735 /**
736 * Returns a negative of the image
737 *
738 * This operation differs from calling WideImage_Image::applyFilter(IMG_FILTER_NEGATIVE), because it's 8-bit and transparency safe.
739 * 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,
740 * the resulting image will keep transparency.
741 *
742 * @return WideImage_Image negative of the image
743 */
744 function asNegative()
745 {
746 return $this->getOperation('AsNegative')->execute($this);
747 }
748
749 /**
750 * Returns a grayscale copy of the image
751 *
752 * @return WideImage_Image grayscale copy
753 **/
754 function asGrayscale()
755 {
756 return $this->getOperation('AsGrayscale')->execute($this);
757 }
758
759 /**
760 * Returns a mirrored copy of the image
761 *
762 * @return WideImage_Image Mirrored copy
763 **/
764 function mirror()
765 {
766 return $this->getOperation('Mirror')->execute($this);
767 }
768
769 /**
770 * Applies the unsharp filter
771 *
772 * @param float $amount
773 * @param float $radius
774 * @param float $threshold
775 * @return WideImage_Image Unsharpened copy of the image
776 **/
777 function unsharp($amount, $radius, $threshold)
778 {
779 return $this->getOperation('Unsharp')->execute($this, $amount, $radius, $threshold);
780 }
781
782 /**
783 * Returns a flipped (mirrored over horizontal line) copy of the image
784 *
785 * @return WideImage_Image Flipped copy
786 **/
787 function flip()
788 {
789 return $this->getOperation('Flip')->execute($this);
790 }
791
792 /**
793 * Corrects gamma on the image
794 *
795 * @param float $inputGamma
796 * @param float $outputGamma
797 * @return WideImage_Image Image with corrected gamma
798 **/
799 function correctGamma($inputGamma, $outputGamma)
800 {
801 return $this->getOperation('CorrectGamma')->execute($this, $inputGamma, $outputGamma);
802 }
803
804 /**
805 * Adds noise to the image
806 *
807 * @author Tomasz Kapusta
808 *
809 * @param int $amount Number of noise pixels to add
810 * @param string $type Type of noise 'salt&pepper', 'color' or 'mono'
811 * @return WideImage_Image Image with noise added
812 **/
813 function addNoise($amount, $type)
814 {
815 return $this->getOperation('AddNoise')->execute($this, $amount, $type);
816 }
817
818 /**
819 * Used internally to execute operations
820 *
821 * @param string $name
822 * @param array $args
823 * @return WideImage_Image
824 */
825 function __call($name, $args)
826 {
827 $op = $this->getOperation($name);
828 array_unshift($args, $this);
829 return call_user_func_array(array($op, 'execute'), $args);
830 }
831
832 /**
833 * Returns an image in GIF or PNG format
834 *
835 * @return string
836 */
837 function __toString()
838 {
839 if ($this->isTransparent())
840 return $this->asString('gif');
841 else
842 return $this->asString('png');
843 }
844
845 /**
846 * Returns a copy of the image object
847 *
848 * @return WideImage_Image The copy
849 **/
850 function copy()
851 {
852 $dest = $this->doCreate($this->getWidth(), $this->getHeight());
853 $dest->copyTransparencyFrom($this, true);
854 $this->copyTo($dest, 0, 0);
855 return $dest;
856 }
857
858 /**
859 * Copies this image onto another image
860 *
861 * @param WideImage_Image $dest
862 * @param int $left
863 * @param int $top
864 **/
865 function copyTo($dest, $left = 0, $top = 0)
866 {
867 if (!imagecopy($dest->getHandle(), $this->handle, $left, $top, 0, 0, $this->getWidth(), $this->getHeight()))
868 throw new WideImage_GDFunctionResultException("imagecopy() returned false");
869 }
870
871 /**
872 * Returns the canvas object
873 *
874 * The Canvas object can be used to draw text and shapes on the image
875 *
876 * Examples:
877 * <code>
878 * $img = WideImage::load('pic.jpg);
879 * $canvas = $img->getCanvas();
880 * $canvas->useFont('arial.ttf', 15, $img->allocateColor(200, 220, 255));
881 * $canvas->writeText(10, 50, "Hello world!");
882 *
883 * $canvas->filledRectangle(10, 10, 80, 40, $img->allocateColor(255, 127, 255));
884 * $canvas->line(60, 80, 30, 100, $img->allocateColor(255, 0, 0));
885 * $img->saveToFile('new.png');
886 * </code>
887 *
888 * @return WideImage_Canvas The Canvas object
889 **/
890 function getCanvas()
891 {
892 if ($this->canvas == null)
893 $this->canvas = new WideImage_Canvas($this);
894 return $this->canvas;
895 }
896
897 /**
898 * Returns true if the image is true-color, false otherwise
899 *
900 * @return bool
901 **/
902 abstract function isTrueColor();
903
904 /**
905 * Returns a true-color copy of the image
906 *
907 * @return WideImage_TrueColorImage
908 **/
909 abstract function asTrueColor();
910
911 /**
912 * Returns a palette copy (8bit) of the image
913 *
914 * @param int $nColors Number of colors in the resulting image, more than 0, less or equal to 255
915 * @param bool $dither Use dithering or not
916 * @param bool $matchPalette Set to true to use imagecolormatch() to match the resulting palette more closely to the original image
917 * @return WideImage_Image
918 **/
919 abstract function asPalette($nColors = 255, $dither = null, $matchPalette = true);
920
921 /**
922 * Retrieve an image with selected channels
923 *
924 * Examples:
925 * <code>
926 * $channels = $img->getChannels('red', 'blue');
927 * $channels = $img->getChannels('alpha', 'green');
928 * $channels = $img->getChannels(array('green', 'blue'));
929 * </code>
930 *
931 * @return WideImage_Image
932 **/
933 abstract function getChannels();
934
935 /**
936 * Returns an image without an alpha channel
937 *
938 * @return WideImage_Image
939 **/
940 abstract function copyNoAlpha();
941
942 /**
943 * Returns an array of serializable protected variables. Called automatically upon serialize().
944 *
945 * @return array
946 */
947 function __sleep()
948 {
949 $this->sdata = $this->asString('png');
950 return array('sdata', 'handleReleased');
951 }
952
953 /**
954 * Restores an image from serialization. Called automatically upon unserialize().
955 */
956 function __wakeup()
957 {
958 $temp_image = WideImage::loadFromString($this->sdata);
959 $temp_image->releaseHandle();
960 $this->handle = $temp_image->handle;
961 $temp_image = null;
962 $this->sdata = null;
963 }
964 }
965