ux-dx 0.2.1

3D Graphics Primitives for Angular Rust
Documentation
use std::{cell::RefCell, os::raw::c_ulong};
use winit::dpi::LogicalSize;

use super::Offscreen;

// @short_description: A common interface for manipulating framebuffers
//
// Framebuffers are a collection of buffers that can be rendered too.
// A framebuffer may be comprised of one or more color buffers, an
// optional depth buffer and an optional stencil buffer. Other
// configuration parameters are associated with framebuffers too such
// as whether the framebuffer supports multi-sampling (an anti-aliasing
// technique) or dithering.
//
// There are two kinds of framebuffer in , #Onscreen
// framebuffers and #Offscreen framebuffers. As the names imply
// offscreen framebuffers are for rendering something offscreen
// (perhaps to a texture which is bound as one of the color buffers).
// The exact semantics of onscreen framebuffers depends on the window
// system backend that you are using, but typically you can expect
// rendering to a #Onscreen framebuffer will be immediately
// visible to the user.
//
// If you want to create a new framebuffer then you should start by
// looking at the #Onscreen and #Offscreen constructor
// functions, such as offscreen_new_with_texture() or
// onscreen_new(). The #Framebuffer interface deals with
// all aspects that are common between those two types of framebuffer.
//
// Setup of a new Framebuffer happens in two stages. There is a
// configuration stage where you specify all the options and ancillary
// buffers you want associated with your framebuffer and then when you
// are happy with the configuration you can "allocate" the framebuffer
// using framebuffer_allocate(). Technically explicitly calling
// framebuffer_allocate() is optional for convenience and the
// framebuffer will automatically be allocated when you first try to
// draw to it, but if you do the allocation manually then you can
// also catch any possible errors that may arise from your
// configuration.

use std::{fmt, ptr};

use crate::prelude::*;

use crate::foundation::colorspace::Color;

use super::{
    Bitmap, ColorMask, Context, Euler, Fence, FenceClosure, FramebufferType, LegacySwapChain,
    Matrix, Pipeline, PixelFormat, Primitive, Quaternion, ReadPixelsFlags, StereoMode, Texture,
};

#[derive(Default, Debug, Clone)]
pub struct FramebufferConfig {
    pub swap_chain: Option<LegacySwapChain>,
    pub need_stencil: bool,
    pub samples_per_pixel: i32,
    pub swap_throttled: bool,
    pub depth_texture_enabled: bool,
    pub stereo_enabled: bool,
}

// #define FRAMEBUFFER_STATE_ALL ((1<<FRAMEBUFFER_STATE_INDEX_MAX) - 1)

#[derive(Default, Debug, Clone)]
pub struct FramebufferBits {
    red: i32,
    blue: i32,
    green: i32,
    alpha: i32,
    depth: i32,
    stencil: i32,
}

pub struct GLFramebuffer {
    // GLuint fbo_handle;
    // GList *renderbuffers;
    samples_per_pixel: i32,
}

#[derive(Default, Debug)]
struct FramebufferProps {
    // The user configuration before allocation
    config: FramebufferConfig,

    width: i32,
    height: i32,
    // Format of the pixels in the framebuffer (including the expected premult state)
    internal_format: PixelFormat,
    allocated: bool,

    // MatrixStack    *modelview_stack;
    // MatrixStack    *projection_stack;
    viewport_x: f32,
    viewport_y: f32,
    viewport_width: f32,
    viewport_height: f32,
    viewport_age: i32,
    viewport_age_for_scissor_workaround: i32,

    // ClipStack      *clip_stack;
    dither_enabled: bool,
    depth_writing_enabled: bool,
    color_mask: ColorMask,
    stereo_mode: StereoMode,

    // We journal the textured rectangles we want to submit to OpenGL so
    // we have an oppertunity to batch them together into less draw calls.

    // Journal        *journal;

    // The scene of a given framebuffer may depend on images in other
    // framebuffers... */

    // GList              *deps;

    // As part of an optimization for reading-back single pixels from a
    // framebuffer in some simple cases where the geometry is still
    // available in the journal we need to track the bounds of the last
    // region cleared, its color and we need to track when something
    // does in fact draw to that region so it is no longer clear.
    clear_color_red: f32,
    clear_color_green: f32,
    clear_color_blue: f32,
    clear_color_alpha: f32,
    clear_clip_x0: i32,
    clear_clip_y0: i32,
    clear_clip_x1: i32,
    clear_clip_y1: i32,
    clear_clip_dirty: bool,

    // Whether something has been drawn to the buffer since the last
    // swap buffers or swap region.
    mid_scene: bool,

    // driver specific
    dirty_bitmasks: bool,
    bits: FramebufferBits,

    samples_per_pixel: i32,
}

#[derive(Default, Debug)]
pub struct Framebuffer {
    frontend: FramebufferType,
    props: RefCell<FramebufferProps>,
}

impl Framebuffer {
    // pub fn error_quark() -> u32 {
    //     unsafe { ffi::framebuffer_error_quark() }
    // }

    fn init(&mut self, frontend: FramebufferType, width: i32, height: i32) {
        self.frontend = frontend;

        let mut props = self.props.borrow_mut();

        props.width = width;
        props.height = height;
        props.internal_format = PixelFormat::Rgba8888Pre;
        props.viewport_x = 0.0;
        props.viewport_y = 0.0;
        props.viewport_width = width as f32;
        props.viewport_height = height as f32;
        props.viewport_age = 0;
        props.viewport_age_for_scissor_workaround = -1;
        props.dither_enabled = true;
        props.depth_writing_enabled = true;

        // props.modelview_stack = matrix_stack_new();
        // props.projection_stack = matrix_stack_new();

        props.dirty_bitmasks = true;

        props.color_mask = ColorMask::ALL;

        props.samples_per_pixel = 0;

        // props.clip_stack = None;

        // props.journal = _journal_new(framebuffer);

        // Ensure we know the props.clear_color* members can't be
        // referenced for our fast-path read-pixel optimization (see
        // _journal_try_read_pixel()) until some region of the
        // framebuffer is initialized.

        props.clear_clip_dirty = true;

        // XXX: We have to maintain a central list of all framebuffers
        // because at times we need to be able to flush all known journals.

        // Examples where we need to flush all journals are:
        // - because journal entries can reference OpenGL texture
        //   coordinates that may not survive texture-atlas reorganization
        //   so we need the ability to flush those entries.
        // - because although we generally advise against modifying
        //   pipelines after construction we have to handle that possibility
        //   and since pipelines may be referenced in journal entries we
        //   need to be able to flush them before allowing the pipelines to
        //   be changed.

        // Note we don't maintain a list of journals and associate
        // framebuffers with journals by e.g. having a journal->framebuffer
        // reference since that would introduce a circular reference.

        // Note: As a future change to try and remove the need to index all
        // journals it might be possible to defer resolving of OpenGL
        // texture coordinates for rectangle primitives until we come to
        // flush a journal. This would mean for instance that a single
        // rectangle entry in a journal could later be expanded into
        // multiple quad primitives to handle sliced textures but would mean
        // we don't have to worry about retaining references to OpenGL
        // texture coordinates that may later become invalid.

        // ctx->framebuffers = g_list_prepend (ctx->framebuffers, framebuffer);
    }

    /// set_window_size:
    /// @window: A #Stage
    /// @width: A width, in pixels
    /// @height: A height, in pixels
    ///
    /// Sets the size of the window, taking into account any window border. This
    /// corresponds to the window's available area for its child, minus the area
    /// occupied by the window's toolbar, if it's enabled.
    ///
    /// <para>
    /// Setting the window size may involve a request to the underlying windowing
    /// system, and may not immediately be reflected.
    ///
    ///
    ///
    pub fn set_window_size(&self, width: i32, height: i32) {
        match &self.frontend {
            FramebufferType::OnScreen(onscreen) => match &onscreen.window {
                Some(window) => window.set_inner_size(LogicalSize::new(width, height)),
                None => {}
            },
            FramebufferType::OffScreen(_) => {}
        }
    }
}

impl Object for Framebuffer {}
impl Is<Framebuffer> for Framebuffer {}

impl AsRef<Framebuffer> for Framebuffer {
    fn as_ref(&self) -> &Framebuffer {
        self
    }
}

impl AsRef<FramebufferType> for Framebuffer {
    fn as_ref(&self) -> &FramebufferType {
        &self.frontend
    }
}

/// Trait containing all `Framebuffer` methods.
///
/// # Implementors
///
/// [`Framebuffer`](struct.Framebuffer.html), [`Onscreen`](struct.Onscreen.html)
pub trait FramebufferExt: 'static {
    /// Calls the provided callback when all previously-submitted commands have
    /// been executed by the GPU.
    ///
    /// Returns non-NULL if the fence succeeded, or `None` if it was unable to
    /// be inserted and the callback will never be called. The user does not
    /// need to free the closure; it will be freed automatically when the
    /// callback is called, or cancelled.
    ///
    /// ## `callback`
    /// A `FenceCallback` to be called when
    ///  all commands submitted to  have been executed
    /// ## `user_data`
    /// Private data that will be passed to the callback
    fn add_fence_callback<P: Fn(&Fence) + 'static>(&self, callback: P) -> Option<FenceClosure>;

    /// Explicitly allocates a configured `Framebuffer` allowing developers to
    /// check and handle any errors that might arise from an unsupported
    /// configuration so that fallback configurations may be tried.
    ///
    /// Many applications don't support any fallback options at least when
    /// they are initially developed and in that case the don't need to use this API
    /// since  will automatically allocate a framebuffer when it first gets
    /// used. The disadvantage of relying on automatic allocation is that the
    /// program will abort with an error message if there is an error during
    /// automatic allocation.
    ///
    /// # Returns
    ///
    /// `true` if there were no error allocating the framebuffer, else `false`.
    fn allocate(&self) -> bool;

    /// Removes a fence previously submitted with
    /// `Framebuffer::add_fence_callback`; the callback will not be
    /// called.
    ///
    /// ## `closure`
    /// The `FenceClosure` returned from
    ///  `Framebuffer::add_fence_callback`
    fn cancel_fence_callback(&self, closure: &mut FenceClosure);

    /// Clears all the auxiliary buffers identified in the `buffers` mask, and if
    /// that includes the color buffer then the specified `color` is used.
    /// ## `buffers`
    /// A mask of `BufferBit`'s identifying which auxiliary
    ///  buffers to clear
    /// ## `color`
    /// The color to clear the color buffer too if specified in
    ///  `buffers`.
    fn clear(&self, buffers: c_ulong, color: &Color);

    /// Clears all the auxiliary buffers identified in the `buffers` mask, and if
    /// that includes the color buffer then the specified `color` is used.
    /// ## `buffers`
    /// A mask of `BufferBit`'s identifying which auxiliary
    ///  buffers to clear
    /// ## `red`
    /// The red component of color to clear the color buffer too if
    ///  specified in `buffers`.
    /// ## `green`
    /// The green component of color to clear the color buffer too if
    ///  specified in `buffers`.
    /// ## `blue`
    /// The blue component of color to clear the color buffer too if
    ///  specified in `buffers`.
    /// ## `alpha`
    /// The alpha component of color to clear the color buffer too if
    ///  specified in `buffers`.
    fn clear4f(&self, buffers: c_ulong, red: f32, green: f32, blue: f32, alpha: f32);

    /// Declares that the specified `buffers` no longer need to be referenced
    /// by any further rendering commands. This can be an important
    /// optimization to avoid subsequent frames of rendering depending on
    /// the results of a previous frame.
    ///
    /// For example; some tile-based rendering GPUs are able to avoid allocating and
    /// accessing system memory for the depth and stencil buffer so long as these
    /// buffers are not required as input for subsequent frames and that can save a
    /// significant amount of memory bandwidth used to save and restore their
    /// contents to system memory between frames.
    ///
    /// It is currently considered an error to try and explicitly discard the color
    /// buffer by passing `BufferBit::Color`. This is because the color buffer is
    /// already implicitly discard when you finish rendering to a `Onscreen`
    /// framebuffer, and it's not meaningful to try and discard the color buffer of
    /// a `Offscreen` framebuffer since they are single-buffered.
    /// ## `buffers`
    /// A `BufferBit` mask of which ancillary buffers you want
    ///  to discard.
    fn discard_buffers(&self, buffers: c_ulong);

    /// Draws a textured rectangle to `self` with the given `pipeline`
    /// state with the top left corner positioned at (`x_1`, `y_1`) and the
    /// bottom right corner positioned at (`x_2`, `y_2`). As a pipeline may
    /// contain multiple texture layers this interface lets you supply
    /// texture coordinates for each layer of the pipeline.
    ///
    /// The position is the position before the rectangle has been
    /// transformed by the model-view matrix and the projection
    /// matrix.
    ///
    /// This is a high level drawing api that can handle any kind of
    /// `MetaTexture` texture for the first layer such as
    /// `Texture2DSliced` textures which may internally be comprised of
    /// multiple low-level textures. This is unlike low-level drawing apis
    /// such as `Primitive::draw` which only support low level texture
    /// types that are directly supported by GPUs such as `Texture2D`.
    ///
    /// This api can not currently handle multiple high-level meta
    /// texture layers. The first layer may be a high level meta texture
    /// such as `Texture2DSliced` but all other layers much be low
    /// level textures such as `Texture2D` and additionally they
    /// should be textures that can be sampled using normalized coordinates
    /// (so not `TextureRectangle` textures).
    ///
    // The top left texture coordinate for layer 0 of any pipeline will be
    // (tex_coords[0], tex_coords[1]) and the bottom right coordinate will
    // be (tex_coords[2], tex_coords[3]). The coordinates for layer 1
    // would be (tex_coords[4], tex_coords[5]) (tex_coords[6],
    // tex_coords[7]) and so on...
    //
    // The given texture coordinates should always be normalized such that
    // (0, 0) corresponds to the top left and (1, 1) corresponds to the
    // bottom right. To map an entire texture across the rectangle pass
    // in tex_coords[0]=0, tex_coords[1]=0, tex_coords[2]=1,
    // tex_coords[3]=1.
    //
    /// Even if you have associated a `TextureRectangle` texture
    /// which normally implies working with non-normalized texture
    /// coordinates this api should still be passed normalized texture
    /// coordinates.
    ///
    /// The first pair of coordinates are for the first layer (with the
    /// smallest layer index) and if you supply less texture coordinates
    /// than there are layers in the current source material then default
    /// texture coordinates (0.0, 0.0, 1.0, 1.0) are generated.
    /// ## `pipeline`
    /// A `Pipeline` state object
    /// ## `x_1`
    /// x coordinate upper left on screen.
    /// ## `y_1`
    /// y coordinate upper left on screen.
    /// ## `x_2`
    /// x coordinate lower right on screen.
    /// ## `y_2`
    /// y coordinate lower right on screen.
    /// ## `tex_coords`
    /// An array containing groups of
    ///  4 float values: [s_1, t_1, s_2, t_2] that are interpreted as two texture
    ///  coordinates; one for the top left texel, and one for the bottom right
    ///  texel. Each value should be between 0.0 and 1.0, where the coordinate
    ///  (0.0, 0.0) represents the top left of the texture, and (1.0, 1.0) the
    ///  bottom right.
    /// ## `tex_coords_len`
    /// The length of the `tex_coords` array. (For one layer
    ///  and one group of texture coordinates, this would be 4)
    fn draw_multitextured_rectangle(
        &self,
        pipeline: &Pipeline,
        x_1: f32,
        y_1: f32,
        x_2: f32,
        y_2: f32,
        tex_coords: &[f32],
    );

    /// Draws a rectangle to `self` with the given `pipeline` state
    /// and with the top left corner positioned at (`x_1`, `y_1`) and the
    /// bottom right corner positioned at (`x_2`, `y_2`).
    ///
    /// The position is the position before the rectangle has been
    /// transformed by the model-view matrix and the projection
    /// matrix.
    ///
    /// If you want to describe a rectangle with a texture mapped on
    /// it then you can use
    /// `Framebuffer::draw_textured_rectangle`.
    /// ## `pipeline`
    /// A `Pipeline` state object
    /// ## `x_1`
    /// X coordinate of the top-left corner
    /// ## `y_1`
    /// Y coordinate of the top-left corner
    /// ## `x_2`
    /// X coordinate of the bottom-right corner
    /// ## `y_2`
    /// Y coordinate of the bottom-right corner
    fn draw_rectangle(&self, pipeline: &Pipeline, x_1: f32, y_1: f32, x_2: f32, y_2: f32);

    //fn draw_rectangles(&self, pipeline: &Pipeline, coordinates: &[f32], n_rectangles: u32);

    /// Draws a textured rectangle to `self` using the given
    /// `pipeline` state with the top left corner positioned at (`x_1`, `y_1`)
    /// and the bottom right corner positioned at (`x_2`, `y_2`). The top
    /// left corner will have texture coordinates of (`s_1`, `t_1`) and the
    /// bottom right corner will have texture coordinates of (`s_2`, `t_2`).
    ///
    /// The position is the position before the rectangle has been
    /// transformed by the model-view matrix and the projection
    /// matrix.
    ///
    /// This is a high level drawing api that can handle any kind of
    /// `MetaTexture` texture such as `Texture2DSliced` textures
    /// which may internally be comprised of multiple low-level textures.
    /// This is unlike low-level drawing apis such as `Primitive::draw`
    /// which only support low level texture types that are directly
    /// supported by GPUs such as `Texture2D`.
    ///
    /// The given texture coordinates will only be used for the first
    /// texture layer of the pipeline and if your pipeline has more than
    /// one layer then all other layers will have default texture
    /// coordinates of `s_1`=0.0 `t_1`=0.0 `s_2`=1.0 `t_2`=1.0 
    ///
    /// The given texture coordinates should always be normalized such that
    /// (0, 0) corresponds to the top left and (1, 1) corresponds to the
    /// bottom right. To map an entire texture across the rectangle pass
    /// in `s_1`=0, `t_1`=0, `s_2`=1, `t_2`=1.
    ///
    /// Even if you have associated a `TextureRectangle` texture
    /// with one of your `pipeline` layers which normally implies working
    /// with non-normalized texture coordinates this api should still be
    /// passed normalized texture coordinates.
    /// ## `pipeline`
    /// A `Pipeline` state object
    /// ## `x_1`
    /// x coordinate upper left on screen.
    /// ## `y_1`
    /// y coordinate upper left on screen.
    /// ## `x_2`
    /// x coordinate lower right on screen.
    /// ## `y_2`
    /// y coordinate lower right on screen.
    /// ## `s_1`
    /// S texture coordinate of the top-left coorner
    /// ## `t_1`
    /// T texture coordinate of the top-left coorner
    /// ## `s_2`
    /// S texture coordinate of the bottom-right coorner
    /// ## `t_2`
    /// T texture coordinate of the bottom-right coorner
    fn draw_textured_rectangle(
        &self,
        pipeline: &Pipeline,
        x_1: f32,
        y_1: f32,
        x_2: f32,
        y_2: f32,
        s_1: f32,
        t_1: f32,
        s_2: f32,
        t_2: f32,
    );

    //fn draw_textured_rectangles(&self, pipeline: &Pipeline, coordinates: &[f32], n_rectangles: u32);

    /// This blocks the CPU until all pending rendering associated with the
    /// specified framebuffer has completed. It's very rare that developers should
    /// ever need this level of synchronization with the GPU and should never be
    /// used unless you clearly understand why you need to explicitly force
    /// synchronization.
    ///
    /// One example might be for benchmarking purposes to be sure timing
    /// measurements reflect the time that the GPU is busy for not just the time it
    /// takes to queue rendering commands.
    fn finish(&self);

    /// Replaces the current projection matrix with a perspective matrix
    /// for a given viewing frustum defined by 4 side clip planes that
    /// all cross through the origin and 2 near and far clip planes.
    /// ## `left`
    /// X position of the left clipping plane where it
    ///  intersects the near clipping plane
    /// ## `right`
    /// X position of the right clipping plane where it
    ///  intersects the near clipping plane
    /// ## `bottom`
    /// Y position of the bottom clipping plane where it
    ///  intersects the near clipping plane
    /// ## `top`
    /// Y position of the top clipping plane where it intersects
    ///  the near clipping plane
    /// ## `z_near`
    /// The distance to the near clipping plane (Must be positive)
    /// ## `z_far`
    /// The distance to the far clipping plane (Must be positive)
    fn frustum(&self, left: f32, right: f32, bottom: f32, top: f32, z_near: f32, z_far: f32);

    /// Retrieves the number of alpha bits of `self`
    ///
    /// # Returns
    ///
    /// the number of bits
    fn alpha_bits(&self) -> i32;

    /// Retrieves the number of blue bits of `self`
    ///
    /// # Returns
    ///
    /// the number of bits
    fn blue_bits(&self) -> i32;

    /// Gets the current `ColorMask` of which channels would be written to the
    /// current framebuffer. Each bit set in the mask means that the
    /// corresponding color would be written.
    ///
    /// # Returns
    ///
    /// A `ColorMask`
    fn color_mask(&self) -> ColorMask;

    /// Can be used to query the `Context` a given `self` was
    /// instantiated within. This is the `Context` that was passed to
    /// `Onscreen::new` for example.
    ///
    /// # Returns
    ///
    /// The `Context` that the given
    ///  `self` was instantiated within.
    fn context(&self) -> Option<Context>;

    /// Retrieves the number of depth bits of `self`
    ///
    ///
    /// # Returns
    ///
    /// the number of bits
    fn depth_bits(&self) -> i32;

    /// Retrieves the depth buffer of `self` as a `Texture`. You need to
    /// call framebuffer_get_depth_texture(fb, true); before using this
    /// function.
    ///
    /// Calling this fn implicitely allocates the framebuffer.
    /// The texture returned stays valid as long as the framebuffer stays
    /// valid.
    ///
    /// # Returns
    ///
    /// the depth texture
    fn depth_texture(&self) -> Option<Texture>;

    /// Queries whether texture based depth buffer has been enabled via
    /// `Framebuffer::set_depth_texture_enabled`.
    ///
    /// # Returns
    ///
    /// `true` if a depth texture has been enabled, else
    ///  `false`.
    fn depth_texture_enabled(&self) -> bool;

    /// Queries whether depth buffer writing is enabled for `self`. This
    /// can be controlled via `Framebuffer::set_depth_write_enabled`.
    ///
    /// # Returns
    ///
    /// `true` if depth writing is enabled or `false` if not.
    fn depth_write_enabled(&self) -> bool;

    /// Returns whether dithering has been requested for the given `self`.
    /// See `Framebuffer::set_dither_enabled` for more details about dithering.
    ///
    /// This may return `true` even when the underlying `self`
    /// display pipeline does not support dithering. This value only represents
    /// the user's request for dithering.
    ///
    /// # Returns
    ///
    /// `true` if dithering has been requested or `false` if not.
    fn dither_enabled(&self) -> bool;

    /// Retrieves the number of green bits of `self`
    ///
    /// # Returns
    ///
    /// the number of bits
    fn green_bits(&self) -> i32;

    /// Queries the current height of the given `self`.
    ///
    /// # Returns
    ///
    /// The height of `self`.
    fn height(&self) -> i32;

    fn is_stereo(&self) -> bool;

    /// Stores the current model-view matrix in `matrix`.
    /// ## `matrix`
    /// return location for the model-view matrix
    fn modelview_matrix(&self) -> Matrix;

    /// Stores the current projection matrix in `matrix`.
    /// ## `matrix`
    /// return location for the projection matrix
    fn projection_matrix(&self) -> Matrix;

    /// Retrieves the number of red bits of `self`
    ///
    /// # Returns
    ///
    /// the number of bits
    fn red_bits(&self) -> i32;

    /// Gets the number of points that are sampled per-pixel when
    /// rasterizing geometry. Usually by default this will return 0 which
    /// means that single-sample not multisample rendering has been chosen.
    /// When using a GPU supporting multisample rendering it's possible to
    /// increase the number of samples per pixel using
    /// `Framebuffer::set_samples_per_pixel`.
    ///
    /// Calling `Framebuffer::get_samples_per_pixel` before the
    /// framebuffer has been allocated will simply return the value set
    /// using `Framebuffer::set_samples_per_pixel`. After the
    /// framebuffer has been allocated the value will reflect the actual
    /// number of samples that will be made by the GPU.
    ///
    /// # Returns
    ///
    /// The number of point samples made per pixel when
    ///  rasterizing geometry or 0 if single-sample rendering
    ///  has been chosen.
    fn samples_per_pixel(&self) -> i32;

    /// Gets the current `StereoMode`, which defines which stereo buffers
    /// should be drawn to. See `Framebuffer::set_stereo_mode`.
    ///
    /// # Returns
    ///
    /// A `StereoMode`
    fn stereo_mode(&self) -> StereoMode;

    //fn viewport4fv(&self, viewport: /*Unimplemented*/FixedArray TypeId { ns_id: 0, id: 20 }; 4);

    /// Queries the height of the viewport as set using `Framebuffer::set_viewport`
    /// or the default value which is the height of the framebuffer.
    ///
    /// # Returns
    ///
    /// The height of the viewport.
    fn viewport_height(&self) -> f32;

    /// Queries the width of the viewport as set using `Framebuffer::set_viewport`
    /// or the default value which is the width of the framebuffer.
    ///
    /// # Returns
    ///
    /// The width of the viewport.
    fn viewport_width(&self) -> f32;

    /// Queries the x coordinate of the viewport origin as set using `Framebuffer::set_viewport`
    /// or the default value which is 0.
    ///
    /// # Returns
    ///
    /// The x coordinate of the viewport origin.
    fn viewport_x(&self) -> f32;

    /// Queries the y coordinate of the viewport origin as set using `Framebuffer::set_viewport`
    /// or the default value which is 0.
    ///
    /// # Returns
    ///
    /// The y coordinate of the viewport origin.
    fn viewport_y(&self) -> f32;

    /// Queries the current width of the given `self`.
    ///
    /// # Returns
    ///
    /// The width of `self`.
    fn width(&self) -> i32;

    /// Resets the current model-view matrix to the identity matrix.
    fn identity_matrix(&self);

    /// Replaces the current projection matrix with an orthographic projection
    /// matrix.
    /// ## `x_1`
    /// The x coordinate for the first vertical clipping plane
    /// ## `y_1`
    /// The y coordinate for the first horizontal clipping plane
    /// ## `x_2`
    /// The x coordinate for the second vertical clipping plane
    /// ## `y_2`
    /// The y coordinate for the second horizontal clipping plane
    /// ## `near`
    /// The distance to the near clipping
    ///  plane (will be negative if the plane is
    ///  behind the viewer)
    /// ## `far`
    /// The distance to the far clipping
    ///  plane (will be negative if the plane is
    ///  behind the viewer)
    fn orthographic(&self, x_1: f32, y_1: f32, x_2: f32, y_2: f32, near: f32, far: f32);

    /// Replaces the current projection matrix with a perspective matrix
    /// based on the provided values.
    ///
    /// You should be careful not to have to great a `z_far` / `z_near`
    /// ratio since that will reduce the effectiveness of depth testing
    /// since there wont be enough precision to identify the depth of
    /// objects near to each other.
    /// ## `fov_y`
    /// Vertical field of view angle in degrees.
    /// ## `aspect`
    /// The (width over height) aspect ratio for display
    /// ## `z_near`
    /// The distance to the near clipping plane (Must be positive,
    ///  and must not be 0)
    /// ## `z_far`
    /// The distance to the far clipping plane (Must be positive)
    fn perspective(&self, fov_y: f32, aspect: f32, z_near: f32, z_far: f32);

    /// Reverts the clipping region to the state before the last call to
    /// `Framebuffer::push_scissor_clip`, `Framebuffer::push_rectangle_clip`
    /// `framebuffer_push_path_clip`, or `Framebuffer::push_primitive_clip`.
    fn pop_clip(&self);

    /// Restores the model-view matrix on the top of the matrix stack.
    fn pop_matrix(&self);

    /// Copies the current model-view matrix onto the matrix stack. The matrix
    /// can later be restored with `Framebuffer::pop_matrix`.
    fn push_matrix(&self);

    /// Sets a new clipping area using a 2D shaped described with a
    /// `Primitive`. The shape must not contain self overlapping
    /// geometry and must lie on a single 2D plane. A bounding box of the
    /// 2D shape in local coordinates (the same coordinates used to
    /// describe the shape) must be given. It is acceptable for the bounds
    /// to be larger than the true bounds but behaviour is undefined if the
    /// bounds are smaller than the true bounds.
    ///
    /// The primitive is transformed by the current model-view matrix and
    /// the silhouette is intersected with the previous clipping area. To
    /// restore the previous clipping area, call
    /// `Framebuffer::pop_clip`.
    /// ## `primitive`
    /// A `Primitive` describing a flat 2D shape
    /// ## `bounds_x1`
    /// x coordinate for the top-left corner of the primitives
    ///  bounds
    /// ## `bounds_y1`
    /// y coordinate for the top-left corner of the primitives
    ///  bounds
    /// ## `bounds_x2`
    /// x coordinate for the bottom-right corner of the
    ///  primitives bounds.
    /// ## `bounds_y2`
    /// y coordinate for the bottom-right corner of the
    ///  primitives bounds.
    fn push_primitive_clip(
        &self,
        primitive: &Primitive,
        bounds_x1: f32,
        bounds_y1: f32,
        bounds_x2: f32,
        bounds_y2: f32,
    );

    /// Specifies a modelview transformed rectangular clipping area for all
    /// subsequent drawing operations. Any drawing commands that extend
    /// outside the rectangle will be clipped so that only the portion
    /// inside the rectangle will be displayed. The rectangle dimensions
    /// are transformed by the current model-view matrix.
    ///
    /// The rectangle is intersected with the current clip region. To undo
    /// the effect of this function, call `Framebuffer::pop_clip`.
    /// ## `x_1`
    /// x coordinate for top left corner of the clip rectangle
    /// ## `y_1`
    /// y coordinate for top left corner of the clip rectangle
    /// ## `x_2`
    /// x coordinate for bottom right corner of the clip rectangle
    /// ## `y_2`
    /// y coordinate for bottom right corner of the clip rectangle
    fn push_rectangle_clip(&self, x_1: f32, y_1: f32, x_2: f32, y_2: f32);

    /// Specifies a rectangular clipping area for all subsequent drawing
    /// operations. Any drawing commands that extend outside the rectangle
    /// will be clipped so that only the portion inside the rectangle will
    /// be displayed. The rectangle dimensions are not transformed by the
    /// current model-view matrix.
    ///
    /// The rectangle is intersected with the current clip region. To undo
    /// the effect of this function, call `Framebuffer::pop_clip`.
    /// ## `x`
    /// left edge of the clip rectangle in window coordinates
    /// ## `y`
    /// top edge of the clip rectangle in window coordinates
    /// ## `width`
    /// width of the clip rectangle
    /// ## `height`
    /// height of the clip rectangle
    fn push_scissor_clip(&self, x: i32, y: i32, width: i32, height: i32);

    /// This is a convenience wrapper around
    /// `Framebuffer::read_pixels_into_bitmap` which allocates a
    /// temporary `Bitmap` to read pixel data directly into the given
    /// buffer. The rowstride of the buffer is assumed to be the width of
    /// the region times the bytes per pixel of the format. The source for
    /// the data is always taken from the color buffer. If you want to use
    /// any other rowstride or source, please use the
    /// `Framebuffer::read_pixels_into_bitmap` fn directly.
    ///
    /// The implementation of the fn looks like this:
    ///
    ///
    /// ```text
    /// bitmap = bitmap_new_for_data (context,
    ///                                    width, height,
    ///                                    format,
    ///                                    /* rowstride */
    ///                                    bpp * width,
    ///                                    pixels);
    /// framebuffer_read_pixels_into_bitmap (framebuffer,
    ///                                           x, y,
    ///                                           READ_PIXELS_COLOR_BUFFER,
    ///                                           bitmap);
    /// object_unref (bitmap);
    /// ```
    /// ## `x`
    /// The x position to read from
    /// ## `y`
    /// The y position to read from
    /// ## `width`
    /// The width of the region of rectangles to read
    /// ## `height`
    /// The height of the region of rectangles to read
    /// ## `format`
    /// The pixel format to store the data in
    /// ## `pixels`
    /// The address of the buffer to store the data in
    ///
    /// # Returns
    ///
    /// `true` if the read succeeded or `false` otherwise.
    fn read_pixels(
        &self,
        x: i32,
        y: i32,
        width: i32,
        height: i32,
        format: PixelFormat,
        pixels: &[u8],
    ) -> bool;

    /// This reads a rectangle of pixels from the given framebuffer where
    /// position (0, 0) is the top left. The pixel at (x, y) is the first
    /// read, and a rectangle of pixels with the same size as the bitmap is
    /// read right and downwards from that point.
    ///
    /// Currently  assumes that the framebuffer is in a premultiplied
    /// format so if the format of `bitmap` is non-premultiplied it will
    /// convert it. To read the pixel values without any conversion you
    /// should either specify a format that doesn't use an alpha channel or
    /// use one of the formats ending in PRE.
    /// ## `x`
    /// The x position to read from
    /// ## `y`
    /// The y position to read from
    /// ## `source`
    /// Identifies which auxillary buffer you want to read
    ///  (only READ_PIXELS_COLOR_BUFFER supported currently)
    /// ## `bitmap`
    /// The bitmap to store the results in.
    ///
    /// # Returns
    ///
    /// `true` if the read succeeded or `false` otherwise. The
    ///  fn is only likely to fail if the bitmap points to a pixel
    ///  buffer and it could not be mapped.
    fn read_pixels_into_bitmap(
        &self,
        x: i32,
        y: i32,
        source: ReadPixelsFlags,
        bitmap: &Bitmap,
    ) -> bool;

    /// When point sample rendering (also known as multisample rendering)
    /// has been enabled via `Framebuffer::set_samples_per_pixel`
    /// then you can optionally call this fn (or
    /// `Framebuffer::resolve_samples_region`) to explicitly resolve
    /// the point samples into values for the final color buffer.
    ///
    /// Some GPUs will implicitly resolve the point samples during
    /// rendering and so this fn is effectively a nop, but with other
    /// architectures it is desirable to defer the resolve step until the
    /// end of the frame.
    ///
    /// Since  will automatically ensure samples are resolved if the
    /// target color buffer is used as a source this API only needs to be
    /// used if explicit control is desired - perhaps because you want to
    /// ensure that the resolve is completed in advance to avoid later
    /// having to wait for the resolve to complete.
    ///
    /// If you are performing incremental updates to a framebuffer you
    /// should consider using `Framebuffer::resolve_samples_region`
    /// instead to avoid resolving redundant pixels.
    fn resolve_samples(&self);

    /// When point sample rendering (also known as multisample rendering)
    /// has been enabled via `Framebuffer::set_samples_per_pixel`
    /// then you can optionally call this fn (or
    /// `Framebuffer::resolve_samples`) to explicitly resolve the point
    /// samples into values for the final color buffer.
    ///
    /// Some GPUs will implicitly resolve the point samples during
    /// rendering and so this fn is effectively a nop, but with other
    /// architectures it is desirable to defer the resolve step until the
    /// end of the frame.
    ///
    /// Use of this API is recommended if incremental, small updates to
    /// a framebuffer are being made because by default  will
    /// implicitly resolve all the point samples of the framebuffer which
    /// can result in redundant work if only a small number of samples have
    /// changed.
    ///
    /// Because some GPUs implicitly resolve point samples this function
    /// only guarantees that at-least the region specified will be resolved
    /// and if you have rendered to a larger region then it's possible that
    /// other samples may be implicitly resolved.
    /// ## `x`
    /// top-left x coordinate of region to resolve
    /// ## `y`
    /// top-left y coordinate of region to resolve
    /// ## `width`
    /// width of region to resolve
    /// ## `height`
    /// height of region to resolve
    fn resolve_samples_region(&self, x: i32, y: i32, width: i32, height: i32);

    /// Multiplies the current model-view matrix by one that rotates the
    /// model around the axis-vector specified by `x`, `y` and `z`. The
    /// rotation follows the right-hand thumb rule so for example rotating
    /// by 10 degrees about the axis-vector (0, 0, 1) causes a small
    /// counter-clockwise rotation.
    /// ## `angle`
    /// Angle in degrees to rotate.
    /// ## `x`
    /// X-component of vertex to rotate around.
    /// ## `y`
    /// Y-component of vertex to rotate around.
    /// ## `z`
    /// Z-component of vertex to rotate around.
    fn rotate(&self, angle: f32, x: f32, y: f32, z: f32);

    /// Multiplies the current model-view matrix by one that rotates
    /// according to the rotation described by `euler`.
    ///
    /// ## `euler`
    /// A `Euler`
    fn rotate_euler(&self, euler: &Euler);

    /// Multiplies the current model-view matrix by one that rotates
    /// according to the rotation described by `quaternion`.
    ///
    /// ## `quaternion`
    /// A `Quaternion`
    fn rotate_quaternion(&self, quaternion: &Quaternion);

    /// Multiplies the current model-view matrix by one that scales the x,
    /// y and z axes by the given values.
    /// ## `x`
    /// Amount to scale along the x-axis
    /// ## `y`
    /// Amount to scale along the y-axis
    /// ## `z`
    /// Amount to scale along the z-axis
    fn scale(&self, x: f32, y: f32, z: f32);

    /// Defines a bit mask of which color channels should be written to the
    /// given `self`. If a bit is set in `color_mask` that means that
    /// color will be written.
    /// ## `color_mask`
    /// A `ColorMask` of which color channels to write to
    ///  the current framebuffer.
    fn set_color_mask(&self, color_mask: ColorMask);

    /// If `enabled` is `true`, the depth buffer used when rendering to `self`
    /// is available as a texture. You can retrieve the texture with
    /// `Framebuffer::get_depth_texture`.
    ///
    /// It's possible that your GPU does not support depth textures. You
    /// should check the `FeatureID::OglFeatureIdDepthTexture` feature before using this
    /// function.
    /// It's not valid to call this fn after the framebuffer has been
    /// allocated as the creation of the depth texture is done at allocation time.
    /// 
    /// ## `enabled`
    /// true or false
    fn set_depth_texture_enabled(&self, enabled: bool);

    /// Enables or disables depth buffer writing when rendering to `self`.
    /// If depth writing is enabled for both the framebuffer and the rendering
    /// pipeline, and the framebuffer has an associated depth buffer, depth
    /// information will be written to this buffer during rendering.
    ///
    /// Depth buffer writing is enabled by default.
    /// ## `depth_write_enabled`
    /// `true` to enable depth writing or `false` to disable
    fn set_depth_write_enabled(&self, depth_write_enabled: bool);

    /// Enables or disabled dithering if supported by the hardware.
    ///
    /// Dithering is a hardware dependent technique to increase the visible
    /// color resolution beyond what the underlying hardware supports by playing
    /// tricks with the colors placed into the framebuffer to give the illusion
    /// of other colors. (For example this can be compared to half-toning used
    /// by some news papers to show varying levels of grey even though their may
    /// only be black and white are available).
    ///
    /// If the current display pipeline for `self` does not support dithering
    /// then this has no affect.
    ///
    /// Dithering is enabled by default.
    /// ## `dither_enabled`
    /// `true` to enable dithering or `false` to disable
    fn set_dither_enabled(&self, dither_enabled: bool);

    /// Sets `matrix` as the new model-view matrix.
    /// ## `matrix`
    /// the new model-view matrix
    fn set_modelview_matrix(&self, matrix: &Matrix);

    /// Sets `matrix` as the new projection matrix.
    /// ## `matrix`
    /// the new projection matrix
    fn set_projection_matrix(&self, matrix: &Matrix);

    /// Requires that when rendering to `self` then `n` point samples
    /// should be made per pixel which will all contribute to the final
    /// resolved color for that pixel. The idea is that the hardware aims
    /// to get quality similar to what you would get if you rendered
    /// everything twice as big (for 4 samples per pixel) and then scaled
    /// that image back down with filtering. It can effectively remove the
    /// jagged edges of polygons and should be more efficient than if you
    /// were to manually render at a higher resolution and downscale
    /// because the hardware is often able to take some shortcuts. For
    /// example the GPU may only calculate a single texture sample for all
    /// points of a single pixel, and for tile based architectures all the
    /// extra sample data (such as depth and stencil samples) may be
    /// handled on-chip and so avoid increased demand on system memory
    /// bandwidth.
    ///
    /// By default this value is usually set to 0 and that is referred to
    /// as "single-sample" rendering. A value of 1 or greater is referred
    /// to as "multisample" rendering.
    ///
    /// There are some semantic differences between single-sample
    /// rendering and multisampling with just 1 point sample such as it
    /// being redundant to use the `Framebuffer::resolve_samples` and
    /// `Framebuffer::resolve_samples_region` apis with single-sample
    /// rendering.
    ///
    /// It's recommended that
    /// `Framebuffer::resolve_samples_region` be explicitly used at the
    /// end of rendering to a point sample buffer to minimize the number of
    /// samples that get resolved. By default  will implicitly resolve
    /// all framebuffer samples but if only a small region of a
    /// framebuffer has changed this can lead to redundant work being
    /// done.
    /// ## `samples_per_pixel`
    /// The minimum number of samples per pixel
    fn set_samples_per_pixel(&self, samples_per_pixel: i32);

    /// Sets which stereo buffers should be drawn to. The default
    /// is `StereoMode::Both`, which means that both the left and
    /// right buffers will be affected by drawing. For this to have
    /// an effect, the display system must support stereo drawables,
    /// and the framebuffer must have been created with stereo
    /// enabled. (See `OnscreenTemplate::set_stereo_enabled`,
    /// `Framebuffer::get_is_stereo`.)
    /// ## `stereo_mode`
    /// A `StereoMode` specifying which stereo buffers
    ///  should be drawn tow.
    fn set_stereo_mode(&self, stereo_mode: StereoMode);

    /// Defines a scale and offset for everything rendered relative to the
    /// top-left of the destination framebuffer.
    ///
    /// By default the viewport has an origin of (0,0) and width and height
    /// that match the framebuffer's size. Assuming a default projection and
    /// modelview matrix then you could translate the contents of a window
    /// down and right by leaving the viewport size unchanged by moving the
    /// offset to (10,10). The viewport coordinates are measured in pixels.
    /// If you left the x and y origin as (0,0) you could scale the windows
    /// contents down by specify and width and height that's half the real
    /// size of the framebuffer.
    ///
    /// Although the fn takes floating point arguments, existing
    /// drivers only allow the use of integer values. In the future floating
    /// point values will be exposed via a checkable feature.
    /// ## `x`
    /// The top-left x coordinate of the viewport origin (only integers
    ///  supported currently)
    /// ## `y`
    /// The top-left y coordinate of the viewport origin (only integers
    ///  supported currently)
    /// ## `width`
    /// The width of the viewport (only integers supported currently)
    /// ## `height`
    /// The height of the viewport (only integers supported currently)
    fn set_viewport(&self, x: f32, y: f32, width: f32, height: f32);

    /// Multiplies the current model-view matrix by the given matrix.
    /// ## `matrix`
    /// the matrix to multiply with the current model-view
    fn transform(&self, matrix: &Matrix);

    /// Multiplies the current model-view matrix by one that translates the
    /// model along all three axes according to the given values.
    /// ## `x`
    /// Distance to translate along the x-axis
    /// ## `y`
    /// Distance to translate along the y-axis
    /// ## `z`
    /// Distance to translate along the z-axis
    fn translate(&self, x: f32, y: f32, z: f32);
}

impl<O: Is<Framebuffer>> FramebufferExt for O {
    fn add_fence_callback<P: Fn(&Fence) + 'static>(&self, callback: P) -> Option<FenceClosure> {
        // let callback_data: Box<P> = Box::new(callback);
        // unsafe extern "C" fn callback_func<P: Fn(&Fence) + 'static>(
        //     fence: *mut ffi::Fence,
        //     user_data: glib_sys::gpointer,
        // ) {
        //     let fence = from_glib_borrow(fence);
        //     let callback: &P = &*(user_data as *mut _);
        //     (*callback)(&fence);
        // }
        // let callback = Some(callback_func::<P> as _);
        // let super_callback0: Box<P> = callback_data;
        // unsafe {
        //     from_glib_none(ffi::framebuffer_add_fence_callback(
        //         self.as_ref().to_glib_none().0,
        //         callback,
        //         Box::into_raw(super_callback0) as *mut _,
        //     ))
        // }
        unimplemented!()
    }

    fn allocate(&self) -> bool {
        let framebuffer = self.as_ref();
        let mut props = framebuffer.props.borrow_mut();

        if props.allocated {
            return true;
        }

        // Context *ctx = framebuffer.context;

        match &framebuffer.frontend {
            FramebufferType::OnScreen(onscreen) => {
                if props.config.depth_texture_enabled {
                    // _set_error(error, FRAMEBUFFER_ERROR,
                    //                 FRAMEBUFFER_ERROR_ALLOCATE,
                    //                 "Can't allocate onscreen framebuffer with a texture based depth buffer");
                    return false;
                }

                // let winsys: &WinsysVtable = _framebuffer_get_winsys(framebuffer);
                // if !winsys.onscreen_init(onscreen, error) {
                //     return false;
                // }

                // If the winsys doesn't support dirty events then we'll report
                // one on allocation so that if the application only paints in
                // response to dirty events then it will at least paint once to start

                // if !_has_private_feature(ctx, PRIVATE_FEATURE_DIRTY_EVENTS) {
                //     _onscreen_queue_full_dirty(onscreen);
                // }
            }
            FramebufferType::OffScreen(offscreen) => {
                // if !has_feature(ctx, FEATURE_ID_OFFSCREEN) {
                //     _set_error(error, SYSTEM_ERROR,
                //                     SYSTEM_ERROR_UNSUPPORTED,
                //                     "Offscreen framebuffers not supported by system");
                //     return false;
                // }

                // if !texture_allocate(offscreen.texture, error) {
                //     return false;
                // }

                // NB: it's only after allocating the texture that we will
                // determine whether a texture needs slicing...

                // if texture_is_sliced(offscreen.texture) {
                //     _set_error(error, SYSTEM_ERROR,
                //                     SYSTEM_ERROR_UNSUPPORTED,
                //                     "Can't create offscreen framebuffer from sliced texture");
                //     return false;
                // }

                // // Now that the texture has been allocated we can determine a
                // // size for the framebuffer...
                // props.width = texture_get_width(offscreen.texture);
                // props.height = texture_get_height(offscreen.texture);
                // props.viewport_width = props.width as f32;
                // props.viewport_height = props.height as f32;

                // // Forward the texture format as the internal format of the
                // // framebuffer
                // props.internal_format =
                //     _texture_get_format(offscreen.texture);

                // if !ctx.driver_vtable.offscreen_allocate(offscreen, error) {
                //     return false;
                // }
            }
        }

        props.allocated = true;

        true
    }

    fn cancel_fence_callback(&self, closure: &mut FenceClosure) {
        // unsafe {
        //     ffi::framebuffer_cancel_fence_callback(
        //         self.as_ref().to_glib_none().0,
        //         closure.to_glib_none_mut().0,
        //     );
        // }
        unimplemented!()
    }

    fn clear(&self, buffers: c_ulong, color: &Color) {
        self.clear4f(buffers, color.red, color.green, color.blue, color.alpha);
    }

    fn clear4f(&self, buffers: c_ulong, red: f32, green: f32, blue: f32, alpha: f32) {
        // unsafe {
        //     ffi::framebuffer_clear4f(
        //         self.as_ref().to_glib_none().0,
        //         buffers,
        //         red,
        //         green,
        //         blue,
        //         alpha,
        //     );
        // }
        unimplemented!()
    }

    fn discard_buffers(&self, buffers: c_ulong) {
        // unsafe {
        //     ffi::framebuffer_discard_buffers(self.as_ref().to_glib_none().0, buffers);
        // }
        unimplemented!()
    }

    fn draw_multitextured_rectangle(
        &self,
        pipeline: &Pipeline,
        x_1: f32,
        y_1: f32,
        x_2: f32,
        y_2: f32,
        tex_coords: &[f32],
    ) {
        // let tex_coords_len = tex_coords.len() as i32;
        // unsafe {
        //     ffi::framebuffer_draw_multitextured_rectangle(
        //         self.as_ref().to_glib_none().0,
        //         pipeline.to_glib_none().0,
        //         x_1,
        //         y_1,
        //         x_2,
        //         y_2,
        //         tex_coords.to_glib_none().0,
        //         tex_coords_len,
        //     );
        // }
        unimplemented!()
    }

    fn draw_rectangle(&self, pipeline: &Pipeline, x_1: f32, y_1: f32, x_2: f32, y_2: f32) {
        // unsafe {
        //     ffi::framebuffer_draw_rectangle(
        //         self.as_ref().to_glib_none().0,
        //         pipeline.to_glib_none().0,
        //         x_1,
        //         y_1,
        //         x_2,
        //         y_2,
        //     );
        // }
        unimplemented!()
    }

    //fn draw_rectangles(&self, pipeline: &Pipeline, coordinates: &[f32], n_rectangles: u32) {
    //    unsafe { TODO: call sys:framebuffer_draw_rectangles() }
    //}

    fn draw_textured_rectangle(
        &self,
        pipeline: &Pipeline,
        x_1: f32,
        y_1: f32,
        x_2: f32,
        y_2: f32,
        s_1: f32,
        t_1: f32,
        s_2: f32,
        t_2: f32,
    ) {
        // unsafe {
        //     ffi::framebuffer_draw_textured_rectangle(
        //         self.as_ref().to_glib_none().0,
        //         pipeline.to_glib_none().0,
        //         x_1,
        //         y_1,
        //         x_2,
        //         y_2,
        //         s_1,
        //         t_1,
        //         s_2,
        //         t_2,
        //     );
        // }
        unimplemented!()
    }

    //fn draw_textured_rectangles(&self, pipeline: &Pipeline, coordinates: &[f32], n_rectangles: u32) {
    //    unsafe { TODO: call sys:framebuffer_draw_textured_rectangles() }
    //}

    fn finish(&self) {
        // unsafe {
        //     ffi::framebuffer_finish(self.as_ref().to_glib_none().0);
        // }
        unimplemented!()
    }

    fn frustum(&self, left: f32, right: f32, bottom: f32, top: f32, z_near: f32, z_far: f32) {
        // unsafe {
        //     ffi::framebuffer_frustum(
        //         self.as_ref().to_glib_none().0,
        //         left,
        //         right,
        //         bottom,
        //         top,
        //         z_near,
        //         z_far,
        //     );
        // }
        unimplemented!()
    }

    fn alpha_bits(&self) -> i32 {
        // Context::global()
        // unsafe { ffi::framebuffer_get_alpha_bits(self.as_ref().to_glib_none().0) }
        unimplemented!()
    }

    fn blue_bits(&self) -> i32 {
        // unsafe { ffi::framebuffer_get_blue_bits(self.as_ref().to_glib_none().0) }
        unimplemented!()
    }

    fn color_mask(&self) -> ColorMask {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.color_mask
    }

    fn context(&self) -> Option<Context> {
        // unsafe { from_glib_none(ffi::framebuffer_get_context(self.as_ref().to_glib_none().0)) }
        unimplemented!()
    }

    fn depth_bits(&self) -> i32 {
        // unsafe { ffi::framebuffer_get_depth_bits(self.as_ref().to_glib_none().0) }
        unimplemented!()
    }

    fn depth_texture(&self) -> Option<Texture> {
        // unsafe {
        //     from_glib_none(ffi::framebuffer_get_depth_texture(
        //         self.as_ref().to_glib_none().0,
        //     ))
        // }
        unimplemented!()
    }

    fn depth_texture_enabled(&self) -> bool {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.config.depth_texture_enabled
    }

    fn depth_write_enabled(&self) -> bool {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.depth_writing_enabled
    }

    fn dither_enabled(&self) -> bool {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.dither_enabled
    }

    fn green_bits(&self) -> i32 {
        // unsafe { ffi::framebuffer_get_green_bits(self.as_ref().to_glib_none().0) }
        unimplemented!()
    }

    fn height(&self) -> i32 {
        // TODO: ensure_size_initialized();
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.height
    }

    fn is_stereo(&self) -> bool {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.config.stereo_enabled
    }

    fn modelview_matrix(&self) -> Matrix {
        // unsafe {
        //     let mut matrix = Matrix::uninitialized();
        //     ffi::framebuffer_get_modelview_matrix(
        //         self.as_ref().to_glib_none().0,
        //         matrix.to_glib_none_mut().0,
        //     );
        //     matrix
        // }
        unimplemented!()
    }

    fn projection_matrix(&self) -> Matrix {
        // unsafe {
        //     let mut matrix = Matrix::uninitialized();
        //     ffi::framebuffer_get_projection_matrix(
        //         self.as_ref().to_glib_none().0,
        //         matrix.to_glib_none_mut().0,
        //     );
        //     matrix
        // }
        unimplemented!()
    }

    fn red_bits(&self) -> i32 {
        // unsafe { ffi::framebuffer_get_red_bits(self.as_ref().to_glib_none().0) }
        unimplemented!()
    }

    fn samples_per_pixel(&self) -> i32 {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();

        if props.allocated {
            props.samples_per_pixel
        } else {
            props.config.samples_per_pixel
        }
    }

    fn stereo_mode(&self) -> StereoMode {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();

        props.stereo_mode
    }

    //fn viewport4fv(&self, viewport: /*Unimplemented*/FixedArray TypeId { ns_id: 0, id: 20 }; 4) {
    //    unsafe { TODO: call sys:framebuffer_get_viewport4fv() }
    //}

    fn viewport_height(&self) -> f32 {
        // TODO: ensure_size_initialized();
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.viewport_height
    }

    fn viewport_width(&self) -> f32 {
        // TODO: ensure_size_initialized();
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.viewport_width
    }

    fn viewport_x(&self) -> f32 {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.viewport_x
    }

    fn viewport_y(&self) -> f32 {
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.viewport_y
    }

    fn width(&self) -> i32 {
        // TODO: ensure_size_initialized();
        let framebuffer = self.as_ref();
        let props = framebuffer.props.borrow();
        props.width
    }

    fn identity_matrix(&self) {
        // unsafe {
        //     ffi::framebuffer_identity_matrix(self.as_ref().to_glib_none().0);
        // }
        unimplemented!()
    }

    fn orthographic(&self, x_1: f32, y_1: f32, x_2: f32, y_2: f32, near: f32, far: f32) {
        // unsafe {
        //     ffi::framebuffer_orthographic(
        //         self.as_ref().to_glib_none().0,
        //         x_1,
        //         y_1,
        //         x_2,
        //         y_2,
        //         near,
        //         far,
        //     );
        // }
        unimplemented!()
    }

    fn perspective(&self, fov_y: f32, aspect: f32, z_near: f32, z_far: f32) {
        // unsafe {
        //     ffi::framebuffer_perspective(
        //         self.as_ref().to_glib_none().0,
        //         fov_y,
        //         aspect,
        //         z_near,
        //         z_far,
        //     );
        // }
        unimplemented!()
    }

    fn pop_clip(&self) {
        // unsafe {
        //     ffi::framebuffer_pop_clip(self.as_ref().to_glib_none().0);
        // }
        unimplemented!()
    }

    fn pop_matrix(&self) {
        // unsafe {
        //     ffi::framebuffer_pop_matrix(self.as_ref().to_glib_none().0);
        // }
        unimplemented!()
    }

    fn push_matrix(&self) {
        // unsafe {
        //     ffi::framebuffer_push_matrix(self.as_ref().to_glib_none().0);
        // }
        unimplemented!()
    }

    fn push_primitive_clip(
        &self,
        primitive: &Primitive,
        bounds_x1: f32,
        bounds_y1: f32,
        bounds_x2: f32,
        bounds_y2: f32,
    ) {
        // unsafe {
        //     ffi::framebuffer_push_primitive_clip(
        //         self.as_ref().to_glib_none().0,
        //         primitive.to_glib_none().0,
        //         bounds_x1,
        //         bounds_y1,
        //         bounds_x2,
        //         bounds_y2,
        //     );
        // }
        unimplemented!()
    }

    fn push_rectangle_clip(&self, x_1: f32, y_1: f32, x_2: f32, y_2: f32) {
        // unsafe {
        //     ffi::framebuffer_push_rectangle_clip(
        //         self.as_ref().to_glib_none().0,
        //         x_1,
        //         y_1,
        //         x_2,
        //         y_2,
        //     );
        // }
        unimplemented!()
    }

    fn push_scissor_clip(&self, x: i32, y: i32, width: i32, height: i32) {
        // unsafe {
        //     ffi::framebuffer_push_scissor_clip(self.as_ref().to_glib_none().0, x, y, width, height);
        // }
        unimplemented!()
    }

    fn read_pixels(
        &self,
        x: i32,
        y: i32,
        width: i32,
        height: i32,
        format: PixelFormat,
        pixels: &[u8],
    ) -> bool {
        // unsafe {
        //     ffi::framebuffer_read_pixels(
        //         self.as_ref().to_glib_none().0,
        //         x,
        //         y,
        //         width,
        //         height,
        //         format.to_glib(),
        //         pixels.to_glib_none().0,
        //     ) == crate::true
        // }
        unimplemented!()
    }

    fn read_pixels_into_bitmap(
        &self,
        x: i32,
        y: i32,
        source: ReadPixelsFlags,
        bitmap: &Bitmap,
    ) -> bool {
        // unsafe {
        //     ffi::framebuffer_read_pixels_into_bitmap(
        //         self.as_ref().to_glib_none().0,
        //         x,
        //         y,
        //         source.to_glib(),
        //         bitmap.to_glib_none().0,
        //     ) == crate::true
        // }
        unimplemented!()
    }

    fn resolve_samples(&self) {
        // unsafe {
        //     ffi::framebuffer_resolve_samples(self.as_ref().to_glib_none().0);
        // }
        unimplemented!()
    }

    fn resolve_samples_region(&self, x: i32, y: i32, width: i32, height: i32) {
        // unsafe {
        //     ffi::framebuffer_resolve_samples_region(
        //         self.as_ref().to_glib_none().0,
        //         x,
        //         y,
        //         width,
        //         height,
        //     );
        // }
        unimplemented!()
    }

    fn rotate(&self, angle: f32, x: f32, y: f32, z: f32) {
        // unsafe {
        //     ffi::framebuffer_rotate(self.as_ref().to_glib_none().0, angle, x, y, z);
        // }
        unimplemented!()
    }

    fn rotate_euler(&self, euler: &Euler) {
        // unsafe {
        //     ffi::framebuffer_rotate_euler(self.as_ref().to_glib_none().0, euler.to_glib_none().0);
        // }
        unimplemented!()
    }

    fn rotate_quaternion(&self, quaternion: &Quaternion) {
        // unsafe {
        //     ffi::framebuffer_rotate_quaternion(
        //         self.as_ref().to_glib_none().0,
        //         quaternion.to_glib_none().0,
        //     );
        // }
        unimplemented!()
    }

    fn scale(&self, x: f32, y: f32, z: f32) {
        // unsafe {
        //     ffi::framebuffer_scale(self.as_ref().to_glib_none().0, x, y, z);
        // }
        unimplemented!()
    }

    fn set_color_mask(&self, color_mask: ColorMask) {
        // unsafe {
        //     ffi::framebuffer_set_color_mask(self.as_ref().to_glib_none().0, color_mask.to_glib());
        // }
        unimplemented!()
    }

    fn set_depth_texture_enabled(&self, enabled: bool) {
        // unsafe {
        //     ffi::framebuffer_set_depth_texture_enabled(
        //         self.as_ref().to_glib_none().0,
        //         enabled as i32,
        //     );
        // }
        unimplemented!()
    }

    fn set_depth_write_enabled(&self, depth_write_enabled: bool) {
        // unsafe {
        //     ffi::framebuffer_set_depth_write_enabled(
        //         self.as_ref().to_glib_none().0,
        //         depth_write_enabled as i32,
        //     );
        // }
        unimplemented!()
    }

    fn set_dither_enabled(&self, dither_enabled: bool) {
        // unsafe {
        //     ffi::framebuffer_set_dither_enabled(
        //         self.as_ref().to_glib_none().0,
        //         dither_enabled as i32,
        //     );
        // }
        unimplemented!()
    }

    fn set_modelview_matrix(&self, matrix: &Matrix) {
        // unsafe {
        //     ffi::framebuffer_set_modelview_matrix(
        //         self.as_ref().to_glib_none().0,
        //         matrix.to_glib_none().0,
        //     );
        // }
        unimplemented!()
    }

    fn set_projection_matrix(&self, matrix: &Matrix) {
        // unsafe {
        //     ffi::framebuffer_set_projection_matrix(
        //         self.as_ref().to_glib_none().0,
        //         matrix.to_glib_none().0,
        //     );
        // }
        unimplemented!()
    }

    fn set_samples_per_pixel(&self, samples_per_pixel: i32) {
        // unsafe {
        //     ffi::framebuffer_set_samples_per_pixel(
        //         self.as_ref().to_glib_none().0,
        //         samples_per_pixel,
        //     );
        // }
        unimplemented!()
    }

    fn set_stereo_mode(&self, stereo_mode: StereoMode) {
        // unsafe {
        //     ffi::framebuffer_set_stereo_mode(self.as_ref().to_glib_none().0, stereo_mode.to_glib());
        // }
        unimplemented!()
    }

    fn set_viewport(&self, x: f32, y: f32, width: f32, height: f32) {
        // unsafe {
        //     ffi::framebuffer_set_viewport(self.as_ref().to_glib_none().0, x, y, width, height);
        // }
        unimplemented!()
    }

    fn transform(&self, matrix: &Matrix) {
        // unsafe {
        //     ffi::framebuffer_transform(self.as_ref().to_glib_none().0, matrix.to_glib_none().0);
        // }
        unimplemented!()
    }

    fn translate(&self, x: f32, y: f32, z: f32) {
        // unsafe {
        //     ffi::framebuffer_translate(self.as_ref().to_glib_none().0, x, y, z);
        // }
        unimplemented!()
    }
}

impl fmt::Display for Framebuffer {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Framebuffer")
    }
}