1#![allow(clippy::wrong_self_convention)]
2
3use crate::device::{DropManager, ResourceId};
4use crate::{Device, DeviceBackend};
5use notan_math::Rect;
6use std::fmt::{Debug, Formatter};
7use std::sync::Arc;
8
9pub trait TextureSource {
10 fn create(
11 &self,
12 device: &mut dyn DeviceBackend,
13 info: TextureInfo,
14 ) -> Result<(u64, TextureInfo), String>;
15
16 fn update(&self, device: &mut dyn DeviceBackend, opts: TextureUpdate) -> Result<(), String>;
17}
18
19#[derive(Debug)]
20pub struct TextureRead {
21 pub x_offset: u32,
22 pub y_offset: u32,
23 pub width: u32,
24 pub height: u32,
25 pub format: TextureFormat,
26}
27
28#[derive(Debug, Clone)]
29pub struct TextureUpdate {
30 pub x_offset: u32,
31 pub y_offset: u32,
32 pub width: u32,
33 pub height: u32,
34 pub format: TextureFormat,
35}
36
37#[derive(Debug, Clone)]
38pub struct TextureInfo {
39 pub width: u32,
40 pub height: u32,
41 pub format: TextureFormat,
42 pub min_filter: TextureFilter,
43 pub mag_filter: TextureFilter,
44 pub wrap_x: TextureWrap,
45 pub wrap_y: TextureWrap,
46 pub premultiplied_alpha: bool,
47 pub mipmap_filter: Option<TextureFilter>,
48
49 pub depth: bool,
51}
52
53impl Default for TextureInfo {
54 fn default() -> Self {
55 Self {
56 format: TextureFormat::Rgba32,
57 mag_filter: TextureFilter::Nearest,
58 min_filter: TextureFilter::Nearest,
59 wrap_x: TextureWrap::Clamp,
60 wrap_y: TextureWrap::Clamp,
61 width: 1,
62 height: 1,
63 depth: false,
64 premultiplied_alpha: false,
65 mipmap_filter: None,
66 }
67 }
68}
69
70impl TextureInfo {
71 #[inline]
72 pub fn bytes_per_pixel(&self) -> u8 {
73 self.format.bytes_per_pixel()
74 }
75}
76
77impl TextureFormat {
78 pub fn bytes_per_pixel(&self) -> u8 {
79 use TextureFormat::*;
80 match self {
81 R8 => 1,
82 R8Uint => 1,
83 R16Uint => 2,
84 Rgba32Float => 4 * 4,
85 _ => 4,
86 }
87 }
88}
89
90struct TextureIdRef {
91 id: u64,
92 drop_manager: Arc<DropManager>,
93}
94
95impl Drop for TextureIdRef {
96 fn drop(&mut self) {
97 self.drop_manager.push(ResourceId::Texture(self.id));
98 }
99}
100
101impl Debug for TextureIdRef {
102 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
103 write!(f, "TextureIdRef({})", self.id)
104 }
105}
106
107#[derive(Debug, Clone)]
108pub struct Texture {
109 id: u64,
110 _id_ref: Arc<TextureIdRef>,
111 width: u32,
112 height: u32,
113 format: TextureFormat,
114 min_filter: TextureFilter,
115 mag_filter: TextureFilter,
116 frame: Rect,
117 pub(crate) is_render_texture: bool,
118}
119
120impl Texture {
122 pub(crate) fn new(id: u64, info: TextureInfo, drop_manager: Arc<DropManager>) -> Self {
123 let id_ref = Arc::new(TextureIdRef { id, drop_manager });
124
125 let TextureInfo {
126 width,
127 height,
128 format,
129 min_filter,
130 mag_filter,
131 ..
132 } = info;
133
134 let frame = Rect {
136 x: 0.0,
137 y: 0.0,
138 width: width as _,
139 height: height as _,
140 };
141
142 Self {
143 id,
144 _id_ref: id_ref,
145 width,
146 height,
147 format,
148 min_filter,
149 mag_filter,
150 frame,
151 is_render_texture: false,
152 }
153 }
154
155 #[inline(always)]
156 pub fn id(&self) -> u64 {
157 self.id
158 }
159
160 #[inline(always)]
161 pub fn format(&self) -> &TextureFormat {
162 &self.format
163 }
164
165 #[inline(always)]
166 pub fn min_filter(&self) -> &TextureFilter {
167 &self.min_filter
168 }
169
170 #[inline(always)]
171 pub fn mag_filter(&self) -> &TextureFilter {
172 &self.mag_filter
173 }
174
175 #[inline(always)]
176 pub fn frame(&self) -> &Rect {
177 &self.frame
178 }
179
180 #[inline]
181 pub fn with_frame(&self, x: f32, y: f32, width: f32, height: f32) -> Texture {
182 let frame = Rect {
183 x,
184 y,
185 width,
186 height,
187 };
188
189 let mut texture = self.clone();
190 texture.frame = frame;
191 texture
192 }
193
194 #[inline(always)]
195 pub fn width(&self) -> f32 {
196 self.frame.width
197 }
198
199 #[inline(always)]
200 pub fn height(&self) -> f32 {
201 self.frame.height
202 }
203
204 #[inline(always)]
205 pub fn base_width(&self) -> f32 {
206 self.width as _
207 }
208
209 #[inline(always)]
210 pub fn base_height(&self) -> f32 {
211 self.height as _
212 }
213
214 pub fn size(&self) -> (f32, f32) {
215 (self.frame.width, self.frame.height)
216 }
217
218 pub fn base_size(&self) -> (f32, f32) {
219 (self.width as _, self.height as _)
220 }
221
222 #[cfg(feature = "texture_to_file")]
223 pub fn to_file<P: AsRef<std::path::Path>>(
224 &self,
225 gfx: &mut Device,
226 path: P,
227 ) -> Result<(), String> {
228 crate::to_file::save_to_png_file(gfx, self, false, path)
229 }
230
231 pub fn is_render_texture(&self) -> bool {
232 self.is_render_texture
233 }
234}
235
236impl std::cmp::PartialEq for Texture {
237 fn eq(&self, other: &Self) -> bool {
238 self.id() == other.id() && self.frame() == other.frame()
239 }
240}
241
242impl AsRef<Texture> for Texture {
243 fn as_ref(&self) -> &Texture {
244 self
245 }
246}
247
248#[derive(Debug, Clone, Copy, Eq, PartialEq)]
250pub enum TextureFormat {
251 SRgba8,
252 Rgba32,
253 R8,
254 R8Uint,
255 R16Uint,
256 R32Float,
257 R32Uint,
258 Depth16,
259 Rgba32Float,
260}
261
262#[derive(Debug, Clone, Copy, Eq, PartialEq)]
263pub enum TextureFilter {
264 Linear,
265 Nearest,
266}
267
268#[derive(Debug, Clone, Copy, Eq, PartialEq)]
269pub enum TextureWrap {
270 Clamp,
271 Repeat,
272}
273
274enum TextureKind<'a> {
275 Image(&'a [u8]),
276 Bytes(&'a [u8]),
277 EmptyBuffer,
278}
279
280pub enum TextureSourceKind {
281 Empty,
282 Image(Vec<u8>),
283 Bytes(Vec<u8>),
284 Raw(Box<dyn TextureSource>),
285}
286
287pub enum TextureUpdaterSourceKind<'a> {
288 Bytes(&'a [u8]),
289 Raw(Box<dyn TextureSource>),
290}
291
292impl Debug for TextureUpdaterSourceKind<'_> {
293 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
294 write!(
295 f,
296 "{}",
297 match self {
298 TextureUpdaterSourceKind::Bytes(bytes) => format!("Bytes({bytes:?})"),
299 TextureUpdaterSourceKind::Raw(_) => "Raw(dyn TextureSource)".to_string(), }
301 )
302 }
303}
304
305pub struct TextureBuilder<'a, 'b> {
306 device: &'a mut Device,
307 info: TextureInfo,
308 kind: Option<TextureKind<'b>>,
309 source: Option<TextureSourceKind>,
310}
311
312impl<'a, 'b> TextureBuilder<'a, 'b> {
313 pub fn new(device: &'a mut Device) -> Self {
314 Self {
315 device,
316 info: Default::default(),
317 kind: None,
318 source: None,
319 }
320 }
321
322 pub fn from_source<S: TextureSource + 'static>(mut self, source: S) -> Self {
325 self.kind = None;
326 self.source = Some(TextureSourceKind::Raw(Box::new(source)));
327 self
328 }
329
330 pub fn from_image(mut self, bytes: &'b [u8]) -> Self {
332 self.source = None;
333 self.kind = Some(TextureKind::Image(bytes)); self
335 }
336
337 pub fn from_bytes(mut self, bytes: &'b [u8], width: u32, height: u32) -> Self {
339 self.source = None;
340 self.kind = Some(TextureKind::Bytes(bytes));
341 self.info.width = width;
342 self.info.height = height;
343 self
344 }
345
346 pub fn from_empty_buffer(mut self, width: u32, height: u32) -> Self {
348 self.source = None;
349 self.kind = Some(TextureKind::EmptyBuffer);
350 self.with_size(width, height)
351 }
352
353 pub fn with_size(mut self, width: u32, height: u32) -> Self {
355 self.info.width = width;
356 self.info.height = height;
357 self
358 }
359
360 pub fn with_depth(mut self) -> Self {
362 self.info.depth = true;
363 self
364 }
365
366 pub fn with_format(mut self, format: TextureFormat) -> Self {
368 self.info.format = format;
369 self
370 }
371
372 pub fn with_filter(mut self, min: TextureFilter, mag: TextureFilter) -> Self {
374 self.info.min_filter = min;
375 self.info.mag_filter = mag;
376 self
377 }
378
379 pub fn with_wrap(mut self, x: TextureWrap, y: TextureWrap) -> Self {
381 self.info.wrap_x = x;
382 self.info.wrap_y = y;
383 self
384 }
385
386 pub fn with_premultiplied_alpha(mut self) -> Self {
388 self.info.premultiplied_alpha = true;
389 self
390 }
391
392 pub fn with_mipmaps(mut self, enable: bool) -> Self {
394 if enable {
395 self.info.mipmap_filter = Some(TextureFilter::Linear);
396 } else {
397 self.info.mipmap_filter = None;
398 }
399 self
400 }
401
402 pub fn with_mipmap_filter(mut self, filter: TextureFilter) -> Self {
404 self.info.mipmap_filter = Some(filter);
405 self
406 }
407
408 pub fn build(self) -> Result<Texture, String> {
409 let TextureBuilder {
410 info,
411 device,
412 kind,
413 mut source,
414 } = self;
415
416 match kind {
417 Some(TextureKind::Image(bytes)) => {
418 source = Some(TextureSourceKind::Image(bytes.to_vec()));
419 }
420 Some(TextureKind::Bytes(bytes)) => {
421 let size = (info.width * info.height * (info.bytes_per_pixel() as u32)) as usize;
422 if bytes.len() != size {
423 return Err(format!(
424 "Texture type {:?} with {} bytes, when it should be {} (width: {} * height: {} * bytes: {})",
425 info.format,
426 bytes.len(),
427 size,
428 info.width,
429 info.height,
430 info.bytes_per_pixel()
431 ));
432 }
433
434 source = Some(TextureSourceKind::Bytes(bytes.to_vec()));
435 }
436 Some(TextureKind::EmptyBuffer) => {
437 let size = info.width * info.height * (info.bytes_per_pixel() as u32);
438 source = Some(TextureSourceKind::Bytes(vec![0; size as _]));
439 }
440 None => {}
441 }
442
443 let s = source.unwrap_or(TextureSourceKind::Empty);
444 device.inner_create_texture(s, info)
445 }
446}
447
448pub struct TextureReader<'a> {
449 device: &'a mut Device,
450 texture: &'a Texture,
451 x_offset: u32,
452 y_offset: u32,
453 width: u32,
454 height: u32,
455 format: TextureFormat,
456}
457
458impl<'a> TextureReader<'a> {
459 pub fn new(device: &'a mut Device, texture: &'a Texture) -> Self {
460 let rect = *texture.frame();
461 let x_offset = rect.x as _;
462 let y_offset = rect.y as _;
463 let width = rect.width as _;
464 let height = rect.height as _;
465 let format = texture.format;
466 Self {
467 device,
468 texture,
469 x_offset,
470 y_offset,
471 width,
472 height,
473 format,
474 }
475 }
476
477 pub fn with_x_offset(mut self, offset: u32) -> Self {
479 self.x_offset = offset;
480 self
481 }
482
483 pub fn with_y_offset(mut self, offset: u32) -> Self {
485 self.y_offset = offset;
486 self
487 }
488
489 pub fn with_width(mut self, width: u32) -> Self {
491 self.width = width;
492 self
493 }
494
495 pub fn with_height(mut self, height: u32) -> Self {
497 self.height = height;
498 self
499 }
500
501 pub fn read_to(self, bytes: &mut [u8]) -> Result<(), String> {
502 let Self {
503 device,
504 texture,
505 x_offset,
506 y_offset,
507 width,
508 height,
509 format,
510 } = self;
511
512 let info = TextureRead {
513 x_offset,
514 y_offset,
515 width,
516 height,
517 format,
518 };
519
520 device.inner_read_pixels(texture, bytes, &info)
521 }
522}
523
524pub struct TextureUpdater<'a> {
525 device: &'a mut Device,
526 texture: &'a mut Texture,
527 x_offset: u32,
528 y_offset: u32,
529 width: u32,
530 height: u32,
531 format: TextureFormat,
532 source: Option<TextureUpdaterSourceKind<'a>>,
533}
534
535impl<'a> TextureUpdater<'a> {
536 pub fn new(device: &'a mut Device, texture: &'a mut Texture) -> Self {
537 let x_offset = texture.frame.x as _;
538 let y_offset = texture.frame.y as _;
539 let width = texture.frame.width as _;
540 let height = texture.frame.height as _;
541 let format = texture.format;
542
543 Self {
544 device,
545 texture,
546 x_offset,
547 y_offset,
548 width,
549 height,
550 format,
551 source: None,
552 }
553 }
554
555 pub fn with_x_offset(mut self, offset: u32) -> Self {
557 self.x_offset = offset;
558 self
559 }
560
561 pub fn with_y_offset(mut self, offset: u32) -> Self {
563 self.y_offset = offset;
564 self
565 }
566
567 pub fn with_width(mut self, width: u32) -> Self {
569 self.width = width;
570 self
571 }
572
573 pub fn with_height(mut self, height: u32) -> Self {
575 self.height = height;
576 self
577 }
578
579 pub fn with_source<S: TextureSource + 'static>(mut self, source: S) -> Self {
580 self.source = Some(TextureUpdaterSourceKind::Raw(Box::new(source)));
581 self
582 }
583
584 pub fn with_data(mut self, bytes: &'a [u8]) -> Self {
585 self.source = Some(TextureUpdaterSourceKind::Bytes(bytes));
586 self
587 }
588
589 pub fn update(self) -> Result<(), String> {
590 let Self {
591 device,
592 texture,
593 x_offset,
594 y_offset,
595 width,
596 height,
597 format,
598 source,
599 } = self;
600
601 let source =
602 source.ok_or_else(|| "You need to provide bytes to update a texture".to_string())?;
603
604 let info = TextureUpdate {
605 x_offset,
606 y_offset,
607 width,
608 height,
609 format,
610 };
611
612 device.inner_update_texture(texture, source, info)
613 }
614}