Pages: « 1 2 3 4 »
  Print  
Author Topic: GML: All draw_text functions +(string_width, _height, _width_ext, _height_ext)  (Read 46240 times)
Offline (Male) RetroX
Reply #15 Posted on: October 30, 2010, 09:21:35 am

Master of all things Linux
Contributor
Location: US
Joined: Apr 2008
Posts: 1055
MSN Messenger - classixretrox@gmail.com
View Profile Email
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.
Logged
My Box: Phenom II 3.4GHz X4 | ASUS ATI RadeonHD 5770, 1GB GDDR5 RAM | 1x4GB DDR3 SRAM | Arch Linux, x86_64 (Cube) / Windows 7 x64 (Blob)
Quote from: Fede-lasse
Why do all the pro-Microsoft people have troll avatars? :(
Offline (Male) Josh @ Dreamland
Reply #16 Posted on: October 30, 2010, 09:53:19 am

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2950

View Profile Email
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.
Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Unknown gender) TheExDeus
Reply #17 Posted on: October 30, 2010, 10:23:47 am

Developer
Joined: Apr 2008
Posts: 1860

View Profile
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.
« Last Edit: October 30, 2010, 01:16:24 pm by HaRRiKiRi » Logged
Offline (Male) Josh @ Dreamland
Reply #18 Posted on: October 31, 2010, 10:21:14 am

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2950

View Profile Email
> 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.
Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Unknown gender) TheExDeus
Reply #19 Posted on: October 31, 2010, 10:38:55 am

Developer
Joined: Apr 2008
Posts: 1860

View Profile
For windows I am looking for something like this:
http://msdn.microsoft.com/en-us/library/dd183499%28VS.85%29.aspx
Logged
Offline (Female) IsmAvatar
Reply #20 Posted on: October 31, 2010, 09:27:52 pm

LateralGM Developer
LGM Developer
Location: Pennsylvania/USA
Joined: Apr 2008
Posts: 877

View Profile Email
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.
Logged
Offline (Male) Josh @ Dreamland
Reply #21 Posted on: November 01, 2010, 07:52:02 am

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2950

View Profile Email
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...
« Last Edit: November 01, 2010, 07:53:41 am by Josh @ Dreamland » Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Unknown gender) TheExDeus
Reply #22 Posted on: November 01, 2010, 09:59:18 am

Developer
Joined: Apr 2008
Posts: 1860

View Profile
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.
Logged
Offline (Unknown gender) TheExDeus
Reply #23 Posted on: April 24, 2011, 08:37:04 am

Developer
Joined: Apr 2008
Posts: 1860

View Profile
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.
Logged
Offline (Male) RetroX
Reply #24 Posted on: April 24, 2011, 10:11:14 am

Master of all things Linux
Contributor
Location: US
Joined: Apr 2008
Posts: 1055
MSN Messenger - classixretrox@gmail.com
View Profile Email
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.
Logged
My Box: Phenom II 3.4GHz X4 | ASUS ATI RadeonHD 5770, 1GB GDDR5 RAM | 1x4GB DDR3 SRAM | Arch Linux, x86_64 (Cube) / Windows 7 x64 (Blob)
Quote from: Fede-lasse
Why do all the pro-Microsoft people have troll avatars? :(
Offline (Unknown gender) TheExDeus
Reply #25 Posted on: April 24, 2011, 11:29:07 am

Developer
Joined: Apr 2008
Posts: 1860

View Profile
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.
« Last Edit: April 24, 2011, 11:32:13 am by HaRRiKiRi » Logged
Offline (Male) RetroX
Reply #26 Posted on: April 24, 2011, 05:26:09 pm

Master of all things Linux
Contributor
Location: US
Joined: Apr 2008
Posts: 1055
MSN Messenger - classixretrox@gmail.com
View Profile Email
Yeah, for some reason, I was thinking of the font as individual points, when really, it's just a bunch of glyphs.
Logged
My Box: Phenom II 3.4GHz X4 | ASUS ATI RadeonHD 5770, 1GB GDDR5 RAM | 1x4GB DDR3 SRAM | Arch Linux, x86_64 (Cube) / Windows 7 x64 (Blob)
Quote from: Fede-lasse
Why do all the pro-Microsoft people have troll avatars? :(
Offline (Male) Josh @ Dreamland
Reply #27 Posted on: April 26, 2011, 10:42:52 am

Prince of all Goldfish
Developer
Location: Pittsburgh, PA, USA
Joined: Feb 2008
Posts: 2950

View Profile Email
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).
« Last Edit: April 26, 2011, 10:53:09 am by Josh @ Dreamland » Logged
"That is the single most cryptic piece of code I have ever seen." -Master PobbleWobble
"I disapprove of what you say, but I will defend to the death your right to say it." -Evelyn Beatrice Hall, Friends of Voltaire
Offline (Unknown gender) TheExDeus
Reply #28 Posted on: April 29, 2011, 04:24:43 pm

Developer
Joined: Apr 2008
Posts: 1860

View Profile
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.
Logged
Offline (Unknown gender) TheExDeus
Reply #29 Posted on: May 11, 2011, 03:22:00 pm

Developer
Joined: Apr 2008
Posts: 1860

View Profile
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.
Logged
Pages: « 1 2 3 4 »
  Print