HTML5 Crispy canvas in WebKit-based browsers

Discussion in 'GameMaker Studio 2 Community Tech Support' started by Filipp_BSG, Sep 11, 2017.

  1. Filipp_BSG

    Filipp_BSG Member

    Joined:
    Jun 21, 2016
    Posts:
    28
    *posting it into GMS2 subforum, but it occurs in GMS1 too*
    When exporting to HTML5 and scaling the canvas (for example, as shown in YYG tutorial), it looks pixelated on mobiles even with interpolation turned on. Although, you can remove the following line from index.html:
    Code:
    <meta name ="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
    This will remove the issue when using non-WebKit-based browsers (eg. Google Chrome 60 for Android):
    Screenshot_20170911-222800.png

    However, the crispiness still occurs when using WebKit-based browsers (eg. Firefox Focus for Android):
    Screenshot_20170911-222740.png

    Here is a better comparison:
    scr.PNG

    It seems like the initial canvas is scaled up and then down to fit the screen (I've left the live demo user-scalable, so, you can check it by achieving the pixelation with pinch-up scaling, which even increases the effect).

    You can check the live demo here: https://black-snowflake.org/scale
    Or download a .gmz: https://www.dropbox.com/s/pdmmwpeyq1t1vxh/scaletest.gmz?dl=0

    I've tried applying different canvas CSS properties like image-rendering: auto;, but it had no effect. Also tried inserting this JS snippet right before executing the game script:
    Code:
        <script type="text/javascript">
        var canvas = document.getElementById('canvas');
        var ctx = canvas.getContext('2d');
        ctx.mozImageSmoothingEnabled = true;
        ctx.webkitImageSmoothingEnabled = true;
        ctx.msImageSmoothingEnabled = true;
        ctx.imageSmoothingEnabled = true;
        </script>
    I hope that @Mike or someone else from GMS devs or community can point out a solution. If it's a bug, I'm going to fill it, but will be very grateful if there is a JS-based solution, which can help right now, because it's urgent for me.

    Best regards,
    Filipp
     
    Last edited: Sep 12, 2017
  2. Mike

    Mike nobody important GMC Elder

    Joined:
    Apr 12, 2016
    Posts:
    2,358
    Try resizing the application surface to be the correct size, and not the view size - which will obviously be bigger due to having to run on the larger screen sizes. If you don't, it'll draw the initial shape to the larger surface just like on Desktop Chrome, and then shrink it to fit the mobile device screen - as you'd expect.
     
  3. Filipp_BSG

    Filipp_BSG Member

    Joined:
    Jun 21, 2016
    Posts:
    28
    The scaling method in the example doesn't include view resizing. I'm using the following script:
    Code:
    /// scale_canvas(base width, base height, current width, current height, center);
    //argument0 = base width;
    //argument1 = base height;
    //argument2 = current width
    //argument3 = current height
    //argument4 = center window (true, false);
    
    var aspect = (argument0 / argument1);
    
    if ((argument2 / aspect) > argument3)
        {
        window_set_size((argument3 * aspect), argument3);
        }
    else
        {
        window_set_size(argument2, (argument2 / aspect));
        }
    
    if (argument4) window_center();
    
    surface_resize(application_surface, min(window_get_width(), window_get_width()), min(window_get_height(), argument1));
     
  4. True Valhalla

    True Valhalla Full-Time Developer GMC Elder

    Joined:
    Jun 20, 2016
    Posts:
    318
    Not OP, but this is an issue I noticed on iPhones when we started making HD games (640x960)...the assets we made looked awful on mobile devices. The canvas was being downsampled on high-DPI devices.

    My fix for this is now included in the Mobility Engine for HTML5 games, but it would be nice to see this addressed on a GMS-level.
     
    Last edited: Apr 30, 2019
    Kaguva likes this.
  5. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    45
    I'd come to this thread because I had this same problem. After some research, I finally found the solution by myself so I'm gonna share it now for everybody that is facing this same issue.

    It seems that the resolution values received on mobile devices are DPI dependant when the "viewport" META tag is used. That DPI value must be taken into account when we define the scale of the viewport. Otherwise, browser_width and browser _height won't return the real device resolution but a lower value. That is because the viewport has been defined with a default scale of 1, which is fine for desktop browsers, but wrong for mobiles.

    I have been able to fix it by changing the viewport scale with a small javascript function that can be easily implemented. You only have to edit your 'index.html' template file this way:

    1: Replace the whole '<meta name="viewport" ...' line with this one:
    HTML:
      <meta name="viewport" id="viewport" content="">
    2: And add these lines right after the '<body>' line:
    HTML:
            <script type="text/javascript">
                var scale = (1/window.devicePixelRatio).toString();
                document.getElementById("viewport").setAttribute("content", "initial-scale="+scale+", maximum-scale="+scale+", minimum-scale="+scale+", width=device-width, user-scalable=no, minimal-ui");
            </script>
    And that's it. This way you create the "viewport" and modify it's scale on the page load with the correct pixel ratio value.
     
    Last edited: Apr 2, 2018
  6. daniFM

    daniFM Member

    Joined:
    Apr 5, 2018
    Posts:
    2
    Thank you for sharing your solution. Unfortunately, this doesn't work for fullscreen. It looks like if the browser ignored the viewport tag while in fullscreen. ¿Is there a chance you know some walkaround to this problem?
     
  7. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    45
    No, sorry. I've also noticed that this solution doesn't work for Facebook Instant Games either. I've contacted Yoyo's staff and they have confirmed the issue and opened a bug thread here with a high priority. Let's hope they solve it soon and it works also for your case.
     
  8. daniFM

    daniFM Member

    Joined:
    Apr 5, 2018
    Posts:
    2
    I'm making a guess here, but could it be because Facebook puts your game inside an iframe? If that's the case, maybe you can solve it by adding this line to your script:
    HTML:
    <script type="text/javascript">
      var scale = (1/window.devicePixelRatio).toString();
      window.parent.document.querySelector('meta[name="viewport"]').setAttribute("content", "initial-scale="+scale+", maximum-scale="+scale+", minimum-scale="+scale+", width=device-width, user-scalable=no, minimal-ui");
      document.getElementById("viewport").setAttribute("content", "initial-scale="+scale+", maximum-scale="+scale+", minimum-scale="+scale+", width=device-width, user-scalable=no, minimal-ui");
    </script>
    
    This won't solve my fullscreen problem, sadly. I'll try to conctact the staff about this, because I think my only chance is to resize the canvas manually (by javascript), which is going to be such a pain.
     
    Speederman likes this.
  9. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    45
    Thank you, but I tried your solution and it didn't work.
     
  10. tomtinderbox

    tomtinderbox Member

    Joined:
    Sep 16, 2019
    Posts:
    6
    This script seemed to have worked up until the second to last upgrade on Chrome (Desktop). It does work on mobile, but as of right the canvas is blurry on Chrome Desktop unless you fullscreen the browser using F11. I'm not really sure if there is a fix for this.

    I checked, it has nothing to do with the last game maker studio upgrades, but it is the last Chrome updates (Chrome versions: 76.0.3809 broke it, still broken on 77.0.3865). As I mentioned, you can get it to be completely sharp if you go full screen, but if you rescale the browser size it gets blurry.
     
    Last edited: Sep 16, 2019
  11. Speederman

    Speederman Member

    Joined:
    Jun 23, 2016
    Posts:
    45
    Hi everybody!

    I just wanted to update this thread, as I found a better solution and never came back to post it. Forget my first post, as my 'meta name="viewport"...' line now looks like this:
    Code:
            <meta name ="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
    
    And the other script is no needed anymore.

    What I'm using now is an extension I created with a .js file that contains this code:
    Code:
    function scale_canvas(baseWidth, baseHeight, targetWidth, targetHeight) {
        var aspect = (baseWidth / baseHeight);
     
        // Calculate pixel ratio and new canvas size
        var pixelRatio = window.devicePixelRatio || 1;
        var backStoreRatio = (g_CurrentGraphics.webkitBackingStorePixelRatio || g_CurrentGraphics.mozBackingStorePixelRatio || g_CurrentGraphics.msBackingStorePixelRatio ||
                              g_CurrentGraphics.oBackingStorePixelRatio || g_CurrentGraphics.backingStorePixelRatio || 1);
        var pixelScale = pixelRatio / backStoreRatio;
     
        var scaledWidth = targetWidth * pixelScale;
        var scaledHeight = targetHeight * pixelScale;
     
        var posx = 0;
        var posy = 0;
        if ((scaledWidth / aspect) > scaledHeight) {
            var sW = scaledWidth;
            scaledWidth = scaledHeight * aspect;
            posx = Math.round(((sW - scaledWidth) / pixelScale) / 2);
            scaledWidth = Math.round(scaledWidth);
        } else {
            var sH = scaledHeight;
            scaledHeight = scaledWidth / aspect;
            posy = Math.round(((sH - scaledHeight) / pixelScale) / 2);
            scaledHeight = Math.round(scaledHeight);
        }
     
        // Update canvas size
        var ret = '{"w":'+scaledWidth+',"h":'+scaledHeight+',"x":'+posx+',"y":'+posy+'}';
        eval("gml_Script_gmcallback_window_set_size(null,null,'"+ret+"')");
     
        // Scale back canvas with CSS
        if(pixelScale != 1) {
            canvas.style.width = (scaledWidth / pixelScale) + "px";
            canvas.style.height = (scaledHeight / pixelScale) + "px";
        } else {
            canvas.style.width = "";
            canvas.style.height = "";
        }
     
        // Update canvas scale
        if(typeof g_CurrentGraphics.scale === "function")
            g_CurrentGraphics.scale(pixelScale, pixelScale);
    }
    
    function resize_canvas(width,height) {
        var displayWidth = window.innerWidth;
        var displayHeight = window.innerHeight;
      
        scale_canvas(width, height, displayWidth, displayHeight);
    }
    
    The function you must create in the extension and call from GM is resize_canvas and pass the width and height as arguments. You can get browser_width and browser_height values in GM and calculate your desired dimensions.

    The js function will then adjust the canvas and run a callback that you must have in your project. Final width, height and x and y positions will be returned. The function places the game right in the middle of the browser canvas, but you can modify this in the .js file. Create a script in GM called gmcallback_window_set_size and enter this code:
    Code:
    /// @func gmcallback_window_set_size(args_json)
    /// @arg args_json
    
    var args_map = json_decode(argument0);
    
    if(ds_exists(args_map, ds_type_map)) {
        var w = args_map[? "w"],
            h = args_map[? "h"],
            xx = args_map[? "x"],
            yy = args_map[? "y"];
        if(!is_undefined(w) and !is_undefined(h) and !is_undefined(xx) and !is_undefined(yy)) {
            view_wport[0] = w;
            view_hport[0] = h;
            window_set_size(w,h);
            window_set_position(xx,yy);
            surface_resize(application_surface,w,h);
        }
        ds_map_destroy(args_map);
    }
    
    As you can see, I'm using a view in GM and I'm adjusting its port width and height here.

    And that's it. It works fine for me now, so I hope you all get it working too.

    Cheers!
     
    Last edited: Sep 17, 2019
    Dmi7ry, Kaguva and tomtinderbox like this.
  12. tomtinderbox

    tomtinderbox Member

    Joined:
    Sep 16, 2019
    Posts:
    6
    I got it working! Will be doing some testing, but for now it seems to be really solid. Thanks!
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice