Skip to content

Commit

Permalink
ImageMedium#derivatives now works with image filters (#1107)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
fredrikekelund authored and rhukster committed Oct 23, 2016
1 parent a96820a commit db4c9c1
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 56 deletions.
20 changes: 9 additions & 11 deletions system/src/Grav/Common/Page/Media.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,16 +74,13 @@ public function __construct($path)
}

// 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)) {
Expand All @@ -103,10 +100,9 @@ public function __construct($path)
// 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;
}
Expand All @@ -115,7 +111,9 @@ public function __construct($path)
}

foreach ($types['alternative'] as $ratio => $altMedium) {
$medium->addAlternative($ratio, $altMedium['file']);
if ($altMedium['file'] != $medium) {
$medium->addAlternative($ratio, $altMedium['file']);
}
}
}

Expand Down
97 changes: 55 additions & 42 deletions system/src/Grav/Common/Page/Medium/ImageMedium.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,6 @@ class ImageMedium extends Medium
'zoomCrop' => [0, 1]
];

/**
* @var array
*/
protected $derivatives = [];

/**
* @var string
*/
Expand Down Expand Up @@ -197,27 +192,19 @@ public function cache()
*/
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);
}
Expand Down Expand Up @@ -257,31 +244,53 @@ public function getImagePrettyName()
* @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;
}

/**
Expand Down Expand Up @@ -489,6 +498,10 @@ public function __call($method, $args)
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
Expand Down
4 changes: 3 additions & 1 deletion system/src/Grav/Common/Page/Medium/Medium.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ public function addAlternative($ratio, Medium $alternative)
}

$alternative->set('ratio', $ratio);
$this->alternatives[(float) $ratio] = $alternative;
$width = $alternative->get('width');

$this->alternatives[$width] = $alternative;
}

/**
Expand Down
4 changes: 2 additions & 2 deletions system/src/Grav/Common/Page/Medium/MediumFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ public static function scaledFromMedium($medium, $from, $to)
}

$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);
Expand Down

0 comments on commit db4c9c1

Please sign in to comment.