Games fonts with outline

developer_123

Active Member
Licensed User
Longtime User
I am drawing my score using the method :
B4X:
    Dim gname As String = X2.GraphicCache.GetTempName
    Dim ShapeSize As B2Vec2 = X2.GetShapeWidthAndHeight(template.FixtureDef.Shape)
    ShapeSize.MultiplyThis(X2.mBCPixelsPerMeter)
    Dim cvs As B4XCanvas = X2.GraphicCache.GetCanvas(ShapeSize.X / X2.BmpSmoothScale)
    cvs.ClearRect(cvs.TargetRect)
    Dim text As String = Score
    Dim r As B4XRect = cvs.MeasureText(text, ScoreFont)
    Dim BaseLine As Int = ShapeSize.Y / 2 - r.Height / 2 - r.Top
    cvs.DrawText(text, ShapeSize.X / 2, BaseLine, ScoreFont,  xui.Color_white, "CENTER")
    Dim sb As X2ScaledBitmap
    sb.Bmp = cvs.CreateBitmap.Crop(0, 0, ShapeSize.X, ShapeSize.Y)
    sb.Scale = 1
    X2.GraphicCache.PutGraphic(gname, Array(sb))
    bw.GraphicName = gname

Because the background landscape of the different levels have different colors, sometimes the drawn text is lost. Therefore, I am looking for a way to use outline fonts, but only the border of the text is drawn in the selected color and the interior of the text remains transparent, which generates the same visual awkwardness. Searching the forums I found DrawTextWithBorder and I adjusted it to my case thinking that the result would be a text with an outline, but what I got was a rectangle around the text and a blue dot in the center. I would like to know if there is a way to obtain solid color text, with another color outline. Any ideas?
 

developer_123

Active Member
Licensed User
Longtime User
Have you tried to draw the text twice with different font size and different colors?


Yes, in fact it was my first attempt, but when creating the text in the foreground in black with a larger font size, it is not symmetrically centered since the spaces between letters mean that the entire text does not look outlined. What I was able to get is the shadow effect, with the same size but shifted a little to the right and a little down. I thought maybe it was possible to create the outline from the text drawing function.
 

Sandman

Expert
Licensed User
Longtime User
Traditionally, in the newspaper world (which I have history in), this was made by selecting the object (in QuarkXPress), pressing a key-combo (picked up by QuicKeys) wich launched a macro duplicating the object, moving it to the bottom, changing its colour to white, and moving it 0.5 mm to the right. Then duplicate that object, move it 0.5 mm higher up. Then duplicate that object and move it 0.5 mm to the left. Repeat until the original text block is surrounded with eight identical, but white, text blocks. And you got a white outline. (This works because they were moved not very far. If you tried to make a huge outline like this, and tried to move them 10 mm or so, it would look awful.)

This is a long-whinded way of saying you could solve this by making a mask that you apply whatever colour you want your outline to be. Do the mask like what I explained above. It's left as an exercise to the reader to figure out more fitting tools than QuarkXpress and QuicKeys. :)
 

kimstudio

Active Member
Licensed User
Longtime User
Use string.charat to get each char and draw it one by one with same char interval space.
 

developer_123

Active Member
Licensed User
Longtime User
Traditionally, in the newspaper world (which I have history in), this was made by selecting the object (in QuarkXPress), pressing a key-combo (picked up by QuicKeys) wich launched a macro duplicating the object, moving it to the bottom, changing its colour to white, and moving it 0.5 mm to the right. Then duplicate that object, move it 0.5 mm higher up. Then duplicate that object and move it 0.5 mm to the left. Repeat until the original text block is surrounded with eight identical, but white, text blocks. And you got a white outline. (This works because they were moved not very far. If you tried to make a huge outline like this, and tried to move them 10 mm or so, it would look awful.)

This is a long-whinded way of saying you could solve this by making a mask that you apply whatever colour you want your outline to be. Do the mask like what I explained above. It's left as an exercise to the reader to figure out more fitting tools than QuarkXpress and QuicKeys. :)
Thanks for your comments Sandman. Finally I did it. Is necesary calibrate the correct space to get a right sensation.
 

developer_123

Active Member
Licensed User
Longtime User
My pleasure

What I do have a problem with is the quality of the graphics. The score looks very pixelated and in general the other graphics of the application too.

I have already included the following code to improve the graphics and they obviously improve, but they still look very pixelated.

B4X:
ImageView1.Width = ImageView1.Width * xui.Scale
ImageView1.Height = ImageView1.Height * xui.Scale
X2.Initialize(Me, ImageView1, world) '<--- this is the original line in Game.Initialize
ImageView1.Width = ImageView1.Width / xui.Scale
ImageView1.Height = ImageView1.Height / xui.Scale

This is the result of the score (with a font size of 30) together with the contour as we have talked about it in the previous messages. As for the graphics, I've already played a lot with the sizes and resolution of the sprites, but the truth is that I can't get clear images that give a result similar to an image contained in an ImageView.

1653067596463.png



This morning a colleague showed me the sharpness of images of a small project he is doing with Unity, and the difference is quite a bit.
 

Sandman

Expert
Licensed User
Longtime User
Yeah, that looks awful. Assuming you didn't do any mistakes in your code, I would try rendering it at (for instance) 400% on my off-screen canvas and then draw that canvas at 25% when you put it on screen. That should absolutely solve the problem. (This is how anti-aliasing works, so it's an established and battle-tested solution.)

Alternative solutions might exist, using more specialized B4X solutions, but that's beyond my competence horizon.
 

kimstudio

Active Member
Licensed User
Longtime User
An ugly solution that can be optimized. It is in B4J not sure about B4A, but the idea is to create font mask and redraw it with larger circles which is antialiased.

fo.png


cv is in memory, Canvas1 is put on form using desinger.

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI 
    Private Button1 As B4XView
    Dim cv As Canvas
    Dim im As Image
    Private Canvas1 As Canvas
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    
    Dim s As String = "kimstudio"
    cv.Initialize("")
    cv.Width = 400
    cv.Height = 100
    cv.ClearRect(0,0,cv.Width,cv.Height)
    cv.DrawText(s, 60, 60, fx.CreateFont("Arial", 64, True, False), fx.Colors.Blue, "LEFT")
    im = cv.Snapshot2(fx.Colors.ARGB(0,0,0,0))
    
    For y = 0 To cv.Height - 1
        For x = 0 To cv.Width - 1
            If im.GetPixel(x, y) <> 0x00000000 Then
                Canvas1.DrawCircle(x, y, 3, fx.Colors.Red, True, 0.0)
            End If
        Next
    Next
    Canvas1.DrawImage(im, 0,0,cv.Width, cv.Height)
End Sub

Sub Button1_Click
    xui.MsgboxAsync("Hello World!", "B4X")
End Sub
 

developer_123

Active Member
Licensed User
Longtime User
Yeah, that looks awful. Assuming you didn't do any mistakes in your code, I would try rendering it at (for instance) 400% on my off-screen canvas and then draw that canvas at 25% when you put it on screen. That should absolutely solve the problem. (This is how anti-aliasing works, so it's an established and battle-tested solution.)

Alternative solutions might exist, using more specialized B4X solutions, but that's beyond my competence horizon.
SandMan I try to understand what you mean, but I don't know how to implement it. When you tell me to render it 400% on my off-screen canvas, what do you mean? To the size on the Tiled? When I then have to put it at 25% inside the screen, what do you mean? how would i do it?
In my case I have sprite images and text that is drawn in a Tiled object with the routine that I indicated at the beginning of this thread.

On the other hand, what would be the correct ratio of pixels per pattern that should be configured in Tiled to achieve good image quality?
 

developer_123

Active Member
Licensed User
Longtime User
An ugly solution that can be optimized. It is in B4J not sure about B4A, but the idea is to create font mask and redraw it with larger circles which is antialiased.

View attachment 129405

cv is in memory, Canvas1 is put on form using desinger.

B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView
    Dim cv As Canvas
    Dim im As Image
    Private Canvas1 As Canvas
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
   
    Dim s As String = "kimstudio"
    cv.Initialize("")
    cv.Width = 400
    cv.Height = 100
    cv.ClearRect(0,0,cv.Width,cv.Height)
    cv.DrawText(s, 60, 60, fx.CreateFont("Arial", 64, True, False), fx.Colors.Blue, "LEFT")
    im = cv.Snapshot2(fx.Colors.ARGB(0,0,0,0))
   
    For y = 0 To cv.Height - 1
        For x = 0 To cv.Width - 1
            If im.GetPixel(x, y) <> 0x00000000 Then
                Canvas1.DrawCircle(x, y, 3, fx.Colors.Red, True, 0.0)
            End If
        Next
    Next
    Canvas1.DrawImage(im, 0,0,cv.Width, cv.Height)
End Sub

Sub Button1_Click
    xui.MsgboxAsync("Hello World!", "B4X")
End Sub
Kim Studio, I see that with the "For" routine you implement the creation of the contour. As my application is multiplatform and I use X2 as Game Engine, I will have to do the relevant tests to adjust your code. Thanks for your input, I'll be testing.
 

developer_123

Active Member
Licensed User
Longtime User
I already solved the outline thing (but with poor image quality), but I had to create my own .ttf font, in fact I created 2 fonts. I just created the numbers and letters with the thickness of the desired outline so that it would be hollow inside, and then I created a second font with the same size and shape, only I left it without an outline, so that when I placed the second font behind the first, the result is as expected. What I still can't do is correct the quality of the images. I'm going to play around with the number of pixels per pattern in the Tiled setting, increase the pixels and create sprites whose pixel resolution per frame exactly matches the number of pixels in the Tiled patterns. I hope in this way to be able to give a solution, while SandMan clarifies to me the idea of adjusting the proportions. :)
 
Last edited:

Sandman

Expert
Licensed User
Longtime User
Now you mention Tiled, something you didn't bring up before. I have no clue about Tiled, sorry. I was merely trying to explain from a general perspective on how you might be able to solve the problem.

To clarify, this is what I meant. (*)

Imagine having an off-screen canvas where you draw your text. For some reason it's choppy and ugly. You draw this four times larger than what you actually need.

1653288996854.png


Then, when everything is drawn to the off-screen canvas, you draw that on screen in one quarter of its' size, which is the size you actually want. That would make it look like this, which is beautiful.

1653289108890.png


This is what I tried to explain. Then you brought in Tiled, which probably (or not) changed everything. I have no idea. I don't think I can help more with this, good luck. :)

(*) Example is exaggarated to illustrate the concept.
 

developer_123

Active Member
Licensed User
Longtime User
Thank you very much SandMan for your support. The idea you propose is quite good, but unfortunately in Tiled it does not apply since apparently when X2 draws the sprites, for some performance reason, the algorithm considerably reduces the quality of the images, especially when they are small, so trying it in the practice I already realized that unfortunately your idea doesn't work with Tiled. ho! yes, sorry, I hadn't mentioned Tiled, you're absolutely right, I see how the thread started regarding the outline of the text and the quality of this image is not the best, this led to touching on the subject of the other images, until naturally indicate that it was Tiled and X2. In any case, I appreciate your interest and it is good to know that the whole community we are always in the forums trying to learn more and more.

In any case, I would appreciate if someone else has had the same problem or if maybe Erel can help me with some functional idea in this case!
 

WizardOz

Member
Licensed User
Longtime User
Traditionally, in the newspaper world (which I have history in), this was made by selecting the object (in QuarkXPress), pressing a key-combo (picked up by QuicKeys) wich launched a macro duplicating the object, moving it to the bottom, changing its colour to white, and moving it 0.5 mm to the right. Then duplicate that object, move it 0.5 mm higher up. Then duplicate that object and move it 0.5 mm to the left. Repeat until the original text block is surrounded with eight identical, but white, text blocks. And you got a white outline. (This works because they were moved not very far. If you tried to make a huge outline like this, and tried to move them 10 mm or so, it would look awful.)

This is a long-whinded way of saying you could solve this by making a mask that you apply whatever colour you want your outline to be. Do the mask like what I explained above. It's left as an exercise to the reader to figure out more fitting tools than QuarkXpress and QuicKeys. :)
Finally, another person from the QuarkXPress-days! We are few and far between! :)
 

ilan

Expert
Licensed User
Longtime User
normally in games, you use Bitmap Fonts (like in libgdx)

the idea is to have a bitmap file with all letters (sprite sheet) and then you crop the letter you need and draw it.
like this, your font can look however you like with outline or not and it will be drawn much much faster than the solution in post #10.
there are many tools to create bitmap fonts or websites (https://snowb.org/)
you may have a look at how it is working in libgdx and implement it in X2.

libgdx examples: https://www.b4x.com/android/forum/threads/libgdx-game-engine.32594/#content

have a look at the lGDX BitmapFont example!

i am using it in my game Pixel knight (https://play.google.com/store/apps/details?id=www.sagital.pknight&hl=en&gl=US) and it is working great!
i created a custom BMfont but i dont remember what tool i used (i guess i am getting senile) ?
 

TelKel81

Active Member
Licensed User
This is how I draw the outline

B4X:
Canvas.DrawText(Text, cx, baseline - OutlineSize, Font, OutlineColor, "CENTER") 'Top
Canvas.DrawText(Text, cx, baseline + OutlineSize, Font, OutlineColor, "CENTER") 'Bottom
        
Canvas.DrawText(Text, cx - OutlineSize, baseline, Font, OutlineColor, "CENTER") 'Left
Canvas.DrawText(Text, cx + OutlineSize, baseline, Font, OutlineColor, "CENTER") 'Right
        
Canvas.DrawText(Text, cx + OutlineSize, baseline - OutlineSize, Font, OutlineColor, "CENTER") 'Top Right
Canvas.DrawText(Text, cx + OutlineSize, baseline + OutlineSize, Font, OutlineColor, "CENTER") 'Btm Right
        
Canvas.DrawText(Text, cx - OutlineSize, baseline - OutlineSize, Font, OutlineColor, "CENTER") 'Top Left
Canvas.DrawText(Text, cx - OutlineSize, baseline + OutlineSize, Font, OutlineColor, "CENTER") 'Btm Left

Result (gradient not in code) :
1.png
 

emexes

Expert
Licensed User
The outlining by first drawing offset copies of the text in outline color, might be sabotaged by the DrawText antialiasing at the edges of the text, because the outline would be composed of "diluted" partially-filled pixels.

Doing the antialiasing manually per this post by @Sandman might reduce this effect.
 

emexes

Expert
Licensed User
B4X:
ImageView1.Width = ImageView1.Width * xui.Scale
ImageView1.Height = ImageView1.Height * xui.Scale
X2.Initialize(Me, ImageView1, world) '<--- this is the original line in Game.Initialize
ImageView1.Width = ImageView1.Width / xui.Scale
ImageView1.Height = ImageView1.Height / xui.Scale

I am guessing that rescaling by changing the width and height, probably uses nearest-neighbor, which is fast but crap, and using it with antialiased images is worse again.

List of some scaling algorithms (from another program, not from Android or B4A :( )

1664328682846.png


If you are reducing image size, then Downsample is much better, especially for integral factors (eg @Sandman 1/4 is good, but 768/1920 is bad not so good).

On the bright side, if you're using ImageViews as sprites, and the alpha channel of those ImageViews is working, then that'd be something to try. Albeit re: font outlines, you'd still have the antialiasing about the character edges to deal with.
 
Top