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

Classes

  • ArrayHash
  • ArrayList
  • Arrays
  • Callback
  • DateTime
  • FileSystem
  • Finder
  • Html
  • Image
  • Json
  • ObjectHelpers
  • ObjectMixin
  • Paginator
  • Random
  • Reflection
  • SafeStream
  • Strings
  • TokenIterator
  • Tokenizer
  • Validators

Interfaces

  • IHtmlString

Exceptions

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