<commit_before>//! SpriteBatch type.

use context::Context;
use graphics;
use error;
use gfx;
use gfx::Factory;
use GameResult;
use super::pixelshader::BlendMode;

// Owning the given Image is inconvenient because we might want, say,
// the same Rc<Image> shared among many SpriteBatch'es.
//
// But draw() doesn't handle that particularly well...
//
// The right way to fix this might be to have another object that implements
// Drawable that is created from a SpriteBatch and an image reference.
// BoundSpriteBatch or something.  That *feels* like the right way to go...
//
// Or just not implement Drawable and provide your own drawing functions, though
// that's squirrelly.
//
// Oh, or maybe make it take a Cow<Image> ?  that might work.
//
// For now though, let's mess around with the gfx bits rather than the ggez bits.
// We need to be able to, essentially, have an array of RectProperties.


/// A SpriteBatch draws a number of copies of the same image, using a single draw call.
#[derive(Debug)]
pub struct SpriteBatch {
    image: graphics::Image,
    sprites: Vec<graphics::InstanceProperties>,
    blend_mode: Option<BlendMode>,
}

/// An index of a particular sprite in a SpriteBatch.
pub type SpriteIdx = usize;

impl SpriteBatch {
    /// Creates a new `SpriteBatch`, drawing with the given image.
    pub fn new(image: graphics::Image) -> Self {
        Self {
            image: image,
            sprites: vec![],
            blend_mode: None,
        }
    }

    /// Adds a new sprite to the sprite batch.
    ///
    /// Returns a handle with which to modify the sprite using `set()`
    pub fn add(&mut self, param: graphics::DrawParam) -> SpriteIdx {
        let src_width = param.src.w;
        let src_height = param.src.h;
        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        // We also invert the Y scale if our screen coordinates
        // are "upside down", because by default we present the
        // illusion that the screen is addressed in pixels.
        // BUGGO: Which I rather regret now.
        // let invert_y = if gfx.screen_rect.h < 0.0 { 1.0 } else { -1.0 };
        // TODO: Figure out whether implementing this is needed/how to do it cleanly
        let real_scale = graphics::Point2::new(src_width * param.scale.x * self.image.width as f32,
                                               src_height * param.scale.y *
                                               self.image.height as f32);
        let mut new_param = param;
        new_param.scale = real_scale;
        // Not entirely sure why the inversion is necessary, but oh well.
        new_param.offset.x *= -1.0 * param.scale.x;
        new_param.offset.y *= param.scale.y;
        self.sprites.push(new_param.into());
        self.sprites.len() - 1
    }

    /// Alters a sprite in the batch to use the given draw params
    pub fn set(&mut self, handle: SpriteIdx, param: graphics::DrawParam) -> GameResult<()> {
        if handle < self.sprites.len() - 1 {
            self.sprites[handle] = param.into();
            Ok(())
        } else {
            Err(error::GameError::RenderError(String::from("Provided index is out of bounds.")))
        }
    }

    /// Immediately sends all data in the batch to the graphics card.
    ///
    /// Generally just calling `graphics::draw()` on the `SpriteBatch`
    /// will do this automatically.
    pub fn flush(&self, ctx: &mut Context) -> GameResult<()> {
        let gfx = &mut ctx.gfx_context;
        if gfx.data.rect_instance_properties.len() < self.sprites.len() {
            gfx.data.rect_instance_properties = gfx.factory
                .create_buffer(self.sprites.len(),
                               gfx::buffer::Role::Vertex,
                               gfx::memory::Usage::Dynamic,
                               gfx::TRANSFER_DST)?;
        }
        gfx.encoder
            .update_buffer(&gfx.data.rect_instance_properties, &self.sprites[..], 0)?;<commit_msg>Fix bounds check in Spritebatch::set<commit_after>//! SpriteBatch type.

use context::Context;
use graphics;
use error;
use gfx;
use gfx::Factory;
use GameResult;
use super::pixelshader::BlendMode;

// Owning the given Image is inconvenient because we might want, say,
// the same Rc<Image> shared among many SpriteBatch'es.
//
// But draw() doesn't handle that particularly well...
//
// The right way to fix this might be to have another object that implements
// Drawable that is created from a SpriteBatch and an image reference.
// BoundSpriteBatch or something.  That *feels* like the right way to go...
//
// Or just not implement Drawable and provide your own drawing functions, though
// that's squirrelly.
//
// Oh, or maybe make it take a Cow<Image> ?  that might work.
//
// For now though, let's mess around with the gfx bits rather than the ggez bits.
// We need to be able to, essentially, have an array of RectProperties.


/// A SpriteBatch draws a number of copies of the same image, using a single draw call.
#[derive(Debug)]
pub struct SpriteBatch {
    image: graphics::Image,
    sprites: Vec<graphics::InstanceProperties>,
    blend_mode: Option<BlendMode>,
}

/// An index of a particular sprite in a SpriteBatch.
pub type SpriteIdx = usize;

impl SpriteBatch {
    /// Creates a new `SpriteBatch`, drawing with the given image.
    pub fn new(image: graphics::Image) -> Self {
        Self {
            image: image,
            sprites: vec![],
            blend_mode: None,
        }
    }

    /// Adds a new sprite to the sprite batch.
    ///
    /// Returns a handle with which to modify the sprite using `set()`
    pub fn add(&mut self, param: graphics::DrawParam) -> SpriteIdx {
        let src_width = param.src.w;
        let src_height = param.src.h;
        // We have to mess with the scale to make everything
        // be its-unit-size-in-pixels.
        // We also invert the Y scale if our screen coordinates
        // are "upside down", because by default we present the
        // illusion that the screen is addressed in pixels.
        // BUGGO: Which I rather regret now.
        // let invert_y = if gfx.screen_rect.h < 0.0 { 1.0 } else { -1.0 };
        // TODO: Figure out whether implementing this is needed/how to do it cleanly
        let real_scale = graphics::Point2::new(src_width * param.scale.x * self.image.width as f32,
                                               src_height * param.scale.y *
                                               self.image.height as f32);
        let mut new_param = param;
        new_param.scale = real_scale;
        // Not entirely sure why the inversion is necessary, but oh well.
        new_param.offset.x *= -1.0 * param.scale.x;
        new_param.offset.y *= param.scale.y;
        self.sprites.push(new_param.into());
        self.sprites.len() - 1
    }

    /// Alters a sprite in the batch to use the given draw params
    pub fn set(&mut self, handle: SpriteIdx, param: graphics::DrawParam) -> GameResult<()> {
        if handle < self.sprites.len() {
            self.sprites[handle] = param.into();
            Ok(())
        } else {
            Err(error::GameError::RenderError(String::from("Provided index is out of bounds.")))
        }
    }

    /// Immediately sends all data in the batch to the graphics card.
    ///
    /// Generally just calling `graphics::draw()` on the `SpriteBatch`
    /// will do this automatically.
    pub fn flush(&self, ctx: &mut Context) -> GameResult<()> {
        let gfx = &mut ctx.gfx_context;
        if gfx.data.rect_instance_properties.len() < self.sprites.len() {
            gfx.data.rect_instance_properties = gfx.factory
                .create_buffer(self.sprites.len(),
                               gfx::buffer::Role::Vertex,
                               gfx::memory::Usage::Dynamic,
                               gfx::TRANSFER_DST)?;
        }
        gfx.encoder
            .update_buffer(&gfx.data.rect_instance_properties, &self.sprites[..], 0)?;
{'fraction_non_alphanumeric': 0.10914307097417987, 'fraction_numerical': 0.0038667830859423725, 'mean_word_length': 2.1517295597484276, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2460129', 'n_tokens_mistral': 2292, 'n_tokens_neox': 2249, 'n_words': 816}