pythonarcade / arcade

Easy to use Python library for creating 2D arcade games.
http://arcade.academy
Other
1.69k stars 319 forks source link

Break up arcade.create_text_sprite into several useful functions #1410

Open bunny-therapist opened 1 year ago

bunny-therapist commented 1 year ago

Enhancement request:

What should be added/changed?

In 2.6.16, there was a function called arcade.create_text_image. In 2.7.1.dev3, it is gone. Instead, there is arcade.create_text_sprite. This function first creates arcade.Text, then renders that into an arcade.Texture, then puts that into arcade.Sprite and returns it. It seems like it would be possible to have arcade.create_text_image put back, but using the new rending method instead. Also, there could be an arcade.create_text_texture. All that is needed is to break out parts of arcade.create_text_sprite into functions which return texture and/or image.

What would it help with?

I want to be able to create instances of arcade.Texture which contain text, which I can use in my game. To do this now, I am replacing calls to arcade.create_text_image with calls to arcade.create_text_sprite and then take .texture on it, which means Sprite is needlessly instantiated.

bunny-therapist commented 1 year ago

Transitioning from the old arcade.create_text_image to arcade.create_text_sprite, I am also facing issues with having to set a width, and a loaded font (using arcade.load_font) not being found.

gran4 commented 1 year ago

Meant to put it in another pull request. I will come back to this soon.

bunny-therapist commented 1 year ago

I know it was already like that, but it would be a huge improvement if one did not need a texture atlas for this (if you pass None, it will use the default atlas, so it is always using an atlas). Typically, when creating a texture, there is no atlas needed. The atlas only comes into play when adding the texture to an atlas/spritelist. I also think that implementing this by rendering into an atlas means that the texture only really works for that one atlas (in other atlases the texture would just be an empty texture). That is a kind of a gotcha/restriction.

gran4 commented 1 year ago

@bunny-therapist I was wrong: Look at the lines

    if not texture_atlas:
        texture_atlas = arcade.get_window().ctx.default_atlas

It automatically does it

bunny-therapist commented 1 year ago

What exactly is not working? I basically copied your code into my game while waiting for the PR, and I have not had any issues.

bunny-therapist commented 1 year ago

Is it related to https://github.com/pythonarcade/arcade/issues/1839 ?

gran4 commented 1 year ago

I do not think it is related to #1839 . The problem is that there is no second line at all if multiline is true.

bunny-therapist commented 1 year ago

How do you test it?

einarf commented 7 months ago

Related PR here we might be able to salvage something from https://github.com/pythonarcade/arcade/pull/1679

pushfoo commented 7 months ago

I found something interesting in pyglet's examples while reading up on #1159:

# On Windows DirectWrite can render directly to an image for special cases!
# Note: Labels are recommended unless you know what you are doing, or if you use these in a limited fashion.
font = pyglet.font.load("Segoe UI")
image = font.render_to_image("I am rendered as a texture! 🌎", 100, 300)
sprite = pyglet.sprite.Sprite(image, x=400, y=400, batch=batch)
sprite.rotation = 45

It looks like this returns a pyglet ImageData object, but we might be able to either convert that or use their code as reference. Optimizations like that could be saved for 3.1 or later if a GL-only approach is best for now.

pushfoo commented 7 months ago

As another update to this: pyglet 2.0.11 added a TextLayout.get_as_texture method:

    def get_as_texture(self, min_filter=GL_NEAREST, mag_filter=GL_NEAREST) -> pyglet.image.Texture:
        """Returns a Texture with the TextLayout drawn to it. Each call to this function returns a new
        Texture.
        ~Warning: Usage is recommended only if you understand how texture generation affects your application.
        """
        framebuffer = pyglet.image.Framebuffer()
        temp_pos = self.position
        width = int(round(self.content_width))
        height = int(round(self.content_height))
        texture = pyglet.image.Texture.create(width, height, min_filter=min_filter, mag_filter=mag_filter)
        depth_buffer = pyglet.image.buffer.Renderbuffer(width, height, GL_DEPTH_COMPONENT)
        framebuffer.attach_texture(texture)
        framebuffer.attach_renderbuffer(depth_buffer, attachment=GL_DEPTH_ATTACHMENT)

        self.position = (0-self._anchor_left, 0-self._anchor_bottom, 0)
        framebuffer.bind()
        self.draw()
        framebuffer.unbind()

        self.position = temp_pos
        return texture