Jump to content

Webp support


jacmaes

Recommended Posts

Please can someone test the .htaccess solution for WebP support in other environments?

It is not much work, you don't have to install test branches or anything else. You only need to copy some lines into your .htaccess file and create a single webp image with the tool of your choice. Or if you don't have a converter tool for webp, simply make a copy of a jpeg and rename it with type .webp instead of .jpg.

Steps to proceed:

  1. Copy the rewrite directives into the top of your .htaccess file 
  2. Grab any image variation you already have embedded into a PW site, create a webp variation of and save it into the same assets/files/{ID}/ folder.
    (Maybe you want to invert the colors of the webp copy for better detection!)
  3. Create or go to a page where this image should be rendered as jpeg in a regular img tag, and look if the webp copy is served and displayed.
    (Don't forget to flush browser caches)

The .htaccess code:

Spoiler

AddType image/webp .webp

<IfModule mod_rewrite.c>

    RewriteEngine On
    AddDefaultCharset UTF-8

    ### Redirect regular PW JPEGs and PNGs to their WEBP image copies,
    ### if available and if the Browser supports WEBP:

        ## Does Browser accept WEBP images?
        RewriteCond %{HTTP_ACCEPT} image/webp

        ## Is it an existing file?
        RewriteCond %{REQUEST_FILENAME} -f

        ## Does a WEBP copy exist for it?
        RewriteCond %{DOCUMENT_ROOT}/$1$2$3/$4.webp -f

        ## With an added GET var or GET value of skiprewrite we do send the original JPEG or PNG
        RewriteCond expr "! %{QUERY_STRING} -strmatch '*skiprewrite*'"
        ## With an added GET var or GET value from the PW Page Editor, we do send the original JPEG or PNG
        RewriteCond expr "! %{QUERY_STRING} -strmatch 'nc=*'"

    ## Is it a regular PW JPEG or PNG image, stored under site/assets/files?
    RewriteRule ^(.*?)(site/assets/files/)([0-9]+)/(.*)\.(jpe?g|png)(.*)$  /$1$2$3/$4.webp [L]


	## Test that should show the original jpeg or png
    ###RewriteRule ^(.*?)(site/assets/files/)([0-9]+)/(.*)\.(jpe?g|png)$  /$1$2$3/$4.$5 [L]
  
	## Test with a PHP file in web root, that shows the references builded by the conditions. Useful for debugging with a direct call to a image url.
    ###RewriteRule ^(.*?)(site/assets/files/)([0-9]+)/(.*)\.(jpe?g|png)$  /matched.php?1=$1&2=$2&3=$3&4=$4&5=$5  [L]

</IfModule>

 

My matched.php for debugging:

Spoiler

<?php namespace ProcessWire;

echo "<ol>";
foreach($_GET as $item) echo "<li>{$item}</li>";
echo "</ol>";

$fn = $_SERVER['DOCUMENT_ROOT'] . '/' . $_GET[1] . $_GET[2] . $_GET[3] . '/' . $_GET[4] . '.webp';

echo "<p>{$fn} :: " . (file_exists($fn) ? 'yes' : 'no') . "</p>";

 

 

Examples in Opera with opened Network-Tab, requesting one image that has a webp copy, and requesting the second image that do not have a webp copy:

pw_htaccess_webp_support.thumb.gif.7791ff59d467fc7ee3dfaf0ea9aaf2d7.gif

Same page in Opera and old IE11:

pw_htaccess_webp_support.png.d110db833030e6825bb9c426f6bbcf0b.png

 

If we have a wider test case, the chance that this can be embedded into the PW .htaccess file for optional usage (commented by default) would be helpful for many users to support webp with less effort on their sites!

 

  • Like 3
Link to comment
Share on other sites

11 hours ago, horst said:

Please can someone test the .htaccess solution for WebP support in other environments?

It's working here in Laragon, Windows.

But won't this have the effect that if a visitor saves the image from their browser they will be saving a WEBP image with a JPG (PNG, GIF, etc) file extension? Might cause some confusion.

  • Like 2
Link to comment
Share on other sites

On 4/28/2019 at 7:44 AM, Robin S said:

But won't this have the effect that if a visitor saves the image from their browser they will be saving a WEBP image with a JPG (PNG, GIF, etc) file extension? Might cause some confusion.

Thanks for testing. And yes, this is right.

But for me personally, (and for most of my clients), the main goal is to present pages with text and images on the screen, and not to let the visitors save images for later use. I think, if a visitor need a visual copy of a page, he still can save the complete page with his browser, or he can do screenshots. On photographer sites, the use of images independend from viewing the site / page are often prohibited. There it even should not be recognized that images with filetype jpeg in real are weppys. But that is my personal opinion, and maybe thats not a sufficient reason to include this into the PW htaccess.

But it also could be done the other way round: send .webp in markup and let the htaccess directives change this to (png|jpg) if a) the browser doesn't support this, or b) if there is no webp file available. This way round, in a 100% perfect site, all newer browsers would get 100% webp with correct file type. Only older browsers would get jpegs or pngs with file type webp. 🙂

  • Like 6
Link to comment
Share on other sites

5 hours ago, horst said:

I think, if a visitor need a visual copy of a page, he still can save the complete page with his browser, or he can do screenshots.

Because of this, is it worth also confusing the maintainers of the site? When debugging issues, it is easy to trip over files named in misleading ways.

  • Like 2
Link to comment
Share on other sites

3 hours ago, szabesz said:

When debugging issues,

you may use the new debug info for images.

and / or skip the rewrite: /site/assets/files/1/basename.jpg?skiprewrite 

On 4/27/2019 at 8:37 PM, horst said:

## With an added GET var or GET value of skiprewrite we do send the original JPEG or PNG
RewriteCond expr "! %{QUERY_STRING} -strmatch '*skiprewrite*'"

And: nobody is forced to use it this way. You always can create conditional markup. My goal is to collect different solutions, and with the .htaccess only, to show and discuss a solution that would not need a rewrite for existing sites, or at least, makes the rewrite as small as possible. (change all image variations in markup from .jpg to .webp).

EDIT:

3 hours ago, szabesz said:

confusing the maintainers

I think maintainers should know about the fact if they have implemented a webp solution and if so, wich one. If, for what ever reason, someone don't like this solution in a site, he shouldn't use it.

  • Like 3
Link to comment
Share on other sites

Hi @horst,
My ImageSizerEngineVips is based on your ImageSizerEngineIMagickCLI with code from other sizers. The module works fine, but at the moment only for my needs, because I optimized for speed and left some options like sharpening. But if I have the time and interest, I can polish it.
Another thing:
I only use WebP in pages with lazy loading and srcset. Because of that I have only variations as WebP, not the uploaded pageimage. Your change here checks not only variations, right? For me, it changes many variations that already exist. 

  • Like 1
Link to comment
Share on other sites

Hi Horst, I'm late on that one, thx for all your work! 🙂 

$image = $page->images->first()->size(300, 300);

?>
<picture>
    <?php if($image->hasWebp) { ?>
    <source srcset="<?=$image->srcWebp?>" type="image/webp" />
    <?php } ?>
    <source srcset="<?=$image->src?>" type="image/jpeg" />
    <img src="<?=$image->url?>" alt="Alt Text!" />
</picture>

What about something like this to ?

$image = $page->images->first()->size(300, 300);
echo $image->hasWebp(
  "<source srcset='{srcWebp}' type='image/webp'/>",
  "<source srcset='{src}' type='image/jpeg'/><img src='{url}' alt='{description}'/>"
);

Such a method could even be extended with custom templates set via $config->webp or the like and could also get a third argument for options. Then the example above could be:

echo $page->images->first()->size(300, 300)->hasWebp();

...and produce the exact same output as your example. Maybe $img->webp(...) would be a better name for such a method than $img->hasWebp(...)

Link to comment
Share on other sites

On 4/29/2019 at 9:38 PM, Noboru said:

I only use WebP in pages with lazy loading and srcset. Because of that I have only variations as WebP, not the uploaded pageimage. Your change here checks not only variations, right? For me, it changes many variations that already exist. 

@Noboru I now have changed it to work with all regular use cases in PW. But this includes to have a regular file and optionally a webp file. But this is/was the behave before the core webp addition too.

Please try if it now works for you, or report back. If not, we can define a custom hook or a custom function to suite your setup.

  • Like 2
Link to comment
Share on other sites

Hello @horst,

thank you for your efforts to bring WebP to ProcessWire.

I can't wait till it is implemented in the core and want to try it out, but have some problems getting it to work. I hope you can help me:

I have downloaded your modified ProcessWire from your pull request and replaced the wire folder with your wire folder, because I don't want to mess up the Git development branch of my project. Maybe I don't understand the concept of pull requests right. 😀

Following files have changed:

  • wire/config.php
  • wire/core/ImageSizerEngine.php
  • wire/core/ImageSizerEngineGD.php
  • wire/core/Pageimage.php
  • wire/modules/Image/ImageSizerEngineIMagick/ImageSizerEngineIMagick.module

Now if I have understood correctly, I should be able to generate a WebP of an image with:

<?php

$options = [
	"webpAdd" => true,
	"webpQuality"=> 90
];

page()->image->size(800, 600, $options);

Now I get the error:

image.png.a580d1a56e219be19b70d247563286a9.png

Am I missing something or has it to do with my local MAMP?

Regards, Andreas

Link to comment
Share on other sites

Thank you for the hint, that seems to be it. Here is a screenshot of my local ImageMagick installed with Homebrew on MacOS:

image.png.ee146d3e6dd774c575def84476fb8d5a.png

But even when I uninstall the core module IMagick Image Sizer, I cannot create a WebP image with ImageSizerEngineGD.

Our hoster doesn't seem to support WebP either unfortunately.

Not so easy to use WepP at the moment. 😀

  • Sad 1
Link to comment
Share on other sites

@AndZyk I have sent a new commit to github to cover this. Please can you download the 3 new files:

core/Pageimage.php
core/ImageSizerEngineGD.php
modules/Image/ImageSizerEngineIMagick.module

If you want to test / debug the webp functionality of both engines in one call, please use code like this in a template file:

    $options = [
        'forceEngine' => 'ImageSizerEngineGD',
        'suffix' => 'gd',
        'forceNew' => true,
        'webpAdd' => true,
        'webpQuality' => 90,
        ];
    $img = $page->images->first->size(150, 150, $options);
	echo $img->getDebugInfo();

    $options = [
        'forceEngine' => 'ImageSizerEngineIMagick',
        'suffix' => 'im',
        'forceNew' => true,
        'webpAdd' => true,
        'webpQuality' => 90,
        ];
    $img = $page->images->first->size(150, 150, $options);
	echo $img->getDebugInfo();

A) it now should run without errors when you request a webp copy and the engine doesn't support it, and B) you can see it in the debug info if the selected engine supports webp.

engine-webp-supported.png.851f511528c85eeeac2cd50668695ad5.png

  • Like 1
Link to comment
Share on other sites

Thank you, I have downloaded your new commit and now I don't get an error anymore.

Unfortunately it seems, that the GD library and Imagick by MAMP doesn't support WebP yet. I found an tutorial how to recompile PHP for MAMP with WebP, but I rather wait for an update.

I noticed that the forceEngine option doesn't change the engine for me. I first had to uninstall the core module IMagick Image Sizer, so that the GD library would be used. Could that be a bug or have I done something wrong? 

It seems, that for now I will have to wait for more WebP support by MAMP and my hoster. But at least ProcessWire will be ready. 😉

Link to comment
Share on other sites

53 minutes ago, AndZyk said:

I noticed that the forceEngine option doesn't change the engine for me. 

Hmm, but it should. What priority settings do you have set?

Link to comment
Share on other sites

Hi @AndZyk,

you could try this:
brew install imagemagick
brew install pkg-config
pecl install imagick

For me this installs 
/Applications/MAMP/bin/php/php7.1.22/lib/php/extensions/no-debug-non-zts-20160303/imagick.so

In MAMP I switched to PHP 7.1.22 and edited in corresponding php.ini
MAMP_Imagick_MAMPextension=/Applications/MAMP/bin/php/php7.1.22/lib/php/extensions/no-debug-non-zts-20160303/imagick.so
 

Now MAMP should use a newer Imagmagick with WebP-Support.

Hope that helps.

Gruß
Armin

phpinfo().png

  • Like 2
Link to comment
Share on other sites

On 4/28/2019 at 7:44 AM, Robin S said:

But won't this have the effect that if a visitor saves the image from their browser they will be saving a WEBP image with a JPG (PNG, GIF, etc) file extension? Might cause some confusion.

This is the only disadvantage I see, but it might outweigh all the advantages.

On 4/29/2019 at 8:18 AM, arjen said:

Thanks for your effort on this. I'll check it out this week and let you know my test findings.

It works fine on my local docker environment. Nice 👍 I am moving servers, but I will check it out on a VPS but I won't see why it shouldn't work.

  • Thanks 1
Link to comment
Share on other sites

Thank you @Noboru, I will try this out. 😉

I am wondering: Can you even open WebP files in apps other than browsers without having the codec installed?

As far as I know you cannot simply open a .webp file. After all it is meant for browers. But I haven't tried it much.

In my opinion I would not serve WebP content in a .jpg or .png file. I would serve only WebP in a .webp file for browsers that support it. 😀

  • Like 2
Link to comment
Share on other sites

My favourite atm is to serve webp format in webp files and use the htaccess solution to detect missing browser support and then, as fallback, serve jpeg format in webp files. This false file types automatically will go away by time, as browser support becomes complete.

Edited by horst
  • Like 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Robin S
      Select Images
      An inputfield that allows the visual selection and sorting of images, intended for use with the FieldtypeDynamicOptions module. Together these modules can be used to create a kind of "image reference" field.

      Integration with FieldtypeDynamicOptions
      InputfieldSelectImages was developed to be used together with FieldtypeDynamicOptions (v0.1.3 or newer):
      Create a Dynamic Options field. Choose "Select Images" as the "Inputfield type". Select Images appears in the "Multiple item selection" category but you can set "Maximum number of items" to 1 if you want to use Select Images for single image selections. Define selectable options for the field via a FieldtypeDynamicOptions::getSelectableOptions hook. See some examples below. FieldtypeDynamicOptions is recommended but is not a strict requirement for installing InputfieldSelectImages in case you want to use an alternative way to store the field data.
      Selection of Pageimages
      In this example the field allows selection of Pageimages that are in the "images" field of the home page.
      The field will store URLs to the Pageimages so it works as a kind of "image reference" field. You can use the "Format as Pagefile/Pageimage object(s)" option for the Dynamic Options field to have the formatted value of the field be automatically converted from the stored Pageimage URLs to Pageimage objects.
      $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get Pageimages within the "images" field on the home page foreach($event->wire()->pages(1)->images as $image) { // Add an option for each Pageimage // When the key is a Pageimage URL the inputfield will automatically create a thumbnail // In this example the label includes the basename and the filesize /** @var Pageimage $image */ $options[$image->url] = "{$image->basename}<br>{$image->filesizeStr}"; } $event->return = $options; } }); Selection of image files not associated with a Page
      When not working with Pageimages you must add a "data-thumb" attribute for each selectable option which contains a URL to a thumbnail/image.
      In this example the field allows selection of image files in a "/pics/" folder which is in the site root.
      $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "select_images" if($field->name === 'select_images') { $options = []; // Get files that are in the /pics/ folder $root = $event->wire()->config->paths->root; $path = $root . 'pics/'; $files = $event->wire()->files->find($path); // Add an option for each file foreach($files as $file) { $basename = str_replace($path, '', $file); $url = str_replace($root, '/', $file); // The value must be an array with the following structure... $options[$url] = [ // The label for the image 'label' => $basename, 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => $url, ], ]; } $event->return = $options; } }); The field values don't have to be image URLs
      The values stored by the Dynamic Options field don't have to be image URLs. For example, you could use the images to represent different layout options for a page, or to represent widgets that will be inserted on the page.
      Also, you can use external URLs for the thumbnails. In the example below the options "calm" and "crazy" are represented by thumbnails from placecage.com.
      $wire->addHookAfter('FieldtypeDynamicOptions::getSelectableOptions', function(HookEvent $event) { // The page being edited $page = $event->arguments(0); // The Dynamic Options field $field = $event->arguments(1); // For a field named "calm_or_crazy" if($field->name === 'calm_or_crazy') { $options = []; // Add options that are illustrated with thumbnails from placecage.com $options['calm'] = [ // The label for the option 'label' => 'Nicolas Cage is a calm man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/260/260', ] ]; $options['crazy'] = [ // The label for the option 'label' => 'Nicolas Cage is a crazy man', 'attributes' => [ // An image URL in the "data-thumb" attribute 'data-thumb' => 'https://www.placecage.com/c/260/260', ] ]; $event->return = $options; } }); Field configuration
      You can define labels for the button, notices, etc, that are used within the inputfield if the defaults don't suit.

       
      https://github.com/Toutouwai/InputfieldSelectImages
      https://processwire.com/modules/inputfield-select-images/
    • By Robin S
      Process Images
      A basic, proof-of-concept Textformatter module for ProcessWire. When the Textformatter is applied to a rich text field it uses Simple HTML DOM to find <img> tags in the field value and passes each img node through a hookable TextformatterProcessImages::processImg() method.
      This is a very simple module that doesn't have any configurable settings and doesn't do anything to the field value unless you hook the TextformatterProcessImages::processImg() method.
      Hook example
      When added to /site/ready.php the hook below will replace any Pageimages in a rich text field with a 250px square variation and wrap the <img> tag in a link to the original full-size image.
      For help with Simple HTML DOM refer to its documentation.
      $wire->addHookAfter('TextformatterProcessImages::processImg', function(HookEvent $event) { // The Simple HTML DOM node for the <img> tag /** @var \simple_html_dom_node $img */ $img = $event->arguments(0); // The Pageimage in the <img> src, if any (will be null for external images) /** @var Pageimage $pageimage */ $pageimage = $event->arguments(1); // The Page object in case you need it /** @var Page $page */ $page = $event->arguments(2); // The Field object in case you need it /** @var Field $field */ $field = $event->arguments(3); // Only for images that have a src corresponding to a PW Pageimage if($pageimage) { // Set the src to a 250x250 variation $img->src = $pageimage->size(250,250)->url; // Wrap the img in a lightbox link to the original $img->outertext = "<a class='lightboxclass' href='{$pageimage->url}'>{$img->outertext}</a>"; } });  
      GitHub: https://github.com/Toutouwai/TextformatterProcessImages
      Modules directory: https://processwire.com/modules/textformatter-process-images/
    • By spercy16
      These issues should be fairly easy for any intermediate to advanced ProcessWire developer to answer. I'm new to PHP and relatively new to ProcessWire and just need a bit of help. What I'm trying to do is bring in a couple of cards from my Projects page to display on my home page. I finally got the code right to bring in the cards but right now they're using my original images instead of my resized "variations". So firstly, I would like to know how to reference the variations of my images instead of using the original. Secondly, I need to grab only four of the cards from the Project page and not import in all ten. It should be just two small changes to my code to do these things (I would imagine). Here is the code I currently have for that section:
      <?php // https://processwire.com/api/arrays/ // check if the array of images has items if (count($pages->get("/projects/")->images)) : // get array of images from the field $images = $pages->get("/projects/")->images; $count = 0; // iterate over each one foreach ($images as $image) : $count++; $sectionText = $pages->get("/projects/")->get("paragraph_$count"); $img = $image; $buttonCode = $pages->get("/projects/")->get("url_$count"); ?> <span id="card<?php echo $count?>" class="card"> <img class="cardThumb" src="<?php echo $img->url; ?>" alt="<?php echo $image->description; ?> Thumbnail" /> <div class="cardBody"> <div class="cardText"> <h2><?php echo $img->description; ?></h2> <?php echo $sectionText; ?> </div> <div class="primaryBtn"> <a href="https://www.paypal.com/donate?hosted_button_id= <?php echo $buttonCode; ?> &source=url"> <button> <i class="fas fa-donate"></i> Donate </button> </a> </div> </div> </span> <?php endforeach; endif; ?> Thanks in advance for any help!
    • By skeltern
      WebP image support is great and works fine. But once created I've issues to get rid of all API generated WebP variations.
      The backend image field variations "Delete" works and I can remove all variations JPEG plus WebP. Image list is clean but all WebP API variations are still stored in file system (for instance files/12345/84.900x675.webp etc). I can only use ImageSizer with temp 'force' option to request fresh WebP variations or have to delete WebP files from folders. No other way so far. Tested with 2 sites and latest master PW 3.0.165.
      Is there somewhere a "magic button" or config/setup thing to solve my sticky WebP issue?
    • By psy
      I've searched, and maybe missed, the solution. I have a 'normal' images field and uploading images with a file size greater than 10Kb is fine. Any image size smaller results in the never-ending spinner and no upload.
      No min/max width/height set on image uploads in admin, ie just the defaults.
      Any ideas on how to fix?
      Using:
      PW: 3.0.175
      PHP: 7.3
      Marking it as "Resolved" rather than "Solved" as it auto-magically fixed itself. No idea whether PW, PHP, or just an internet hiccough... All good now 🤞
×
×
  • Create New...