ImageMedium#derivatives now works with image filters (#1107)

* 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
This commit is contained in:
Fredrik Ekelund
2016-10-23 04:44:28 +02:00
committed by Andy Miller
parent a96820af36
commit db4c9c1844
4 changed files with 69 additions and 56 deletions

View File

@@ -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']);
}
}
}

View File

@@ -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

View File

@@ -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;
}
/**

View File

@@ -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);