1#![deny(missing_docs)]
2
3extern 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
20pub struct TextureContext<F, R, C>
22 where F: gfx::Factory<R>,
23 R: gfx::Resources,
24 C: gfx::CommandBuffer<R>,
25{
26 pub factory: F,
28 pub encoder: gfx::Encoder<R, C>,
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub enum Error {
35 Create(gfx::CombinedError),
37 Update(gfx::UpdateError<[u16; 3]>),
39 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#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
83pub enum Flip {
84 None,
86 Vertical,
88 Horizontal,
90 Both,
92}
93
94#[derive(Clone, Debug, PartialEq)]
96pub struct Texture<R> where R: gfx::Resources {
97 pub surface: gfx::handle::Texture<R, R8_G8_B8_A8>,
99 pub sampler: gfx::handle::Sampler<R>,
101 pub view: gfx::handle::ShaderResourceView<R, [f32; 4]>
103}
104
105impl<R: gfx::Resources> Texture<R> {
106 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 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 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 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 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 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 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}