1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//! The `canvas` module enables creating render targets to be used instead of
//! the screen.  This allows graphics to be rendered to images off-screen
//! in order to do things like saving to an image file or creating cool effects.

use gfx::{Factory, RENDER_TARGET, SHADER_RESOURCE};
use gfx::format::{ChannelTyped, Srgb, Srgba8, Swizzle};
use gfx::handle::RenderTargetView;
use gfx::memory::Usage;
use gfx::texture::{AaMode, Kind};

use Context;
use conf::*;
use error::*;
use graphics::*;

/// A generic canvas independent of graphics backend. This type should probably
/// never be used directly; use `ggez::graphics::Canvas` instead.
#[derive(Debug)]
pub struct CanvasGeneric<Spec>
where
    Spec: BackendSpec,
{
    target: RenderTargetView<Spec::Resources, Srgba8>,
    image: Image,
}

/// A canvas that can be rendered to instead of the screen (sometimes referred
/// to as "render target" or "render to texture"). Set the canvas with the
/// `ggez::graphics::set_canvas()` function, and then anything you
/// draw will be drawn to the canvas instead of the screen.  
///
/// Resume drawing to the screen by calling `ggez::graphics::set_canvas(None)`.
pub type Canvas = CanvasGeneric<GlBackendSpec>;

impl Canvas {
    /// Create a new canvas with the given size and number of samples.
    pub fn new(
        ctx: &mut Context,
        width: u32,
        height: u32,
        samples: NumSamples,
    ) -> GameResult<Canvas> {
        let (w, h) = (width as u16, height as u16);
        let aa = match samples {
            NumSamples::One => AaMode::Single,
            s => AaMode::Multi(s as u8),
        };
        let kind = Kind::D2(w, h, aa);
        let cty = Srgb::get_channel_type();
        let levels = 1;
        let factory = &mut ctx.gfx_context.factory;
        let tex = factory.create_texture(
            kind,
            levels,
            SHADER_RESOURCE | RENDER_TARGET,
            Usage::Data,
            Some(cty),
        )?;
        let resource = factory.view_texture_as_shader_resource::<Srgba8>(
            &tex,
            (0, levels - 1),
            Swizzle::new(),
        )?;
        let target = factory.view_texture_as_render_target(&tex, 0, None)?;
        Ok(Canvas {
            target,
            image: Image {
                texture: resource,
                sampler_info: ctx.gfx_context.default_sampler_info,
                blend_mode: None,
                width,
                height,
            },
        })
    }

    /// Create a new canvas with the current window dimensions.
    pub fn with_window_size(ctx: &mut Context) -> GameResult<Canvas> {
        use graphics;
        let (w, h) = graphics::get_drawable_size(ctx);
        // Default to no multisampling
        Canvas::new(ctx, w, h, NumSamples::One)
    }

    /// Gets the backend `Image` that is being rendered to.
    pub fn get_image(&self) -> &Image {
        &self.image
    }

    /// Destroys the Canvas and returns the `Image` it contains.
    pub fn into_inner(self) -> Image {
        // This texture is created with different settings
        // than the default; does that matter?
        self.image
    }
}

impl Drawable for Canvas {
    fn draw_ex(&self, ctx: &mut Context, param: DrawParam) -> GameResult<()> {
        self.image.draw_ex(ctx, param)
    }
    fn set_blend_mode(&mut self, mode: Option<BlendMode>) {
        self.image.blend_mode = mode;
    }
    fn get_blend_mode(&self) -> Option<BlendMode> {
        self.image.blend_mode
    }
}

/// Set the canvas to render to. Specifying `Option::None` will cause all
/// rendering to be done directly to the screen.
pub fn set_canvas(ctx: &mut Context, target: Option<&Canvas>) {
    match target {
        Some(surface) => {
            ctx.gfx_context.data.out = surface.target.clone();
        }
        None => {
            ctx.gfx_context.data.out = ctx.gfx_context.screen_render_target.clone();
        }
    };
}