macroquad/
texture.rs

1//! Loading and rendering textures. Also render textures, per-pixel image manipulations.
2
3use crate::{
4    color::Color, file::load_file, get_context, get_quad_context, math::Rect,
5    text::atlas::SpriteKey, Error,
6};
7
8pub use crate::quad_gl::FilterMode;
9use crate::quad_gl::{DrawMode, Vertex};
10use glam::{vec2, Vec2};
11use slotmap::{TextureIdSlotMap, TextureSlotId};
12use std::sync::Arc;
13
14mod slotmap;
15
16#[derive(Debug, Clone, PartialEq)]
17pub(crate) struct TextureSlotGuarded(pub TextureSlotId);
18
19#[derive(Debug, Clone, PartialEq)]
20pub(crate) enum TextureHandle {
21    // texture that belongs to macroquad and follows normal garbage collection rules
22    Managed(Arc<TextureSlotGuarded>),
23    ManagedWeak(TextureSlotId),
24    // raw miniquad texture, there are no guarantees that this texture is not yet deleted
25    Unmanaged(miniquad::TextureId),
26}
27
28pub(crate) struct TexturesContext {
29    textures: TextureIdSlotMap,
30    removed: Vec<TextureSlotId>,
31}
32impl TexturesContext {
33    pub fn new() -> TexturesContext {
34        TexturesContext {
35            textures: TextureIdSlotMap::new(),
36            removed: Vec::with_capacity(200),
37        }
38    }
39    fn schedule_removed(&mut self, texture: TextureSlotId) {
40        self.removed.push(texture);
41    }
42    fn store_texture(&mut self, texture: miniquad::TextureId) -> TextureHandle {
43        TextureHandle::Managed(Arc::new(TextureSlotGuarded(self.textures.insert(texture))))
44    }
45    pub fn texture(&self, texture: TextureSlotId) -> Option<miniquad::TextureId> {
46        self.textures.get(texture)
47    }
48    // fn remove(&mut self, texture: TextureSlotId) {
49    //     self.textures.remove(texture);
50    // }
51    pub const fn len(&self) -> usize {
52        self.textures.len()
53    }
54    pub fn garbage_collect(&mut self, ctx: &mut miniquad::Context) {
55        for texture in self.removed.drain(0..) {
56            if let Some(texture) = self.textures.get(texture) {
57                ctx.delete_texture(texture);
58            }
59            self.textures.remove(texture);
60        }
61    }
62}
63
64/// Image, data stored in CPU memory
65#[derive(Clone)]
66pub struct Image {
67    pub bytes: Vec<u8>,
68    pub width: u16,
69    pub height: u16,
70}
71
72impl std::fmt::Debug for Image {
73    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74        f.debug_struct("Image")
75            .field("width", &self.width)
76            .field("height", &self.height)
77            .field("bytes.len()", &self.bytes.len())
78            .finish()
79    }
80}
81
82impl Image {
83    /// Creates an empty Image.
84    ///
85    /// ```
86    /// # use macroquad::prelude::*;
87    /// let image = Image::empty();
88    /// ```
89    pub const fn empty() -> Image {
90        Image {
91            width: 0,
92            height: 0,
93            bytes: vec![],
94        }
95    }
96
97    /// Creates an Image from a slice of bytes that contains an encoded image.
98    ///
99    /// If `format` is None, it will make an educated guess on the
100    /// [ImageFormat][image::ImageFormat].
101    ///
102    /// # Example
103    ///
104    /// ```
105    /// # use macroquad::prelude::*;
106    /// let icon = Image::from_file_with_format(
107    ///     include_bytes!("../examples/rust.png"),
108    ///     Some(ImageFormat::Png),
109    ///     );
110    /// ```
111    pub fn from_file_with_format(
112        bytes: &[u8],
113        format: Option<image::ImageFormat>,
114    ) -> Result<Image, Error> {
115        let img = if let Some(fmt) = format {
116            image::load_from_memory_with_format(bytes, fmt)?.to_rgba8()
117        } else {
118            image::load_from_memory(bytes)?.to_rgba8()
119        };
120        let width = img.width() as u16;
121        let height = img.height() as u16;
122        let bytes = img.into_raw();
123
124        Ok(Image {
125            width,
126            height,
127            bytes,
128        })
129    }
130
131    /// Creates an Image filled with the provided [Color].
132    pub fn gen_image_color(width: u16, height: u16, color: Color) -> Image {
133        let mut bytes = vec![0; width as usize * height as usize * 4];
134        for i in 0..width as usize * height as usize {
135            bytes[i * 4 + 0] = (color.r * 255.) as u8;
136            bytes[i * 4 + 1] = (color.g * 255.) as u8;
137            bytes[i * 4 + 2] = (color.b * 255.) as u8;
138            bytes[i * 4 + 3] = (color.a * 255.) as u8;
139        }
140        Image {
141            width,
142            height,
143            bytes,
144        }
145    }
146
147    /// Updates this image from a slice of [Color]s.
148    pub fn update(&mut self, colors: &[Color]) {
149        assert!(self.width as usize * self.height as usize == colors.len());
150
151        for i in 0..colors.len() {
152            self.bytes[i * 4] = (colors[i].r * 255.) as u8;
153            self.bytes[i * 4 + 1] = (colors[i].g * 255.) as u8;
154            self.bytes[i * 4 + 2] = (colors[i].b * 255.) as u8;
155            self.bytes[i * 4 + 3] = (colors[i].a * 255.) as u8;
156        }
157    }
158
159    /// Returns the width of this image.
160    pub const fn width(&self) -> usize {
161        self.width as usize
162    }
163
164    /// Returns the height of this image.
165    pub const fn height(&self) -> usize {
166        self.height as usize
167    }
168
169    /// Returns this image's data as a slice of 4-byte arrays.
170    pub fn get_image_data(&self) -> &[[u8; 4]] {
171        use std::slice;
172
173        unsafe {
174            slice::from_raw_parts(
175                self.bytes.as_ptr() as *const [u8; 4],
176                self.width as usize * self.height as usize,
177            )
178        }
179    }
180
181    /// Returns this image's data as a mutable slice of 4-byte arrays.
182    pub fn get_image_data_mut(&mut self) -> &mut [[u8; 4]] {
183        use std::slice;
184
185        unsafe {
186            slice::from_raw_parts_mut(
187                self.bytes.as_mut_ptr() as *mut [u8; 4],
188                self.width as usize * self.height as usize,
189            )
190        }
191    }
192
193    /// Modifies a pixel [Color] in this image.
194    pub fn set_pixel(&mut self, x: u32, y: u32, color: Color) {
195        assert!(x < self.width as u32);
196        assert!(y < self.height as u32);
197
198        let width = self.width;
199
200        self.get_image_data_mut()[(y * width as u32 + x) as usize] = color.into();
201    }
202
203    /// Returns a pixel [Color] from this image.
204    pub fn get_pixel(&self, x: u32, y: u32) -> Color {
205        self.get_image_data()[(y * self.width as u32 + x) as usize].into()
206    }
207
208    /// Returns an Image from a rect inside this image.
209    pub fn sub_image(&self, rect: Rect) -> Image {
210        let width = rect.w as usize;
211        let height = rect.h as usize;
212        let mut bytes = vec![0; width * height * 4];
213
214        let x = rect.x as usize;
215        let y = rect.y as usize;
216        let mut n = 0;
217        for y in y..y + height {
218            for x in x..x + width {
219                bytes[n] = self.bytes[y * self.width as usize * 4 + x * 4 + 0];
220                bytes[n + 1] = self.bytes[y * self.width as usize * 4 + x * 4 + 1];
221                bytes[n + 2] = self.bytes[y * self.width as usize * 4 + x * 4 + 2];
222                bytes[n + 3] = self.bytes[y * self.width as usize * 4 + x * 4 + 3];
223                n += 4;
224            }
225        }
226        Image {
227            width: width as u16,
228            height: height as u16,
229            bytes,
230        }
231    }
232
233    /// Blends this image with another image (of identical dimensions)
234    /// Inspired by  OpenCV saturated blending
235    pub fn blend(&mut self, other: &Image) {
236        assert!(
237            self.width as usize * self.height as usize
238                == other.width as usize * other.height as usize
239        );
240
241        for i in 0..self.bytes.len() / 4 {
242            let c1: Color = Color {
243                r: self.bytes[i * 4] as f32 / 255.,
244                g: self.bytes[i * 4 + 1] as f32 / 255.,
245                b: self.bytes[i * 4 + 2] as f32 / 255.,
246                a: self.bytes[i * 4 + 3] as f32 / 255.,
247            };
248            let c2: Color = Color {
249                r: other.bytes[i * 4] as f32 / 255.,
250                g: other.bytes[i * 4 + 1] as f32 / 255.,
251                b: other.bytes[i * 4 + 2] as f32 / 255.,
252                a: other.bytes[i * 4 + 3] as f32 / 255.,
253            };
254            let new_color: Color = Color {
255                r: f32::min(c1.r * c1.a + c2.r * c2.a, 1.),
256                g: f32::min(c1.g * c1.a + c2.g * c2.a, 1.),
257                b: f32::min(c1.b * c1.a + c2.b * c2.a, 1.),
258                a: f32::max(c1.a, c2.a) + (1. - f32::max(c1.a, c2.a)) * f32::min(c1.a, c2.a),
259            };
260            self.bytes[i * 4] = (new_color.r * 255.) as u8;
261            self.bytes[i * 4 + 1] = (new_color.g * 255.) as u8;
262            self.bytes[i * 4 + 2] = (new_color.b * 255.) as u8;
263            self.bytes[i * 4 + 3] = (new_color.a * 255.) as u8;
264        }
265    }
266
267    /// Overlays an image on top of this one.
268    /// Slightly different from blending two images,
269    /// overlaying a completely transparent image has no effect
270    /// on the original image, though blending them would.
271    pub fn overlay(&mut self, other: &Image) {
272        assert!(
273            self.width as usize * self.height as usize
274                == other.width as usize * other.height as usize
275        );
276
277        for i in 0..self.bytes.len() / 4 {
278            let c1: Color = Color {
279                r: self.bytes[i * 4] as f32 / 255.,
280                g: self.bytes[i * 4 + 1] as f32 / 255.,
281                b: self.bytes[i * 4 + 2] as f32 / 255.,
282                a: self.bytes[i * 4 + 3] as f32 / 255.,
283            };
284            let c2: Color = Color {
285                r: other.bytes[i * 4] as f32 / 255.,
286                g: other.bytes[i * 4 + 1] as f32 / 255.,
287                b: other.bytes[i * 4 + 2] as f32 / 255.,
288                a: other.bytes[i * 4 + 3] as f32 / 255.,
289            };
290            let new_color: Color = Color {
291                r: f32::min(c1.r * (1. - c2.a) + c2.r * c2.a, 1.),
292                g: f32::min(c1.g * (1. - c2.a) + c2.g * c2.a, 1.),
293                b: f32::min(c1.b * (1. - c2.a) + c2.b * c2.a, 1.),
294                a: f32::min(c1.a + c2.a, 1.),
295            };
296
297            self.bytes[i * 4] = (new_color.r * 255.) as u8;
298            self.bytes[i * 4 + 1] = (new_color.g * 255.) as u8;
299            self.bytes[i * 4 + 2] = (new_color.b * 255.) as u8;
300            self.bytes[i * 4 + 3] = (new_color.a * 255.) as u8;
301        }
302    }
303
304    /// Saves this image as a PNG file.
305    /// This method is not supported on web and will panic.
306    pub fn export_png(&self, path: &str) {
307        let mut bytes = vec![0; self.width as usize * self.height as usize * 4];
308
309        // flip the image before saving
310        for y in 0..self.height as usize {
311            for x in 0..self.width as usize * 4 {
312                bytes[y * self.width as usize * 4 + x] =
313                    self.bytes[(self.height as usize - y - 1) * self.width as usize * 4 + x];
314            }
315        }
316
317        image::save_buffer(
318            path,
319            &bytes[..],
320            self.width as _,
321            self.height as _,
322            image::ColorType::Rgba8,
323        )
324        .unwrap();
325    }
326}
327
328/// Loads an [Image] from a file into CPU memory.
329pub async fn load_image(path: &str) -> Result<Image, Error> {
330    let bytes = load_file(path).await?;
331
332    Image::from_file_with_format(&bytes, None)
333}
334
335/// Loads a [Texture2D] from a file into GPU memory.
336pub async fn load_texture(path: &str) -> Result<Texture2D, Error> {
337    let bytes = load_file(path).await?;
338
339    Ok(Texture2D::from_file_with_format(&bytes[..], None))
340}
341
342#[derive(Debug, Clone)]
343pub struct RenderPass {
344    pub color_texture: Texture2D,
345    pub depth_texture: Option<Texture2D>,
346    pub(crate) render_pass: Arc<miniquad::RenderPass>,
347}
348
349#[derive(Debug, Clone)]
350pub struct RenderTargetParams {
351    /// 1 means no multi sampling.
352    /// Note that sample_count > 1 is not supported on GL2, GLES2 and WebGL1
353    pub sample_count: i32,
354
355    /// depth: true creates a depth render target attachment and allows
356    /// such a render target being used for a depth-testing cameras
357    pub depth: bool,
358}
359impl Default for RenderTargetParams {
360    fn default() -> RenderTargetParams {
361        RenderTargetParams {
362            sample_count: 1,
363            depth: false,
364        }
365    }
366}
367
368impl RenderPass {
369    /// Returns the miniquad handle for this render pass.
370    pub fn raw_miniquad_id(&self) -> miniquad::RenderPass {
371        *self.render_pass
372    }
373}
374
375impl Drop for RenderPass {
376    fn drop(&mut self) {
377        if Arc::strong_count(&self.render_pass) < 2 {
378            let context = get_quad_context();
379            context.delete_render_pass(*self.render_pass);
380        }
381    }
382}
383
384#[derive(Clone, Debug)]
385pub struct RenderTarget {
386    pub texture: Texture2D,
387    pub render_pass: RenderPass,
388}
389
390/// A shortcut to create a render target with sample_count: 1 and no depth buffer
391pub fn render_target(width: u32, height: u32) -> RenderTarget {
392    render_target_ex(width, height, RenderTargetParams::default())
393}
394
395/// A shortcut to create a render target with no depth buffer and `sample_count: 4`
396pub fn render_target_msaa(width: u32, height: u32) -> RenderTarget {
397    render_target_ex(
398        width,
399        height,
400        RenderTargetParams {
401            sample_count: 4,
402            ..Default::default()
403        },
404    )
405}
406
407pub fn render_target_ex(width: u32, height: u32, params: RenderTargetParams) -> RenderTarget {
408    let context = get_context();
409
410    let color_texture = get_quad_context().new_render_texture(miniquad::TextureParams {
411        width,
412        height,
413        sample_count: params.sample_count,
414        ..Default::default()
415    });
416    let depth_texture = if params.depth {
417        Some(
418            get_quad_context().new_render_texture(miniquad::TextureParams {
419                width,
420                height,
421                format: miniquad::TextureFormat::Depth,
422                sample_count: params.sample_count,
423                ..Default::default()
424            }),
425        )
426    } else {
427        None
428    };
429    let render_pass;
430    let texture;
431    if params.sample_count != 0 {
432        let color_resolve_texture =
433            get_quad_context().new_render_texture(miniquad::TextureParams {
434                width,
435                height,
436                ..Default::default()
437            });
438        render_pass = get_quad_context().new_render_pass_mrt(
439            &[color_texture],
440            Some(&[color_resolve_texture]),
441            depth_texture,
442        );
443        texture = color_resolve_texture;
444    } else {
445        render_pass = get_quad_context().new_render_pass_mrt(&[color_texture], None, depth_texture);
446        texture = color_texture;
447    }
448
449    let texture = Texture2D {
450        texture: context.textures.store_texture(texture),
451    };
452
453    let render_pass = RenderPass {
454        color_texture: texture.clone(),
455        depth_texture: None,
456        render_pass: Arc::new(render_pass),
457    };
458    RenderTarget {
459        texture,
460        render_pass,
461    }
462}
463
464#[derive(Debug, Clone)]
465pub struct DrawTextureParams {
466    pub dest_size: Option<Vec2>,
467
468    /// Part of texture to draw. If None - draw the whole texture.
469    /// Good use example: drawing an image from texture atlas.
470    /// Is None by default
471    pub source: Option<Rect>,
472
473    /// Rotation in radians
474    pub rotation: f32,
475
476    /// Mirror on the X axis
477    pub flip_x: bool,
478
479    /// Mirror on the Y axis
480    pub flip_y: bool,
481
482    /// Rotate around this point.
483    /// When `None`, rotate around the texture's center.
484    /// When `Some`, the coordinates are in screen-space.
485    /// E.g. pivot (0,0) rotates around the top left corner of the screen, not of the
486    /// texture.
487    pub pivot: Option<Vec2>,
488}
489
490impl Default for DrawTextureParams {
491    fn default() -> DrawTextureParams {
492        DrawTextureParams {
493            dest_size: None,
494            source: None,
495            rotation: 0.,
496            pivot: None,
497            flip_x: false,
498            flip_y: false,
499        }
500    }
501}
502
503pub fn draw_texture(texture: &Texture2D, x: f32, y: f32, color: Color) {
504    draw_texture_ex(texture, x, y, color, Default::default());
505}
506
507pub fn draw_texture_ex(
508    texture: &Texture2D,
509    x: f32,
510    y: f32,
511    color: Color,
512    params: DrawTextureParams,
513) {
514    let context = get_context();
515
516    let [mut width, mut height] = texture.size().to_array();
517
518    let Rect {
519        x: mut sx,
520        y: mut sy,
521        w: mut sw,
522        h: mut sh,
523    } = params.source.unwrap_or(Rect {
524        x: 0.,
525        y: 0.,
526        w: width,
527        h: height,
528    });
529
530    let texture_opt = context
531        .texture_batcher
532        .get(texture)
533        .map(|(batched_texture, uv)| {
534            let [batched_width, batched_height] = batched_texture.size().to_array();
535            sx = ((sx / width) * uv.w + uv.x) * batched_width;
536            sy = ((sy / height) * uv.h + uv.y) * batched_height;
537            sw = (sw / width) * uv.w * batched_width;
538            sh = (sh / height) * uv.h * batched_height;
539
540            width = batched_width;
541            height = batched_height;
542
543            batched_texture
544        });
545    let texture = texture_opt.as_ref().unwrap_or(texture);
546
547    let (mut w, mut h) = match params.dest_size {
548        Some(dst) => (dst.x, dst.y),
549        _ => (sw, sh),
550    };
551    let mut x = x;
552    let mut y = y;
553    if params.flip_x {
554        x += w;
555        w = -w;
556    }
557    if params.flip_y {
558        y += h;
559        h = -h;
560    }
561
562    let pivot = params.pivot.unwrap_or(vec2(x + w / 2., y + h / 2.));
563    let m = pivot;
564    let p = [
565        vec2(x, y) - pivot,
566        vec2(x + w, y) - pivot,
567        vec2(x + w, y + h) - pivot,
568        vec2(x, y + h) - pivot,
569    ];
570    let r = params.rotation;
571    let p = [
572        vec2(
573            p[0].x * r.cos() - p[0].y * r.sin(),
574            p[0].x * r.sin() + p[0].y * r.cos(),
575        ) + m,
576        vec2(
577            p[1].x * r.cos() - p[1].y * r.sin(),
578            p[1].x * r.sin() + p[1].y * r.cos(),
579        ) + m,
580        vec2(
581            p[2].x * r.cos() - p[2].y * r.sin(),
582            p[2].x * r.sin() + p[2].y * r.cos(),
583        ) + m,
584        vec2(
585            p[3].x * r.cos() - p[3].y * r.sin(),
586            p[3].x * r.sin() + p[3].y * r.cos(),
587        ) + m,
588    ];
589    #[rustfmt::skip]
590    let vertices = [
591        Vertex::new(p[0].x, p[0].y, 0.,  sx      /width,  sy      /height, color),
592        Vertex::new(p[1].x, p[1].y, 0., (sx + sw)/width,  sy      /height, color),
593        Vertex::new(p[2].x, p[2].y, 0., (sx + sw)/width, (sy + sh)/height, color),
594        Vertex::new(p[3].x, p[3].y, 0.,  sx      /width, (sy + sh)/height, color),
595    ];
596    let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
597
598    context.gl.texture(Some(texture));
599    context.gl.draw_mode(DrawMode::Triangles);
600    context.gl.geometry(&vertices, &indices);
601}
602
603/// Get pixel data from screen buffer and return an Image (screenshot)
604pub fn get_screen_data() -> Image {
605    unsafe {
606        crate::window::get_internal_gl().flush();
607    }
608
609    let context = get_context();
610
611    let texture_id = get_quad_context().new_render_texture(miniquad::TextureParams {
612        width: context.screen_width as _,
613        height: context.screen_height as _,
614        ..Default::default()
615    });
616
617    let texture = Texture2D {
618        texture: context.textures.store_texture(texture_id),
619    };
620
621    texture.grab_screen();
622
623    texture.get_texture_data()
624}
625
626/// Texture, data stored in GPU memory
627#[derive(Clone, Debug, PartialEq)]
628pub struct Texture2D {
629    pub(crate) texture: TextureHandle,
630}
631
632impl Drop for TextureSlotGuarded {
633    fn drop(&mut self) {
634        let ctx = get_context();
635        ctx.textures.schedule_removed(self.0);
636    }
637}
638
639impl Texture2D {
640    pub fn weak_clone(&self) -> Texture2D {
641        match &self.texture {
642            TextureHandle::Unmanaged(id) => Texture2D::unmanaged(*id),
643            TextureHandle::Managed(t) => Texture2D {
644                texture: TextureHandle::ManagedWeak(t.0),
645            },
646            TextureHandle::ManagedWeak(t) => Texture2D {
647                texture: TextureHandle::ManagedWeak(*t),
648            },
649        }
650    }
651    pub(crate) const fn unmanaged(texture: miniquad::TextureId) -> Texture2D {
652        Texture2D {
653            texture: TextureHandle::Unmanaged(texture),
654        }
655    }
656    /// Creates an empty Texture2D.
657    ///
658    /// # Example
659    /// ```
660    /// # use macroquad::prelude::*;
661    /// # #[macroquad::main("test")]
662    /// # async fn main() {
663    /// let texture = Texture2D::empty();
664    /// # }
665    /// ```
666    pub fn empty() -> Texture2D {
667        let ctx = get_context();
668
669        Texture2D::unmanaged(ctx.gl.white_texture)
670    }
671
672    /// Creates a Texture2D from a slice of bytes that contains an encoded image.
673    ///
674    /// If `format` is None, it will make an educated guess on the
675    /// [ImageFormat][image::ImageFormat].
676    ///
677    /// # Example
678    /// ```
679    /// # use macroquad::prelude::*;
680    /// # #[macroquad::main("test")]
681    /// # async fn main() {
682    /// let texture = Texture2D::from_file_with_format(
683    ///     include_bytes!("../examples/rust.png"),
684    ///     None,
685    ///     );
686    /// # }
687    /// ```
688    pub fn from_file_with_format(bytes: &[u8], format: Option<image::ImageFormat>) -> Texture2D {
689        let img = if let Some(fmt) = format {
690            image::load_from_memory_with_format(bytes, fmt)
691                .unwrap_or_else(|e| panic!("{}", e))
692                .to_rgba8()
693        } else {
694            image::load_from_memory(bytes)
695                .unwrap_or_else(|e| panic!("{}", e))
696                .to_rgba8()
697        };
698        let width = img.width() as u16;
699        let height = img.height() as u16;
700        let bytes = img.into_raw();
701
702        Self::from_rgba8(width, height, &bytes)
703    }
704
705    /// Creates a Texture2D from an [Image].
706    pub fn from_image(image: &Image) -> Texture2D {
707        Texture2D::from_rgba8(image.width, image.height, &image.bytes)
708    }
709
710    /// Creates a Texture2D from a miniquad
711    /// [Texture](https://docs.rs/miniquad/0.3.0-alpha/miniquad/graphics/struct.Texture.html)
712    pub const fn from_miniquad_texture(texture: miniquad::TextureId) -> Texture2D {
713        Texture2D {
714            texture: TextureHandle::Unmanaged(texture),
715        }
716    }
717
718    /// Creates a Texture2D from a slice of bytes in an R,G,B,A sequence,
719    /// with the given width and height.
720    ///
721    /// # Example
722    ///
723    /// ```
724    /// # use macroquad::prelude::*;
725    /// # #[macroquad::main("test")]
726    /// # async fn main() {
727    /// // Create a 2x2 texture from a byte slice with 4 rgba pixels
728    /// let bytes: Vec<u8> = vec![255, 0, 0, 192, 0, 255, 0, 192, 0, 0, 255, 192, 255, 255, 255, 192];
729    /// let texture = Texture2D::from_rgba8(2, 2, &bytes);
730    /// # }
731    /// ```
732    pub fn from_rgba8(width: u16, height: u16, bytes: &[u8]) -> Texture2D {
733        let texture = get_quad_context().new_texture_from_rgba8(width, height, bytes);
734        let ctx = get_context();
735        let texture = ctx.textures.store_texture(texture);
736        let texture = Texture2D { texture };
737        texture.set_filter(ctx.default_filter_mode);
738
739        ctx.texture_batcher.add_unbatched(&texture);
740
741        texture
742    }
743
744    /// Uploads [Image] data to this texture.
745    pub fn update(&self, image: &Image) {
746        let ctx = get_quad_context();
747        let (width, height) = ctx.texture_size(self.raw_miniquad_id());
748
749        assert_eq!(width, image.width as u32);
750        assert_eq!(height, image.height as u32);
751
752        ctx.texture_update(self.raw_miniquad_id(), &image.bytes);
753    }
754
755    // Updates the texture from an array of bytes.
756    pub fn update_from_bytes(&self, width: u32, height: u32, bytes: &[u8]) {
757        let ctx = get_quad_context();
758        let (texture_width, texture_height) = ctx.texture_size(self.raw_miniquad_id());
759
760        assert_eq!(texture_width, width);
761        assert_eq!(texture_height, height);
762
763        ctx.texture_update(self.raw_miniquad_id(), bytes);
764    }
765
766    /// Uploads [Image] data to part of this texture.
767    pub fn update_part(
768        &self,
769        image: &Image,
770        x_offset: i32,
771        y_offset: i32,
772        width: i32,
773        height: i32,
774    ) {
775        let ctx = get_quad_context();
776
777        ctx.texture_update_part(
778            self.raw_miniquad_id(),
779            x_offset,
780            y_offset,
781            width,
782            height,
783            &image.bytes,
784        );
785    }
786
787    /// Returns the width of this texture.
788    pub fn width(&self) -> f32 {
789        let ctx = get_quad_context();
790        let (width, _) = ctx.texture_size(self.raw_miniquad_id());
791
792        width as f32
793    }
794
795    /// Returns the height of this texture.
796    pub fn height(&self) -> f32 {
797        let ctx = get_quad_context();
798        let (_, height) = ctx.texture_size(self.raw_miniquad_id());
799
800        height as f32
801    }
802
803    pub fn size(&self) -> Vec2 {
804        let ctx = get_quad_context();
805        let (width, height) = ctx.texture_size(self.raw_miniquad_id());
806
807        vec2(width as f32, height as f32)
808    }
809
810    /// Sets the [FilterMode] of this texture.
811    ///
812    /// Use Nearest if you need integer-ratio scaling for pixel art, for example.
813    ///
814    /// # Example
815    /// ```
816    /// # use macroquad::prelude::*;
817    /// # #[macroquad::main("test")]
818    /// # async fn main() {
819    /// let texture = Texture2D::empty();
820    /// texture.set_filter(FilterMode::Linear);
821    /// # }
822    /// ```
823    pub fn set_filter(&self, filter_mode: FilterMode) {
824        let ctx = get_quad_context();
825
826        ctx.texture_set_filter(
827            self.raw_miniquad_id(),
828            filter_mode,
829            miniquad::MipmapFilterMode::None,
830        );
831    }
832
833    /// Returns the handle for this texture.
834    pub fn raw_miniquad_id(&self) -> miniquad::TextureId {
835        let ctx = get_context();
836
837        ctx.raw_miniquad_id(&self.texture)
838    }
839
840    /// Updates this texture from the screen.
841    #[allow(unreachable_patterns)]
842    pub fn grab_screen(&self) {
843        use miniquad::*;
844        let texture = self.raw_miniquad_id();
845        let ctx = get_quad_context();
846        let params = ctx.texture_params(texture);
847        let raw_id = match unsafe { ctx.texture_raw_id(texture) } {
848            miniquad::RawId::OpenGl(id) => id,
849            _ => unimplemented!(),
850        };
851        let internal_format = match params.format {
852            TextureFormat::RGB8 => miniquad::gl::GL_RGB,
853            TextureFormat::RGBA8 => miniquad::gl::GL_RGBA,
854            TextureFormat::RGBA16F => miniquad::gl::GL_RGBA,
855            TextureFormat::Depth => miniquad::gl::GL_DEPTH_COMPONENT,
856            TextureFormat::Depth32 => miniquad::gl::GL_DEPTH_COMPONENT,
857            #[cfg(target_arch = "wasm32")]
858            TextureFormat::Alpha => miniquad::gl::GL_ALPHA,
859            #[cfg(not(target_arch = "wasm32"))]
860            TextureFormat::Alpha => miniquad::gl::GL_R8,
861        };
862        unsafe {
863            gl::glBindTexture(gl::GL_TEXTURE_2D, raw_id);
864            gl::glCopyTexImage2D(
865                gl::GL_TEXTURE_2D,
866                0,
867                internal_format,
868                0,
869                0,
870                params.width as _,
871                params.height as _,
872                0,
873            );
874        }
875    }
876
877    /// Returns an [Image] from the pixel data in this texture.
878    ///
879    /// This operation can be expensive.
880    pub fn get_texture_data(&self) -> Image {
881        let ctx = get_quad_context();
882        let (width, height) = ctx.texture_size(self.raw_miniquad_id());
883        let mut image = Image {
884            width: width as _,
885            height: height as _,
886            bytes: vec![0; width as usize * height as usize * 4],
887        };
888        ctx.texture_read_pixels(self.raw_miniquad_id(), &mut image.bytes);
889        image
890    }
891}
892
893pub(crate) struct Batcher {
894    unbatched: Vec<Texture2D>,
895    atlas: crate::text::atlas::Atlas,
896}
897
898impl Batcher {
899    pub fn new(ctx: &mut dyn miniquad::RenderingBackend) -> Batcher {
900        Batcher {
901            unbatched: vec![],
902            atlas: crate::text::atlas::Atlas::new(ctx, miniquad::FilterMode::Linear),
903        }
904    }
905
906    pub fn add_unbatched(&mut self, texture: &Texture2D) {
907        self.unbatched.push(texture.weak_clone());
908    }
909
910    pub fn get(&mut self, texture: &Texture2D) -> Option<(Texture2D, Rect)> {
911        let id = SpriteKey::Texture(texture.raw_miniquad_id());
912        let uv_rect = self.atlas.get_uv_rect(id)?;
913        Some((Texture2D::unmanaged(self.atlas.texture()), uv_rect))
914    }
915}
916
917/// Build an atlas out of all currently loaded texture
918/// Later on all draw_texture calls with texture available in the atlas will use
919/// the one from the atlas
920/// NOTE: the GPU memory and texture itself in Texture2D will still be allocated
921/// and Texture->Image conversions will work with Texture2D content, not the atlas
922pub fn build_textures_atlas() {
923    let context = get_context();
924
925    for texture in context.texture_batcher.unbatched.drain(0..) {
926        let sprite: Image = texture.get_texture_data();
927        let id = SpriteKey::Texture(texture.raw_miniquad_id());
928
929        context.texture_batcher.atlas.cache_sprite(id, sprite);
930    }
931
932    let texture = context.texture_batcher.atlas.texture();
933    let (w, h) = get_quad_context().texture_size(texture);
934    crate::telemetry::log_string(&format!("Atlas: {w} {h}"));
935}
936
937#[doc(hidden)]
938/// Macroquad do not have track of all loaded fonts.
939/// Fonts store their characters as ID's in the atlas.
940/// There fore resetting the atlas will render all fonts unusable.
941pub unsafe fn reset_textures_atlas() {
942    let context = get_context();
943    context.fonts_storage = crate::text::FontsStorage::new(&mut *context.quad_context);
944    context.texture_batcher = Batcher::new(&mut *context.quad_context);
945}
946
947pub fn set_default_filter_mode(filter: FilterMode) {
948    let context = get_context();
949
950    context.default_filter_mode = filter;
951    context.texture_batcher.atlas.set_filter(filter);
952}