VectorText! -Drawing Strings via DrawLine Function

BlitzMax Forums/BlitzMax Beginners Area/VectorText! -Drawing Strings via DrawLine Function

Matt McFarland(Posted 2009) [#1]
Hey guys,

I spent a few hours last night programming a realtime "Font" of neat vectortext.

The code was made in superstrict but I removed superstrict as I include it in my projects.

If you're going to use this function for your own projects:
Save it as a file called "VectorText.bmx" or whatever you want to call it, Then make a new bmx file and be sure to put:
Include "VectorText.bmx"

OR put it in a mod!

It basically is one function that you can use called "VecDrawText"
To use:
VecDrawText(X,Y,TEXTSIZE)


Your feedback is welcome, including if I wasted my time and there is a WAY easier way to code this type of function, or if it's already been done LOL.

Thanks guys!

Here is the code:


Simply Copy Paste and run. (It has an example running at the bottom that you should comment out if you use this for your own projects)


xlsior(Posted 2009) [#2]
Any idea how the draw speed of this compared to normas DrawText?

If it's faster, it could be very convenient for printing debug diagnostic info to the screen


Matt McFarland(Posted 2009) [#3]
I'd assume it'd be slower because instead of drawing a line of text we're drawing several lines.


ImaginaryHuman(Posted 2009) [#4]
I was starting to read through your code and then realized that you made one function for every character of the alphabet. lol .... You know, all you need is some kind of array of `font data` ie local coordinates to draw a line from-to, and then just zip through a string of text pulling data from the array and drawing lines in a single function. But still, the way you did it works :-)

I would think this might actually render faster then DrawText because DrawText has to consider texture pixels which have to be masked, whereas you aren't doing any texturing or masking ... just that you have a bit more geometry data.


xMicky(Posted 2009) [#5]
A little modification of the code above after "'EXAMPLE !...." as shown below for getting the time results leads to more or less 5 times more time needed for VecDrawText() against DrawText().

'EXAMPLE! COMMENT THIS OUT IF YOU ARE USING THIS AS AN INCLUDE

Local start:Int
Local start2:Int
Local endtext:Int
Local PrintText:String = "Lorem ipsum dolor sit amet"

Graphics 640, 480
Repeat
Cls

  If endtext =0 Then
  start =MilliSecs()
  For Local z:Int =0 To 1000
    VecDrawText(PrintText, 10, 100)
  Next
  start2 =MilliSecs()
  For Local z:Int =0 To 1000
    DrawText PrintText, 10,100
  Next
  endtext =MilliSecs()
  End If
  DrawText "Time for 1000x VecDrawText() : "+Trim$(start2 -start), 10,100
  DrawText "Time for 1000x DrawText ()   : "+Trim$(endtext -start2), 10,130



Flip

Forever



xlsior(Posted 2009) [#6]
A little modification of the code above after "'EXAMPLE !...." as shown below for getting the time results leads to more or less 5 times more time needed for VecDrawText() against DrawText().


Interesting.

On my PC, the DrawVectorText version is almost 8 times slower than DrawText in that sample.


Matt McFarland(Posted 2009) [#7]
wow.. pretty slow! Thanks xMicky. I wonder if it's how I made it (ie: lots of function calls) - not sure though.

Anywhoot, I made it primarily for a vector based video game.


_Skully(Posted 2009) [#8]
Whoa... you'd be better to vectorize the pixels in a font and then use those points to redraw them at whatever scale... you could in fact add special effects too at that point. This is aside from using some vector font mod of course. Since you are considering a vector based video game I would suggest getting a vector based mod and do everything up vector based.

I say that because you are limited to LED type display here


Brucey(Posted 2009) [#9]
DrawLine is very inefficient.


_Skully(Posted 2009) [#10]
Brucey,

I use drawline a little, whats faster?


matibee(Posted 2009) [#11]
whats faster?
At a guess, drawing an OpenGL or DX7 line list.


Brucey(Posted 2009) [#12]
You need to remember that Max2D is fairly generic. This makes it easier for you to knock something cool together relatively quickly, without needing to know about all the underlying lower-level graphics stuff.
This comes at a price, though.

Here's what happens, more or less, when you call DrawLine for OpenGL :
level 1 : max2d

	_max2dDriver.DrawLine gc.handle_x,gc.handle_y, gc.handle_x+x2-x,gc.handle_y+y2-y,..
		x+gc.origin_x,y+gc.origin_y

level 2 : glmax2d

		DisableTex
		glBegin GL_LINES
		glVertex2f x0*ix+y0*iy+tx+.5,x0*jx+y0*jy+ty+.5
		glVertex2f x1*ix+y1*iy+tx+.5,x1*jx+y1*jy+ty+.5
		glEnd

This is also handling origin and transformations for you.

If you know what you want, you can obviously create your own drawline which you can skip stuff you don't need, and perhaps, as matibee suggests, batch a whole set of lines together - which will be much faster.


Brucey(Posted 2009) [#13]
TonyG once suggested a list of improvements that would fit well within a better Max2D... things like batched drawing.

DrawText is another painfully slow routine - if you consider how it works.
Single-surface text drawing is an order of magnitude faster.


Who was John Galt?(Posted 2009) [#14]
Matt...

The way you're doing things is pretty inefficient, at least in terms of code length. As IH suggested, an array of plot points for each letter would have been easier, however, I find your approach to the problem intriguing. You must have put a lot of effort into it, and it's interesting how you've built the characters up from a set of parts.


Matt McFarland(Posted 2009) [#15]
Hey thanks guys, yeah I just tackled the project with limited knowledge. I plan on abandoning this method and using bitmap fonts from this point on. It still was a good coding exercise I guess lol :) I appreciate your feedback.


Who was John Galt?(Posted 2009) [#16]
Don't be put off. Speed isn't everything, and vector drawing has its place... like the retro game you're making.


xMicky(Posted 2009) [#17]
One great advantage of your vector text is that you don't have to care, what fonts are installed on the end-users PC and that there are no font-related-copyright-questions to deal with at all.

I assume, the speed could be increased in the way ImaginaryHuman pointed out; many function-calls and string-operations for each char are probably a heavy weight slow-down, Drawline() alone should not be the reason for the major time difference against DrawText().


Matt McFarland(Posted 2009) [#18]
I was planning on using the vector text code, but turning all of the letters into images (via code) and just drawing the pictures instead. My primary focus is just making a cool font for the game. I'm thinking that pixmapping should increase speed.


Matt McFarland(Posted 2009) [#19]
I have no idea how to do this as an array. All of the from-to coordinates are dependant on what the X,Y and lettersize are. If I need to draw a letter, then I use functions to draw each part of the letter (each line).

I only know the basics of arrays, not sure how arrays of arrays work or resizing them etc. Not sure if I'd have to do that, but I can't seem to figure out how it would work just using an array.

I've spent the last 4 hours trying to figure this out. Do I need to create an array for every part?

I did it like this:
	Local X:Float = Ox + Lettersize
	Local Y:Float = Oy + Lettersize
	Local Hx:Float = Ox + Lettersize / 2
	Local Hy:Float = Oy + Lettersize / 2
	'Top [ (TopLeftStr) IS Ox, oY, Ox, Hy
	Local TopLeftStr:Float[3]
	TopLeftStr[0] = Ox ; TopLeftStr[1] = Oy ; TopLeftStr[2] = Ox ; TopLeftStr[3] = Hy
	'Bottom [ = Ox, Y, Ox, Hy
	Local BottomLeftStr:Float[3]

But that doesn't make any sense to me. It's like I'm creating a bunch of arrays for no reason.

I can't figure out what type of Array to make becuase I dont know how I'd pull the data from the array to draw each line. Since every single letter uses a different amount of lines, I'd be telling the computer to draw so many lines per letter thus the lines of code would differ. I dont know how many arrays to make, and reading the BlitzMax manual on arrays is not helping me at all. If you care to shed some light then please do so. Otherwise I can just keep the code as it is and do some pixmapping. I've spent the last 4 hours getting frustrated with arrays so I think I'm going to take a much needed break.. :S


xMicky(Posted 2009) [#20]
How to code a text with DrawLine() is not at least a question of the personal style of the programmer; for a fast hack, I would think of something like (only for "x", "y", "z") the following, which needs round about 3times more than DrawText() (which may become worse, if letters are used which need more Drawline()'s) :



Perhaps the question for the speed was a turn into a wrong direction anyway, because even a 5 to 10 times slower text drawing compared to DrawText() may be more than fast enough to keep the app running with a sufficient frame rate, if not the whole screen is filled with text; it might be a good idea to test this performance first before spending time to make your text drawing faster...


Matt McFarland(Posted 2009) [#21]
I thought that was an interesting way that you went through the print string. Still don't fully grasp it but wow that's a neat way to do it! As far as drawing the text through if statements! that's the same thing I did with the exception of calling a function isntead of just putting the drawline commands there. I did that to make it easier on me to read my own code.

Anyway I was trying to see if I could make it all grab the drawline points from an array but that just didnt work. I think that's because it's not possible! lol.

My goal right now is to learn how to use grabimage in a for next loop by grabbing different parts of the screen so I can take the alphabet and use a drawimage function instead of a drawline function. This should be faster than drawing lines with each step.

I wanted to say that my friends computer (factory shipped xp) runs my game at only 25fps, while on my computer it runs at 120fps. So I am hoping that if I get this vectortext thing turned into just a bitmap font that I create on the fly in the game itself I can increase speed on his computer.

My main goal of coding is to code as efficiently as possible without sacrificing a reasonable understanding of the syntax.


xMicky(Posted 2009) [#22]
OK, you demanded it...;)




Matt McFarland(Posted 2009) [#23]
Fascinating..

SuperStrict
Local t:String = "hello world"

For Local i:Int = 0 To t.length - 1
	Print t[i]
	
Next

I just hacked that together because I've just learned how to find the ASCII codes of each character in a string without using old basic string functions. Very nice and helpful, thank you! I'm going to dissect your code a bit further so I can fully understand it.


Matt McFarland(Posted 2009) [#24]
I want to thank you for that bit of code.

I've been dissecting it and I'm starting to understand what it does.
I have added print functions and a waitkey that helps explain what its doing within each cycle.

The only part I don't understand is how [zz*3+4] works??? I assume it's used to gather how many maximum lines are drawn, but what I don't understand is that if [zz] = 120, and you multiple that by 3 and add 4 you come up with a number that is much higher then how many lines that are needed to be drawn. Since there is no CharData[364] that would = the amount of lines drawn I'm also confused. Can you tell me how that part works? Thanks!




Matt McFarland(Posted 2009) [#25]
Well I have updated the code.

You now have two functions

VecDrawText()

And Vecdraw_img_text()

VecDraw_img_text() is faster than drawtext!!




xMicky(Posted 2009) [#26]
True, on my notebook VecDraw_img_ext needed 69 ms, DrawText 79 ms. Seems, you have made very good progress over the last few days :)

What does the term [zz*3 +4] means ?
zz is the array index where the asc-value of the char was found, which is to print next. For each char then the array contains three special informations:
#1: where in the array the data for the Drawline-commands starts,
#2: how many lines are to draw to display that char,
#3: the width in pixel of that char.
Those triples starts at index 7 in the array, which belongs to the first defined char, then at index 10 stands the triple for the second defined char, at 13 the infos for char#3...you get 7, 10, 13 as values for zz =1,2,3 if you calculate: zz*3 +4.

Erm, don't know, whether your question indicates, that you already discovered this: the term is correct, but ONLY for 6 defined chars in the
array . For the general case of n chars the term must be: [zz *3 +charData[0] -2]. Sorry, I wrote the code yesterday on the fly and took the first term that jumped into my eye.


Matt McFarland(Posted 2009) [#27]
Ugh, I don't get it!

What's a triple?

Is the #1,#2,#3 the zz the 3 and the 4?

How does [zz*3+4] find the amount of lines that need to be drawn?

It's my understanding that [zz] where exactly in the array we're dealing with.. That's called the index right? (not 100%) but since [zz]'s index holds an ASCII code, of say... 120, then isn't 120*3+4 like way higher than the amount of lines that need to be drawn, say 2???? :S :S :S

         For Local zzz:Int = 1 To charData[zz * 3 + 5] 'For zzz = 1 to how many lines we need to draw.


Draw a line in this for..next loop.. WTF? I dont get that part lol


xMicky(Posted 2009) [#28]
a "triple" is a set of three, here: data values belonging to/describing the same char; is this in english not a used phrase, I took it as such, translating from german ?

[zz*3 +4] does NOT lead to the count of lines to draw, but to the array index, where the coordinates for the char starts.

[zz*3 +5] does NOT hold an ASCII-value, but the count of lines of the char

In a short, the array is build as such:

charData[0] =count of defined chars
charData[1] =ASCII of first defined char
charData[2] =ASCII of second defined char
...and so on until:
charData[6] =ASCII of sixth defined char
chardata[1*3 +4=7]: array index, which hold array index, where
chars#1 data starts
chardata[1*3 +5=8]:line count of char #1
chardata[1*3 +6=9]:width of char#1
chardata[2*3 +4=10]: array index, which hold array index, where
chars#2 data starts
chardata[2*3 +5=11]:line count of char #2
chardata[2*3 +6=12]:width of char#2
...
charData[charData[7]] :start of data of char#1
....
charData[charData[10]] :start of data of char#2
....


Matt McFarland(Posted 2009) [#29]
ohhhhhh, that makes sense. Thanks! so if I had about 36 characters or more, then I'd do zzz*36 + start coordinates, then + the line count.. then + another thing which is for the condense part.. condense.. sup with condense anyway?


xMicky(Posted 2009) [#30]
Condense is just an additional distance between the chars to make the text wider or more condensed.