Detect Ratina Displays

Permalink
https://www.concrete5.org/documentation/developers/5.7/designing-for-concrete5/supporting-responsive-images-in-your-concrete5-theme/

I am using above code in fruitful theme to show responsive image. But above code uses view port to detect screen resolution.

How to detect ratina displays to show images that supported images in ratina ?

srjahir32
View Replies: View Best Answer
derykmarl replied on at Permalink Reply
Good question, c5 doesn't seem to take retina into account much from what I've found. It generates "retina thumbnails" but I can't find anywhere they're actually used.

You can add an attribute "srcset" to an img tag with the format "normal_url 1x, retina_url 2x". Most browsers will just fall back to the src image (which should be your normal version) whilst those designed for retina on a retina screen (tested on Safari on an iPhone personally) will look up the "2x" url in the srcset attribute.

This only helps for FIXED size images but:
I found that the image block, if you choose "constrain size", uses the image helper to generate a thumbnail at that size (so you don't waste bandwidth if your content editors uploaded a large image). BUT it only serves up a standard 1x image.

My solution that I came up with yesterday (not fully tested yet, but so far so good) was to copy /concrete/blocks/image/view.php to /application/blocks/image/view.php and modify this section of the code as follows:

...
    if ($maxWidth > 0 || $maxHeight > 0) {
        $im = Core::make('helper/image');
        $thumb = $im->getThumbnail(
            $f,
            $maxWidth,
            $maxHeight
        ); //<-- set these 2 numbers to max width and height of thumbnails
        $thumb2x = $im->getThumbnail(
           $f,
           $maxWidth * 2,
           $maxHeight * 2
        );
        $tag = new \HtmlObject\Image();
        $tag->src($thumb->src)->width($thumb->width)->height($thumb->height);


So I added the thumb2x parts, which generate a second thumbnail at 2x the requested constraint. It can be very slightly wasteful as getThumbnail always generates a thumbnail even if the source image is the same size or smaller (not sure if it warrants a bug report. It's at least smart enough not to "blow up" an image). But there is a check in my edits to make sure the 2x thumbnail is actually any bigger than the 1x thumbnail before bothering to output the srcset attribute.

My idea is to instruct content editors to always upload at least a 2x image and to use constrain when looking for a fixed size.

Haven't come up with a solution for redactor yet but should be a similar job.

HTH somewhat. Not exactly what you were looking for, I'm looking for similar, but it's a start.



As for the built in responsive image/thumbnail system I don't really understand much of a benefit with it. I only found it of limited use depending how it's set up, either:

- As it's set up by default: a downsize that makes sure you don't waste bandwidth serving a 4000px image to a 1200px standard viewport (it'd resize down to 1200) etc - basically like a "worst case catch-all" in case your editors are uploading giant images without scaling them down first. As far as I know it does detect retina already, so if you're on a 320px retina viewport it'll serve the nearest it can find to a 640px thumbnail, at least going by the fact it always generates retina thumbnails in the file manager.

or

- If you're going to be using actual thumbnails around the site for something like a list of product images (let's say 100x100px on mobile, using a 200px image for retina, but you might want a 300px image on a desktop) it can auto generate those and pick them automatically but it's only of any use if you only use thumbnails through the entire site, because it's defined at the theme level (getThemeResponsiveImageMap in page_theme.php) and not at the block level.

I've not found any way to have a different set of thumbnail mappings per block.



=== Edit: Apologies for the lengthy and disorganised reply, still learning responsive design myself.
Thinking about it, by default the smallest thumbnail break is 768px I believe. That should always be enough for a phone in portrait mode (if the pixels are there, it will use them). Not sure sure about the influx of retina desktops though. I wonder how many people bought those whilst having slow or metered internet connections and not thinking about their bandwidth...
derykmarl replied on at Permalink Best Answer Reply
FYI I have enabled retina support in responsive images now on my copy. It seems to me that the foundations are there e.g. generation of 2x thumbnails but not yet implemented outside of the file manager. So I just hacked things a bit to look up the 2x thumbnail and add it to srcset using the "file.jpg 1x, file2.jpg 2x" syntax (if it differs i.e. a 2x thumbnail exists, otherwise it leaves it alone) when my replacement for the html/image service is called.

This was done by by creating /application/src/Retina/RetinaImage.php based on concrete/src/Html/Image.php:

<?php
namespace Application\Src\Retina;
class RetinaImage
{
    protected $usePictureTag = false;
    protected $tag;
    protected $theme;
    protected function loadPictureSettingsFromTheme()
    {
        $c = \Page::getCurrentPage();
        if (is_object($c)) {
            $th = $c->getCollectionThemeObject();
            if (is_object($th)) {
                $this->theme = $th;
                $this->usePictureTag = count($th->getThemeResponsiveImageMap()) > 0;


(uses existing getDoubledVersion() and puts the result in src2x then passes to my picture service override)

and /application/src/Retina/RetinaPicture.php based on concrete/src/Html/Object/Picture.php:

<?php
namespace Application\Src\Retina;
use HtmlObject\Element;
use HtmlObject\Image;
class RetinaPicture extends Element
{
    /**
     * Default element
     *
     * @var string
     */
    protected $element = 'picture';
    /**
     * Whether the element is self closing
     *


(looks for src2x in the array passed to it from the image service and if it's different from src, adds the 1x/2x syntax to srcset)

Then an override of the image block view.php emcompassing both mods as /application/blocks/image/view.php - I just commented out the old Core::make:

<?php defined('C5_EXECUTE') or die("Access Denied.");
use \Application\Src\Retina;
$c = Page::getCurrentPage();
if (is_object($f)) {
    if ($maxWidth > 0 || $maxHeight > 0) {
        $im = Core::make('helper/image');
        $thumb = $im->getThumbnail(
            $f,
            $maxWidth,
            $maxHeight
        ); //<-- set these 2 numbers to max width and height of thumbnails
        $thumb2x = $im->getThumbnail(
           $f,
           $maxWidth * 2,
           $maxHeight * 2


This is just me blindly fumbling around again, so it may not be a "proper" way to do it, the c5 devs could very well cringe at it but works for me.
Ironically I'm not sure whether to even use it most of the time because I don't want to be 'punishing' people for having retina phones by guzzling their data allowance, but it's nice to have the option.
srjahir32 replied on at Permalink Reply
srjahir32
Hi, I tried this example.
Here is the example.
http://exrx.net/concrete/skybluesofa/Aerobic...

It seem that this code only works with Image block, Is it possible to make it for content block too ?

I appreciate you provided this code.
derykmarl replied on at Permalink Reply
Unfortunately I never did figure out how to do it for the content block - that one is much more complicated. Given the calculations I did based on bandwidth (post below) and frankly the headache I was getting (retina+responsive is a mess right now in browser implementation IMO - it's better than it's ever been but still too many variables) I gave up trying and decided if I really need retina in a floated image (pretty much the reason I'd use an image in a content block instead of using an image block next to a content block in the grid) then I might just write a new block or use the HTML block.

I've found that photos tend to look okay at 1x anyway - it's mostly logos or pictures with detail you want to read where you need a retina version. I ended up just uploading double-size images on a case by case basis. (Double the display size for fixed size images or double the maximum size the image is likely to display at for responsive)

It's something I'd probably revisit when browser developers get around to adding a preference for bandwidth vs. quality so the browser can more intelligently decide which image to load.
derykmarl replied on at Permalink Reply
Correction, I did gain some ground in the redactor text editor (content block), I think it became a repressed memory :) After all the calculations I left it out, but here are the diffs for the application/ overrides of various files vs. the concrete originals - including the last version of the image block mod.

--- concrete/src/Editor/LinkAbstractor.php 2015-05-06 23:30:23.000000000 +0100
+++ application/src/Retina/LinkAbstractor.php      2015-07-21 14:30:51.976645006 +0100
@@ -9,7 +9,8 @@
  * on the server (or to a different server).
  */
-namespace Concrete\Core\Editor;
+//namespace Concrete\Core\Editor;
+namespace Application\Src\Retina;
 use Core;
 use File;
 use Page;
@@ -106,10 +107,10 @@
                                $fo = \File::getByID($fID);
                                if (is_object($fo)) {
                                        if ($style) {


--- concrete/src/Html/Object/Picture.php   2015-05-06 23:30:23.000000000 +0100
+++ application/src/Retina/RetinaPicture.php       2015-07-21 12:11:51.798645010 +0100
@@ -1,11 +1,11 @@
 <?php
-namespace Concrete\Core\Html\Object;
+namespace Application\Src\Retina;
 use HtmlObject\Element;
 use HtmlObject\Image;
-class Picture extends Element
+class RetinaPicture extends Element
 {
     /**
      * Default element
@@ -59,8 +59,11 @@
     {


--- concrete/src/Html/Image.php    2015-07-10 12:07:51.248025895 +0100
+++ application/src/Retina/RetinaImage.php 2015-07-21 11:48:41.706644977 +0100
@@ -1,7 +1,7 @@
 <?php
-namespace Concrete\Core\Html;
+namespace Application\Src\Retina;
-class Image
+class RetinaImage
 {
     protected $usePictureTag = false;
@@ -49,13 +49,14 @@
                 $type = \Concrete\Core\File\Image\Thumbnail\Type\Type::getByHandle($thumbnail);
                 if($type != NULL) {
                     $src = $f->getThumbnailURL($type->getBaseVersion());
-                    $sources[] = array('src' => $src, 'width' => $width);


--- concrete/blocks/image/view.php 2015-05-06 23:30:23.000000000 +0100
+++ application/blocks/image/view.php      2015-07-21 11:32:20.001644963 +0100
@@ -1,4 +1,5 @@
 <?php defined('C5_EXECUTE') or die("Access Denied.");
+        use \Application\Src\Retina;
 $c = Page::getCurrentPage();
 if (is_object($f)) {
@@ -9,10 +10,19 @@
             $maxWidth,
             $maxHeight
         ); //<-- set these 2 numbers to max width and height of thumbnails
+        $thumb2x = $im->getThumbnail(
+           $f,
+           $maxWidth * 2,
+           $maxHeight * 2


Perhaps you can do something with those if you consider it worthwhile.
srjahir32 replied on at Permalink Reply
srjahir32
Can you tell me which file to be edited ? Is it all files that you have suggested in first reply ?
I confused between this files.
srjahir32 replied on at Permalink Reply
srjahir32
It does not support Content block but works fine with Image block.
derykmarl replied on at Permalink Reply
Hope no one minds the lengthy monologue but maybe it'll help when others are pondering the same things. It also helps me to get my thoughts into order to type things out.

I modified the picture block, html/image, html/object/picture and editor/linkabstractor to nicely support picture objects with srcsets for double resolution images in both images blocks and the redactor editor, and only THEN did I start doing a few tests and calculations. Right now, supporting retina mobiles isn't too bad - you can just carry on serving desktop sized images, and because a responsive design will squash them down to a smaller viewport they look fine on retina. That's the default behaviour. It just bugs me a bit - it really feels so incredibly *wrong* to be using more mobile bandwidth than is necessary, which is where say on a 375px 2x viewport (iPhone 6) I wanted to be sending a 750px image, not a 1200px one. For those on a cheaper mobile with a 320px 1x viewport, it feels even more of a crime to send a 1200px image. And that's for full width - more of a crime if you're displaying it at say 30%.

So that's when you start looking at doing picture objects with img fallbacks. And then I saw this happen with my iPhone with a sample image using a 375px breakpoint:
IMG fallback image is loaded (12KB)
'Mobile 2x' image is loaded and overrides it (32KB)
Okay, 12KB wasted but so far it's still a little smaller than the 64KB 1200px image.
What happens if the user rotates the phone?
'Small 2x' image is loaded (90KB) to support the extra pixels
....ah. If a user likes to rotate their device, they're ultimately punished with a 134KB download and 3 calls for the same image. Yet if you just display the 1200px image landscape on a retina phone (667*2=1334px on an iPhone 6) it looks good enough and only costs a single 64KB download, with no swapping around of images when the user switches between portrait and landscape.
Portrait and landscape isn't something I thought of at first, but I've seen users switch between them frequently.

I'm also thinking that if you have a "thumbnail" breakpoint at 375px, users on older desktops might see the 375px fallback image before their higher resolution one loads, which won't look great.

So now I understand why C5 is configured out of the box to just serve whatever size images you feed it, in a traditional manner (but with width=100% to make it responsive). It's WAY easier and a sensible one-size-fits-all compromise befitting the CMS (and Bootstrap). Anything else is a nightmare and if you don't have enough mobile users to justify the effort of real in-depth mobile optimisation then you'd have already been sending them a desktop site anyway (we have been) so are not actually losing anything.

Retina desktops are a whole other can of worms. Do you send the user several megabytes of high resolution images so they look pin sharp and wow them, or do you stick with standard resolution so that the owner of that retina Macbook they have tethered to their mobile hotspot doesn't gobble up a large chunk of their paltry 250MB/month data allowance (this is still a thing, e.g. on Vodafone) just visiting your site?
Until web browsers get things into gear and perhaps make "download high DPI images (may consume a lot of bandwidth)" an option somewhere, I think it may be less of a headache all round to just let retina desktop/notebook users put up with half resolution for now. At least we've come a long way in the past couple of years with the picture object and picturefill.js etc, but I think the overall situation is too much of a mess to justify the effort just yet.

My first contribution to the thread of using srcset if "constrain" is chosen for the image block (which returns a normal img tag instead of a picture object) is the only one I'll stick with I think, albeit perhaps as an option per block. This works well due to the fixed size and the caching, making it deal for logos and some images with fine detail. Safari correctly picks the 2x image from the srcset and ignores the fallback image in src, unlike with picture objects, so nothing is wasted. Being fixed size, it doesn't change with landscape orientation either. For everything else, I'm still thinking, but suspect it's going to be a matter of picking sensible image sizes when uploading in the first place and testing them in various viewports.

Who knew such a great idea as responsive design would be such a minefield! I suppose it's best to always remember that if you were already serving a desktop site to mobile users anyway (because to put things into perspective only 7-10% of our visits are from mobile devices so even just using a responsive design is a lot of effort for the volume of traffic - but dropping IE6+IE7 support has freed up a lot of time for more forward-thinking efforts) then even if you're not improving the bandwidth for them and are wasting some, you're hopefully not making it worse than before (aside from the occasional logo displayed at high DPI) and are still improving the overall experience not forcing them to pinch and scroll around your site, so it's still a progressive step forward.

Will be watching future developments and how others handle all this with interest - I have a gut feeling that (unfortunately) high DPI desktop/notebook usage will grow faster than mobile and tablet usage in the business-to-business arena I'm working in, since most are sat at desks and many are electronic design engineers etc. So it'll be an "interesting" time. Feedback from a friend who's used retina iMacs a lot though is that he's kind of used to a half resolution web and doesn't think it looks too bad on a desktop, so perhaps it's not too much of a panic yet.
krussell replied on at Permalink Reply
Is it possible to set just the source for min-width(900px) to the retina/original image?

I've used your modified code (Thank you for taking the time to share this with us and for the great writeup) but the entire set uses the retina/original image which, as you say, isn't kind to the smart phone users.