Author Topic: Incredibly annoying GetTextWidth vs auto line split behavior  (Read 454 times)  Share 

Crimson Wizard

  • Local Moderator
  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
I believe this is brought on forums every now and then, this is the earliest thread I found that gives a working solution, so I'd mention this for a reference here:
http://www.adventuregamestudio.co.uk/forums/index.php?topic=35731.0

So, what is this all about. Imagine you want to create an Overlay having very particular width, precisely to let your text fit in. What do you do?
Basic example:
Code: Adventure Game Studio
  1. function MakeOverlay(String s, int x, int y, FontType font, int color, int wait)
  2. {
  3.     int width = GetTextWidth(s, font);
  4.     Overlay *o = Overlay.CreateTextual(x, y, width, font, color, s);
  5.     Wait(wait);
  6. }
  7.  

That looks correct, right? Well, it should be. However, when you run the game, you find out that your text is split into more lines than you expected. Try calling this example with a short line, like "Welcome!". You may end up having "Welco" on one line and "me!" on another (depends on font).

Why the heck does it happen?

This is because when you call GetTextWidth - a correct width of text is calculated, however when AGS splits line for drawing (on overlay, on label, Display function, Say function, DrawStringWrapped function, etc), it does additional and undocumented changes to the width parameter:

1) First of all, it decreases width by (2 * padding) pixels, where padding is a specific value which depends on the type of GUI used for display (like TextWindow).
If no GUI is used for Display function (by default), then padding = 3.
Some functions do not do this, like DrawStringWrapped, because it is drawn raw on a DrawingSurface. But even then, there is another thing:

2) When calculating how many characters fit into single line, AGS does a small mistake in condition:
Code: C++
  1. // otherwise, see if we are too wide
  2. else if (wgettextwidth_compensate(theline, fonnt) >= wii) {
  3.  
Notice '>=' sign. It means that even if the text fits into the width precisely, it will still will be split at that point, resulting in at least 2 lines where 1 would be enough.

In other words, to work around this issue, you script should look like:
Code: Adventure Game Studio
  1. function MakeOverlay(String s, int x, int y, FontType font, int color, int wait)
  2. {
  3.     int width = GetTextWidth(s, font);
  4.     width = width + 2*3 + 1; // 2*3 is double default padding, and 1 pixel is to counter wrong condition
  5.     Overlay *o = Overlay.CreateTextual(x, y, width, font, color, s);
  6.     Wait(wait);
  7. }
  8.  


I think something must be done here. I am not yet sure what. There is no problem in modifying calculations, because we know how to detect old games and process them differently to keep backwards compatibility. Fixing the condition in line-split function is easy, but I am not sure how to deal with the padding.


EDIT
What I mean about padding. We certainly cannot remove it out of equation, because then the text will be drawn over TextWindow borders, and such. But then there should be two things:
1) Firstly, it should be clearly documented that overlay's and display window's "width" - that is not just text width, but text width + padding.
2) Secondly, user must have means to get that padding somehow, to use it when arranging stuff on screen.


PS
While I am at this, there is another known issue that you should use "width + 1" in the call to GetTextHeight. Probably because of same line splitting mistake.
« Last Edit: 23 Feb 2017, 17:49 by Crimson Wizard »

Monsieur OUXX

  • Cavefish
  • Mittens Vassal
  • Mittens Half Initiate
    • I can help with proof reading
    •  
    • I can help with translating
    •  
    • I can help with voice acting
    •  
I have no idea what this is about but I'm always enthusiastic about engine inner workings clarifications.
 

Snarky

  • Local Moderator
  • Mittens Earl
  • Private Insultant
    • I can help with proof reading
    •  
    • I can help with translating
    •  
Had this issue with a hotspot tooltip module I made. I just kept increasing the width until it worked, but I never knew exactly why. Thanks!

monkey0506

  • AGS Project Tracker Admins
  • Tasting the banhammer. Strangely, tastes like ham.
Magic numbers!

User was banned for this post.

Crimson Wizard

  • Local Moderator
  • AGS Project Tracker Admins
    • Best Innovation Award Winner 2013, for spearheading the AGS 3.3.0 project
    •  
    • Lifetime Achievement Award Winner
    •  
Magic numbers!

I did similar thing in my module's demo game :)

Code: Adventure Game Studio
  1.   int text_width = GetTextWidth(text, font);
  2.   // counter default display padding and mistake in built-in line splitting condition,
  3.   // trying to match AGS behavior on line splitting.
  4.   text_width = text_width + DEFAULT_DISPLAY_PADDING + INTERNAL_LINE_SPLIT_MISTAKE;
  5.   <...>
  6.   int text_height = GetTextHeight(text, font, text_width - (DEFAULT_DISPLAY_PADDING + INTERNAL_LINE_SPLIT_MISTAKE) + 1);
  7.   text_height = text_height + DEFAULT_DISPLAY_PADDING; // counter built-in overlay padding
  8.  

Where
Code: Adventure Game Studio
  1. // Default padding that AGS subtracts from the width given to fit the text in,
  2. // calculated as default padding 3 multiplied by 2 (both sides).
  3. #define DEFAULT_DISPLAY_PADDING 6
  4. // Number of pixels to counter some uncertain mistakes in the internal text
  5. // splitting calculations of AGS.
  6. #define INTERNAL_LINE_SPLIT_MISTAKE 2
  7.  

PS. ...Hey, it looks like our magic numbers match?! At least there is 6 and 2 there...
« Last Edit: 17 Jun 2017, 13:05 by Crimson Wizard »

monkey0506

  • AGS Project Tracker Admins
  • Tasting the banhammer. Strangely, tastes like ham.
Looks like we probably had similar results. Since I was specifically emulating speech, I also had to account for the "feature" whereby if a character is standing in the far left or far right quarter the screen's horizontal space, the available width for character speech is truncated by 1/5 of the width of the screen... 8-0 Chris Jones was kind enough to cue me in on that.
User was banned for this post.