Repository: ManimCommunity/manim
File: manim/renderer/opengl_renderer.py (Python)

Code before:
            self.update_frame(scene)
            self.file_writer.save_final_image(self.get_image())
        self.file_writer.finish()

    def get_image(self) -> Image.Image:
        """Returns an image from the current frame.

        Returns
        -------
        PIL.Image
            The PIL image of the array.
        """
        image = Image.frombytes(
            "RGBA",
            self.get_pixel_shape(),
            self.context.fbo.read(self.get_pixel_shape(), components=4),
            "raw",
            "RGBA",
            0,
            -1,
        )
        return image

    def save_static_frame_data(self, scene, static_mobjects):
        pass

    def get_frame_buffer_object(self, context, samples=0):
        pixel_width = config["pixel_width"]
        pixel_height = config["pixel_height"]
        num_channels = 4
        return context.framebuffer(
            color_attachments=context.texture(
                (pixel_width, pixel_height),
                components=num_channels,
                samples=samples,
            ),
            depth_attachment=context.depth_renderbuffer(
                (pixel_width, pixel_height), samples=samples
            ),
        )

    def get_raw_frame_buffer_object_data(self, dtype="f1"):
        # Copy blocks from the fbo_msaa to the drawn fbo using Blit
        # pw, ph = self.get_pixel_shape()
        # gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo_msaa.glo)
        # gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, self.fbo.glo)
        # gl.glBlitFramebuffer(
        #     0, 0, pw, ph, 0, 0, pw, ph, gl.GL_COLOR_BUFFER_BIT, gl.GL_LINEAR
        # )
        num_channels = 4
        ret = self.frame_buffer_object.read(

Review comment (suggestion):
I'm not too familliar with `Image`, how did you choose these parameters and what do they mean in this context? If possible it might be worth a description in the docstring.

Code after:
            self.update_frame(scene)
            self.file_writer.save_final_image(self.get_image())
        self.file_writer.finish()

    def get_image(self) -> Image.Image:
        """Returns an image from the current frame. The first argument passed to image represents
        the mode RGB with the alpha channel A. The data we read is from the currently bound frame
        buffer. We pass in 'raw' as the name of the decoder, 0 and -1 args are specifically
        used for the decoder tand represent the stride and orientation. 0 means there is no
        padding expected between bytes and -1 represents the orientation and means the first
        line of the image is the bottom line on the screen.

        Returns
        -------
        PIL.Image
            The PIL image of the array.
        """
        image = Image.frombytes(
            "RGBA",
            self.get_pixel_shape(),
            self.context.fbo.read(self.get_pixel_shape(), components=4),
            "raw",
            "RGBA",
            0,
            -1,
        )
        return image

    def save_static_frame_data(self, scene, static_mobjects):
        pass

    def get_frame_buffer_object(self, context, samples=0):
        pixel_width = config["pixel_width"]
        pixel_height = config["pixel_height"]
        num_channels = 4
        return context.framebuffer(
            color_attachments=context.texture(
                (pixel_width, pixel_height),
                components=num_channels,
                samples=samples,
            ),
            depth_attachment=context.depth_renderbuffer(
                (pixel_width, pixel_height), samples=samples
            ),
        )

    def get_raw_frame_buffer_object_data(self, dtype="f1"):
        # Copy blocks from the fbo_msaa to the drawn fbo using Blit
        # pw, ph = self.get_pixel_shape()
        # gl.glBindFramebuffer(gl.GL_READ_FRAMEBUFFER, self.fbo_msaa.glo)
        # gl.glBindFramebuffer(gl.GL_DRAW_FRAMEBUFFER, self.fbo.glo)