bangle js1 draw speed flicker

Posted on
  • I'm working on an analog watchface using drawn objects such as g.fillCircle, g.fillPoly, etc.
    I have about 15-20 shapes being drawn, and I'm seeing a flicker every second, which is consistent with my setInterval rate. The whole thing flickers for the tiniest fraction of a second, but is noticable enough to be annoying. I'm noticing that the last things drawn (the clock hands) flicker a little darker than the first things drawn (the 12 and 1 hour markers) so I figure the tiny moment of flicker I'm seeing is the time it takes to draw each object after g.clear is called.

    I get no flicker when running the same app on the js2, and I'm not sure if it's a difference in processor speed, or refresh rate of the different LCDs.

    I also tried removing the anti-aliasing options where possible, but that didn't improve the draw time at all, so I put it back.

    Anyway I'm not sure if I'm doing something wrong, or if I've simply hit the limits of the device, but I'll post my code here and hope that someone can spot my issue, or clue me in to a workaround. Thanks!

    // Load fonts
    require("Font7x11Numeric7Seg").add(Graph­ics);
    // position on screen
    const xc = 120, yc = 133;
    let twoPi  = 2*Math.PI;
      let Pi     = Math.PI;
      let halfPi = Math.PI/2;
    
    function glowface() {
      g.reset();
      g.clear();
      //TopDot
      g.setColor(0,1,0.62);
      g.drawCircleAA(120,4,4);
      g.fillCircle(120,4,3);
      //HourMarkers
      g.setColor(0,1,1);
      g.fillRect(123,37,128,57);
      g.drawCircleAA(164,54,5);
      g.fillCircle(164,54,4);
      g.drawCircleAA(197,87,5);
      g.fillCircle(197,87,4);
      g.fillRect(212,129,214,135);
      g.drawCircleAA(197,176,5);
      g.fillCircle(197,176,4);
      g.drawCircleAA(164,208,5);
      g.fillCircle(164,208,4);
      g.fillRect(111,37,116,57);
      g.drawCircleAA(75,208,5);
      g.fillCircle(75,208,4);
      g.drawCircleAA(42,176,5);
      g.fillCircle(42,176,4);
      g.fillRect(25,129,45,135);
      g.drawCircleAA(42,87,5);
      g.fillCircle(42,87,4);
      g.drawCircleAA(75,54,5);
      g.fillCircle(75,54,4);
      g.fillRect(117,206,122,226);
    
      let now = new Date();
      let Hours   = now.getHours() % 12;
      let Minutes = now.getMinutes();
      let Seconds = now.getSeconds();
      let HoursAngle   = (Hours+(Minutes/60))/12 * twoPi - Pi;
      let MinutesAngle = (Minutes/60)            * twoPi - Pi;
      let SecondsAngle = (Seconds/60)            * twoPi - Pi;
      //Hours
      let hSin = Math.sin(HoursAngle), hCos = Math.cos(HoursAngle);
      g.setColor(0,1,1);
      g.drawCircleAA(xc-57*hSin,yc+57*hCos,4);­
      g.fillCircle(xc-57*hSin,yc+57*hCos,3);
      g.drawLineAA(xc-52*hSin+5*hCos,yc+52*hCo­s+5*hSin,xc-20*hSin+5*hCos,yc+20*hCos+5*­hSin);
      g.drawLineAA(xc-52*hSin-5*hCos,yc+52*hCo­s-5*hSin,xc-20*hSin-5*hCos,yc+20*hCos-5*­hSin);
      //Minutes
      let mSin = Math.sin(MinutesAngle), mCos = Math.cos(MinutesAngle);
      g.setColor(0,0,0);
      g.fillPolyAA([xc-96*mSin,yc+96*mCos,
                    xc-78*mSin-11*mCos,yc+78*mCos-11*mSin,
                    xc-14*mSin-5*mCos,yc+14*mCos-5*mSin,
                    xc-14*mSin-5*mCos,yc+14*mCos-5*mSin,
                    xc-14*mSin,yc+14*mCos,
                    xc-16*mSin,yc+16*mCos,
                    xc-16*mSin-1*mCos,yc+16*mCos-1*mSin,
                    xc-77*mSin-7*mCos,yc+77*mCos-7*mSin,
                    xc-77*mSin+7*mCos,yc+77*mCos+7*mSin,
                    xc-16*mSin+1*mCos,yc+16*mCos+1*mSin,
                    xc-16*mSin,yc+16*mCos,
                    xc-14*mSin,yc+14*mCos,
                    xc-14*mSin+5*mCos,yc+14*mCos+5*mSin,
                    xc-14*mSin+5*mCos,yc+14*mCos+5*mSin,
                    xc-78*mSin+11*mCos,yc+78*mCos+11*mSin
                    ]);  //minuteHandShadow
      g.setColor(0,1,0.62);
      g.fillPolyAA([xc-91*mSin,yc+91*mCos,
                    xc-80*mSin+7*mCos,yc+80*mCos+7*mSin,
                    xc-80*mSin-7*mCos,yc+80*mCos-7*mSin]);
      g.drawLineAA(xc-76*mSin+9*mCos,yc+76*mCo­s+9*mSin,
                   xc-17*mSin+3*mCos,yc+17*mCos+3*mSin);
      g.drawLineAA(xc-76*mSin-9*mCos,yc+76*mCo­s-9*mSin,
                   xc-17*mSin-3*mCos,yc+17*mCos-3*mSin);
      //Seconds
      let sSin = Math.sin(SecondsAngle), sCos = Math.cos(SecondsAngle);
      g.setColor(0,0,0);
      g.drawCircleAA(xc-64*sSin,yc+64*sCos,4);­
      g.fillCircle(xc-64*sSin,yc+64*sCos,3);
      g.fillPolyAA([xc+1*sCos,yc+1*sSin,
                    xc-1*sCos,yc-1*sSin,
                    xc-102*sSin,yc+102*sCos]);
      //g.drawLineAA(xc,yc,xc-102*sSin,yc+102*­sCos);  
      g.setColor(0,1,1);
      g.drawCircleAA(xc-64*sSin,yc+64*sCos,2);­
      g.fillCircle(xc-64*sSin,yc+64*sCos,1);
    }
    
    var secondInterval = setInterval(glowface, 1000);
    // Stop updates when LCD is off, restart when on
    Bangle.on('lcdPower',on=>{
      g.clear();
      if (secondInterval) {clearInterval(secondInterval);
      secondInterval = undefined;}
      if (on) {
        secondInterval = setInterval(glowface, 1000);
        glowface(); // draw immediately
      }
    });
    glowface();
    
    // Show launcher when middle button pressed
    Bangle.setUI("clock");
    
  • Bangle 1 and Bangle 2 use very different technologies for the screen. I cant remember the exact hardware terminology but I am sure others can explain it. In short Bangle 1 does flicker and Bangle 2 hardly ever. To reduce flicker on Bangle 1 only redraw the part of the screen that you need to. So if you had a clock that showed 3 square boxes with the digts for hour, minutes and seconds; then only update each when previousValue !== currentValue.

  • I was thinking about trying something like that, I think that means I can't call g.clear(); to clear the second hand, for example. I suppose I can redraw the previous second's second hand in the background color just to cover it up, before drawing the new one in the foreground color. Of course if I consider sacrificing the second hand, I can set the clearInterval to 60000, which is as long as the screen will stay on anyway, so the user won't ever see the flicker.

    Btw, I also tried loading a 3bit image of my watch face, something I hadn't tried because I figured it would be even slower. It was. It took much longer to draw the image. Still under a second, but much more noticable flicker, with a slight rolling wipe from top to bottom.

  • Yes, the issue is that the LCD on Bangle.js 1 is 240x240x16 bits - so 115kb of data - about twice the RAM on the Bangle itself! So there can't be any offscreen buffering.

    On Bangle.js 2 we have more RAM but also the screen is 176x176x3 - so far less data and we can store it all offscreen and blit it.

    I suppose I can redraw the previous second's second hand in the background color just to cover it up, before drawing the new one in the foreground color.

    Yes, that's the best solution.

    OR you can create an offscreen buffer with Graphics.createArrayBuffer and blit it when you're done drawing - if you're only after 2bpp greyscale then it's something that's totally possible to do on Bangle.js 1

  • As you mention, updating every minute on Bangle.js 1 and every second on Bangle.js 2 is a nice easy fix

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

bangle js1 draw speed flicker

Posted by Avatar for NoMusicTuesdays @NoMusicTuesdays

Actions