Complete
over 4 years ago

WERETECH-11147

Fixed

The burn-in protection on Venu2(s) devices with CIQ4 falsely triggers and turns off the display

I have ported several watch faces with Venu1 AOD support to the Venu2 and Venu2s devices.

My AOD modes are straight forward. I'm using a dense pattern clearing every 2nd pixel row or column on the screen alternating each minute starting with the first row/column or the second row/column.

This ensures that less than 10% of the pixels are on and no particular pixel is on for more than 1 minute.

On the Venu2 the simulator detects a screen burn-in. On the Screen Burn-In Simulation window it looks like the overlay pattern is ignored.

Also some drawing artifacts are missing on the burn-in screen for example the minute and hour hands.

I'm in contact with 2 other developers who have similar problems with at least some of their watch faces.

I already uploaded my watch faces with Venu2 support to the Connect IQ store and received feedback from a user who also owns a Venu1 that

the AOD is not working on his new Venu2. - It turns off the screen.

So this is potentially not only a problem of the simulator but also of the actual device.

Here is a screen recording of the problem in action highlighting the differences between Venu1 and Venu2:

https://drive.google.com/file/d/123bDerdGzCovWEcJlxonKbRBSo96lqrq/view?usp=drivesdk

  • I made some progress in my investigations and what feels like some regress as well.
    When I draw into a buffered bitmap and then on to the screen it works better.
    The first frame that is detected by the burn-in detection after entering low power mode is still incorrect.
    With the buffered bitmap I get some weird color changes with the text and when I disable antialiasing again
    I get weird color effects but this time with the background of the text.
    Here is my recording:
    https://drive.google.com/file/d/13mJrxnAMf4dO_2EmDt_NyRSp-spek0G9/view?usp=sharing

  • Change 

    dc.setColor(Graphics.COLOR_WHITE,Graphics.COLOR_BLACK);

    to

    dc.setColor(Graphics.COLOR_BLACK,Graphics.COLOR_BLACK);

    Does it then work?

    You'll need to add something like:

    dc.setColor(Graphics.COLOR_WHITE,Graphics.COLOR_TRANSPARENT);

    before the draw text

  • Ok, to make this easier.

    I have generated a test project that displays the issue fully. For venu, venud, venusq, venusqm it works perfectly. For venu2, venu2s, smallwearable2021 and wearable2021 I see a burn in error as soon as I toggle power mode.

    I cannot either attach files or insert formatted code, so here is the view class in raw text:

    /////////////////////////////////////

    import Toybox.Graphics;
    import Toybox.Lang;
    import Toybox.System;
    import Toybox.WatchUi;

    class LabReqBurninView extends WatchUi.WatchFace {
        var lowPower;
        var requiresBurnInProtection;
        var burnInClockRadius;
        var burnInDegrees;
        var burnInR;
        var burnInFont;

        function initialize() {
            WatchFace.initialize();
            lowPower = false;
        }

        // Load your resources here
        function onLayout(dc as Dc) as Void {
            setLayout(Rez.Layouts.WatchFace(dc));
            var sets = System.getDeviceSettings();
            requiresBurnInProtection = sets has :requiresBurnInProtection ? sets.requiresBurnInProtection : false;
            burnInDegrees = 0.0;
            burnInFont = Graphics.FONT_NUMBER_MEDIUM;
            burnInClockRadius = dc.getWidth() * 44 / 200;
            burnInR = dc.getWidth() / 2 - burnInClockRadius; 
        }

        // Called when this View is brought to the foreground. Restore
        // the state of this View and prepare it to be shown. This includes
        // loading resources into memory.
        function onShow() as Void {
        }

        // Update the view
        function onUpdate(dc as Dc) as Void {
            // SET BLACKSCREEN
            dc.setColor(Graphics.COLOR_WHITE,Graphics.COLOR_BLACK);
            dc.clear();
            var clockTime = System.getClockTime();
            var hour = clockTime.hour;
            if(!System.getDeviceSettings().is24Hour) {  
                hour %= 12;
                if(hour == 0) {         hour = 12;          }
            }
            if(lowPower) {
                burnInDegrees += 52;
                var rads = burnInDegrees * Math.PI / 180;
                var str = hour.format("%02d") + ":\n" + clockTime.min.format("%02d");
                var posx = dc.getWidth()/2+Math.cos(rads)*burnInR;
                var posy = dc.getHeight()/2+Math.sin(rads)*burnInR;
                dc.drawText(posx,posy,burnInFont,str,Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER);
            } else {
                // Get and show the current time
                // Fill the screen with stuff...
                dc.setColor(Graphics.COLOR_RED,Graphics.COLOR_WHITE);
                var val=dc.getWidth(),pos=val/2,lim=val/3;
                for(var x=0;x<lim;x+=3,val-=6) {
                    dc.drawRectangle(x, x, val, val);
                    dc.drawCircle(pos, pos, val/2);
                }
                dc.setColor(Graphics.COLOR_WHITE,Graphics.COLOR_BLACK);
                var tstr = Lang.format("$1$:$2$", [clockTime.hour, clockTime.min.format("%02d")]);
                dc.drawText(pos, pos, Graphics.FONT_NUMBER_THAI_HOT, tstr, Graphics.TEXT_JUSTIFY_CENTER|Graphics.TEXT_JUSTIFY_VCENTER);
            }
        }
        // Called when this View is removed from the screen. Save the
        // state of this View here. This includes freeing resources from
        // memory.
        function onHide() as Void {
        }

        // The user has just looked at their watch. Timers and animations may be started here.
        function onExitSleep() as Void {
           System.println("LOW POWER EXIT");
           lowPower = false;
        }

        // Terminate any active timers and prepare for slow updates.
        function onEnterSleep() as Void {
           System.println("LOW POWER DETECTED");
           lowPower = true;
        }

    }
    //////////////////////////
  • Hi Jim, one can argue that this is what I'm doing. In onUpdate() I always draw my watch face the exact same way except I force the background and other large areas to black.
    In the end I check if protection against burn in is required. If so I overlay a pattern of black lines. This pattern shifts with one pixel each minute.

    This approach has worked with the Venu1 and D2 Air devices just fine.

    With the Venu2 and CIQ4 it almost seems as if the 2 burn-in rules have changed in a way that does not allow the watch face to violate the rules temporarily even if the pixels are cleared again before leaving onUpdate().

    I had an idea how I could workaround this problem simply by drawing into a bitmap buffer, doing the overlay there and then draw the bitmap to the screen.
    Only problem is that the Venu2 only has 124kB vs. 500kB on the Venu1.
    This fact combined with the higher resolution will also make this approach impossible. :-(

    Of course the above theory does not explain why the burn-in protection triggers for others when they just clear the screen with black color.

  • 4.0 devices like the venu 2 use the graphics pool, so a fair amount of things that were in your app's memory space on pre 4.0 devices now are in the graphics pool.  Things like bitmaps and custom fonts...