1use 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 Managed(Arc<TextureSlotGuarded>),
23 ManagedWeak(TextureSlotId),
24 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 pub const fn len(&self) -> usize {
57 self.textures.len()
58 }
59 pub fn garbage_collect(&mut self, ctx: &mut miniquad::Context) {
60 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#[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 pub const fn empty() -> Image {
100 Image {
101 width: 0,
102 height: 0,
103 bytes: vec![],
104 }
105 }
106
107 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 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 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 pub const fn width(&self) -> usize {
171 self.width as usize
172 }
173
174 pub const fn height(&self) -> usize {
176 self.height as usize
177 }
178
179 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 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 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 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 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 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 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 pub fn export_png(&self, path: &str) {
319 let mut bytes = vec![0; self.width as usize * self.height as usize * 4];
320
321 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
340pub 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
347pub 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 pub sample_count: i32,
366
367 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 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 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
404pub fn render_target(width: u32, height: u32) -> RenderTarget {
406 render_target_ex(width, height, RenderTargetParams::default())
407}
408
409pub 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 pub source: Option<Rect>,
486
487 pub rotation: f32,
489
490 pub flip_x: bool,
492
493 pub flip_y: bool,
495
496 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
617pub 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#[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 pub fn empty() -> Texture2D {
681 let ctx = get_context();
682
683 Texture2D::unmanaged(ctx.gl.white_texture)
684 }
685
686 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 pub fn from_image(image: &Image) -> Texture2D {
721 Texture2D::from_rgba8(image.width, image.height, &image.bytes)
722 }
723
724 pub const fn from_miniquad_texture(texture: miniquad::TextureId) -> Texture2D {
727 Texture2D {
728 texture: TextureHandle::Unmanaged(texture),
729 }
730 }
731
732 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 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 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 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 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 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 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 pub fn raw_miniquad_id(&self) -> miniquad::TextureId {
849 let ctx = get_context();
850
851 ctx.raw_miniquad_id(&self.texture)
852 }
853
854 #[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 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
931pub 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)]
952pub 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}