Namespaces

  • Latte
    • Loaders
    • Macros
    • Runtime
  • Nette
    • Application
      • Responses
      • Routers
      • UI
    • Bridges
      • ApplicationDI
      • ApplicationLatte
      • ApplicationTracy
      • CacheDI
      • CacheLatte
      • DatabaseDI
      • DatabaseTracy
      • DITracy
      • FormsDI
      • FormsLatte
      • Framework
      • HttpDI
      • HttpTracy
      • MailDI
      • ReflectionDI
      • SecurityDI
      • SecurityTracy
    • Caching
      • Storages
    • ComponentModel
    • Database
      • Conventions
      • Drivers
      • Reflection
      • Table
    • DI
      • Config
        • Adapters
      • Extensions
    • Forms
      • Controls
      • Rendering
    • Http
    • Iterators
    • Loaders
    • Localization
    • Mail
    • Neon
    • PhpGenerator
    • Reflection
    • Security
    • Utils
  • none
  • Tracy
    • Bridges
      • Nette

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • LimitedScope
  • MimeTypeDetector
  • ObjectMixin
  • Paginator
  • Random
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

  • AssertionException
  • ImageException
  • JsonException
  • RegexpException
  • TokenizerException
  • UnknownImageFileException
  • Overview
  • Namespace
  • Class
  • Tree
  • Deprecated
  • Other releases
  • Nette homepage
  1: <?php
  2: 
  3: /**
  4:  * This file is part of the Nette Framework (https://nette.org)
  5:  * Copyright (c) 2004 David Grudl (https://davidgrudl.com)
  6:  */
  7: 
  8: namespace Nette\Utils;
  9: 
 10: use Nette;
 11: 
 12: 
 13: /**
 14:  * Basic manipulation with images.
 15:  *
 16:  * <code>
 17:  * $image = Image::fromFile('nette.jpg');
 18:  * $image->resize(150, 100);
 19:  * $image->sharpen();
 20:  * $image->send();
 21:  * </code>
 22:  *
 23:  * @method void alphaBlending(bool $on)
 24:  * @method void antialias(bool $on)
 25:  * @method void arc($x, $y, $w, $h, $start, $end, $color)
 26:  * @method void char(int $font, $x, $y, string $char, $color)
 27:  * @method void charUp(int $font, $x, $y, string $char, $color)
 28:  * @method int colorAllocate($red, $green, $blue)
 29:  * @method int colorAllocateAlpha($red, $green, $blue, $alpha)
 30:  * @method int colorAt($x, $y)
 31:  * @method int colorClosest($red, $green, $blue)
 32:  * @method int colorClosestAlpha($red, $green, $blue, $alpha)
 33:  * @method int colorClosestHWB($red, $green, $blue)
 34:  * @method void colorDeallocate($color)
 35:  * @method int colorExact($red, $green, $blue)
 36:  * @method int colorExactAlpha($red, $green, $blue, $alpha)
 37:  * @method void colorMatch(Image $image2)
 38:  * @method int colorResolve($red, $green, $blue)
 39:  * @method int colorResolveAlpha($red, $green, $blue, $alpha)
 40:  * @method void colorSet($index, $red, $green, $blue)
 41:  * @method array colorsForIndex($index)
 42:  * @method int colorsTotal()
 43:  * @method int colorTransparent($color = NULL)
 44:  * @method void convolution(array $matrix, float $div, float $offset)
 45:  * @method void copy(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH)
 46:  * @method void copyMerge(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
 47:  * @method void copyMergeGray(Image $src, $dstX, $dstY, $srcX, $srcY, $srcW, $srcH, $opacity)
 48:  * @method void copyResampled(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
 49:  * @method void copyResized(Image $src, $dstX, $dstY, $srcX, $srcY, $dstW, $dstH, $srcW, $srcH)
 50:  * @method void dashedLine($x1, $y1, $x2, $y2, $color)
 51:  * @method void ellipse($cx, $cy, $w, $h, $color)
 52:  * @method void fill($x, $y, $color)
 53:  * @method void filledArc($cx, $cy, $w, $h, $s, $e, $color, $style)
 54:  * @method void filledEllipse($cx, $cy, $w, $h, $color)
 55:  * @method void filledPolygon(array $points, $numPoints, $color)
 56:  * @method void filledRectangle($x1, $y1, $x2, $y2, $color)
 57:  * @method void fillToBorder($x, $y, $border, $color)
 58:  * @method void filter($filtertype)
 59:  * @method array ftText($size, $angle, $x, $y, $col, string $fontFile, string $text, array $extrainfo = NULL)
 60:  * @method void gammaCorrect(float $inputgamma, float $outputgamma)
 61:  * @method int interlace($interlace = NULL)
 62:  * @method bool isTrueColor()
 63:  * @method void layerEffect($effect)
 64:  * @method void line($x1, $y1, $x2, $y2, $color)
 65:  * @method void paletteCopy(Image $source)
 66:  * @method void polygon(array $points, $numPoints, $color)
 67:  * @method array psText(string $text, $font, $size, $color, $backgroundColor, $x, $y, $space = NULL, $tightness = NULL, float $angle = NULL, $antialiasSteps = NULL)
 68:  * @method void rectangle($x1, $y1, $x2, $y2, $col)
 69:  * @method Image rotate(float $angle, $backgroundColor)
 70:  * @method void saveAlpha(bool $saveflag)
 71:  * @method void setBrush(Image $brush)
 72:  * @method void setPixel($x, $y, $color)
 73:  * @method void setStyle(array $style)
 74:  * @method void setThickness($thickness)
 75:  * @method void setTile(Image $tile)
 76:  * @method void string($font, $x, $y, string $s, $col)
 77:  * @method void stringUp($font, $x, $y, string $s, $col)
 78:  * @method void trueColorToPalette(bool $dither, $ncolors)
 79:  * @method array ttfText($size, $angle, $x, $y, $color, string $fontfile, string $text)
 80:  * @property-read int $width
 81:  * @property-read int $height
 82:  * @property-read resource $imageResource
 83:  */
 84: class Image extends Nette\Object
 85: {
 86:     /** {@link resize()} only shrinks images */
 87:     const SHRINK_ONLY = 1;
 88: 
 89:     /** {@link resize()} will ignore aspect ratio */
 90:     const STRETCH = 2;
 91: 
 92:     /** {@link resize()} fits in given area so its dimensions are less than or equal to the required dimensions */
 93:     const FIT = 0;
 94: 
 95:     /** {@link resize()} fills given area so its dimensions are greater than or equal to the required dimensions */
 96:     const FILL = 4;
 97: 
 98:     /** {@link resize()} fills given area exactly */
 99:     const EXACT = 8;
100: 
101:     /** image types */
102:     const JPEG = IMAGETYPE_JPEG,
103:         PNG = IMAGETYPE_PNG,
104:         GIF = IMAGETYPE_GIF;
105: 
106:     const EMPTY_GIF = "GIF89a\x01\x00\x01\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;";
107: 
108:     /** @deprecated */
109:     const ENLARGE = 0;
110: 
111:     /** @var resource */
112:     private $image;
113: 
114: 
115:     /**
116:      * Returns RGB color.
117:      * @param  int  red 0..255
118:      * @param  int  green 0..255
119:      * @param  int  blue 0..255
120:      * @param  int  transparency 0..127
121:      * @return array
122:      */
123:     public static function rgb($red, $green, $blue, $transparency = 0)
124:     {
125:         return array(
126:             'red' => max(0, min(255, (int) $red)),
127:             'green' => max(0, min(255, (int) $green)),
128:             'blue' => max(0, min(255, (int) $blue)),
129:             'alpha' => max(0, min(127, (int) $transparency)),
130:         );
131:     }
132: 
133: 
134:     /**
135:      * Opens image from file.
136:      * @param  string
137:      * @param  mixed  detected image format
138:      * @throws Nette\NotSupportedException if gd extension is not loaded
139:      * @throws UnknownImageFileException if file not found or file type is not known
140:      * @return static
141:      */
142:     public static function fromFile($file, & $format = NULL)
143:     {
144:         if (!extension_loaded('gd')) {
145:             throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
146:         }
147: 
148:         static $funcs = array(
149:             self::JPEG => 'imagecreatefromjpeg',
150:             self::PNG => 'imagecreatefrompng',
151:             self::GIF => 'imagecreatefromgif',
152:         );
153:         $info = @getimagesize($file); // @ - files smaller than 12 bytes causes read error
154:         $format = $info[2];
155: 
156:         if (!isset($funcs[$format])) {
157:             throw new UnknownImageFileException(is_file($file) ? "Unknown type of file '$file'." : "File '$file' not found.");
158:         }
159:         return new static(Callback::invokeSafe($funcs[$format], array($file), function ($message) {
160:             throw new ImageException($message);
161:         }));
162:     }
163: 
164: 
165:     /**
166:      * @deprecated
167:      */
168:     public static function getFormatFromString($s)
169:     {
170:         trigger_error(__METHOD__ . '() is deprecated; use finfo_buffer() instead.', E_USER_DEPRECATED);
171:         $types = array('image/jpeg' => self::JPEG, 'image/gif' => self::GIF, 'image/png' => self::PNG);
172:         $type = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $s);
173:         return isset($types[$type]) ? $types[$type] : NULL;
174:     }
175: 
176: 
177:     /**
178:      * Create a new image from the image stream in the string.
179:      * @param  string
180:      * @param  mixed  detected image format
181:      * @return static
182:      * @throws ImageException
183:      */
184:     public static function fromString($s, & $format = NULL)
185:     {
186:         if (!extension_loaded('gd')) {
187:             throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
188:         }
189: 
190:         if (func_num_args() > 1) {
191:             $format = @static::getFormatFromString($s); // @ suppress trigger_error
192:         }
193: 
194:         return new static(Callback::invokeSafe('imagecreatefromstring', array($s), function ($message) {
195:             throw new ImageException($message);
196:         }));
197:     }
198: 
199: 
200:     /**
201:      * Creates blank image.
202:      * @param  int
203:      * @param  int
204:      * @param  array
205:      * @return static
206:      */
207:     public static function fromBlank($width, $height, $color = NULL)
208:     {
209:         if (!extension_loaded('gd')) {
210:             throw new Nette\NotSupportedException('PHP extension GD is not loaded.');
211:         }
212: 
213:         $width = (int) $width;
214:         $height = (int) $height;
215:         if ($width < 1 || $height < 1) {
216:             throw new Nette\InvalidArgumentException('Image width and height must be greater than zero.');
217:         }
218: 
219:         $image = imagecreatetruecolor($width, $height);
220:         if (is_array($color)) {
221:             $color += array('alpha' => 0);
222:             $color = imagecolorresolvealpha($image, $color['red'], $color['green'], $color['blue'], $color['alpha']);
223:             imagealphablending($image, FALSE);
224:             imagefilledrectangle($image, 0, 0, $width - 1, $height - 1, $color);
225:             imagealphablending($image, TRUE);
226:         }
227:         return new static($image);
228:     }
229: 
230: 
231:     /**
232:      * Wraps GD image.
233:      * @param  resource
234:      */
235:     public function __construct($image)
236:     {
237:         $this->setImageResource($image);
238:         imagesavealpha($image, TRUE);
239:     }
240: 
241: 
242:     /**
243:      * Returns image width.
244:      * @return int
245:      */
246:     public function getWidth()
247:     {
248:         return imagesx($this->image);
249:     }
250: 
251: 
252:     /**
253:      * Returns image height.
254:      * @return int
255:      */
256:     public function getHeight()
257:     {
258:         return imagesy($this->image);
259:     }
260: 
261: 
262:     /**
263:      * Sets image resource.
264:      * @param  resource
265:      * @return static
266:      */
267:     protected function setImageResource($image)
268:     {
269:         if (!is_resource($image) || get_resource_type($image) !== 'gd') {
270:             throw new Nette\InvalidArgumentException('Image is not valid.');
271:         }
272:         $this->image = $image;
273:         return $this;
274:     }
275: 
276: 
277:     /**
278:      * Returns image GD resource.
279:      * @return resource
280:      */
281:     public function getImageResource()
282:     {
283:         return $this->image;
284:     }
285: 
286: 
287:     /**
288:      * Resizes image.
289:      * @param  mixed  width in pixels or percent
290:      * @param  mixed  height in pixels or percent
291:      * @param  int    flags
292:      * @return static
293:      */
294:     public function resize($width, $height, $flags = self::FIT)
295:     {
296:         if ($flags & self::EXACT) {
297:             return $this->resize($width, $height, self::FILL)->crop('50%', '50%', $width, $height);
298:         }
299: 
300:         list($newWidth, $newHeight) = static::calculateSize($this->getWidth(), $this->getHeight(), $width, $height, $flags);
301: 
302:         if ($newWidth !== $this->getWidth() || $newHeight !== $this->getHeight()) { // resize
303:             $newImage = static::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
304:             imagecopyresampled(
305:                 $newImage, $this->image,
306:                 0, 0, 0, 0,
307:                 $newWidth, $newHeight, $this->getWidth(), $this->getHeight()
308:             );
309:             $this->image = $newImage;
310:         }
311: 
312:         if ($width < 0 || $height < 0) { // flip is processed in two steps for better quality
313:             $newImage = static::fromBlank($newWidth, $newHeight, self::RGB(0, 0, 0, 127))->getImageResource();
314:             imagecopyresampled(
315:                 $newImage, $this->image,
316:                 0, 0, $width < 0 ? $newWidth - 1 : 0, $height < 0 ? $newHeight - 1 : 0,
317:                 $newWidth, $newHeight, $width < 0 ? -$newWidth : $newWidth, $height < 0 ? -$newHeight : $newHeight
318:             );
319:             $this->image = $newImage;
320:         }
321:         return $this;
322:     }
323: 
324: 
325:     /**
326:      * Calculates dimensions of resized image.
327:      * @param  mixed  source width
328:      * @param  mixed  source height
329:      * @param  mixed  width in pixels or percent
330:      * @param  mixed  height in pixels or percent
331:      * @param  int    flags
332:      * @return array
333:      */
334:     public static function calculateSize($srcWidth, $srcHeight, $newWidth, $newHeight, $flags = self::FIT)
335:     {
336:         if (substr($newWidth, -1) === '%') {
337:             $newWidth = round($srcWidth / 100 * abs(substr($newWidth, 0, -1)));
338:             $percents = TRUE;
339:         } else {
340:             $newWidth = (int) abs($newWidth);
341:         }
342: 
343:         if (substr($newHeight, -1) === '%') {
344:             $newHeight = round($srcHeight / 100 * abs(substr($newHeight, 0, -1)));
345:             $flags |= empty($percents) ? 0 : self::STRETCH;
346:         } else {
347:             $newHeight = (int) abs($newHeight);
348:         }
349: 
350:         if ($flags & self::STRETCH) { // non-proportional
351:             if (empty($newWidth) || empty($newHeight)) {
352:                 throw new Nette\InvalidArgumentException('For stretching must be both width and height specified.');
353:             }
354: 
355:             if ($flags & self::SHRINK_ONLY) {
356:                 $newWidth = round($srcWidth * min(1, $newWidth / $srcWidth));
357:                 $newHeight = round($srcHeight * min(1, $newHeight / $srcHeight));
358:             }
359: 
360:         } else {  // proportional
361:             if (empty($newWidth) && empty($newHeight)) {
362:                 throw new Nette\InvalidArgumentException('At least width or height must be specified.');
363:             }
364: 
365:             $scale = array();
366:             if ($newWidth > 0) { // fit width
367:                 $scale[] = $newWidth / $srcWidth;
368:             }
369: 
370:             if ($newHeight > 0) { // fit height
371:                 $scale[] = $newHeight / $srcHeight;
372:             }
373: 
374:             if ($flags & self::FILL) {
375:                 $scale = array(max($scale));
376:             }
377: 
378:             if ($flags & self::SHRINK_ONLY) {
379:                 $scale[] = 1;
380:             }
381: 
382:             $scale = min($scale);
383:             $newWidth = round($srcWidth * $scale);
384:             $newHeight = round($srcHeight * $scale);
385:         }
386: 
387:         return array(max((int) $newWidth, 1), max((int) $newHeight, 1));
388:     }
389: 
390: 
391:     /**
392:      * Crops image.
393:      * @param  mixed  x-offset in pixels or percent
394:      * @param  mixed  y-offset in pixels or percent
395:      * @param  mixed  width in pixels or percent
396:      * @param  mixed  height in pixels or percent
397:      * @return static
398:      */
399:     public function crop($left, $top, $width, $height)
400:     {
401:         list($left, $top, $width, $height) = static::calculateCutout($this->getWidth(), $this->getHeight(), $left, $top, $width, $height);
402:         $newImage = static::fromBlank($width, $height, self::RGB(0, 0, 0, 127))->getImageResource();
403:         imagecopy($newImage, $this->image, 0, 0, $left, $top, $width, $height);
404:         $this->image = $newImage;
405:         return $this;
406:     }
407: 
408: 
409:     /**
410:      * Calculates dimensions of cutout in image.
411:      * @param  mixed  source width
412:      * @param  mixed  source height
413:      * @param  mixed  x-offset in pixels or percent
414:      * @param  mixed  y-offset in pixels or percent
415:      * @param  mixed  width in pixels or percent
416:      * @param  mixed  height in pixels or percent
417:      * @return array
418:      */
419:     public static function calculateCutout($srcWidth, $srcHeight, $left, $top, $newWidth, $newHeight)
420:     {
421:         if (substr($newWidth, -1) === '%') {
422:             $newWidth = round($srcWidth / 100 * substr($newWidth, 0, -1));
423:         }
424:         if (substr($newHeight, -1) === '%') {
425:             $newHeight = round($srcHeight / 100 * substr($newHeight, 0, -1));
426:         }
427:         if (substr($left, -1) === '%') {
428:             $left = round(($srcWidth - $newWidth) / 100 * substr($left, 0, -1));
429:         }
430:         if (substr($top, -1) === '%') {
431:             $top = round(($srcHeight - $newHeight) / 100 * substr($top, 0, -1));
432:         }
433:         if ($left < 0) {
434:             $newWidth += $left;
435:             $left = 0;
436:         }
437:         if ($top < 0) {
438:             $newHeight += $top;
439:             $top = 0;
440:         }
441:         $newWidth = min((int) $newWidth, $srcWidth - $left);
442:         $newHeight = min((int) $newHeight, $srcHeight - $top);
443:         return array($left, $top, $newWidth, $newHeight);
444:     }
445: 
446: 
447:     /**
448:      * Sharpen image.
449:      * @return static
450:      */
451:     public function sharpen()
452:     {
453:         imageconvolution($this->image, array( // my magic numbers ;)
454:             array(-1, -1, -1),
455:             array(-1, 24, -1),
456:             array(-1, -1, -1),
457:         ), 16, 0);
458:         return $this;
459:     }
460: 
461: 
462:     /**
463:      * Puts another image into this image.
464:      * @param  Image
465:      * @param  mixed  x-coordinate in pixels or percent
466:      * @param  mixed  y-coordinate in pixels or percent
467:      * @param  int  opacity 0..100
468:      * @return static
469:      */
470:     public function place(Image $image, $left = 0, $top = 0, $opacity = 100)
471:     {
472:         $opacity = max(0, min(100, (int) $opacity));
473: 
474:         if (substr($left, -1) === '%') {
475:             $left = round(($this->getWidth() - $image->getWidth()) / 100 * substr($left, 0, -1));
476:         }
477: 
478:         if (substr($top, -1) === '%') {
479:             $top = round(($this->getHeight() - $image->getHeight()) / 100 * substr($top, 0, -1));
480:         }
481: 
482:         if ($opacity === 100) {
483:             imagecopy(
484:                 $this->image, $image->getImageResource(),
485:                 $left, $top, 0, 0, $image->getWidth(), $image->getHeight()
486:             );
487: 
488:         } elseif ($opacity != 0) {
489:             $cutting = imagecreatetruecolor($image->getWidth(), $image->getHeight());
490:             imagecopy(
491:                 $cutting, $this->image,
492:                 0, 0, $left, $top, $image->getWidth(), $image->getHeight()
493:             );
494:             imagecopy(
495:                 $cutting, $image->getImageResource(),
496:                 0, 0, 0, 0, $image->getWidth(), $image->getHeight()
497:             );
498: 
499:             imagecopymerge(
500:                 $this->image, $cutting,
501:                 $left, $top, 0, 0, $image->getWidth(), $image->getHeight(),
502:                 $opacity
503:             );
504:         }
505:         return $this;
506:     }
507: 
508: 
509:     /**
510:      * Saves image to the file.
511:      * @param  string  filename
512:      * @param  int  quality 0..100 (for JPEG and PNG)
513:      * @param  int  optional image type
514:      * @return bool TRUE on success or FALSE on failure.
515:      */
516:     public function save($file = NULL, $quality = NULL, $type = NULL)
517:     {
518:         if ($type === NULL) {
519:             switch (strtolower($ext = pathinfo($file, PATHINFO_EXTENSION))) {
520:                 case 'jpg':
521:                 case 'jpeg':
522:                     $type = self::JPEG;
523:                     break;
524:                 case 'png':
525:                     $type = self::PNG;
526:                     break;
527:                 case 'gif':
528:                     $type = self::GIF;
529:                     break;
530:                 default:
531:                     throw new Nette\InvalidArgumentException("Unsupported file extension '$ext'.");
532:             }
533:         }
534: 
535:         switch ($type) {
536:             case self::JPEG:
537:                 $quality = $quality === NULL ? 85 : max(0, min(100, (int) $quality));
538:                 return imagejpeg($this->image, $file, $quality);
539: 
540:             case self::PNG:
541:                 $quality = $quality === NULL ? 9 : max(0, min(9, (int) $quality));
542:                 return imagepng($this->image, $file, $quality);
543: 
544:             case self::GIF:
545:                 return imagegif($this->image, $file);
546: 
547:             default:
548:                 throw new Nette\InvalidArgumentException("Unsupported image type '$type'.");
549:         }
550:     }
551: 
552: 
553:     /**
554:      * Outputs image to string.
555:      * @param  int  image type
556:      * @param  int  quality 0..100 (for JPEG and PNG)
557:      * @return string
558:      */
559:     public function toString($type = self::JPEG, $quality = NULL)
560:     {
561:         ob_start(function () {});
562:         $this->save(NULL, $quality, $type);
563:         return ob_get_clean();
564:     }
565: 
566: 
567:     /**
568:      * Outputs image to string.
569:      * @return string
570:      */
571:     public function __toString()
572:     {
573:         try {
574:             return $this->toString();
575:         } catch (\Throwable $e) {
576:         } catch (\Exception $e) {
577:         }
578:         if (isset($e)) {
579:             if (func_num_args()) {
580:                 throw $e;
581:             }
582:             trigger_error("Exception in " . __METHOD__ . "(): {$e->getMessage()} in {$e->getFile()}:{$e->getLine()}", E_USER_ERROR);
583:         }
584:     }
585: 
586: 
587:     /**
588:      * Outputs image to browser.
589:      * @param  int  image type
590:      * @param  int  quality 0..100 (for JPEG and PNG)
591:      * @return bool TRUE on success or FALSE on failure.
592:      */
593:     public function send($type = self::JPEG, $quality = NULL)
594:     {
595:         if (!in_array($type, array(self::JPEG, self::PNG, self::GIF), TRUE)) {
596:             throw new Nette\InvalidArgumentException("Unsupported image type '$type'.");
597:         }
598:         header('Content-Type: ' . image_type_to_mime_type($type));
599:         return $this->save(NULL, $quality, $type);
600:     }
601: 
602: 
603:     /**
604:      * Call to undefined method.
605:      *
606:      * @param  string  method name
607:      * @param  array   arguments
608:      * @return mixed
609:      * @throws Nette\MemberAccessException
610:      */
611:     public function __call($name, $args)
612:     {
613:         $function = 'image' . $name;
614:         if (!function_exists($function)) {
615:             return parent::__call($name, $args);
616:         }
617: 
618:         foreach ($args as $key => $value) {
619:             if ($value instanceof self) {
620:                 $args[$key] = $value->getImageResource();
621: 
622:             } elseif (is_array($value) && isset($value['red'])) { // rgb
623:                 $args[$key] = imagecolorallocatealpha(
624:                     $this->image,
625:                     $value['red'], $value['green'], $value['blue'], $value['alpha']
626:                 ) ?: imagecolorresolvealpha(
627:                     $this->image,
628:                     $value['red'], $value['green'], $value['blue'], $value['alpha']
629:                 );
630:             }
631:         }
632:         array_unshift($args, $this->image);
633: 
634:         $res = call_user_func_array($function, $args);
635:         return is_resource($res) && get_resource_type($res) === 'gd' ? $this->setImageResource($res) : $res;
636:     }
637: 
638: 
639:     public function __clone()
640:     {
641:         ob_start(function () {});
642:         imagegd2($this->image);
643:         $this->setImageResource(imagecreatefromstring(ob_get_clean()));
644:     }
645: 
646: }
647: 
Nette 2.3-20161221 API API documentation generated by ApiGen 2.8.0