ENIGMA Forums

Contributing to ENIGMA => Function Peer Review => Topic started by: TheExDeus on October 29, 2010, 01:34:35 pm

Title: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on October 29, 2010, 01:34:35 pm
This is updated in 30.04.2011!
Description: These are all draw_text functions + string_width, _height and string_width_ext, and _height_ext functions. They are very optimized (thanks to Josh) and so they should work great.
Notice: They are mostly the same as GM ones, but GM's color interpolation and text position is a little different. There are two images to compare at the next page.
Functions:
Code: [Select]
void draw_set_font(int fnt);
void draw_text(int x,int y,string str);
void draw_text_color(int x,int y,string str,int c1,int c2,int c3,int c4,double a);

void draw_text_ext(int x,int y,string str, int sep, int w);
void draw_text_ext_color(int x,int y,string str, int sep,int w,int c1,int c2,int c3,int c4,double a);

void draw_text_transformed(double x,double y,string str,double xscale,double yscale,double rot);
void draw_text_transformed_color(double x,double y,string str,double xscale,double yscale,double rot,int c1,int c2,int c3,int c4,double a);

void draw_text_ext_transformed(double x,double y,string str,int sep,int w, double xscale,double yscale,double rot);
void draw_text_ext_transformed_color(double x,double y,string str,int sep,int w,double xscale,double yscale,double rot,int c1,int c2,int c3,int c4,double a);

unsigned int font_get_texture(int fnt);
int  font_add_sprite(int spr, unsigned char first, bool prop, int sep);
unsigned int font_get_texture_width(int fnt);
unsigned int font_get_texture_height(int fnt);

int string_width(string str);
int string_height(string str);

unsigned int string_width_ext(string str, int sep, int w);
unsigned int string_height_ext(string str, int sep, int w);

unsigned int string_width_line(string str, int line);
unsigned int string_width_ext_line(string str, int w, int line);
unsigned int string_width_ext_line_count(string str, int w);

You can download GSFont.h and .cpp in attachment, as well as a little screenshot. It shows draw_text_color and draw_text_ext_transformed_color.

P.s.: It wasn't as easy as I thought at the beginning. Mostly because of the trig for _transform and _ext functions that come with it.
Also, what do you suggest on dealing with the # newline character? The easiest thing would be searching for all # and then checking if / is in front of it, if its not then it is a newline char, if there is a / in front, then just replace /# with # and draw it. But I think there should be some better way, like using regular expressions, but that could end up being a lot slower.?

Also, "prop" argument doesn't work in font_add_sprite. With the font sprite I tried it with it just returns 0x0 size, so it doesn't draw anything. I will look into that if Josh doesn't.

edit: Nevermind about the # question. Enigma will do this automatically, at least Josh said so.

edit2: I added string_width, _height and _ext functions.
Title: Re: GML: All draw_text functions (WIP)
Post by: IsmAvatar on October 29, 2010, 02:32:25 pm
Actually regex isn't all that slow. In fact, the term "regular" means that it should be able to search a string in runtime O(N) where N is the length of the string, which is the fastest runtime that you can search an unindexed, unsorted string for a single character (unless you know something about the string, of course).
Title: Re: GML: All draw_text functions (WIP)
Post by: TheExDeus on October 29, 2010, 02:55:45 pm
Well, then I will look into that. But I need to get the regex library right? There is regex built-in C++ x0 specification, but I guess we don't use that? Or use some 3rd party library like boost.regex. Regex could actually be implemented in Enigma as GML functions..
Title: Re: GML: All draw_text functions (WIP)
Post by: IsmAvatar on October 29, 2010, 04:15:48 pm
Regex in EGML would be extremely useful. But for the purpose of finding non-escaped #s in strings... it's overkill. It's really not that hard to do an char *num = indexof(str,'#') and then num != str && *(num-1) != '/'
Title: Re: GML: All draw_text functions (WIP)
Post by: RetroX on October 29, 2010, 04:27:09 pm
It's even simpler than that.

Code: [Select]
for (size_t i = 0; i < str.size(); i++)
{
  if (str[i] == '#' && (i == 0 || str[i-1] != '\\'))
  {
    str[i]='\n';
  }
}

Also, it's C++0x, not C++x0.  The standard C library supports regex, and I think that it would be a good idea to implement it into ENIGMA.
Title: Re: GML: All draw_text functions (WIP)
Post by: IsmAvatar on October 29, 2010, 04:59:21 pm
That's what I meant, I just didn't feel like writing the code out. In fact, I don't know if GM does this, but if "//#" is displays the / glyph followed by a newline, you could do that with a boolean that toggles for each '/' encountered, and you don't have to look backwards then.

Also, watch out for EnigmaSettings.cppStrings, which is currently not passed to Enigma yet, but is togglable by the user. When toggled, # is treated as a literal # glyph, and / no longer serves as an escape character.
Title: Re: GML: All draw_text functions (WIP)
Post by: TheExDeus on October 29, 2010, 05:04:56 pm
But doing that on every draw call? What I want is to make text functions actually usable. I haven't benchmarked anything yet, but in GM having a long multi-line string was the slowest thing ever.

Also, multi line text in code editor doesn't trow errors, but it trows errors on compile. GM allows this, so maybe Enigma should too.
Title: Re: GML: All draw_text functions (WIP)
Post by: Josh @ Dreamland on October 29, 2010, 06:18:36 pm
First off, I'm quite impressed at how quickly you got on this; I haven't even announced that fonts were in.

More importantly, I have to express some concerns before I adopt these:
1) Trig functions and multiple multiplication operations are performed in each loop iteration for the transformed font functions. That is a costly move compared to what could be accomplished with a simple precalculation (considering none of them are dynamic, as far as I know).
2) I was not quite done with linear fonts at the time of commit. For example, look what happens to the "I" in this screen shot (http://dl.dropbox.com/u/1052740/enigma/screens/fonts/Screenshot-Untitled%20Window-2.png). It's not the spacing that concerns me as much as the fact that "I" is only one pixel wide. I fear the worst.

Your work is, of course, appreciated greatly nonetheless. Neither of those are a serious detriment; I will just need to go over them when I have a few more answers.

Good work.


Also, I was looking into regex for ENIGMA, but the best interpreter out there for C++ is apparently Boost, and they totally fucking overkill everything. I just want a damn preg_match and preg_replace for ENIGMA; it shouldn't be 65 source files and it shouldn't have a fucking config file. Damn it.

And one more thing: in case you're wondering where that screenshot came from, it started out as an example someone posted on the GMC, which I stumbled upon looking for an explanation of the parameters to font_add_sprite().
Title: Re: GML: All draw_text functions (WIP)
Post by: RetroX on October 29, 2010, 06:26:48 pm
Eh, I messed up.
Code: [Select]
for (size_t i = 0; i < str.size(); i++)
{
  if (str[i] == '#')
  {
    if (i > 0 || str[i-1] == '\\')
    {
      str.erase(--str[i],1);
    }
    else
    {
      str[i]='\n';
    }
  }
}
Title: Re: GML: All draw_text functions (WIP)
Post by: Josh @ Dreamland on October 29, 2010, 06:32:09 pm
Please don't do that.
ENIGMA lets you choose how to escape string literals. There are two settings, though you can't yet switch between them:
1) GML. "Quote inside quote looks like " + '"this."' + " Newlines are escaped from '#' automatically."
2) C++. "Quote inside quote looks like \"this.\" Newlines must use \\r\\n, like this: \r\n ^ line ^ \r\n # does nothing."
Title: Re: GML: All draw_text functions (WIP)
Post by: RetroX on October 29, 2010, 06:36:12 pm
Right.  I'll let you work on that.

Why does \r\n have to be used?  That's not even a correct escape for Windows; it's \n\r.  Why not just use \n?
Title: Re: GML: All draw_text functions (WIP)
Post by: Josh @ Dreamland on October 29, 2010, 07:20:32 pm
1) It is \r\n
2) The \r is optional in everything not strictly Microsoft (IE, not Notepad).
Title: Re: GML: All draw_text functions (WIP)
Post by: TheExDeus on October 30, 2010, 03:59:55 am
Quote
1) Trig functions and multiple multiplication operations are performed in each loop iteration for the transformed font functions. That is a costly move compared to what could be accomplished with a simple precalculation (considering none of them are dynamic, as far as I know).
I do know that trig is costly, and I did my best to optimize it by calculating sin(rot) and cos(rot) only once. Also, cos(M_PI+rot) and sin(M_PI/2+rot) can be calculated only once and so on. So I will optimize it as much as I can. In the end it will be about 8 trig functions (4 sin and 4 cos I think) that is calculated in each draw call, but not for each symbol. So it shouldn't be that slow... should it? The only slow thing that needs to be calculated for every symbol is sqrt(x^2+y^2) (Pythagoras theorem), but it could be possible to fix that too.
So I will optimize the code a bit, and maybe clean it up a bit too. Then I will show it again. :)

Quote
ENIGMA lets you choose how to escape string literals. There are two settings, though you can't yet switch between them:
1) GML. "Quote inside quote looks like " + '"this."' + " Newlines are escaped from '#' automatically."
2) C++. "Quote inside quote looks like \"this.\" Newlines must use \\r\\n, like this: \r\n ^ line ^ \r\n # does nothing."
Ou, so we don't need to add that for every draw text function? :D Nice. :)

edit: Ok, I changed transform functions so all trig is calculated only once per call. Not once per symbol. This should speed it up a lot, but I want Enigma to use LGM Font resource before I benchmark.

edit2: That sqrt() could be removed by just taking width as xx*xx + yy*yy and then comparing it to w*w no? Because I have heard that sqrt() is slow.

edit3: Ok, reuploaded again. Now sqrt() is replaced and so should be even faster.

edit4: Also, that "ENIGMA lets you choose how to escape string literals", I know how it would work on compile time, but if I need to change the string while running the game? Wouldn't it need to have something to do with # replacing in the functions themselves? Or you will need a way to catch the argument going to the text function and replace #, and only then pass the changed string to the function. Not sure how that would work.

edit5: I hate double posting so this editing will continue. :D
I just added string_width, _height and _ext functions. They do work as far as I tested them, but I did found a bug in string() function. string() doesn't return the correct string when there is unsigned int as an argument. I thought as string_width can't be negative I will use unsigned int as the return type, and it works further in calculations (like position and stuff), but when I put draw_text(10,10,string(string_height())) it will shows "322" for example, and "32" is the actual height, and that extra 2 is a bug. So.. just giving the heads up. :)

Also, I would suggest cleaning up the files. There are a lot of outdated, and as far I as I know, unused files. Like GMsprite.h and so on. 
Title: Re: GML: All draw_text functions (WIP)
Post by: TGMG on October 30, 2010, 08:31:29 am
Thanks HaRRiKiRi nice work :D
Looking forward to converting these to openglES/ES2 when they are done :)
Title: Re: GML: All draw_text functions (WIP)
Post by: RetroX on October 30, 2010, 09:20:42 am
1) It is \r\n
2) The \r is optional in everything not strictly Microsoft (IE, not Notepad).
Okay, it is \r\n. :V
Title: Re: GML: All draw_text functions (WIP)
Post by: RetroX on October 30, 2010, 09:21:35 am
Ou, so we don't need to add that for every draw text function? :D Nice. :)
I'd assume that a better way to do it would be to name them as draw_test_pound and draw_text_newline or something like that and let the parser pick one.
Title: Re: GML: All draw_text functions (WIP)
Post by: Josh @ Dreamland on October 30, 2010, 09:53:19 am
Oh, perhaps you took a different approach than I would have. ...Actually, looking at your code, your approach is exactly what I was about to suggest, only your precalculations are fewer than they could be. Also, I don't understand something...



      ulcx = xx + g.tx * xscale * cvp + g.ty * yscale * cv2;

      ulcy = yy - g.tx * xscale * svp - g.ty * yscale * sv2;

Why are you subtracting the texture coordinate from it? the texture coordinate is between 1 and 0...
From what I can tell. ulcx should just be xx...

Also, you do a lot of xscale * cx... this is the sort of thing that I was going to precalculate, and here's why:
Your method is very close to what I think is the fastest. When you draw text, even rotated, the text is still following a linear pattern: You can trace a line across the tops of the glyphs. So all we really need is a sort of slope calculation based on trig.

This is about as efficient as I can see making the function:

Code: (C++) [Select]
void draw_text_transformed(double x,double y,string str,double xscale,double yscale,double rot)
{
  if (currentfont == -1)
    return;
 
  font *fnt = fontstructarray[currentfont];
 
  if (cur_bou_tha_noo_sho_eve_cha_eve != fnt->texture)
    glBindTexture(GL_TEXTURE_2D, cur_bou_tha_noo_sho_eve_cha_eve = fnt->texture);
 
  float xx = (float)x, yy = (float)y;
  float w, h = fnt->height*yscale;
 
  rot *= M_PI/180;
 
  const float sv = sin(rot), cv = cos(rot),
    svx = sv*xscale, cvx = cv*xscale, hi = h * -sv,
    sw = fnt->height/3 * cvx, sh = fnt->height/3 * svx;
 
  glBegin(GL_QUADS);
  for (unsigned i = 0; i < str.length(); i++)
  {
    if (str[i] == '\r')
      xx = x, yy += hi, i += str[i+1] == '\n';
    else if (str[i] == '\n')
      xx = x, yy += hi;
    else if (str[i] == ' ')
      xx += sw,
      yy -= sh;
    else
    {
      fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
      w = (g.x2-g.x), h = (g.y2-g.y);
     
        glTexCoord2f(g.tx,  g.ty);
          glVertex2f(xx,  yy);
        glTexCoord2f(g.tx2, g.ty);
          glVertex2f(xx + w * cvx, yy - w * svx);

        const float lx = xx + h * svx;
        const float ly = yy + h * cvx;
       
        glTexCoord2f(g.tx2, g.ty2);
          glVertex2f(lx + w * cvx, ly - w * svx);
        glTexCoord2f(g.tx,  g.ty2);
          glVertex2f(lx, ly);
      xx += g.xs * cvx;
      yy -= g.xs * svx;
    }
  }
  glEnd();
}

Also, don't sweat string manipulation. The only way the user could tell the difference between escaping # at compile time or treating it special at run time is to use chr(whatever) and concatenate. Other than that, if they try += "#", they'll just be saying += "\r\n", etc, etc.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on October 30, 2010, 10:23:47 am
Quote
Also, I don't understand something...
Yeah, redundancy is the key. I knew that only one cos and one sin function call should be needed, because I was following the same logic about the line, but I couldn't come up with something useful at that moment, so I basically used lengthdir_x and _y type of thing. I will see if I can make them as efficient as yours.

edit: You got to love optimizing code. Especially when it goes from 8 trig functions per symbol, to 2 trig functions per call.

Even then thou, I think it was faster than GM.

edit2: Uploaded the new .cpp file. Now every transform function has only two trig calls, one cos and one sin.
I am thinking of adding string_width_ext_transformed and other similiar functions. I think they should of been in GM, as for example, you want to place a rocket ship at the end of the rotating text.. :D Even thou it can be done with knowing the angle (which you know because you rotate the text) and using lengthdir+string_width, but still, this could be useful as a built-in function.

edit3: Also, what would be the best way to load fonts on the fly? I know that Josh previously had some way to render vector fonts. If a ttf file would be loaded and then just drawn on a texture, then it would work great. I am looking into 3rd party libraries like FreeType, and it looks promising, but also an overkill. The only thing I want is to recreate font_add function.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on October 31, 2010, 10:21:14 am
> Even then thou, I think it was faster than GM.
It probably was. Or, at least, just as fast.

> Also, what would be the best way to load fonts on the fly?
If I knew that, I'd have done it years ago.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on October 31, 2010, 10:38:55 am
For windows I am looking for something like this:
http://msdn.microsoft.com/en-us/library/dd183499%28VS.85%29.aspx
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: IsmAvatar on October 31, 2010, 09:27:52 pm
Quote
When you draw text, even rotated, the text is still following a linear pattern: You can trace a line across the tops of the glyphs.
Er... Maybe I'm taking this out of context here, but since when has this been true? Glyphs should only be aligned by their baseline, which is usually closer to the bottom of the glyph (even for the "p", "g", "q", and "y" glyphs). Most notably, your theory falls apart for capitalisation - the top of the "c" and "C" glyphs should not align.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on November 01, 2010, 07:52:02 am
Mmmyes. Wonder which one the code uses.

Can't test/fix now; off to class.

Worst case scenario: It's broken and we have to add svx*g-> to each xx and cvx * g->y to each yy...
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on November 01, 2010, 09:59:18 am
Lets just make LGM export fonts and Enigma use them and then lets see. I made my own fast font which just had 10 numbers and that's it. I didn't want to make a whole 127-255 charset.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on April 24, 2011, 08:37:04 am
As Enigma finally supports fonts, then this topic could get a new life. All of the functions worked, but that was with the sprite fonts and they also didn't follow some rules.
1. The origin of the text wasn't at the top left, but the bottom left. I fixed that by doing this: yy = y+fnt->height. It looks almost exactly like the GM's, but this makes it draw a few pixels lower than GM. Dunno how GM does it then.
2. _ext functions cut the text when it was already out of the boundary defined by "width" argument. The only way I thought I could fix this is by doing a loop inside a loop, which is quite painful. This is the code:
Code: [Select]
//The following is certainly not pretty, but this is the best way I thought of to replicate GM's function
void draw_text_ext(int x,int y,string str, int sep, int w)
{
  font *fnt = fontstructarray[currentfont];

  if (bound_texture != fnt->texture)
    glBindTexture(GL_TEXTURE_2D, bound_texture = fnt->texture);

  int xx = x, yy = y+fnt->height, width = 0, tw = 0;
  glBegin(GL_QUADS);
  for (unsigned i = 0; i < str.length(); i++)
  {
    if (str[i] == '\r')
      xx = x, yy +=  (sep+2 ? fnt->height : sep), i += str[i+1] == '\n';
    else if (str[i] == '\n')
      xx = x, yy += (sep+2 ? fnt->height : sep);
    else if (str[i] == ' ')
      xx += fnt->height/3, width = xx-x;
      tw = 0;
      for (unsigned c = i+1; c < str.length(); c++) //This is getting messy
      {
        if (str[c] == ' ')
          break;
        fontglyph &g = fnt->glyphs[(unsigned char)(str[c] - fnt->glyphstart) % fnt->glyphcount];
        tw += g.xs;
      }

      if (width+tw >= w && w != -1)
        xx = x, yy += (sep==-1 ? fnt->height : sep), width = 0, tw = 0;
    else
    {
      fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
        glTexCoord2f(g.tx,  g.ty);
          glVertex2i(xx + g.x,  yy + g.y);
        glTexCoord2f(g.tx2, g.ty);
          glVertex2i(xx + g.x2, yy + g.y);
        glTexCoord2f(g.tx2, g.ty2);
          glVertex2i(xx + g.x2, yy + g.y2);
        glTexCoord2f(g.tx,  g.ty2);
          glVertex2i(xx + g.x,  yy + g.y2);
      xx += g.xs;
      width = xx-x;
    }
  }
  glEnd();
}
Question is if anyone can think of a better way. This works exactly like GM's, but this cuts words in the middle if the "width" is too small. I don't actually see why it does that, as cutting should occur only in spaces.

Anyway, I am still reworking the rest of the functions and in time, this could be committed.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: RetroX on April 24, 2011, 10:11:14 am
This is a random question, but how, exactly, does anyone plan on going about draw_text_color with the gradient colour?  It sounds like it'd be a complete pain.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on April 24, 2011, 11:29:07 am
That would be just a linear interpolation. In GM every line is colored separately, and so linear interpolation shouldn't be hard. Especially when that is needed only horizontally, as vertical interpolation is not needed (just color top and bottom vertexes separately).

edit: That _ext function seems more like a pain in ass. If everybody agrees that my code is ok and +- optimized then I will stop thinking about it.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: RetroX on April 24, 2011, 05:26:09 pm
Yeah, for some reason, I was thinking of the font as individual points, when really, it's just a bunch of glyphs.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on April 26, 2011, 10:42:52 am
Oh, I see. You're doing text wrapping. That's fine, I think.
The reason it's cutting in the middle of words is because you handle it at all letters, not just space.
Add braces around the indented block after "else if (str[i] == ' ')".

Once you do that, it will bring the complexity down to O(N) (Really, a synonymous O(2N),  but that's a sacrifice I believe is necessary).
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on April 29, 2011, 04:24:43 pm
Ok, so I updated all the functions. They all work correctly as far as I have tested them. They are not so pretty thou, as fixing the positioning required some additional math to be added. I did try to precompute everything as much as possible, but some things are still character based. If anyone has any optimizations in mind then please share. The .cpp and .h is at the first page. I have two comparison pictures here:
http://img854.imageshack.us/i/textinenigma.png/
http://img852.imageshack.us/i/textingamemaker80.png/
One is in GM and the other in ENIGMA. Also side by side picture here:
http://img26.imageshack.us/i/textcompare.jpg/
As you can see there isn't much of a change. Its better if you can overlay one on the other, but it is possible to see slight size and color differences. I think colors are interpolated more properly in ENIGMA thou as in GM you can't actually get a black corner when a color is set to black. The biggest difference is in draw_text_ext_transformed as in GM it splits into 4 lines while in ENIGMA it splits into 3. Don't know why GM does that, as it clearly isn't out of bounds. If I increase the width to 301 instead of 300, then GM doesn't split. So its probably some rounding bug.

Also, in Enigma screen you can see arrows pointing in the draw_text_ext_color example. This is done to show off some new functions I added. In GM doing that would be quite a pain, but now in Enigma you can draw them just with this simple code:
Code: [Select]
for (int i=0; i<string_width_ext_line_count(str,w); i+=1){
    twi = string_width_ext_line(str, w, i);
    draw_arrow(430+w, 75+string_height("M")*(i+0.5),430+twi, 75+string_height("M")*(i+0.5), 10, 3, 1);
}
So basically you can get width of a specific line inside a multiline string.

The full source can be downloaded from the attachment bellow.

If no one finds any bugs or does some optimization then I think this could be committed. I will do this myself if no one else will. I will also write wiki entry just like I did for curves and shapes.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on May 11, 2011, 03:22:00 pm
string_height doesn't work like it should. Doesn't take into account glyphs with positive g.y2 coordinate (that's when part of the glyph is lower than the line, like with p or y). It turns out string_height works just fine. The problem is with the drawing functions themselves. To fix it I need to subtract the highest g.y2 from yy before drawing. From my tests this is for the letter g (p also has the same value). g is 103 in ASCII, so for the regular text drawing function this change looks like this:
Code: [Select]
  int botoff = (fnt->glyphs[(unsigned char)(103 - fnt->glyphstart) % fnt->glyphcount]).y2
   xx = x, yy = y+fnt->height-botoff;
The question now remains do I change all the functions to accommodate this, or we add a new variable to the font struct. Like fnt->yoffset which has the needed value? Post here (or in the wiki, or somewhere else where I can read it) to tell me how this should be done.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: IsmAvatar on May 12, 2011, 01:13:06 am
If I'm not passing enough glyph information to the backend, just tell me what you need, and I'll add it. It was a rather rudimentary set of fields, but it seemed sufficient to me. A glyph has a height and a baseline. The y2 would be the difference between the two.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on May 12, 2011, 05:58:07 am
Yeah, but I need the highest offset from the baseline. I will illustrate:
(http://img802.imageshack.us/img802/2747/textexplained.png) (http://imageshack.us/photo/my-images/802/textexplained.png/)
Here is the code:
Code: [Select]
str="This is text. p and q";
draw_set_font(font_0);
draw_text(10,10,str);
draw_circle_color(10,10,2,c_red,c_red,0);
draw_line(10,10+string_height(str),10+string_width(str),10+string_height(str));
I added explanation in paint.net. So I need to get the highest y2 value to subtract from the baseline. The red dot is the drawing origin position.

edit: Also on the side note. Which file is used: GAME_GLOBALS.h or key_game_globals.h? Or both? Or none? Because when I include GAME_GLOBALS.h it shows that "string does not name a type" which makes me think that it is not actually included anywhere (and my search didn't return anything either). key_game_globals.h is included in roomsystem.cpp but it even has this besides it:
"//TODO: Remove all instances of this line. It's just sloppy. Figure out the dependencies manually.". So how exactly I add globals? I want to make keyboard_string (or at least very limited version). The basics is just adding this line:
Code: [Select]
if (last_keybdstatus[i] != keybdstatus[i]) keyboard_string+=chr(i);To void input_push() functions "for" cycle inside WINDOWScallback.cpp. You can clearly see lots of problems (like adding every button on the keyboard to a string), but what I need is just basics for a little test so I don't mind about this.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: IsmAvatar on May 12, 2011, 11:27:50 am
So go through every glyph in the text and return the largest (height - baseline)?

As for your other question, that's a Josh question, since I don't have a clue about that stuff.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on May 12, 2011, 12:56:34 pm
HaRRi: That's because the offsets I calculate in the texture are relative to the baseline rather than to the bottommost point on any glyph or the topmost. You are simply adding the height of the glyph to the supplied "y" coordinate of draw_text. You should instead be adding the difference between the topmost point of the tallest glyph and the baseline.

I will add a calculation for the aforementioned value, which you will use to offset the supplied "y".

IsmAvatar: No further action is required of you.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on May 12, 2011, 01:24:27 pm
Quote
HaRRi: That's because the offsets I calculate in the texture are relative to the baseline rather than to the bottommost point on any glyph or the topmost. You are simply adding the height of the glyph to the supplied "y" coordinate of draw_text. You should instead be adding the difference between the topmost point of the tallest glyph and the baseline.
I am fully aware of this. That is why I asked if a new value will be introduced to show the highest y coordinate of the bottom most point. So basically you are going to give me this value via the struct? So I will be able to just do this:
Code: [Select]
yy = y - fnt->baseoffset;Or something similar? I can actually do all of this myself, but I wanted to ask if this is ok way to do it. Anyway, I will wait until the next rev. Is everything working correctly in the latest rev? That's the reason I still use 710. Time to update I guess.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: IsmAvatar on May 12, 2011, 03:43:11 pm
Windows users: Update at your own risk. After updating, copy ENIGMA.exe from dummystuff to root, delete Compilers/Windows/gcc.ey, and run ENIGMA.exe, which will cause the gcc.ey file to be regenerated with more appropriate settings.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on June 17, 2011, 01:42:40 pm
Checking the repository I am sure the fnt->baseoffset wasn't implemented... so I still can't 100% finish the text functions. I did try to add this myself, but the only place I found the font struct actually being populated was in the font_add_sprite function, but then it works only when the font is created from the sprite. So where is the code that does it for the included fonts? Somewhere in the plugin?
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on June 18, 2011, 03:16:28 pm
CompilerSource/compiler/components/write_font_info.cpp writes the structures containing the font data. This is where you'd insert the baseline position;

CompilerSource/compiler/reshandlers/refont.h defines that structure; add a member to it.

Most of the glyph iteration is done in CompilerSource/compiler/components/module_write_fonts.cpp; you may need to copy one of the for loops out ahead of time to calculate that member (if so, you don't need to edit the structure in refont.h).
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 07, 2011, 08:36:05 am
Its funny that all of this was done already. At the beginning I opened module_write_fonts.cpp and added the necessary code so it would write the y2 position (which actually was texture coordinates and I wasn't paying attention). Then I opened fontstruct.h and added the necessary variable to the structure. Then I opened fontinit.cpp and noticed this:
Code: [Select]
        if (fontstructarray[i]->glyphs[gi].y2 > ymax)
          ymax = fontstructarray[i]->glyphs[gi].y2;
Which is EXACTLY what I wanted. So I just added this:
Code: [Select]
fontstructarray[i]->yoffset = - ymin + 1;(Its not ymax, because I needed height-ymax, which ymax - ymin -ymax = -ymin) And that's it. So Josh could of added thous 3 lines in total himself. I followed logical steps just to be lough at in the end. Thats not funny. :|
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on July 07, 2011, 05:29:31 pm
I made some move toward implementing that for you, but I forget where I left off. *shrug*
Sorry about that.
Case-closed then, yes? :P
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 07, 2011, 05:55:44 pm
Quote
but I forget where I left off
Near the first line probably.
Quote
Case-closed then, yes? :P
Yes, I also committed 801, but SF doesn't show that for some reason. Thou I didn't change font_add_sprite function, forgot about that.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: IsmAvatar on July 08, 2011, 09:39:31 pm
When populating the glyphs, I never assume that a specific character will always be the lowest, because I could always come across a font that either has a different set of glyphs (think wingdings) or works differently (e.g. the 'g' character is level with the baseline for goofiness, but some other character, like capital letters, take up an extra height). As such, I iterate every character looking for the one with the greatest heights/offsets.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 09, 2011, 05:24:43 am
That's good, because my changes don't really change the position or height of a separate glyph. It looks for the biggest y2 and then subtracts that from the whole y position when drawing. When this is done it draws properly.

Also, I just tested and I can't even load wingdings in LGM, it just renders boxes. And that leads to my next question - How UTF support will be added? I am actually quite incompetent in that matter, because UTF standard has more than hundred thousand characters, and then I guess fonts just have the basic chars in them and not all of them, right? I doubt games that has UTF support renders all of thous chars on a texture, but they do use them somehow. For example, in Source Engine games (like CS:S) I have seen chinese symbols in chat while I don't actually have that charset installed. LGM's scripting editor does seem to handle utf chars so no problem there (though I don't know if gm6 format supports them, but the newest gmk does).

Next thing is about draw_set_halign(). I have an idea on how to do this, but that will involve cluttering the draw_text functions. I wouldn't want to make them slower than they need to be. This is the original line:
Code: [Select]
int xx = x, yy = y+fnt->yoffset; and it will have to be changed to this:
Code: [Select]
  if (halign == fa_left){
    xx = x;
  }else if (halign == fa_middle){
    xx = x-string_width(str)/2;
  }else{ //fa_right
    xx = x-string_width(str);
  }
I did fa_left the first because its the default and usual alignment. So in default case this will slow the code down by a one if statement. On the other hand things like fa_middle require me to know the width of the string, and string_width() is O(n). And this only for the halign.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on July 09, 2011, 10:35:51 am
That code doesn't consider newlines. Not having the code as fa_left fucks everything up, efficiency wise. You're best to fork up front.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: polygone on July 09, 2011, 11:05:21 am
There should be functions added for draw_get_halign / valign these are missing from gml.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 09, 2011, 03:12:37 pm
Quote
That code doesn't consider newlines.
Yes, the full code is this (tested and works):
Code: [Select]
void draw_text(int x,int y,string str)
{
  font *fnt = fontstructarray[currentfont];

  if (bound_texture != fnt->texture)
    glBindTexture(GL_TEXTURE_2D, bound_texture = fnt->texture);

  int xx, yy = y+fnt->yoffset, line = 0;
  if (halign == fa_left){
    xx = x;
  }else if (halign == fa_middle){
    xx = x-string_width_line(str,0)/2;
  }else{ //fa_right
    xx = x-string_width_line(str,0);
  }
  glBegin(GL_QUADS);
  for (unsigned i = 0; i < str.length(); i++)
  {
    if (str[i] == '\r'){
      line +=1, yy += fnt->height, i += str[i+1] == '\n';
      if (halign == fa_left){
          xx = x;
      }else if (halign == fa_middle){
          xx = x-string_width_line(str,line)/2;
      }else{ //fa_right
          xx = x-string_width_line(str,line);
      }
    }else if (str[i] == '\n'){
      line +=1, yy += fnt->height;
      if (halign == fa_left){
          xx = x;
      }else if (halign == fa_middle){
          xx = x-string_width_line(str,line)/2;
      }else{ //fa_right
          xx = x-string_width_line(str,line);
      }
    }else if (str[i] == ' ')
      xx += fnt->height/3; // FIXME: what's GM do about this?
    else
    {
      fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
        glTexCoord2f(g.tx,  g.ty);
          glVertex2i(xx + g.x,  yy + g.y);
        glTexCoord2f(g.tx2, g.ty);
          glVertex2i(xx + g.x2, yy + g.y);
        glTexCoord2f(g.tx2, g.ty2);
          glVertex2i(xx + g.x2, yy + g.y2);
        glTexCoord2f(g.tx,  g.ty2);
          glVertex2i(xx + g.x,  yy + g.y2);
      xx += g.xs;
    }
  }
  glEnd();
}
Quote
Not having the code as fa_left fucks everything up, efficiency wise.
Agreed.
Quote
You're best to fork up front.
Could you elaborate? You mean having separate drawing functions for all six alignments? Then how will that be backwards compatible with GM? Or how will draw_set_halign/valign actually work in that case?
Quote
There should be functions added for draw_get_halign / valign these are missing from gml.
I will. That is easy.

edit: Also, why do I need this:
Code: [Select]
    if (str[i] == '\r')
      xx = x, yy += fnt->height, i += str[i+1] == '\n';
    else if (str[i] == '\n')
      xx = x, yy += fnt->height;
and can't have simply:
Code: [Select]
    if (str[i] == '\r' or str[i] == '\n')
      xx = x, yy += fnt->height;
I left that in legacy sake and I know that there are several end line characters, but why I have to  i += str[i+1] == '\n' which actually doesn't make sense (I insert \n as far as I know, but that's funny way to write that). This part was written by Josh or TGMG a long time ago.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on July 09, 2011, 03:39:00 pm
Quote
Could you elaborate? You mean having separate drawing functions for all six alignments? Then how will that be backwards compatible with GM? Or how will draw_set_halign/valign actually work in that case?
No, just put the original function in a big if for align being top-left, and then put the rest of the code in the else with whatever hackery is required, that way you don't have to iterate the string for draw_text() when the aligns are default.

Quote
Also, why do I need this: ...
Otherwise, \r\n will put two newlines, or if you place ", u+= str[i]=='\n';" at the end of the proposed replacement, '\n\n' will be treated as one line.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 09, 2011, 04:14:22 pm
Quote
No, just put the original function in a big if for align being top-left, and then put the rest of the code in the else with whatever hackery is required, that way you don't have to iterate the string for draw_text() when the aligns are default.
So like this?
Code: [Select]
void draw_text(int x,int y,string str)
{
  font *fnt = fontstructarray[currentfont];

  if (bound_texture != fnt->texture)
    glBindTexture(GL_TEXTURE_2D, bound_texture = fnt->texture);

  if (halign == fa_left){
      int xx = x, yy = y+fnt->yoffset;
      glBegin(GL_QUADS);
      for (unsigned i = 0; i < str.length(); i++)
      {
        if (str[i] == '\r')
          xx = x, yy += fnt->height, i += str[i+1] == '\n';
        else if (str[i] == '\n')
          xx = x, yy += fnt->height;
        else if (str[i] == ' ')
          xx += fnt->height/3; // FIXME: what's GM do about this?
        else
        {
          fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
            glTexCoord2f(g.tx,  g.ty);
              glVertex2i(xx + g.x,  yy + g.y);
            glTexCoord2f(g.tx2, g.ty);
              glVertex2i(xx + g.x2, yy + g.y);
            glTexCoord2f(g.tx2, g.ty2);
              glVertex2i(xx + g.x2, yy + g.y2);
            glTexCoord2f(g.tx,  g.ty2);
              glVertex2i(xx + g.x,  yy + g.y2);
          xx += g.xs;
        }
      }
      glEnd();
  }else{
      int xx, yy = y+fnt->yoffset, line = 0;
      if (halign == fa_middle){
        xx = x-string_width_line(str,0)/2;
      }else{ //fa_right
        xx = x-string_width_line(str,0);
      }
      glBegin(GL_QUADS);
      for (unsigned i = 0; i < str.length(); i++)
      {
        if (str[i] == '\r'){
          line +=1, yy += fnt->height, i += str[i+1] == '\n';
          if (halign == fa_middle){
              xx = x-string_width_line(str,line)/2;
          }else{ //fa_right
              xx = x-string_width_line(str,line);
          }
        }else if (str[i] == '\n'){
          line +=1, yy += fnt->height;
          if (halign == fa_middle){
              xx = x-string_width_line(str,line)/2;
          }else{ //fa_right
              xx = x-string_width_line(str,line);
          }
        }else if (str[i] == ' ')
          xx += fnt->height/3; // FIXME: what's GM do about this?
        else
        {
          fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
            glTexCoord2f(g.tx,  g.ty);
              glVertex2i(xx + g.x,  yy + g.y);
            glTexCoord2f(g.tx2, g.ty);
              glVertex2i(xx + g.x2, yy + g.y);
            glTexCoord2f(g.tx2, g.ty2);
              glVertex2i(xx + g.x2, yy + g.y2);
            glTexCoord2f(g.tx,  g.ty2);
              glVertex2i(xx + g.x,  yy + g.y2);
          xx += g.xs;
        }
      }
      glEnd();
  }
}
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on July 09, 2011, 04:33:03 pm
Yep. That way, string_width is never called for simple left-aligned text.
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 09, 2011, 05:07:07 pm
It actually wasn't called in the previous example code either. Though there was an if statement at every newline character while in this code there isn't. So that's something I guess. Now about valign. That actually requires a lot less code, and it doesn't require anything in the for loop. So should I write like this:
Code: [Select]
void draw_text(int x,int y,string str)
{
  font *fnt = fontstructarray[currentfont];

  if (bound_texture != fnt->texture)
    glBindTexture(GL_TEXTURE_2D, bound_texture = fnt->texture);

  if (halign == fa_left){
      int xx = x, yy = y+fnt->yoffset;
      if (valign == fa_middle)
        yy -= string_height(str)/2;
      else if (valign == fa_bottom)
        yy -= string_height(str);
      glBegin(GL_QUADS);
      for (unsigned i = 0; i < str.length(); i++)
      {
        if (str[i] == '\r')
          xx = x, yy += fnt->height, i += str[i+1] == '\n';
        else if (str[i] == '\n')
          xx = x, yy += fnt->height;
        else if (str[i] == ' ')
          xx += fnt->height/3; // FIXME: what's GM do about this?
        else
        {
          fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
            glTexCoord2f(g.tx,  g.ty);
              glVertex2i(xx + g.x,  yy + g.y);
            glTexCoord2f(g.tx2, g.ty);
              glVertex2i(xx + g.x2, yy + g.y);
            glTexCoord2f(g.tx2, g.ty2);
              glVertex2i(xx + g.x2, yy + g.y2);
            glTexCoord2f(g.tx,  g.ty2);
              glVertex2i(xx + g.x,  yy + g.y2);
          xx += g.xs;
        }
      }
      glEnd();
  }else{
      int xx, yy = y+fnt->yoffset, line = 0;
      if (valign == fa_middle)
        yy -= string_height(str)/2;
      else if (valign == fa_bottom)
        yy -= string_height(str);
      if (halign == fa_center){
        xx = x-string_width_line(str,0)/2;
      }else{ //fa_right
        xx = x-string_width_line(str,0);
      }
      glBegin(GL_QUADS);
      for (unsigned i = 0; i < str.length(); i++)
      {
        if (str[i] == '\r'){
          line +=1, yy += fnt->height, i += str[i+1] == '\n';
          if (halign == fa_center){
              xx = x-string_width_line(str,line)/2;
          }else{ //fa_right
              xx = x-string_width_line(str,line);
          }
        }else if (str[i] == '\n'){
          line +=1, yy += fnt->height;
          if (halign == fa_center){
              xx = x-string_width_line(str,line)/2;
          }else{ //fa_right
              xx = x-string_width_line(str,line);
          }
        }else if (str[i] == ' ')
          xx += fnt->height/3; // FIXME: what's GM do about this?
        else
        {
          fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
            glTexCoord2f(g.tx,  g.ty);
              glVertex2i(xx + g.x,  yy + g.y);
            glTexCoord2f(g.tx2, g.ty);
              glVertex2i(xx + g.x2, yy + g.y);
            glTexCoord2f(g.tx2, g.ty2);
              glVertex2i(xx + g.x2, yy + g.y2);
            glTexCoord2f(g.tx,  g.ty2);
              glVertex2i(xx + g.x,  yy + g.y2);
          xx += g.xs;
        }
      }
      glEnd();
  }
}
Or split again? The code size will double if I split.
edit: Fixed "thou" into "though". I know I use it wrong, but no one corrects me so its hard to learn.
Anyway, sleep time. If this code is good then I will implement draw_set_halign and valign tomorrow. There will just be a question about declarations out of the functions (because I need to declare fa_ constants in both .h and .cpp file.. more on that later).
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: Josh @ Dreamland on July 09, 2011, 08:46:59 pm
How on earth does valign have any effect on draw_text? O_o
If it just centers the text around the given y coordinate, then there's no need to fork again. Just set the initial yy to valign == fa_top ? y : valign == fa_middle ? y - string_height(str)/2 : y - string_height(str).

And yeah, it's "though." But no one really cares if you abbreviate, even unintentionally. :P
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: TheExDeus on July 10, 2011, 07:52:15 am
Quote
valign == fa_top ? y : valign == fa_middle ? y - string_height(str)/2 : y - string_height(str)
That's basically what I did, though I typed the whole if statement. Does it actually increase speed with using the shorter if statement then the long one?
The end code is this:
Code: [Select]
void draw_text(int x,int y,string str)
{
  font *fnt = fontstructarray[currentfont];

  if (bound_texture != fnt->texture)
    glBindTexture(GL_TEXTURE_2D, bound_texture = fnt->texture);
  int yy = valign == fa_top ? y+fnt->yoffset : valign == fa_middle ? y +fnt->yoffset - string_height(str)/2 : y + fnt->yoffset - string_height(str);
  if (halign == fa_left){
      int xx = x;
      glBegin(GL_QUADS);
      for (unsigned i = 0; i < str.length(); i++)
      {
        if (str[i] == '\r')
          xx = x, yy += fnt->height, i += str[i+1] == '\n';
        else if (str[i] == '\n')
          xx = x, yy += fnt->height;
        else if (str[i] == ' ')
          xx += fnt->height/3; // FIXME: what's GM do about this?
        else
        {
          fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
            glTexCoord2f(g.tx,  g.ty);
              glVertex2i(xx + g.x,  yy + g.y);
            glTexCoord2f(g.tx2, g.ty);
              glVertex2i(xx + g.x2, yy + g.y);
            glTexCoord2f(g.tx2, g.ty2);
              glVertex2i(xx + g.x2, yy + g.y2);
            glTexCoord2f(g.tx,  g.ty2);
              glVertex2i(xx + g.x,  yy + g.y2);
          xx += g.xs;
        }
      }
      glEnd();
  }else{
      int xx = halign == fa_center ? x-string_width_line(str,0)/2 : x-string_width_line(str,0), line = 0;
      glBegin(GL_QUADS);
      for (unsigned i = 0; i < str.length(); i++)
      {
        if (str[i] == '\r'){
          line +=1, yy += fnt->height, i += str[i+1] == '\n';
          xx = halign == fa_center ? x-string_width_line(str,line)/2 : x-string_width_line(str,line);
        }else if (str[i] == '\n'){
          line +=1, yy += fnt->height;
          xx = halign == fa_center ? x-string_width_line(str,line)/2 : x-string_width_line(str,line);
        }else if (str[i] == ' ')
          xx += fnt->height/3; // FIXME: what's GM do about this?
        else
        {
          fontglyph &g = fnt->glyphs[(unsigned char)(str[i] - fnt->glyphstart) % fnt->glyphcount];
            glTexCoord2f(g.tx,  g.ty);
              glVertex2i(xx + g.x,  yy + g.y);
            glTexCoord2f(g.tx2, g.ty);
              glVertex2i(xx + g.x2, yy + g.y);
            glTexCoord2f(g.tx2, g.ty2);
              glVertex2i(xx + g.x2, yy + g.y2);
            glTexCoord2f(g.tx,  g.ty2);
              glVertex2i(xx + g.x,  yy + g.y2);
          xx += g.xs;
        }
      }
      glEnd();
  }
}
Quote
But no one really cares if you abbreviate
That's not an abbreviation, I just thought its how its spelled. The word thou is a second person singular pronoun in English (http://en.wikipedia.org/wiki/Thou).
Title: Re: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)
Post by: IsmAvatar on July 10, 2011, 11:51:40 am
Quote
That's not an abbreviation, I just thought its how its spelled. The word thou is a second person singular pronoun in English.
It is now largely archaic.

"Thou shalt not eat from the tree of knowledge."
Nobody talks like that anymore unless you're trying to sound biblical. In other contexts, if you use the word "thou" or "tho", we assume you're abbreviating "though" (actually, the more recognized abbreviation on the interwebs is "tho", so we assume the "u" is an accidental extra).