Feature idea for Image Converter

Posted on
  • Since the Image Converter tool found @ http://www.espruino.com/Image+Converter
    and embedded into the IDE via Upload a File button under Device Storage area has features for changing the image, I feel like it would make sense that it also offers resize of the images to the native screen size of device in question.

    How it currently is , means images' have to be loaded in image editor program before upload which could be skipped. I also wonder how many people are lazy and don't resize before upload. Then the device is automatically resizing before render? This is very wasteful on space, which there is few of.


    Edit: I'm not sure what the crop? feature does, is it similar intention?

  • Also the image doesn't draw when I choose optimal 3bit from the colors drop down list /palettes/format.

    Uncaught Error: Unable to get pointer to palette. Image in flash?
    at line 4 col 16

    Are there only a subset that work with the Bangle.js?

    On Bangle.js, 2 bit images blend from background(0) to foreground(1) colours
    On Bangle.js, 4 bit images use the Apple Mac 16 color palette
    On Bangle.js, 8 bit images use the Web Safe 216 color palette


    let rc2 = require("Storage").read("rc3.jpg");
  • I sort of solved the issue with the color format myself, the code in Espruino

    char *dataPtr = jsvGetDataPointer(info->buffer, &dataLen);
            if (info->bpp<=8 && dataPtr && imgStart<dataLen) {
              info->paletteMask = (uint32_t)(paletteEntries-1);
              info->palettePtr = &dataPtr[info->bufferOffset];

    I assumed the issue is within jsvGetDataPointer, so attempted flat string instead of native string (if thats what reading from flash is).. So I did :

    let rc2 = E.toString(require("Storage").read("rc3.­jpg"));

    Issue with this is that its requiring RAM to function and I did like the concept of using direct from flash.

    Still in need of full solution, possible bug.

    When using E.toString (flat string) passed to drawImage, the result is an error when the color format is not of custom palette(3bit optimal). :
    Uncaught Error: Expecting first argument to be an object or a String at line 4 col 19
    So 16bit color format works fine with NativeString, but doesn't like FlatString (unless the reason it failed here was because it didnt fit into memory,possible). And Optimal 3bit doesn't like NativeString but works fine with FlatString.

  • By the way the optimal color palette algorithm is SUPER useful, in the converter if you adjust brightness/contrast, you can extract almost any part of the image with few colors via trial and error!

  • Hi - yes, an option to resize images would be pretty neat. I tend to do exactly what you say (manually resize in an external program).

    'Crop' detects areas of the same colour around the edge of the image and cuts them off. Often if you download an icon from somewhere it's got a bunch of padding around the edge, and that strips it off.

    Palette: the issue is Espruino needs to be able to access the palette from RAM, and you're reading the image direct from flash. For small palettes (4 cols) it just stores the palette on the stack, and while I could extend that to 8 colors for 3 bits, at some point you'll complain that 4 bit fails too :)

    Expecting first argument to be an object or a String

    I think you need to check what the argument actually is, but it's likely it couldn't fit the image into RAM in one long block so E.toString just returned undefined.

  • Yes I am trying this :

    function test(i)
      let imgname = `rc${i}.img`;
      let imgdata = require("Storage").read(imgname);
      if ( !imgdata ) {
        console.log("cant find image");
      } catch(e) {
        catch(e) {
          console.log("too large to flatstring");
    function hashow() {
      let delay = 1000;
      let hihi = 27;
    for(var lol=1;lol<=hihi;lol++){

    And the memory is just getting more and more full, because of the for loop. Is there a way to free memory within a for loop to run a task like this? Its meant to be a slideshow of images..
    EDIT: its related to using setTimeout within the loop I think, it doesnt activate the garbage collection cos of the setTimeout or something?

  • Palette: the issue is Espruino needs to be able to access the palette from RAM, and you're reading the image direct from flash. For small palettes (4 cols) it just stores the palette on the stack, and while I could extend that to 8 colors for 3 bits, at some point you'll complain that 4 bit fails too :)

    I don't really understand. Could you elaborate? The way I see it is that the palette colors are on disk within the image file. I would like to be able to drawImage direct from flash without have to load it into RAM. Is this a bug or feature?

    EDIT: Ah so you are saying that the combined memory usage of storing the palette in RAM means that it can't draw the image? I mean if thats true then it should work for slightly smaller images, I will test it. Also I can't imagine a 3 bit palette taking up that much RAM. 3 bits is 8 colours, so 8 variables or if its 16bit color conversion, 8 x 16 bit, its 4 dwords.

    If it can draw the non-paletted versions from file, it should also the paletted. I see no reason why it should be RAM intensive.

  • its related to using setTimeout within the loop I think

    Yes, you're basically just queuing up 27 timeouts even before the function is run. Probably best to just schedule a new timeout at the end of the test function and then call test once...

    Is this a bug or feature?

    It's neither, but it is intended behaviour. It's a very constrained device and I try hard to ensure you can do things easily that would normally be very difficult.

    The palette needs to be in RAM. In the image is not in RAM, the palette has to be loaded separately. The RAM for that palette has to be allocated on the stack all the time regardless of if a palette is used, so if I increase the number of palette entries required that's RAM that gets wasted in all cases apart from when there's an optimal palette.

  • The palette needs to be in RAM.

    Why does this have to be true, I thought the external SPI was memory-mapped. Can't it read the palette data this way? I can only guess it has to be true for speed concerns?

    A workaround could be that I use an object instead but have the data buffer point to the file, then the palette is on the ram and all is fine?

  • Pastebin Code from my latest attempt
    Above is a link to code snippet I tried.

    My goal is to have a custom-paletted image larger than RAM to be drawn to screen and for it to be supported by the default upload format for images in Upload a File within the ide. It uses the string format and you have told me that its difficult to have what I want. Yet I have demonstrate that with extract the palette from string and load into memory and use the Object method, I can almost correctly draw images.

    The problem that remains :
    @_jswrap_graphics_parseImage for if (jsvIsObject(image))

        JsVar *buf = jsvObjectGetChild(image, "buffer", 0);
        info->buffer = jsvGetArrayBufferBackingString(buf);
        info->bitmapOffset = imageOffset;

    compared to @_jswrap_graphics_parseImage for else if (jsvIsString(image)

    info->bitmapOffset += info->headerLength;
    // modify image start
    info->headerLength += (unsigned short)(paletteEntries*2);
    info->bitmapOffset += (unsigned short)(paletteEntries*2);

    The issue here is that info->buffer = jsvGetArrayBufferBackingString(buf); doesn't respect ArrayBufferViews substring. Or rather that it always takes the 'real' string behind the view of a string. Thus I can't extract just the string data out of the string returned from the require("Storage").read('filename'), forcing me to draw the headerData as pixels, pushing all pixels by 30 or so forward making my images not aligned properly and having weird dots in top row.
    I propose that this info->buffer = jsvGetArrayBufferBackingString(buf); could read the real string length and starting position, end position etc.. instead of defaulting to the 'real buffer'.
    If thats not what you want to do then I suggest you change the default upload of image converter to use the Object method over the string method, but as I already highlighted, it has to save all pixel/buffer data into one file on its own. The palette data loaded into a flatString, so thats 2 files, one for the image object/palette/header, one for the pixel data. The image object file could load the other file.

    I am willing to make these changes to save you the time and effort, if you tell me what you prefer.

  • I thought the external SPI was memory-mapped

    No, it's not. You only think it is because a lot of effort has gone in to hide that - but sometimes you see through the gaps.

    I've just increased the buffer size to handle up to 4bpp palettes, so this should hopefully 'just work' for you now. More memory usage as mentioned but hopefully not too painful.

    jsvGetArrayBufferBackingString(buf); doesn't respect ArrayBufferViews substring

    Good point. Just fixed this I hope.

    You could probably work around it with E.getAddressOf and E.memoryArea but it's a bit of a hack.

    However the last few days you've posted a whole bunch of forum posts and GitHub issues. It feels like you're getting frustrated that I'm not jumping on each of them and fixing them right away but right now I am extremely busy with the KickStarter - I have fixed a few of the things you reported almost immediately but it's just not humanly possible for me to do everything.

  • No, its not like that at all from my end. I am digging at it because I am willing to contribute and try to assist you because I am fully aware that you take on a lot of responsibility and workload. I think that the way I go about trying to be helpful just ends up wasting your attention and you'd prefer if I created a solution in code myself and just pop a PR?

    Edit: There is no rush, forgive my eagerness, take your time and focus on what matters most to you. I will step back a bit. I am not familiar with how developers respond/react to issue improvements. I thought it was always the more the merrier, but if the problems are small, its just annoying?

  • Thanks! Sorry, it's easy to misinterpret just from text alone - I just felt from the tone of some of the above that you were getting a bit frustrated, but I shouldn't be so eager to draw conclusions :)

    Contributions and bug reports are always helpful - especially where you noticed something wasn't working as it's amazing how little things get reported. Sorry if I made it seem that they weren't.

    The only thing that can sometimes be a bit frustrating as a developer (even if the issues are really helpful) is if someone reports a bunch of issues at once. You just start work, look and emails, and your inbox is just a list of bug reports at like 10:45pm, 10:47pm, 10:49pm etc. I don't think you ever actually did that though :)

    Where the documentation is missing something and you can fill it in it'd actually be massively helpful if you could just a pop a PR in instead of an issue - it really does make a big difference and it makes things really easy from my end.

    So sorry, please don't stop working and posting comments. Things should get better here soon and I'll have a bit more time to respond properly as well - it's just when I'm getting 100s of emails a day I tend to end up having to respond quite quickly to each one, and I guess sometimes the responses don't seem as friendly.

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

Feature idea for Image Converter

Posted by Avatar for d3nd3-o0 @d3nd3-o0