Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How do I overwrite a character with another character, using a transparent font #335

Closed
greenonline opened this issue Aug 17, 2017 · 18 comments
Milestone

Comments

@greenonline
Copy link

Let's say I am using a transparent font, such as u8g2_font_unifont_t_symbols. I setFontMode(0), so not transparent. When I overwrite another character that is already on the screen, the background of the current character, or glyph, is not cleared, and so the previous character shows through...

I basically want the same effect as is shown for drawXBM(), as is shown in the reference guide under setBitmapMode(), when setBitmapMode(0) is used, i.e. no transparency and the new character obliterates whatever was behind it. If I use a non-transparent font such as, u8g2_font_6x10_mf, then I get the effect that I desire.

Is this possible when using a transparent font? If so, how?

There are certain glyphs that I need, such as Omega (Ω) which are not available in non-transparent fonts, such as u8g2_font_6x10_mf, and which are only available in some transparent fonts, such as u8g2_font_unifont_t_symbols. I can not find a non-transparent version of u8g2_font_unifont_t_symbols, but I need to be able to overwrite the previous character.

Why? Because I don't really want to have to clearBuffer() and redraw the screen again for scratch, I just want to quickly overwrite a portion text, that is already on the screen.

Example code:

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
//#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C lcd(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA);   // pin remapping with ESP8266 HW I2C

void setup() {
  lcd.begin();
  lcd.setFont(u8g2_font_ncenB08_tr);  // choose a suitable font
  lcd.setFontMode(0);
}

void loop() {
  lcd.clearBuffer();          // clear the internal memory
  lcd.drawStr(0,10,"Hello World!");  // write something to the internal memory
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
  
  lcd.drawStr(10,10,"Goodbye");  // write something to the internal memory
//  lcd.drawStr(0,10,"            ");  // spaces do not clear background either
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
}
@olikraus
Copy link
Owner

Nice questions.

Let me try to clarify some things. First: the availability of special glyphs is not related or restricted to transparent fonts. It is more "accidently" that i did this for t fonts only.

The second point is: u8g2_font_6x10_mf also exists as a transparent version: u8g2_font_6x10_tf as you can see here: https://github.com/olikraus/u8g2/wiki/fntgrpx11#6x10

And now, third: There is NO DIFFERENCE between transparent and none-transparent fonts. It is more a kind of a "suits better" for none-transparency. Or with other words: If you want to use transparent fonts, you can use ALL fonts (whether they are marked as t or m does not matter).
What matters is the fact, that a none-transparent font is bigger (as you can see here:
https://github.com/olikraus/u8g2/wiki/fntgrpx11#6x10). The bigger size comes from the fact, that a none transparent font includes the background rectangle for the glyph, while this is not included for the transparent font. So for example the bitmap for "comma" in the transparent font, will look like this:
.x
.x
x.
While it will look like this in the none-transparent font:
....
....
....
..x.
..x.
.x..

The transparency of fonts is ONLY defined via the setFontMode(1) command.

  lcd.setFont(u8g2_font_ncenB08_tr);  // choose a suitable font
  lcd.setFontMode(1);    // transparency, no need to use mode 0, because the background will be empty
  lcd.clearBuffer();          // clear the internal memory
  lcd.drawStr(0,10,"Hello World!");  // write something to the internal memory
  lcd.drawStr(2,12,"Hello World!");  // write something over it, let the previous text look through
  lcd.sendBuffer();

So this will do a similar effect like the XBM Bitmap example.

@greenonline
Copy link
Author

greenonline commented Aug 17, 2017

Thanks for the quick reply. Yes, after more testing I see what you mean, sort of, although, I do not want transparency. I will need to do some more testing to verify. However, there is an immediate bug, when using setFontMode(0).

Using the code below, and a different font, to better illustrate the issue:

  1. If you use the non transparent font, u8g2_font_6x10_mf, then "Goodbye " (note the space at the end of the "Goodbye " string) is clear. This is exactly the effect that I want to achieve using the transparent fonts.

  2. Using the transparent version, u8g2_font_6x10_tf, I almost achieve this effect, but there are some artifacts left behind above the main line, above the bodies of the characters, i.e. in the "ascent" area. It is as if the ascent area is not fully, or correctly, written to and cleared. To be more explicit, in the example below:

    • the artifacts are above the second "o" (from the first "l" in "Hello") and the "e" (from the "W" in "World") in "Goodbye". Strangely, though, there is nothing left of the second "l", in the ascent area above the "d" in "Goodbye", from the previous "Hello".
    • Also the "o" in "World" is not overwritten by the space at the end of "Goodbye "

If you use u8g2_font_ncenB08_tr the artifacts are even worse, and there seems to be total transparency, i.e. you can not turn off the transparency, as you can with u8g2_font_6x10_tf

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
//#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C lcd(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA);   // pin remapping with ESP8266 HW I2C

void setup() {
  lcd.begin();
//  lcd.setFont(u8g2_font_6x10_mf);  // <--- this font overwrites correctly
  lcd.setFont(u8g2_font_6x10_tf);  // <-- this font doesn't overwrite correctly
//  lcd.setFont(u8g2_font_ncenB08_tr);  // <-- this font is even worse
  lcd.setFontMode(0);
}

void loop() {
  lcd.clearBuffer();          // clear the internal memory
  lcd.drawStr(0,10,"Hello World!");  // write something to the internal memory
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
  
  lcd.drawStr(0,10,"Goodbye ");  // write something to the internal memory
//  lcd.drawStr(0,10,"            ");  // spaces do not clear background either
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
}

@olikraus
Copy link
Owner

I am not quite sure about your problem. Just use the "m" (or "h") version of the font. In fact the only purpose of the "m" font is to avoid the artefacts which had been described by you under 2.
So the solution is simple: use setFontMode(0) and u8g2_font_6x10_mf...

ah, but wait, now you want Greek Omega, which is only avilable as transparent font. But you need the none-transparent version. Got it.

Can you tell me the font name and the unicode glyph range you require as "m" (or "h") version of the font? I can provide information how to generate these fonts by yourself.

@greenonline
Copy link
Author

Hmmm... I still don't fully understand why the transparent fonts draw partially correctly below the ascent line, but not above. I guess I need to examine the font files more closely, when I get time. But yes, I can use a "m" font and setFontMode(0);. Thanks for clarifying that.

After going through all of the font reference pages, I have found a suitable font, u8g2_font_pxplusibmvga8_m_all, it was the only non-transparent font with an omega. So, the problem is solved in both respects... for the moment, even though I don't have any choice in which font to use. However, in the future, should I wish to change font style then, this leads me to my next point.

Related to making my own font, but going slightly off -topic for a moment... I would like to be able to print an omega as part of a string, in order to display resistance. However, I don't seem to be able to just print the Omega symbol using

lcd.print("\x2126");

I seem to have to use

lcd.drawGlyph(5, 30, 0x2126); /* dec 9731/hex 2603 Snowman */

Is that true? Why is that? Is it because the character code is greater than 255 (or 128) and the print statement can't use high code numbers?

I would like to be able to print() an omega as part of a string, and not have to print it separately using drawGlyph(), because it results in some simpler code, which is compatible with some other displays that my code supports.

If I can not use print() to display high character codes (>128, or >256), then yes, I would be interested in knowing how to generate a font myself as I would definitely need to make my own custom (but non-standard) font, with an Omega symbol with a character code of less than 256 (or 128), I would like to adapt an existing font, and just add the omega.

Is the method to create a font not in the reference guide already?

Also, is there a programmatical way of converting the transparent "t" files to non-transparent "m" or "h" files? If I could automate this process, then that would be great. Or would you not be interested in generating them and adding them to U8g2? It would be nice if all fonts had both transparent and non-transparent versions available.

Looking at the symbols fonts, these are the transparent fonts that include an Omega:

From Unifont

  • u8g2_font_unifont_t_symbols 16x16

From X11:

  • u8g2_font_6x12_t_symbols
  • u8g2_font_7x13_t_symbols
  • u8g2_font_8x13_t_symbols
  • u8g2_font_9x15_t_symbols

From Old School PC Fonts (v2.1.x):

  • u8g2_font_pxplustandynewtv_t_all 8x8
  • u8g2_font_pxplustandynewtv_8_all 8x7
  • u8g2_font_pxplusibmvga9_t_all 9x16
  • u8g2_font_pxplusibmvga9_m_all 9x16
  • u8g2_font_pxplusibmvga8_t_all 8x16
  • u8g2_font_pxplusibmvga8_m_all 8x16

From NBP:

  • u8g2_font_mozart_nbp_t_all 5x13
  • u8g2_font_glasstown_nbp_t_all 12x16
  • u8g2_font_shylock_nbp_t_all 12x17
  • u8g2_font_roentgen_nbp_t_all 7x14
  • u8g2_font_calibration_gothic_nbp_t_all 16x25
  • u8g2_font_mercutio_basic_nbp_t_all 10x16
  • u8g2_font_mercutio_sc_nbp_t_all 10x16
  • u8g2_font_nine_by_five_nbp_t_all 10x15
  • u8g2_font_rosencrantz_nbp_t_all 11x17
  • u8g2_font_guildenstern_nbp_t_all 11x17

From cu12:

  • u8g2_font_cu12_t_symbols 20x20

@olikraus
Copy link
Owner

olikraus commented Aug 18, 2017

However, in the future, should I wish to change font style then

Yes, excatly, you tell me the font style (e.g. u8g2_font_6x12) and i will generate the corresponding u8g2_font_6x12_m_symbols font (note the "m" inside).
I can also add this to u8g2, but lets see, this is more of a secondary question.

The point is this: If i generate u8g2_font_6x12_m_symbols then it willl for sure include the omega, but maybe it will also include other chars which are not required for you, wasting space in your controller. So the question is: Is there a specific set of glyphs which you need in your application? You can tell me a list of glyph ranges (unicode positions). Of course i can just create u8g2_font_6x12_m_symbols if you are fine with this.

lcd.drawGlyph(5, 30, 0x2126); /* dec 9731/hex 2603 Snowman */

Yes, this will work, because drawGlyph expects the unicode number.

lcd.print("\x2126");

Assuming you have enable UTF-8 for print, this will still not work, because print (and drawStr()) will expect UTF-8 encoding. For the snowman code 0x2126, the corresponding UTF-8 sequence is E2 84 A6,
which means the correct code is:

lcd.print("\xe2\x84\xa6");

EDIT: drawStr() will not work as mentioned above, instead use drawUTF8()

You can use this page to convert the unicode number to the UTF-8 sequence:
https://r12a.github.io/apps/conversion/

However the question is, why do you make things so complicated? Arduino IDE and most other editors support UTF-8. So any given char is automatically converted to UTF-8. Moreover the c-compiler also interprets UTF-8 correctly, so you can simply write in the Arduino IDE

lcd.print("Ω");

Provided, that the Ω is present in the current font, this will be printing the omega as expected on the GLCD. Ok, maybe because U8g2 is the only library which supports UTF-8, so it might be surprising that things are that simple, but actually i spent a lot of time to implement the UTF-8 decoding algorithms and all that unicode handling. I am happy that you ask, but acutally, things should appear to be simple for the user of u8g2.

Is the method to create a font not in the reference guide already?

No, but it is mentioned in the FAQ (https://github.com/olikraus/u8g2/blob/master/doc/faq.txt#L173). Actually i did not found time to do a proper documentation for this.

Also, is there a programmatical way of converting the transparent "t" files to non-transparent "m" or "h" files?

No, it is part of the conversion process. Actually there is one source, from which the different variants are generated. For example there is 6x10.bdf:
https://github.com/olikraus/u8g2/blob/master/tools/font/bdf/6x10.bdf

It includes everything. From that single bdf file, the u8g2 fonts are generated. It is actually a commandline option of "bdfconv" which decides for type "t" or "m" or "h" fonts.
In principle the conversion is:

6x10.bdf --> bdfconv "some commandline options" --> u8g2_font_6x12_m_symbols

The purpose of the commandline options is to:

  • select t, m or h (or other formats)
  • select the desired glyph set (because we do not always want all glyphs of the original font in the u8g2 font)

If I could automate this process, then that would be great.

It is already fully automatic. All the pages in the u8g2 wiki and all the fonts are generated .

It would be nice if all fonts had both transparent and non-transparent versions available.

Agree, but currently the font file is already 11MB big. I guess it would exceed 20MB if I provide the m file also for each of the fonts. In fact i was waiting for user feedbacks on this. And still I would only generate the full m font for selected fonts which you could tell me.

@greenonline
Copy link
Author

Many thanks for the links and info. Very very interesting.

Regarding the specific glyph set, that is very kind of you to offer - In addition to the usual roman letters and numbers, I only need degree (°) and Ohm (Ω). However, I am not pressed for memory space (yet), so a full char set is fine. Although this might change when I move the code from an Arduino Mega 2560 to a Micro or Nano - I haven't had time to check this yet. However, I think that once I have spent some time understanding the font files I should be able to make my own (cut down) font/glyph set, or easier still, use bdfconv to create 'm' font files.

The reason I resorted to printing character codes is that I had already tried printing the character. Using the IDE on a MacBookPro, running OSX 10.8.5, I can't print the Ω as one would expect. In the following code, this line

lcd.drawStr(0,10,"Goodbye Ω "); // write something to the internal memory

outputs

Goodbye I©

Using your suggestion, I still can't make it work. This line:

lcd.drawStr(0,10,"Goodbye \xe2\x84\xa6 "); // write something to the internal memory

just outputs

Goodbye â| (a +circumflex followed by a broken pipe symbol)

I really can't see what I am missing, or where I am going wrong. This is the code:

#include <Arduino.h>
#include <U8g2lib.h>

#ifdef U8X8_HAVE_HW_SPI
//#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C lcd(U8G2_R0, /* reset=*/ U8X8_PIN_NONE, /* clock=*/ SCL, /* data=*/ SDA);   // pin remapping with ESP8266 HW I2C

void setup() {
  lcd.begin();
  
  lcd.setFont(u8g2_font_pxplusibmvga8_m_all);  // <--- this font overwrites correctly
//  lcd.setFont(u8g2_font_6x10_mf);  // <--- this font overwrites correctly
//  lcd.setFont(u8g2_font_6x10_tf);  // <-- this font doesn't overwrite correctly
//  lcd.setFont(u8g2_font_ncenB08_tr);  // <-- this font is even worse
  lcd.setFontMode(0);
  lcd.enableUTF8Print();  
}

void loop() {
  lcd.clearBuffer();          // clear the internal memory
  lcd.drawStr(0,10,"Hello World!");  // write something to the internal memory
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
  
  lcd.drawStr(0,10,"Goodbye Ω ");  // prints "I©"
//  lcd.drawStr(0,10,"Goodbye \xe2\x84\xa6 ");  // prints "â|"
//  lcd.drawStr(0,10,"            ");  // spaces do not clear background either
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
}

FWIW, I would suggest supplying the fonts as separate downloadable files, or one big font bundle, which needs to downloaded in addition to U8g2. That would seem to be the best compromise. That way, at least the fonts are up on github, and users can still have access to the files if they need them, or are font addicts. Also, even if all of the fonts are bundled together, in one repository, a user would still be able to access the particular font file that they require and either download only that file, or copy and paste the respective code.

I assume that the font file size only matters on the host PC/Mac, and that the entire font file is not loaded on to the microcontroller? Only the fonts used by the compiler are uploaded to the microcontroller? If that is the case, then I would have said that the font size, or overall U8g2 lib size is irrelevant, as PCs these days generally have terabytes of disk space and most people have fast ADSL download connections. However, to support users with low spec machines and slow connections, you could either offer:

  • both a cut down U8g2 lib with the basic fonts, and a full bloat U8g2 version, or;
  • the fonts available separately, as I first suggested.

@olikraus
Copy link
Owner

olikraus commented Aug 18, 2017

I really can't see what I am missing, or where I am going wrong.

My mistake, I was wrong with one of my statements above. drawStr() is the WRONG function for this. You have to use drawUTF8(). However print() should work as expected (because you have called enableUTF8Print()).

void loop() {
  lcd.clearBuffer();          // clear the internal memory
  lcd.drawStr(0,10,"Hello World!");  // write something to the internal memory
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
  
  lcd.drawUTR8(0,10,"Goodbye Ω ");  // will this work?
//  lcd.drawUTF8(0,10,"Goodbye \xe2\x84\xa6 ");  // will this work?
//  lcd.drawStr(0,10,"            ");  // spaces do not clear background either
  lcd.sendBuffer();          // transfer internal memory to the display
  delay(1000); 
}

Only the fonts used by the compiler are uploaded to the microcontroller?

Yes exactly. This is one of the nice features of the Arduino IDE beeing already there since the initial version of the Arduino IDE: It configures the gcc/g++ in such a way, that unused code and data are thrown out automatically. So it does not matter if U8g2 comes with several 100MB of font data. Only those fonts which are refered in your c-code are downloaded to your controller. This makes it also very easy for the user: No additional download or font configuration is required.

I also do not care much about occupied space on the hard disk of a host PC. The only topic is the fact that the compiler has to compile that 11MB file, which now takes around 5 seconds or so. So adding all the additional m fonts will probably increase this to 10 seconds.

Well, still not a big issue... but a notable delay in the Arduino IDE.

@olikraus olikraus added this to the 2.17 milestone Aug 18, 2017
@olikraus
Copy link
Owner

olikraus commented Aug 18, 2017

ToDo

Generate m variants of some of the symbol fonts

From Unifont

u8g2_font_unifont_m_symbols 16x16

From X11:

u8g2_font_6x12_m_symbols
u8g2_font_7x13_m_symbols
u8g2_font_8x13_m_symbols
u8g2_font_9x15_m_symbols

Any other fonts required?

@greenonline
Copy link
Author

Thank you, using

lcd.drawUTF8(0,10,"Goodbye Ω "); // write something to the internal memory

works fine. Also, print(" Ω") works fine with enableUTF8Print() called beforehand. :-)

Thank you ever so much for all of your help, I have been pulling my hair out since Monday about all of this. It has been a bit of a blocking issue... I can now continue with the rest of the project, GhettoVaper.

Regarding the fonts, that should be fine for the moment, thanks, although I noticed that u8g2_font_cu12_t_symbols contains °C and °F glyphs that look very handy, although at 20x20 they are too big for use on a 128x32 display. So, I am keeping with 8x16 fonts for the moment, that is why the u8g2_font_pxplusibmvga8_m_all font suits me perfectly. I might fancy a bit more of a "flowery" font, like something from NBP, such as u8g2_font_roentgen_nbp_t_all or u8g2_font_mozart_nbp_t_all, at a later stage, but for now I am OK.

Thanks again.

olikraus added a commit that referenced this issue Aug 18, 2017
@olikraus
Copy link
Owner

add all mentioned fonts from above as m or h version.

@olikraus
Copy link
Owner

this should be implemented, closing...

@Sebastian-Simon
Copy link

Hi Oli, first of all great library, I have used it for various projects without any issues. Now I have encountered a problem with the overwrite of characters on the screen using the u8g2_font_profont22_mr and u8g2_font_profont29_mr font. The background of the current character seemed to be transparent, and so the previous character shows through. I attached a picture to highlight the problem (the temperature is jumping from 20 to 19). Strangely the artifacts only left behind above the center line of the font. It is as if the upper area is not fully, or correctly, written to and cleared. If I’m using the smaller fonts like u8g2_font_profont12_mr and u8g2_font_profont15_mr with the same code there is no issue, so I think it had something to do with the fonts. I also tried setFontMode(0), but no difference. Hopefully you could help me with this issue.
IMG_20211211_103733
.

@olikraus
Copy link
Owner

Looks like the previous number is not cleared. Probably a programming error. Difficult to tell without more code.

@Sebastian-Simon
Copy link

I used this loop to display the data.

u8g2.firstPage();
do {
/* all graphics commands have to appear within the loop body. */
} while ( u8g2.nextPage() );

And it works with the smaller fonts as I said before. It seem to be only an issue with the 22 and 29 font size. However I will post the code later. Thank you in advance.

@olikraus
Copy link
Owner

olikraus commented Dec 11, 2021

Remember: You must not change any value within the above while loop:

temperature = measure_temperature();
u8g2.firstPage();
do {
/* draw value of variable 'temperature' */
} while ( u8g2.nextPage() );

This means you MUST measure the temperature outside of the loop.

See also here: https://github.com/olikraus/u8glib/wiki/tpictureloop

@Sebastian-Simon
Copy link

Ok, 😁 I think this is the main misstage I have done. Tomorrow I have some time to change the code and test it. However, for the smaller fonts it is no problem to meassure the temperature inside the loop. I try to understand this behaviour, maybe smaller fonts are displayed faster and thus the artefact of the bigger fonts are not visible?

@olikraus
Copy link
Owner

It depends whether the value on the display crosses a page boundary. The upper page will display the old the lower page the new value, which then will not fit together

@Sebastian-Simon
Copy link

Hi Oli, your advice solved the problem and I understand now more how this buffer system works. Thank you very much.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants