M5Paper text



  • When I use the code

    canvas1.createRender(100);
    canvas1.setTextSize(100);
    
    uur=random(23);
    minuut=random(59);
    seconde=random(59);
    sprintf(charBuffer,"%02d:%02d:%02d",uur,minuut,seconde);
    int nWidth = canvas1.textWidth(charBuffer);
    

    nWidth is zero. How can I get the width of a string?
    When I test is wihth :
    nWidth = canvas1.textWidth("test");
    the answer is sill zero.
    Any sugestion?



  • Hello @Powersoft

    would you mind posting your complete code? I cannot compile the snippet you posted w/o errors.

    Thanks
    Felix



  • Are you using a freetype font?

    Looking at the source for textWidth, it does not support freetype fonts:

    int16_t TFT_eSPI::textWidth(const char *string, uint8_t font)
    {
      int32_t str_width = 0;
      uint16_t uniCode  = 0;
    
    #ifdef FREETYPE_FONT
      if((gfxFont == NULL) && _is_freetype_loaded && _use_freetype_font)
      {
        // TODO: Measure freetype width
        return 0;
      }
    #endif
    


  • You can workaround this by using drawString instead, to an offscreen location.
    drawString returns the total width of the string drawn, and works for freetype fonts.

    int nWidth = canvas1.drawString(charBuffer,0,1000);
    


  • It looks like you are trying to get information from the canvas before anything is written to the canvas which is why you are getting zero.
    As @murraypaul you need to query the data in the memory before it is drawn on the screen.



  • @murraypaul

    Thanks, have also take a look into the API documentation.
    Found the function drawString, but what is the meaning of "uint8_t font"

    Function: Draw string
    int16_t drawString(const char *string, int32_t poX, int32_t poY, uint8_t font)

    Thanks for any help.



  • @murraypaul
    Thanks for this.Was helpful



  • @powersoft said in M5Paper text:

    @murraypaul

    Thanks, have also take a look into the API documentation.
    Found the function drawString, but what is the meaning of "uint8_t font"

    Function: Draw string
    int16_t drawString(const char *string, int32_t poX, int32_t poY, uint8_t font)

    The font parameter is not used if you are using FreeType fonts, there is a drawString version that does not require it.

    I'm still working through this as well, but it seems that there are a number of different ways of using fonts on the device.

    • TTF fonts.
      • Use loadFont to load a TTF font file,
      • Use createRender to convert the vector TTF data into bitmap data at as many sizes as you want
      • Use setTextSize to select one of the pre-rendered sizes
      • Use drawString with no font argument to draw the text
      • Allows use of multiple font sizes easily, but is expensive to switch fonts or bold/italic
    • Pre-rendered vector fonts
      • Convert a TTF font file into a pre-rendered source file at a specified size, and compile it in to your application
      • A number of fonts are already converted and available, see libraries\M5EPD\src\Fonts\GFXFF in your Arduino directory
      • See https://learn.adafruit.com/adafruit-gfx-graphics-library/using-fonts to convert more fonts
      • To use one of these fonts
        • call useFreetypeFont(false)
        • call setFreeFont(&FreeSans12pt7b) or whatever the font is defined as in the header file created by the conversion app
        • call drawString with GFXFF as the font argument
        • setTextSize does not do anything
      • Quickly switch between fonts and bold/italic
      • Sizes are pre-calculated, and you can only use the ones you have compiled in
    • (There are also build-in bitmap fonts at a few sizes, these are selected with a font parameter != GFXFF)


  • I think this all means that you have to choose between being able to quickly display the same font at any arbitrary size, or being able to quickly switch between different fonts, or enable/disable bold and italic.

    To be able to efficiently do both would I think require changes to the In_eSPI class to store multiple rendered fonts.



  • @murraypaul Thanks for the explanation. Was verry helpfully



  • Another thing worth mentioning is that if you are using TTF fonts, the size of the render cache makes a huge performance difference. If you just call createRender(size), only the single most recently used glyph is cached each time, which means each character has to be re-rendered each time it is used, unless you are printing the same character again and again.

    Switching from createRender(24) to createRender(24,256) dropped the time to make 162 calls to drawString to draw an entire page of text from ~4.5 seconds to ~0.13 seconds, at the expense of using around 90K more memory for the cache. The optimal number is probably lower than that, if you are just using standard ascii.



  • @murraypaul
    Thanks again.
    Do you have experience with converting bmp to a c-file.
    Found the window program "lcd-image-converter", know how to works, but how to setup the conversion?



  • @murraypaul Do you have try the command:

    Function: flushes the data in the buffer to the specified area of the screen in the specified mode.

    m5epd_err_t UpdateArea(uint16_t x, uint16_t y, uint16_t w, uint16_t h, m5epd_update_mode_t mode);

    to update partial the canvas? Have tried it but with no results!

    Cheers,

    Jan



  • @Powersoft Sorry, didn't see the question about bmps. No, I don't have any experience there.

    With regard to partial updating, I think this is working correctly, but there is an extra step required which is usually hidden from the user.

    To display something on the EPD requires three steps:

    1. Draw to the internal framebuffer of the Canvas object
    2. Transfer that data to the memory of the EPD
    3. Refresh the EPD display

    Normally steps 2 and 3 are combined in the pushCanvas(x,y,mode) function.
    To do a partial update I think you need to do step 2 manually.
    The key thing to note is that that UpdateArea is a member of M5EPD_Driver, not of M5EPD_Canvas, so it doesn't have access to the canvas framebuffer, you have to transfer the data yourself.

    The program below demonstrates how to do that.
    The circle is drawn (1), and pushed to the EPD memory (2) all in one go, but the screen is refreshed (3) in small chunks.
    Then the text is drawn over the top, and you can see from the screen flash that only the area around the text is drawn.

    You could push only partial memory to the EPD, but that is more complex, and shouldn't be a real performance issue, so I think it is simpler to always push the complete framebuffer but only update the screen area you want to change.

    #include "M5EPD.h"
    
    M5EPD_Canvas Canvas(&M5.EPD);
    
    void setup() {
      M5.begin();
      M5.EPD.SetRotation(90);
      M5.EPD.Clear(true);
      M5.TP.SetRotation(90);
      M5.RTC.begin();
    
      Canvas.createCanvas(540,960); 
    }
    
    void loop() {
      Canvas.fillCanvas(0);
      Canvas.fillCircle(540/2,540/2,540/2,15);
      M5.EPD.WriteFullGram4bpp((uint8_t*)Canvas.frameBuffer());
    
      const int xChunk = 64;
      const int yChunk = 64;
      for( int x = 0 ; x < 540 ; x += xChunk )
        for( int y = 0 ; y < 540 ; y += yChunk )
        {
          M5.EPD.UpdateArea(x,y,xChunk,yChunk,UPDATE_MODE_DU);
          delay(100);
        }
    
      Canvas.setTextColor(0);
      Canvas.setTextDatum(CC_DATUM);
      Canvas.useFreetypeFont(false);
      Canvas.setFreeFont(&FreeSans12pt7b);
      Canvas.drawString("Hello",540/2,540/2);  
      M5.EPD.WriteFullGram4bpp((uint8_t*)Canvas.frameBuffer());
      M5.EPD.UpdateArea(540/2-64,540/2-16,128,32,UPDATE_MODE_GC16);
    
      delay(1000);
    }
    

    [Edit: This assumes your canvas is the full screen. If not, there is a WritePartGram4bpp function you can use.]



  • @murraypaul said in M5Paper text:

    this is what i was looking for. Now also applied it for the clock in the OpenWeatherMap program. Thank you very much!!!!



  • @murraypaul How do you get this nice listing?



  • @powersoft The code showing on the black background? That is a markdown code block.
    Before and after the code you want to list, have a line with just three back-quote characters: ```
    So:

    ```
    this = code;
    ```
    Will display as:

    this = code;
    

    You can also add a little bit of inline code with single back-quote characters, so `this = code` shows as this = code as part of a sentance.

    If you click the little question mark by the word compose in the top-right of the edit box when writing a post, that will give you a link to the markdown documentation.



  • @murraypaul
    Thanks again, this is very helpful.

    I need to switch often from the font

    canvas.loadFont("/fonts/DSEG7Classic-Bold.ttf", SD); 
    

    Is it posible to store the font in memory and call it when I need it, or should I convert the font to a "C" file?
    Is there a program that can do the translation work?



  • @powersoft This website will convert a TTF font at a specified size to a C header file that you can include and use: https://rop.nl/truetype2gfx/

    For example, I downloaded the Ballet font from here: https://fonts.google.com/specimen/Ballet?preview.text_type=custom, and extracted the TTF file.

    Then on the website I clicked Choose File, selected that file and clicked Uploaded, then Get GFX Font File.
    This created a file called Ballet_Regular_VariableFont_opsz20pt7b.h

    The, in Arduino, create a new sketch and copy that header file into the sketch folder, and use this as the code:

    #include <M5EPD.h>
    
    #include "Ballet_Regular_VariableFont_opsz20pt7b.h"
    
    M5EPD_Canvas canvas(&M5.EPD);
    
    void setup()
    {
        M5.begin();
        M5.EPD.SetRotation(90);
        M5.EPD.Clear(true);
        M5.RTC.begin();
        canvas.createCanvas(540, 960);
        canvas.setFreeFont(&Ballet_Regular_VariableFont_opsz20pt7b);
    
        canvas.drawString("Hello World", 45, 150);
        
        canvas.setTextSize(1);
        canvas.drawString("Hello World", 45, 250);
        
        canvas.setTextSize(2);
        canvas.drawString("Hello World", 45, 350);
        
        canvas.setTextSize(3);
        canvas.drawString("Hello World", 45, 450);
        
        canvas.pushCanvas(0,0,UPDATE_MODE_DU4);
    }
    
    void loop() {}
    

    The advantages of this method are:

    • No reliance on external files
    • Can use any TTF font
    • Quicker to switch between different fonts, as no need to render them as bitmaps, that has already been done
    • Can use this to display bold/italic, which cannot be efficiently done with TTF fonts

    The downsides are:

    • The font is pre-rendered at a specific size. The only scaling you can do is to print it x2, x3,... the size, which just upscales the bitmap and looks blocky, as you can see in the example
    • So if you need multiple sizes, you need to prerender multiple files and include them all
    • You have to make font and size choices at compile time, users cannot switch in and out any font they want

    The code does also support loading a TTF font from memory. So you could convert the entire TTF font to a header file and read it directly with M5EPD_Canvas::loadFont(const uint8_t *memory_ptr, uint32_t length). This is an example of a tool that would do this: https://sourceforge.net/projects/bin2header/

    I tried this with the Ballet font example, and it crashed the device.
    With the standard Arial font, it worked fine, but the font used about 20% of the available program memory, so I don't know if it is really reasonable.

    To test, just drag the Arial font from your c:\Windows\Fonts directory to somewhere else, run bin2header on arial.ttf to get arial.ttf.h and do this in Arduino:

    #include "arial.ttf.h"
    ...
        canvas.loadFont(arial_ttf,sizeof(arial_ttf));
    
        canvas.createRender(30,32);
        canvas.setTextSize(30);
        canvas.drawString("Hello World", 45, 150);
    


  • @murraypaul

    can I copy these generated fonts onto the m5stack flash and import them via micropython ?



  • @medy I've never used micropython, so I can't help with that.