gfx_texture/
lib.rs

1#![deny(missing_docs)]
2
3//! A Gfx texture representation that works nicely with Piston libraries.
4
5extern crate gfx;
6extern crate gfx_core;
7extern crate texture;
8extern crate image;
9
10pub use texture::*;
11
12use std::path::Path;
13use image::{
14    DynamicImage,
15    ImageError,
16    RgbaImage,
17};
18use gfx::format::{Srgba8, R8_G8_B8_A8};
19
20/// Context required to create and update textures.
21pub struct TextureContext<F, R, C>
22    where F: gfx::Factory<R>,
23          R: gfx::Resources,
24          C: gfx::CommandBuffer<R>,
25{
26    /// A factory to create textures.
27    pub factory: F,
28    /// An encoder to update textures.
29    pub encoder: gfx::Encoder<R, C>,
30}
31
32/// Create creation or update error.
33#[derive(Debug, Clone, PartialEq)]
34pub enum Error {
35    /// An error when creating texture.
36    Create(gfx::CombinedError),
37    /// An error when updating texture.
38    Update(gfx::UpdateError<[u16; 3]>),
39    /// An error when performing an image operation.
40    Image(String),
41}
42
43impl From<gfx::UpdateError<[u16; 3]>> for Error {
44    fn from(val: gfx::UpdateError<[u16; 3]>) -> Error {
45        Error::Update(val)
46    }
47}
48
49impl From<gfx::texture::CreationError> for Error {
50    fn from(val: gfx::texture::CreationError) -> Error {
51        Error::Create(val.into())
52    }
53}
54
55impl From<ImageError> for Error {
56    fn from(val: ImageError) -> Error {
57        Error::Image(val.to_string())
58    }
59}
60
61impl From<gfx::ResourceViewError> for Error {
62    fn from(val: gfx::ResourceViewError) -> Error {
63        Error::Create(val.into())
64    }
65}
66
67impl std::fmt::Display for Error {
68    fn fmt(&self, w: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
69        use std::fmt::{Display, Debug};
70
71        match *self {
72            Error::Create(ref err) => Display::fmt(err, w),
73            Error::Update(ref err) => Debug::fmt(err, w),
74            Error::Image(ref err) => Display::fmt(err, w),
75        }
76    }
77}
78
79impl std::error::Error for Error {}
80
81/// Flip settings.
82#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
83pub enum Flip {
84    /// Does not flip.
85    None,
86    /// Flips image vertically.
87    Vertical,
88    /// Flips image horizontally.
89    Horizontal,
90    /// Flips image both vertically and horizontally.
91    Both,
92}
93
94/// Represents a texture.
95#[derive(Clone, Debug, PartialEq)]
96pub struct Texture<R> where R: gfx::Resources {
97    /// Pixel storage for texture.
98    pub surface: gfx::handle::Texture<R, R8_G8_B8_A8>,
99    /// Sampler for texture.
100    pub sampler: gfx::handle::Sampler<R>,
101    /// View used by shader.
102    pub view: gfx::handle::ShaderResourceView<R, [f32; 4]>
103}
104
105impl<R: gfx::Resources> Texture<R> {
106    /// Returns empty texture.
107    pub fn empty<F, C>(context: &mut TextureContext<F, R, C>) -> Result<Self, Error>
108        where F: gfx::Factory<R>,
109              C: gfx::CommandBuffer<R>,
110    {
111        CreateTexture::create(context, Format::Rgba8, &[0u8; 4], [1, 1],
112                              &TextureSettings::new())
113    }
114
115    /// Creates a texture from path.
116    pub fn from_path<F, C, P>(
117        context: &mut TextureContext<F, R, C>,
118        path: P,
119        flip: Flip,
120        settings: &TextureSettings,
121    ) -> Result<Self, Error>
122        where F: gfx::Factory<R>,
123              C: gfx::CommandBuffer<R>,
124              P: AsRef<Path>
125    {
126        let img = image::open(path)?;
127
128        let img = match img {
129            DynamicImage::ImageRgba8(img) => img,
130            img => img.to_rgba8()
131        };
132
133        let img = match flip {
134            Flip::Vertical => image::imageops::flip_vertical(&img),
135            Flip::Horizontal => image::imageops::flip_horizontal(&img),
136            Flip::Both => {
137                let img = image::imageops::flip_vertical(&img);
138                image::imageops::flip_horizontal(&img)
139            }
140            Flip::None => img,
141        };
142
143        Texture::from_image(context, &img, settings)
144    }
145
146    /// Creates a texture from image.
147    pub fn from_image<F, C>(
148        context: &mut TextureContext<F, R, C>,
149        img: &RgbaImage,
150        settings: &TextureSettings
151    ) -> Result<Self, Error>
152        where F: gfx::Factory<R>,
153              C: gfx::CommandBuffer<R>,
154    {
155        let (width, height) = img.dimensions();
156        CreateTexture::create(context, Format::Rgba8,
157                              img, [width, height], settings)
158    }
159
160    /// Creates texture from memory alpha.
161    pub fn from_memory_alpha<F, C>(
162        context: &mut TextureContext<F, R, C>,
163        buffer: &[u8],
164        width: u32,
165        height: u32,
166        settings: &TextureSettings
167    ) -> Result<Self, Error>
168        where F: gfx::Factory<R>,
169              C: gfx::CommandBuffer<R>,
170    {
171        if width == 0 || height == 0 {
172            return Texture::empty(context);
173        }
174
175        let size = [width, height];
176        let buffer = texture::ops::alpha_to_rgba8(buffer, size);
177        CreateTexture::create(context, Format::Rgba8, &buffer, size, settings)
178    }
179
180    /// Updates the texture with an image.
181    pub fn update<F, C>(
182        &mut self,
183        context: &mut TextureContext<F, R, C>,
184        img: &RgbaImage
185    ) -> Result<(), Error>
186        where F: gfx::Factory<R>,
187              C: gfx::CommandBuffer<R>
188    {
189        let (width, height) = img.dimensions();
190        let offset = [0, 0];
191        let size = [width, height];
192        UpdateTexture::update(self, context, Format::Rgba8, img, offset, size)
193    }
194}
195
196impl<F, R> TextureOp<F> for Texture<R> where R: gfx::Resources {
197    type Error = Error;
198}
199
200impl<F, R, C> CreateTexture<TextureContext<F, R, C>> for Texture<R>
201    where F: gfx::Factory<R>,
202          R: gfx::Resources,
203          C: gfx::CommandBuffer<R>,
204{
205    fn create<S: Into<[u32; 2]>>(
206        context: &mut TextureContext<F, R, C>,
207        _format: Format,
208        memory: &[u8],
209        size: S,
210        settings: &TextureSettings
211    ) -> Result<Self, Self::Error> {
212        let factory = &mut context.factory;
213        // Modified `Factory::create_texture_immutable_u8` for dynamic texture.
214        fn create_texture<T, F, R>(
215            factory: &mut F,
216            kind: gfx::texture::Kind,
217            data: &[&[u8]]
218        ) -> Result<(
219            gfx::handle::Texture<R, T::Surface>,
220            gfx::handle::ShaderResourceView<R, T::View>
221        ), Error>
222            where F: gfx::Factory<R>,
223                  R: gfx::Resources,
224                  T: gfx::format::TextureFormat
225        {
226            use gfx::{format, texture};
227            use gfx::memory::{Usage, Bind};
228            use gfx_core::memory::Typed;
229            use gfx_core::texture::Mipmap;
230
231            let surface = <T::Surface as format::SurfaceTyped>::get_surface_type();
232            let num_slices = kind.get_num_slices().unwrap_or(1) as usize;
233            let num_faces = if kind.is_cube() {6} else {1};
234            let desc = texture::Info {
235                kind: kind,
236                levels: (data.len() / (num_slices * num_faces)) as texture::Level,
237                format: surface,
238                bind: Bind::SHADER_RESOURCE,
239                usage: Usage::Dynamic,
240            };
241            let cty = <T::Channel as format::ChannelTyped>::get_channel_type();
242            let raw = factory.create_texture_raw(desc, Some(cty), Some((data, Mipmap::Provided)))?;
243            let levels = (0, raw.get_info().levels - 1);
244            let tex = Typed::new(raw);
245            let view = factory.view_texture_as_shader_resource::<T>(
246                &tex, levels, format::Swizzle::new())?;
247            Ok((tex, view))
248        }
249
250        let size = size.into();
251        let (width, height) = (size[0] as u16, size[1] as u16);
252        let tex_kind = gfx::texture::Kind::D2(width, height,
253            gfx::texture::AaMode::Single);
254
255        // FIXME Use get_min too. gfx has only one filter setting for both.
256        let filter_method = match settings.get_mag() {
257            texture::Filter::Nearest => gfx::texture::FilterMethod::Scale,
258            texture::Filter::Linear => gfx::texture::FilterMethod::Bilinear,
259        };
260
261        let wrap_mode_u = match settings.get_wrap_u() {
262            Wrap::ClampToEdge => gfx::texture::WrapMode::Clamp,
263            Wrap::ClampToBorder => gfx::texture::WrapMode::Border,
264            Wrap::Repeat => gfx::texture::WrapMode::Tile,
265            Wrap::MirroredRepeat => gfx::texture::WrapMode::Mirror,
266        };
267
268        let wrap_mode_v = match settings.get_wrap_v() {
269            Wrap::ClampToEdge => gfx::texture::WrapMode::Clamp,
270            Wrap::ClampToBorder => gfx::texture::WrapMode::Border,
271            Wrap::Repeat => gfx::texture::WrapMode::Tile,
272            Wrap::MirroredRepeat => gfx::texture::WrapMode::Mirror,
273        };
274
275        let mut sampler_info = gfx::texture::SamplerInfo::new(
276            filter_method,
277            wrap_mode_u
278        );
279        sampler_info.wrap_mode.1 = wrap_mode_v;
280        sampler_info.border = settings.get_border_color().into();
281
282        let (surface, view) = create_texture::<Srgba8, F, R>(
283            factory, tex_kind, &[memory])?;
284        let sampler = factory.create_sampler(sampler_info);
285        Ok(Texture { surface: surface, sampler: sampler, view: view })
286    }
287}
288
289impl<F, R, C> UpdateTexture<TextureContext<F, R, C>> for Texture<R>
290    where F: gfx::Factory<R>,
291          R: gfx::Resources,
292          C: gfx::CommandBuffer<R>
293{
294    fn update<O, S>(
295        &mut self,
296        context: &mut TextureContext<F, R, C>,
297        format: Format,
298        memory: &[u8],
299        offset: O,
300        size: S,
301    ) -> Result<(), Self::Error>
302        where O: Into<[u32; 2]>,
303              S: Into<[u32; 2]>,
304    {
305        let encoder = &mut context.encoder;
306        let offset = offset.into();
307        let size = size.into();
308        let tex = &self.surface;
309        let face = None;
310        let img_info = gfx::texture::ImageInfoCommon {
311            xoffset: offset[0] as u16,
312            yoffset: offset[1] as u16,
313            zoffset: 0,
314            width: size[0] as u16,
315            height: size[1] as u16,
316            depth: 0,
317            format: (),
318            mipmap: 0,
319        };
320        let data = gfx::memory::cast_slice(memory);
321
322        match format {
323            Format::Rgba8 => {
324                use gfx::format::Rgba8;
325                encoder.update_texture::<_, Rgba8>(tex, face, img_info, data).map_err(Into::into)
326            },
327        }
328    }
329}
330
331impl<R> ImageSize for Texture<R> where R: gfx::Resources {
332    #[inline(always)]
333    fn get_size(&self) -> (u32, u32) {
334        let (w, h, _, _) = self.surface.get_info().kind.get_dimensions();
335        (w as u32, h as u32)
336    }
337}