How to draw outline on the font(Pygame)
Asked Answered
E

3

6

How can I draw an outline on the font?
I want to use black font, but the background has to be blackish so it's hard to see the font.

I assume myfont.render doesn't support drawing outline on the font.

Is there other way?

Exponible answered 25/1, 2019 at 10:8 Comment(0)
D
6

Pygame doesn't support this out of the box, but one way to do it is render the text in the outline color and blit it to the result surface shifted multiple times, then render the text in the desired color on top of it.

pgzero uses this technique; a trimmed down version of its code is shown below:

import pygame

_circle_cache = {}
def _circlepoints(r):
    r = int(round(r))
    if r in _circle_cache:
        return _circle_cache[r]
    x, y, e = r, 0, 1 - r
    _circle_cache[r] = points = []
    while x >= y:
        points.append((x, y))
        y += 1
        if e < 0:
            e += 2 * y - 1
        else:
            x -= 1
            e += 2 * (y - x) - 1
    points += [(y, x) for x, y in points if x > y]
    points += [(-x, y) for x, y in points if x]
    points += [(x, -y) for x, y in points if y]
    points.sort()
    return points

def render(text, font, gfcolor=pygame.Color('dodgerblue'), ocolor=(255, 255, 255), opx=2):
    textsurface = font.render(text, True, gfcolor).convert_alpha()
    w = textsurface.get_width() + 2 * opx
    h = font.get_height()

    osurf = pygame.Surface((w, h + 2 * opx)).convert_alpha()
    osurf.fill((0, 0, 0, 0))

    surf = osurf.copy()

    osurf.blit(font.render(text, True, ocolor).convert_alpha(), (0, 0))

    for dx, dy in _circlepoints(opx):
        surf.blit(osurf, (dx + opx, dy + opx))

    surf.blit(textsurface, (opx, opx))
    return surf

def main():
    pygame.init()

    font = pygame.font.SysFont(None, 64)

    screen = pygame.display.set_mode((350, 100))
    clock = pygame.time.Clock()

    while True:
        events = pygame.event.get()
        for e in events:
            if e.type == pygame.QUIT:
                return
        screen.fill((30, 30, 30))

        screen.blit(render('Hello World', font), (20, 20))

        pygame.display.update()
        clock.tick(60)

if __name__ == '__main__':
    main()

enter image description here

Dov answered 25/1, 2019 at 13:2 Comment(3)
I don't know how this works cause I'm a beginner, but it works perfectly! Just as I wanted. However, the lower part of the letter looks a little cut. Not much, just a little. It might be my font's problem, but it wasn't like this when I normally rendered it. Do you know why this happens?Exponible
Which font do you use?Dov
Well, it was my font's problem. Now I'm using another font so it's okay.Exponible
W
6

If you are a beginer and are not looking for very complex code, and you are only using a simple font such as arial, helvetica, calibri, etc... You can just blit the text in the outline colour 4 times in the 'corners', then blit the actual text over it:

white = pygame.Color(255,255,255)
black = pygame.Color(0,0,0)

def draw_text(x, y, string, col, size, window):
    font = pygame.font.SysFont("Impact", size )
    text = font.render(string, True, col)
    textbox = text.get_rect()
    textbox.center = (x, y)
    window.blit(text, textbox)

x = 300
y = 300

# TEXT OUTLINE

# top left
draw_text(x - 2, y-2, "Hello", black, 40, win)
# top right
draw_text(x + 2, y-2, "Hello", black, 40, win)
# btm left
draw_text(x - 2, y + 2, "Hello", black, 40, win)
# btm right
draw_text(x + 2, y + 2, "Hello", black, 40, win) 

# TEXT FILL

draw_text(x, y, "Hello", white, 40, win)
Wriggle answered 13/1, 2021 at 15:27 Comment(2)
This is more or less a hack. As you say, for simple fonts it will probably work ok, but you will still see that in the corners of the various duplicates. So even though simple in nature, I would not recommend this solution.Haith
I don't think you wanted to put the - and + this way. This is probably a copy-paste error since we can clearly see in the comments (top left, top right, btm left and btm right) that you wanted to draw the border in the 4 corners of the text. Read again, you only draw the text in 2 places. Once you edit, this answer works.Gaikwar
I
0

You can use masks to add an outline to your text. Here is an example:

import pygame

def add_outline_to_image(image: pygame.Surface, thickness: int, color: tuple, color_key: tuple = (255, 0, 255)) -> pygame.Surface:
    mask = pygame.mask.from_surface(image)
    mask_surf = mask.to_surface(setcolor=color)
    mask_surf.set_colorkey((0, 0, 0))

    new_img = pygame.Surface((image.get_width() + 2, image.get_height() + 2))
    new_img.fill(color_key)
    new_img.set_colorkey(color_key)

    for i in -thickness, thickness:
        new_img.blit(mask_surf, (i + thickness, thickness))
        new_img.blit(mask_surf, (thickness, i + thickness))
    new_img.blit(image, (thickness, thickness))

    return new_img

And here is how I use this function to create a white text with a 2px black outline:

text_surf = myfont.render("test", False, (255, 255, 255)).convert()
text_with_ouline = add_outline_to_image(text_surf, 2, (0, 0, 0))
Intuitivism answered 11/3, 2022 at 14:18 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.