From db4c9c184466968dfc9193575b3ec0cc27efc4f5 Mon Sep 17 00:00:00 2001 From: Fredrik Ekelund Date: Sun, 23 Oct 2016 04:44:28 +0200 Subject: [PATCH] ImageMedium#derivatives now works with image filters (#1107) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ImageMedium->derivatives now works with image filters Previously, using ImageMedium->derivatives would not work well in combination with image filters or the other method of generating srcset variants of images (by appending eg. "@2x" to their filenames). This commit hopefully fixes that. * Modified initialization of image alternatives The biggest alternative will now become the base medium, and alternatives will be filled out as necessary in a descending manner. * Fully reset image when derivatives method is called Otherwise we get some funky results, with the possibility of two different images being rendered between the full-width srcset version and the original src version. * Account for risk of original file not existing when generating image derivatives * Fixed an issue where too many alternatives would be generated When using naming conventions to generate image alternatives, this patch would previously generate a “@1x” alternative if one didn’t exist. That’s no longer the case * Add an "@1x" alternative when an image lacks a base medium When an image only has an alternative medium - ie. the only version of the image ends in eg. "@3x", then we construct the missing alternatives automatically. Previously, we would only do this down till "@2x", meaning that the image that would have been the base medium, had the image been manually resized, wasn't created. This has now been fixed. * Always make smallest image alternative the base medium When an image lacks a base medium on disk (eg. the only existing image is an @2x version), then we make a scaled down version the base medium, which ensures that the smaller version is served up in the src attribute in the HTML. Also, don't reset the image alternatives when calling ImageMedium#derivatives, instead only generate the image alternatives that are missing. * Set better prettynames for image derivatives * Changed image derivatives prettynames to be width based Instead of example2x.jpeg, we now have eg. example1280w.jpeg --- system/src/Grav/Common/Page/Media.php | 20 ++-- .../Grav/Common/Page/Medium/ImageMedium.php | 97 +++++++++++-------- system/src/Grav/Common/Page/Medium/Medium.php | 4 +- .../Grav/Common/Page/Medium/MediumFactory.php | 4 +- 4 files changed, 69 insertions(+), 56 deletions(-) diff --git a/system/src/Grav/Common/Page/Media.php b/system/src/Grav/Common/Page/Media.php index c8385ea15..c4451e08c 100644 --- a/system/src/Grav/Common/Page/Media.php +++ b/system/src/Grav/Common/Page/Media.php @@ -74,16 +74,13 @@ class Media extends Getters } // Create the base medium - if (!empty($types['base'])) { + if (empty($types['base'])) { + $max = max(array_keys($types['alternative'])); + $medium = $types['alternative'][$max]['file']; + $medium = MediumFactory::scaledFromMedium($medium, $max, 1)['file']; + } else { $medium = MediumFactory::fromFile($types['base']['file']); $medium && $medium->set('size', $types['base']['size']); - } else if (!empty($types['alternative'])) { - $altMedium = reset($types['alternative']); - $ratio = key($types['alternative']); - - $altMedium = $altMedium['file']; - - $medium = MediumFactory::scaledFromMedium($altMedium, $ratio, 1)['file']; } if (empty($medium)) { @@ -103,10 +100,9 @@ class Media extends Getters // Build missing alternatives if (!empty($types['alternative'])) { $alternatives = $types['alternative']; - $max = max(array_keys($alternatives)); - for ($i=2; $i < $max; $i++) { + for ($i=$max; $i > 1; $i--) { if (isset($alternatives[$i])) { continue; } @@ -115,7 +111,9 @@ class Media extends Getters } foreach ($types['alternative'] as $ratio => $altMedium) { - $medium->addAlternative($ratio, $altMedium['file']); + if ($altMedium['file'] != $medium) { + $medium->addAlternative($ratio, $altMedium['file']); + } } } diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index e3900baef..a2bb7b169 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -65,11 +65,6 @@ class ImageMedium extends Medium 'zoomCrop' => [0, 1] ]; - /** - * @var array - */ - protected $derivatives = []; - /** * @var string */ @@ -197,27 +192,19 @@ class ImageMedium extends Medium */ public function srcset($reset = true) { - if (empty($this->alternatives) && empty($this->derivatives)) { + if (empty($this->alternatives)) { if ($reset) { $this->reset(); } + return ''; } - if (!empty($this->derivatives)) { - asort($this->derivatives); - - foreach ($this->derivatives as $url => $width) { - $srcset[] = $url . ' ' . $width . 'w'; - } - - $srcset[] = $this->url($reset) . ' ' . $this->get('width') . 'w'; - } else { - $srcset = [$this->url($reset) . ' ' . $this->get('width') . 'w']; - foreach ($this->alternatives as $ratio => $medium) { - $srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w'; - } + $srcset = []; + foreach ($this->alternatives as $ratio => $medium) { + $srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w'; } + $srcset[] = $this->url($reset) . ' ' . $this->get('width') . 'w'; return implode(', ', $srcset); } @@ -257,31 +244,53 @@ class ImageMedium extends Medium * @return $this */ public function derivatives($min_width, $max_width, $step = 200) { - $width = $min_width; - - // Do not upscale images. - if ($max_width > $this->get('width')) { - $max_width = $this->get('width'); - } - - while ($width <= $max_width) { - $ratio = $width / $this->get('width'); - $derivative = MediumFactory::scaledFromMedium($this, 1, $ratio); - if (is_array($derivative)) { - $this->addDerivative($derivative['file']); + if (!empty($this->alternatives)) { + $max = max(array_keys($this->alternatives)); + $base = $this->alternatives[$max]; + } else { + $base = $this; } - $width += $step; - } - return $this; - } - /** - * Add a derivative - * - * @param ImageMedium $image - */ - public function addDerivative(ImageMedium $image) { - $this->derivatives[$image->url()] = $image->get('width'); + // Do not upscale images. + $max_width = min($max_width, $base->get('width')); + + for ($width = $min_width; $width < $max_width; $width = $width + $step) { + // Only generate image alternatives that don't already exist + if (array_key_exists((int) $width, $this->alternatives)) { + continue; + } + + $derivative = MediumFactory::fromFile($base->get('filepath')); + + // It's possible that MediumFactory::fromFile returns null if the + // original image file no longer exists and this class instance was + // retrieved from the page cache + if (isset($derivative)) { + $index = 2; + $widths = array_keys($this->alternatives); + sort($widths); + + foreach ($widths as $i => $key) { + if ($width > $key) { + $index += max($i, 1); + } + } + + $basename = preg_replace('/(@\d+x){0,1}$/', "@{$width}w", $base->get('basename'), 1); + $derivative->setImagePrettyName($basename); + + $ratio = $base->get('width') / $width; + $height = $derivative->get('height') / $ratio; + + $derivative->resize($width, $height); + $derivative->set('width', $width); + $derivative->set('height', $height); + + $this->addAlternative($ratio, $derivative); + } + } + + return $this; } /** @@ -489,6 +498,10 @@ class ImageMedium extends Medium call_user_func_array([$this->image, $method], $args); foreach ($this->alternatives as $ratio => $medium) { + if (!$medium->image) { + $medium->image(); + } + $args_copy = $args; // regular image: resize 400x400 -> 200x200 diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index b13306f52..b9e338854 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -100,7 +100,9 @@ class Medium extends Data implements RenderableInterface } $alternative->set('ratio', $ratio); - $this->alternatives[(float) $ratio] = $alternative; + $width = $alternative->get('width'); + + $this->alternatives[$width] = $alternative; } /** diff --git a/system/src/Grav/Common/Page/Medium/MediumFactory.php b/system/src/Grav/Common/Page/Medium/MediumFactory.php index c75044f5c..3743a81cf 100644 --- a/system/src/Grav/Common/Page/Medium/MediumFactory.php +++ b/system/src/Grav/Common/Page/Medium/MediumFactory.php @@ -119,8 +119,8 @@ class MediumFactory } $ratio = $to / $from; - $width = (int) ($medium->get('width') * $ratio); - $height = (int) ($medium->get('height') * $ratio); + $width = $medium->get('width') * $ratio; + $height = $medium->get('height') * $ratio; $prev_basename = $medium->get('basename'); $basename = str_replace('@'.$from.'x', '@'.$to.'x', $prev_basename);