Drawing Images from Array

Posted on
  • Sorry for posting this twice. I'm not sure which board is the right one to ask something for my Bangle.js project. Please close the wrong one if needed (http://forum.espruino.com/conversations/­373677/#comment16426952)


    Hello everyone,
    in my project for my Bangle.js I need to draw a picture from an Array.
    Since it has to be performant - and the display isn't capable of many more colors - I was thinking of using the 4bit images.
    The API
    https://www.espruino.com/Reference#l_Gra­phics_drawImage
    states
    "On Bangle.js, 4 bit images use the Apple Mac 16 color palette"
    However, I don't seem to understand how this palette is organized.

    From what I understand when painting the image, defining the bpp to 4 enables this dirctly. And the values that are drawn are not color values (e.g. RGB) but values of a fixed color set. E.g. 0 = black, 1 = yellow or such kind)

    So I made a minimal example code like this hoping to see some colors:

    var colorPalette3 = new Uint8Array([
         0b00010000
    ]);
    
    var img = {
      width : 1, height : 1, bpp : 4,
      buffer : colorPalette3.buffer
    };
    
    g.drawImage(img, 10, 10)
    

    However this is almost all the time showing a black pixel. Some values lead to other colors but I can't understand the pattern. I also tried it with integer values hoping to understand the palette but without success.

    Could anyone help me with that? Or maybe a like would be nice.
    I even tried the color scheme I saw on Wikipedia where each bit suggests the color canal to be 1 or 0. But even then a 001 should not be black, right?
    https://en.wikipedia.org/wiki/List_of_mo­nochrome_and_RGB_color_formats#3-bit_RGB­

    Thanks for the help!

  • Okay, some updates.

    I made it. The issue was I had to define a palette myself. I falsely assumed there was one already existing.

    However there seems to be still an issue.

    I defined this:

    var pal = new Uint16Array([
    0x0000, //0000000000000000 BLACK
    0xF800, //1111100000000000 5 bit RED
    0x07E0, //0000011111100000 6 bits GREEN
    0x001F, //0000000000011111 5 bit BLUE
    0x0000, //0000000000000000 BLACK
    0xF800, //1111100000000000 5 bit RED
    0x07E0, //0000011111100000 6 bits GREEN
    0x001F, //0000000000011111 5 bit BLUE
    ]);
    
    var colors = new Uint8Array([
      0b00110011, 0b00110010,
      0b00110011, 0b00110010,
      0b00110011, 0b00110010,
      0b00110011, 0b00110010,
    ]);
    
    var img = {
      width : 4, height : 4, bpp : 4,
      palette:pal,
      buffer : colors.buffer
    };
    
    g.drawImage(img, 10, 10)
    

    But the error I get goes as follows:

    Uncaught Error: Palette specified, but must be a flat Uint16Array of 2,4,8,16,256 elements
    at line 1 col 24

    But I have 8 elements there. If I chose 2, 4 or 16 everything works fine. So is this a bug? Am I missing something? Why would it tell me 8 colors are fine when they are obviously not?

  • The constructor for those arrays doens't bother trying to create a flatstring if the length of array is small, I tested with length of 10 , and it did create a flatstring. So you have to find a way to force it / convert it to flatstring.

    There are functions like E.toString, which try to return a flatstring. I don't know the absolute solution, but this should help point in right direction.

    Oh and you can confirm its underlying type by using trace(yourObject)
    eg.

    trace(pal);
    
  • I fiddled with it and found a little tricky way to make it behave. Creating initial ArrayBufferView length 9, to ensure FlatString creation, then using offset to copy this to a new one.

      var pal = new Uint16Array( new Uint16Array([
        0,
    0x0000, //0000000000000000 BLACK
    0xF800, //1111100000000000 5 bit RED
    0x07E0, //0000011111100000 6 bits GREEN
    0x001F, //0000000000011111 5 bit BLUE
    0x0000, //0000000000000000 BLACK
    0xF800, //1111100000000000 5 bit RED
    0x07E0, //0000011111100000 6 bits GREEN
    0x001F, //0000000000011111 5 bit BLUE
    ]).buffer,1,8);
      trace(pal);
    var colors = new Uint8Array([
      0b00110011, 0b00110010,
      0b00110011, 0b00110010,
      0b00110011, 0b00110010,
      0b00110011, 0b00110010,
    ]);
    var img = {
      width : 4, height : 4, bpp : 4,
      palette:pal,
      buffer : colors.buffer
    };
    g.drawImage(img, 10, 10)
    

    This works because when an ArrayBuffer is passed to the constructor, as opposed to an ArrayBufferView, the underlying buffer is referenced instead of copied.

    Uint16Array is the ArrayBufferView , it views the data below it.

  • Hi! I'll look into this - it might be a firmware issue.

    On the Bangle.js 1, it's a 16 bit display and the Mac palette is honoured - however I believe on the 2 we might just pass the 4 bits right through. I'll check on that as it'd explain a lot of your issues.

    For your Palette specified, but must be a flat Uint16Array of 2,4,8,16,256 elements error, it looks like you've asked for a 4 bit image which would require a 16 entry palette, but you only supplied an 8 entry one? I'd imagine that could be your problem

  • Ok, I just did some testing here... Looks like Bangle.js 2 does do the right thing...

    For instance:

    var img = require("heatshrink").decompress(atob("p­lcwkDmYAP5gACCiAAOngkDAAPDEkYmeEg50cJQnB­jhMeEonIxgleNYkYwJyeDwmIxFcEsPCEoJyEErsY­EoOCErq9C4IkBAALlCcbpKCAAK/CErRyB4okDX4Y­kaJgIlHJTYABmAlFgYkcmcAEosAEv5wDgIlGOTgl­/EuUA1AkDwIldDwJMEiAlaDwWJgAmBiMREgMAzAL­BwYjWAAOZ0AgBAAcKxINDE6E4EgmJzOKEoupzJMC­AAQkTJQIAByIkDhIICCIpOLCIokDAAOaEgMRklEo­lCkQmPJIxvBIA0t7oAEpAmMEg5RGAAVNEonSJpgd­JAA+NEonUBowkWxGEOQoNGJgZvGABmCJIa+GJgok­SAAJuJJgwlVEhZMCOCYlClAOMwYlVpAONwYkUAH4­ALzAkjxJK/JTk4EiOZCSGDEqOYxJxQwczN6QmQmc­zJhwhDOaAlBJhxGEJhwkCJhuZD4oGGSo4ADN5znF­ExQkEExQbJExQkGOg4ZBWpQMHNwoAGCgQACUJgAD­EZglGdpoSCzAkNEoYjNUwolOTIoAPOBwl8Ehwlmm­ZwjEvYkQEsy+SOCIl/TD4kSEsxyREsiWTOSIl7OR­4l7OR4kVEouZABAlWnAhJAAWRiYmWEpkcEq+JJRf­MEq5yLjnMqYlWJhWl5nM4YlXJhAkC5nBEq5MHiok­CAAKYXJguRrgkEJjeqiNVEYoACX7ExERAACrODMA­oGFABaSFAAnFPgOYEAWIzBNSjglIjLKFEiYmJrQk­FzKbWOgvBJLbCErgkIxAkYAAURiIiEEZAA="))
    
    g.drawImage(img);
    

    or

    var img = {
      width : 76, height : 92, bpp : 4,
      transparent : 3,
      buffer : require("heatshrink").decompress(atob("m­YAP5gACCiAAOngkDAAPDEkYmeEg50cJQnBjhMeEo­nIxgleNYkYwJyeDwmIxFcEsPCEoJyEErsYEoOCEr­q9C4IkBAALlCcbpKCAAK/CErRyB4okDX4YkaJgIl­HJTYABmAlFgYkcmcAEosAEv5wDgIlGOTgl/EuUA1­AkDwIldDwJMEiAlaDwWJgAmBiMREgMAzALBwYjWA­AOZ0AgBAAcKxINDE6E4EgmJzOKEoupzJMCAAQkTJ­QIAByIkDhIICCIpOLCIokDAAOaEgMRklEolCkQmP­JIxvBIA0t7oAEpAmMEg5RGAAVNEonSJpgdJAA+NE­onUBowkWxGEOQoNGJgZvGABmCJIa+GJgokSAAJuJ­JgwlVEhZMCOCYlClAOMwYlVpAONwYkUAH4ALzAkj­xJK/JTk4EiOZCSGDEqOYxJxQwczN6QmQmczJhwhD­OaAlBJhxGEJhwkCJhuZD4oGGSo4ADN5znFExQkEE­xQbJExQkGOg4ZBWpQMHNwoAGCgQACUJgADEZglGd­poSCzAkNEoYjNUwolOTIoAPOBwl8EhwlmmZwjEvY­kQEsy+SOCIl/TD4kSEsxyREsiWTOSIl7OR4l7OR4­kVEouZABAlWnAhJAAWRiYmWEpkcEq+JJRfMEq5yL­jnMqYlWJhWl5nM4YlXJhAkC5nBEq5MHiokCAAKYX­JguRrgkEJjeqiNVEYoACX7ExERAACrODMAoGFABa­SFAAnFPgOYEAWIzBNSjglIjLKFEiYmJrQkFzKbWO­gvBJLbCErgkIxAkYAAURiIiEEZA"))
    }
    

    Draws a blue genie - that's using 4bpp and no palette, which means the Mac palette is used.

    If I take your original code and run it in a FOR loop (for all 16 colors) I get the attached image, which looks good to me.

    g.clear();
    for (var i=0;i<16;i++) {
      var colorPalette3 = new Uint8Array([
          i<<4
      ]);
      var img = {
        width : 1, height : 1, bpp : 4,
        buffer : colorPalette3.buffer
      };
      g.drawImage(img, 10 + (i&3)*20, 10 + ((i>>2)&3)*20, {scale:20})
    }
    

    Worth noting that because images are MSB aligned and you have 8 bits but only one 4 bit pixel, the color data is in the top 4 bits.


    1 Attachment

    • download.png
  • Thanks, I guess I gonna look into this a bit further.

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

Drawing Images from Array

Posted by Avatar for Tobias @Tobias

Actions