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}
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 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#[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 pub const fn empty() -> Image {
90 Image {
91 width: 0,
92 height: 0,
93 bytes: vec![],
94 }
95 }
96
97 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 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 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 pub const fn width(&self) -> usize {
161 self.width as usize
162 }
163
164 pub const fn height(&self) -> usize {
166 self.height as usize
167 }
168
169 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 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 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 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 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 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 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 pub fn export_png(&self, path: &str) {
307 let mut bytes = vec![0; self.width as usize * self.height as usize * 4];
308
309 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
328pub 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
335pub 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 pub sample_count: i32,
354
355 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 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
390pub fn render_target(width: u32, height: u32) -> RenderTarget {
392 render_target_ex(width, height, RenderTargetParams::default())
393}
394
395pub 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 pub source: Option<Rect>,
472
473 pub rotation: f32,
475
476 pub flip_x: bool,
478
479 pub flip_y: bool,
481
482 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
603pub 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#[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 pub fn empty() -> Texture2D {
667 let ctx = get_context();
668
669 Texture2D::unmanaged(ctx.gl.white_texture)
670 }
671
672 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 pub fn from_image(image: &Image) -> Texture2D {
707 Texture2D::from_rgba8(image.width, image.height, &image.bytes)
708 }
709
710 pub const fn from_miniquad_texture(texture: miniquad::TextureId) -> Texture2D {
713 Texture2D {
714 texture: TextureHandle::Unmanaged(texture),
715 }
716 }
717
718 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 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 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 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 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 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 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 pub fn raw_miniquad_id(&self) -> miniquad::TextureId {
835 let ctx = get_context();
836
837 ctx.raw_miniquad_id(&self.texture)
838 }
839
840 #[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 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
917pub 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)]
938pub 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}