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