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
    • Tokenizer
    • Utils
  • Tracy
    • Bridges
      • Nette
  • none

Classes

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