Skip to main content

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