rust_pixel/render/
adapter.rs

1// RustPixel
2// copyright zipxing@hotmail.com 2022~2024
3
4#![allow(unused_variables)]
5use crate::{
6    event::Event,
7    render::{buffer::Buffer, sprite::Sprites},
8    util::{Rand, Rect},
9};
10#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
11use crate::{
12    render::adapter::gl::{color::GlColor, pixel::GlPixel, transform::GlTransform},
13    render::style::Color,
14    util::{ARect, PointF32, PointI32, PointU16},
15    LOGO_FRAME,
16};
17use std::any::Any;
18use std::sync::OnceLock;
19use std::time::Duration;
20// use log::info;
21
22/// opengl codes for sdl & web mode
23pub mod gl;
24
25/// sdl adapter
26#[cfg(all(feature = "sdl", not(target_arch = "wasm32")))]
27pub mod sdl;
28
29/// web adapter
30#[cfg(target_arch = "wasm32")]
31pub mod web;
32
33/// crossterm adapter
34#[cfg(not(any(
35    feature = "sdl",
36    target_os = "android",
37    target_os = "ios",
38    target_arch = "wasm32"
39)))]
40pub mod cross;
41
42/// symbols texture contains 8x8 blocks
43/// each block contain 16x16 symbols
44/// total 128 * 128 symbols
45#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
46pub const PIXEL_TEXTURE_FILE: &str = "assets/pix/symbols.png";
47
48/// symbol size is calculated based on the size of the texture
49pub fn init_sym_width(width: u32) -> f32 {
50    width as f32 / 128.0
51}
52pub fn init_sym_height(height: u32) -> f32 {
53    height as f32 / 128.0
54}
55pub static PIXEL_SYM_WIDTH: OnceLock<f32> = OnceLock::new();
56pub static PIXEL_SYM_HEIGHT: OnceLock<f32> = OnceLock::new();
57
58/// logo data
59pub const PIXEL_LOGO_WIDTH: usize = 27;
60pub const PIXEL_LOGO_HEIGHT: usize = 12;
61pub const PIXEL_LOGO: [u8; PIXEL_LOGO_WIDTH * PIXEL_LOGO_HEIGHT * 3] = [
62    32, 15, 1, 32, 202, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 239, 1, 32, 15, 1, 100, 239, 1, 32,
63    239, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0,
64    32, 15, 1, 32, 15, 1, 32, 15, 0, 32, 15, 1, 32, 15, 1, 32, 15, 0, 32, 15, 1, 32, 165, 1, 32,
65    165, 0, 32, 87, 1, 32, 15, 1, 18, 202, 1, 21, 202, 1, 19, 202, 1, 20, 202, 1, 32, 15, 1, 47,
66    239, 1, 47, 239, 1, 116, 239, 1, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15,
67    0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32,
68    15, 0, 32, 87, 1, 32, 165, 0, 32, 165, 1, 32, 240, 1, 100, 239, 1, 100, 239, 1, 100, 239, 1,
69    100, 239, 1, 100, 239, 1, 81, 49, 1, 47, 239, 1, 32, 239, 1, 100, 239, 1, 32, 239, 1, 32, 15,
70    1, 32, 239, 1, 100, 239, 1, 32, 239, 1, 100, 239, 1, 100, 239, 1, 100, 239, 1, 100, 239, 1,
71    100, 239, 1, 32, 239, 1, 100, 239, 1, 32, 239, 1, 32, 15, 0, 32, 87, 1, 32, 15, 0, 32, 165, 0,
72    47, 239, 1, 104, 239, 1, 104, 239, 1, 104, 239, 1, 104, 239, 1, 47, 239, 1, 47, 238, 1, 47,
73    238, 1, 47, 238, 1, 47, 239, 1, 100, 239, 1, 46, 239, 1, 47, 239, 1, 47, 239, 1, 47, 239, 1,
74    104, 239, 1, 104, 239, 1, 104, 239, 1, 104, 239, 1, 47, 239, 1, 47, 239, 1, 47, 239, 1, 84,
75    239, 1, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 160, 49, 1, 160, 49, 1, 160, 49, 1, 160,
76    49, 1, 81, 49, 1, 32, 15, 1, 160, 86, 1, 32, 15, 1, 160, 49, 1, 47, 236, 1, 47, 236, 1, 46,
77    234, 1, 160, 49, 1, 47, 239, 1, 81, 49, 1, 160, 49, 1, 160, 49, 1, 160, 49, 1, 160, 49, 1, 47,
78    239, 1, 160, 49, 1, 32, 15, 1, 84, 239, 1, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 87, 1, 160, 45,
79    1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 160, 45, 1, 32, 15, 1, 160, 45, 1, 32, 235, 1, 116, 235, 1,
80    160, 45, 1, 47, 236, 1, 160, 45, 1, 47, 239, 1, 116, 239, 1, 160, 45, 1, 46, 234, 1, 32, 15, 1,
81    46, 234, 1, 47, 239, 1, 116, 239, 1, 160, 45, 1, 32, 15, 1, 84, 239, 1, 32, 15, 0, 32, 15, 1,
82    32, 15, 0, 32, 197, 1, 160, 147, 1, 32, 239, 1, 100, 239, 1, 100, 239, 1, 160, 147, 1, 32, 15,
83    1, 160, 147, 1, 32, 235, 1, 116, 235, 1, 46, 235, 1, 81, 147, 1, 47, 239, 1, 47, 239, 1, 100,
84    239, 1, 160, 147, 1, 160, 147, 1, 160, 147, 1, 160, 147, 1, 47, 239, 1, 32, 15, 1, 160, 147, 1,
85    32, 239, 1, 84, 239, 1, 100, 239, 1, 100, 239, 1, 100, 239, 1, 32, 239, 1, 160, 147, 1, 47,
86    239, 1, 104, 239, 1, 104, 240, 1, 160, 147, 1, 32, 15, 1, 160, 147, 1, 32, 15, 1, 116, 235, 1,
87    160, 147, 1, 47, 239, 1, 160, 147, 1, 47, 239, 1, 47, 239, 1, 160, 147, 1, 104, 238, 1, 104,
88    238, 1, 104, 238, 1, 104, 238, 1, 47, 242, 1, 160, 147, 1, 47, 239, 1, 104, 239, 1, 104, 239,
89    1, 104, 239, 1, 47, 239, 1, 84, 239, 1, 160, 214, 1, 160, 214, 1, 160, 214, 1, 160, 214, 1, 81,
90    214, 1, 47, 239, 1, 81, 214, 1, 47, 239, 1, 160, 214, 1, 47, 239, 1, 32, 0, 1, 46, 235, 1, 160,
91    214, 1, 47, 236, 1, 81, 214, 1, 160, 214, 1, 160, 214, 1, 160, 214, 1, 160, 214, 1, 47, 242, 1,
92    81, 214, 1, 81, 214, 1, 81, 214, 1, 81, 214, 1, 81, 214, 1, 47, 239, 1, 32, 165, 1, 160, 214,
93    1, 103, 239, 1, 32, 242, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 0, 1,
94    32, 0, 1, 32, 87, 1, 32, 87, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15,
95    0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 165, 0, 32,
96    165, 0, 160, 214, 1, 103, 239, 1, 32, 242, 1, 32, 97, 1, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32,
97    15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 97, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0,
98    32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 97,
99    0, 32, 165, 0, 32, 15, 1, 90, 214, 1, 47, 239, 1, 32, 0, 1, 32, 15, 0, 32, 0, 1, 32, 0, 1, 32,
100    15, 0, 32, 15, 0, 32, 15, 0, 32, 15, 0, 32, 0, 1, 32, 15, 0, 32, 0, 1, 32, 0, 1, 32, 0, 1, 32,
101    0, 1, 32, 0, 1, 32, 0, 1, 32, 0, 1, 32, 15, 0, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32, 15, 1, 32,
102    15, 1, 32, 15, 1, 32, 15, 1,
103];
104
105/// pre-render cell...
106/// this struct used for opengl render and webgl render...
107#[derive(Clone, Copy, Default, Debug, PartialEq)]
108pub struct RenderCell {
109    pub fcolor: (f32, f32, f32, f32),
110    pub bcolor: Option<(f32, f32, f32, f32)>,
111    pub texsym: usize,
112    pub x: f32,
113    pub y: f32,
114    pub w: u32,
115    pub h: u32,
116    pub angle: f32,
117    pub cx: f32,
118    pub cy: f32,
119}
120
121pub struct AdapterBase {
122    pub game_name: String,
123    pub project_path: String,
124    pub title: String,
125    pub cell_w: u16,
126    pub cell_h: u16,
127    pub pixel_w: u32,
128    pub pixel_h: u32,
129    pub ratio_x: f32,
130    pub ratio_y: f32,
131    pub rd: Rand,
132    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
133    pub rflag: bool,
134    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
135    pub rbuf: Vec<RenderCell>,
136    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
137    pub gl: Option<glow::Context>,
138    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
139    pub gl_pixel: Option<GlPixel>,
140}
141
142impl AdapterBase {
143    pub fn new(gn: &str, project_path: &str) -> Self {
144        Self {
145            game_name: gn.to_string(),
146            project_path: project_path.to_string(),
147            title: "".to_string(),
148            cell_w: 0,
149            cell_h: 0,
150            pixel_w: 0,
151            pixel_h: 0,
152            ratio_x: 1.0,
153            ratio_y: 1.0,
154            rd: Rand::new(),
155            #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
156            rflag: true,
157            #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
158            rbuf: vec![],
159            #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
160            gl: None,
161            #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
162            gl_pixel: None,
163        }
164    }
165}
166
167pub trait Adapter {
168    fn init(&mut self, w: u16, h: u16, rx: f32, ry: f32, s: String);
169    fn reset(&mut self);
170    fn get_base(&mut self) -> &mut AdapterBase;
171    fn poll_event(&mut self, timeout: Duration, ev: &mut Vec<Event>) -> bool;
172
173    fn draw_all_to_screen(
174        &mut self,
175        current_buffer: &Buffer,
176        previous_buffer: &Buffer,
177        pixel_sprites: &mut Vec<Sprites>,
178        stage: u32,
179    ) -> Result<(), String>;
180
181    fn set_size(&mut self, w: u16, h: u16) -> &mut Self
182    where
183        Self: Sized,
184    {
185        let bs = self.get_base();
186        bs.cell_w = w;
187        bs.cell_h = h;
188        self
189    }
190
191    fn size(&mut self) -> Rect {
192        let bs = self.get_base();
193        Rect::new(0, 0, bs.cell_w, bs.cell_h)
194    }
195
196    fn set_ratiox(&mut self, rx: f32) -> &mut Self
197    where
198        Self: Sized,
199    {
200        let bs = self.get_base();
201        bs.ratio_x = rx;
202        self
203    }
204
205    fn set_ratioy(&mut self, ry: f32) -> &mut Self
206    where
207        Self: Sized,
208    {
209        let bs = self.get_base();
210        bs.ratio_y = ry;
211        self
212    }
213
214    fn set_pixel_size(&mut self) -> &mut Self
215    where
216        Self: Sized,
217    {
218        let bs = self.get_base();
219        bs.pixel_w = ((bs.cell_w + 2) as f32 * PIXEL_SYM_WIDTH.get().expect("lazylock init")
220            / bs.ratio_x) as u32;
221        bs.pixel_h = ((bs.cell_h + 2) as f32 * PIXEL_SYM_HEIGHT.get().expect("lazylock init")
222            / bs.ratio_y) as u32;
223        self
224    }
225
226    fn set_title(&mut self, s: String) -> &mut Self
227    where
228        Self: Sized,
229    {
230        let bs = self.get_base();
231        bs.title = s;
232        self
233    }
234
235    fn cell_width(&self) -> f32;
236    fn cell_height(&self) -> f32;
237    fn hide_cursor(&mut self) -> Result<(), String>;
238    fn show_cursor(&mut self) -> Result<(), String>;
239    fn set_cursor(&mut self, x: u16, y: u16) -> Result<(), String>;
240    fn get_cursor(&mut self) -> Result<(u16, u16), String>;
241
242    // sdl & web main render pass...
243    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
244    fn draw_all_graph(
245        &mut self,
246        current_buffer: &Buffer,
247        previous_buffer: &Buffer,
248        pixel_sprites: &mut Vec<Sprites>,
249        stage: u32,
250    ) {
251        // render main_buffer & pixel_sprites to rbuf
252        let rbuf =
253            self.draw_all_to_render_buffer(current_buffer, previous_buffer, pixel_sprites, stage);
254        // for y in 0..24 {
255        //     let mut s = "".to_string();
256        //     for x in 0..24 {
257        //         let cc = &current_buffer.content[y * 24 + x];
258        //         s.push_str(&format!("{}.{} ", cc.tex, cc.symbol));
259        //     }
260        //     info!("...{}", s);
261        // }
262        // info!("{:?} len={}", current_buffer.content.len(), rbuf.len());
263        if self.get_base().rflag {
264            // draw rbuf to render_texture 2
265            self.draw_render_buffer_to_texture(&rbuf, 2, false);
266            // draw render_texture 2 & 3 to screen
267            self.draw_render_textures_to_screen();
268        } else {
269            // copy rbuf to base.rbuf
270            self.get_base().rbuf = rbuf;
271            // info!("rbuf len...{}", self.get_base().rbuf.len());
272        }
273    }
274
275    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
276    fn only_render_buffer(&mut self) {
277        self.get_base().rflag = false;
278    }
279
280    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
281    fn draw_render_textures_to_screen(&mut self) {
282        let bs = self.get_base();
283
284        if let (Some(pix), Some(gl)) = (&mut bs.gl_pixel, &mut bs.gl) {
285            pix.bind_screen(gl);
286            let c = GlColor::new(1.0, 1.0, 1.0, 1.0);
287
288            // draw render_texture 2 ( main buffer )
289            if !pix.get_render_texture_hidden(2) {
290                let t = GlTransform::new();
291                pix.draw_general2d(gl, 2, [0.0, 0.0, 1.0, 1.0], &t, &c);
292            }
293
294            // draw render_texture 3 ( gl transition )
295            if !pix.get_render_texture_hidden(3) {
296                let pcw = pix.canvas_width as f32;
297                let pch = pix.canvas_height as f32;
298                let rx = bs.ratio_x;
299                let ry = bs.ratio_y;
300                let pw = 40.0 * PIXEL_SYM_WIDTH.get().expect("lazylock init") / rx;
301                let ph = 25.0 * PIXEL_SYM_HEIGHT.get().expect("lazylock init") / ry;
302
303                let mut t2 = GlTransform::new();
304                t2.scale(pw / pcw, ph / pch);
305                pix.draw_general2d(
306                    gl,
307                    3,
308                    [0.0 / pcw, (pch - ph) / pch, pw / pcw, ph / pch],
309                    &t2,
310                    &c,
311                );
312            }
313        }
314    }
315
316    // draw buffer to render texture...
317    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
318    fn draw_buffer_to_texture(&mut self, buf: &Buffer, rtidx: usize) {
319        let rbuf = self.buffer_to_render_buffer(buf);
320        // For debug...
321        // self.draw_render_buffer(&rbuf, rtidx, true);
322        self.draw_render_buffer_to_texture(&rbuf, rtidx, false);
323    }
324
325    // draw render buffer to render texture...
326    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
327    fn draw_render_buffer_to_texture(&mut self, rbuf: &[RenderCell], rtidx: usize, debug: bool) {
328        let bs = self.get_base();
329        let rx = bs.ratio_x;
330        let ry = bs.ratio_y;
331        if let (Some(pix), Some(gl)) = (&mut bs.gl_pixel, &mut bs.gl) {
332            pix.bind_target(gl, rtidx);
333            if debug {
334                // set red background for debug...
335                pix.set_clear_color(GlColor::new(1.0, 0.0, 0.0, 1.0));
336            } else {
337                pix.set_clear_color(GlColor::new(0.0, 0.0, 0.0, 1.0));
338            }
339            pix.clear(gl);
340            pix.render_rbuf(gl, rbuf, rx, ry);
341        }
342    }
343
344    // buffer to render buffer...
345    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
346    fn buffer_to_render_buffer(&mut self, cb: &Buffer) -> Vec<RenderCell> {
347        let mut rbuf = vec![];
348        let rx = self.get_base().ratio_x;
349        let ry = self.get_base().ratio_y;
350        let pz = PointI32 { x: 0, y: 0 };
351        let mut rfunc = |fc: &(u8, u8, u8, u8),
352                         bc: &Option<(u8, u8, u8, u8)>,
353                         _s0: ARect,
354                         _s1: ARect,
355                         s2: ARect,
356                         texidx: usize,
357                         symidx: usize| {
358            push_render_buffer(&mut rbuf, fc, bc, texidx, symidx, s2, 0.0, &pz);
359        };
360        render_main_buffer(cb, cb.area.width, rx, ry, true, &mut rfunc);
361        rbuf
362    }
363
364    // draw main buffer & pixel sprites to render buffer...
365    #[cfg(any(feature = "sdl", target_arch = "wasm32"))]
366    fn draw_all_to_render_buffer(
367        &mut self,
368        cb: &Buffer,
369        _pb: &Buffer,
370        ps: &mut Vec<Sprites>,
371        stage: u32,
372    ) -> Vec<RenderCell> {
373        let mut rbuf = vec![];
374        let width = cb.area.width;
375        let pz = PointI32 { x: 0, y: 0 };
376
377        // render logo...
378        if stage <= LOGO_FRAME {
379            render_logo(
380                self.get_base().ratio_x,
381                self.get_base().ratio_y,
382                self.get_base().pixel_w,
383                self.get_base().pixel_h,
384                &mut self.get_base().rd,
385                stage,
386                |fc, _s1, s2, texidx, symidx| {
387                    push_render_buffer(&mut rbuf, fc, &None, texidx, symidx, s2, 0.0, &pz);
388                },
389            );
390            return rbuf;
391        }
392
393        let cw = self.get_base().cell_w;
394        let ch = self.get_base().cell_h;
395        let rx = self.get_base().ratio_x;
396        let ry = self.get_base().ratio_y;
397        let mut rfunc = |fc: &(u8, u8, u8, u8),
398                         bc: &Option<(u8, u8, u8, u8)>,
399                         _s0: ARect,
400                         _s1: ARect,
401                         s2: ARect,
402                         texidx: usize,
403                         symidx: usize| {
404            push_render_buffer(&mut rbuf, fc, bc, texidx, symidx, s2, 0.0, &pz);
405        };
406
407        // render windows border, only at sdl mode
408        #[cfg(feature = "sdl")]
409        render_border(cw, ch, rx, ry, &mut rfunc);
410
411        // render main buffer...
412        if stage > LOGO_FRAME {
413            render_main_buffer(cb, width, rx, ry, false, &mut rfunc);
414        }
415
416        // render pixel_sprites...
417        if stage > LOGO_FRAME {
418            for item in ps {
419                if item.is_pixel && !item.is_hidden {
420                    render_pixel_sprites(
421                        item,
422                        rx,
423                        ry,
424                        |fc, bc, _s0, _s1, s2, texidx, symidx, angle, ccp| {
425                            push_render_buffer(&mut rbuf, fc, bc, texidx, symidx, s2, angle, &ccp);
426                        },
427                    );
428                }
429            }
430        }
431        rbuf
432    }
433
434    fn as_any(&mut self) -> &mut dyn Any;
435}
436
437#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
438fn push_render_buffer(
439    rbuf: &mut Vec<RenderCell>,
440    fc: &(u8, u8, u8, u8),
441    bgc: &Option<(u8, u8, u8, u8)>,
442    texidx: usize,
443    symidx: usize,
444    s: ARect,
445    angle: f64,
446    ccp: &PointI32,
447) {
448    let mut wc = RenderCell {
449        fcolor: (
450            fc.0 as f32 / 255.0,
451            fc.1 as f32 / 255.0,
452            fc.2 as f32 / 255.0,
453            fc.3 as f32 / 255.0,
454        ),
455        ..Default::default()
456    };
457    if let Some(bc) = bgc {
458        wc.bcolor = Some((
459            bc.0 as f32 / 255.0,
460            bc.1 as f32 / 255.0,
461            bc.2 as f32 / 255.0,
462            bc.3 as f32 / 255.0,
463        ));
464    } else {
465        wc.bcolor = None;
466    }
467    let x = symidx as u32 % 16u32 + (texidx as u32 % 8u32) * 16u32;
468    let y = symidx as u32 / 16u32 + (texidx as u32 / 8u32) * 16u32;
469    wc.texsym = (y * 16u32 * 8u32 + x) as usize;
470    wc.x = s.x as f32 + PIXEL_SYM_WIDTH.get().expect("lazylock init");
471    wc.y = s.y as f32 + PIXEL_SYM_HEIGHT.get().expect("lazylock init");
472    wc.w = s.w;
473    wc.h = s.h;
474    if angle == 0.0 {
475        wc.angle = angle as f32;
476    } else {
477        let mut aa = (1.0 - angle / 180.0) * std::f64::consts::PI;
478        let pi2 = std::f64::consts::PI * 2.0;
479        while aa < 0.0 {
480            aa += pi2;
481        }
482        while aa > pi2 {
483            aa -= pi2;
484        }
485        wc.angle = aa as f32;
486    }
487    wc.cx = ccp.x as f32;
488    wc.cy = ccp.y as f32;
489    rbuf.push(wc);
490}
491
492#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
493fn render_helper(
494    cell_w: u16,
495    r: PointF32,
496    i: usize,
497    sh: &(u8, u8, Color, Color),
498    p: PointU16,
499    is_border: bool,
500) -> (ARect, ARect, ARect, usize, usize) {
501    let w = *PIXEL_SYM_WIDTH.get().expect("lazylock init") as i32;
502    let h = *PIXEL_SYM_HEIGHT.get().expect("lazylock init") as i32;
503    let dstx = i as u16 % cell_w;
504    let dsty = i as u16 / cell_w;
505    let tex_count = 64;
506    let tx = if sh.1 < tex_count { sh.1 as usize } else { 1 };
507    let srcy = sh.0 as u32 / w as u32 + (tx as u32 / 2u32) * w as u32;
508    let srcx = sh.0 as u32 % w as u32 + (tx as u32 % 2u32) * w as u32;
509    let bsrcy = 160u32 / w as u32;
510    let bsrcx = 160u32 % w as u32 + w as u32;
511
512    (
513        // background sym rect in texture(sym=160 tex=1)
514        ARect {
515            x: w * bsrcx as i32,
516            y: h * bsrcy as i32,
517            w: w as u32,
518            h: h as u32,
519        },
520        // sym rect in texture
521        ARect {
522            x: w * srcx as i32,
523            y: h * srcy as i32,
524            w: w as u32,
525            h: h as u32,
526        },
527        // dst rect in render texture
528        ARect {
529            x: (dstx + if is_border { 0 } else { 1 }) as i32 * (w as f32 / r.x) as i32 + p.x as i32,
530            y: (dsty + if is_border { 0 } else { 1 }) as i32 * (h as f32 / r.y) as i32 + p.y as i32,
531            w: (w as f32 / r.x) as u32,
532            h: (h as f32 / r.y) as u32,
533        },
534        // texture id
535        tx,
536        // sym id
537        sh.0 as usize,
538    )
539}
540
541#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
542pub fn render_pixel_sprites<F>(pixel_spt: &mut Sprites, rx: f32, ry: f32, mut f: F)
543where
544    // rgba, back rgba, back rect, sym rect, dst rect, tex, sym, angle, center point
545    F: FnMut(
546        &(u8, u8, u8, u8),
547        &Option<(u8, u8, u8, u8)>,
548        ARect,
549        ARect,
550        ARect,
551        usize,
552        usize,
553        f64,
554        PointI32,
555    ),
556{
557    // sort by render_weight...
558    pixel_spt.update_render_index();
559    for si in &pixel_spt.render_index {
560        let s = &pixel_spt.sprites[si.0];
561        if s.is_hidden() {
562            continue;
563        }
564        let px = s.content.area.x;
565        let py = s.content.area.y;
566        let pw = s.content.area.width;
567        let ph = s.content.area.height;
568
569        for (i, cell) in s.content.content.iter().enumerate() {
570            let sh = &cell.get_cell_info();
571            let (s0, s1, s2, texidx, symidx) = render_helper(
572                pw,
573                PointF32 { x: rx, y: ry },
574                i,
575                sh,
576                PointU16 { x: px, y: py },
577                false,
578            );
579            let x = i % pw as usize;
580            let y = i / pw as usize;
581            // center point ...
582            let ccp = PointI32 {
583                x: ((pw as f32 / 2.0 - x as f32) * PIXEL_SYM_WIDTH.get().expect("lazylock init") / rx) as i32,
584                y: ((ph as f32 / 2.0 - y as f32) * PIXEL_SYM_HEIGHT.get().expect("lazylock init") / ry) as i32,
585            };
586            let mut fc = sh.2.get_rgba();
587            fc.3 = s.alpha;
588            let bc;
589            if sh.3 != Color::Reset {
590                let mut brgba = sh.3.get_rgba();
591                brgba.3 = s.alpha;
592                bc = Some(brgba);
593            } else {
594                bc = None;
595            }
596            f(&fc, &bc, s0, s1, s2, texidx, symidx, s.angle, ccp);
597        }
598    }
599}
600
601#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
602pub fn render_main_buffer<F>(buf: &Buffer, width: u16, rx: f32, ry: f32, border: bool, mut f: F)
603where
604    F: FnMut(&(u8, u8, u8, u8), &Option<(u8, u8, u8, u8)>, ARect, ARect, ARect, usize, usize),
605{
606    for (i, cell) in buf.content.iter().enumerate() {
607        // symidx, texidx, fg, bg
608        let sh = cell.get_cell_info();
609        let (s0, s1, s2, texidx, symidx) = render_helper(
610            width,
611            PointF32 { x: rx, y: ry },
612            i,
613            &sh,
614            PointU16 { x: 0, y: 0 },
615            border,
616        );
617        let fc = sh.2.get_rgba();
618        let bc = if sh.3 != Color::Reset {
619            Some(sh.3.get_rgba())
620        } else {
621            None
622        };
623        f(&fc, &bc, s0, s1, s2, texidx, symidx);
624    }
625}
626
627#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
628pub fn render_border<F>(cell_w: u16, cell_h: u16, rx: f32, ry: f32, mut f: F)
629where
630    F: FnMut(&(u8, u8, u8, u8), &Option<(u8, u8, u8, u8)>, ARect, ARect, ARect, usize, usize),
631{
632    let sh_top = (102u8, 1u8, Color::Indexed(7), Color::Reset);
633    let sh_other = (24u8, 2u8, Color::Indexed(7), Color::Reset);
634    let sh_close = (214u8, 1u8, Color::Indexed(7), Color::Reset);
635
636    for n in 0..cell_h as usize + 2 {
637        for m in 0..cell_w as usize + 2 {
638            if n != 0 && n != cell_h as usize + 1 && m != 0 && m != cell_w as usize + 1 {
639                continue;
640            }
641            let rsh;
642            if n == 0 {
643                if m as u16 <= cell_w {
644                    rsh = &sh_top;
645                } else {
646                    rsh = &sh_close;
647                }
648            } else {
649                rsh = &sh_other;
650            }
651            let (s0, s1, s2, texidx, symidx) = render_helper(
652                cell_w + 2,
653                PointF32 { x: rx, y: ry },
654                n * (cell_w as usize + 2) + m,
655                rsh,
656                PointU16 { x: 0, y: 0 },
657                true,
658            );
659            let fc = rsh.2.get_rgba();
660            let bc = None;
661            f(&fc, &bc, s0, s1, s2, texidx, symidx);
662        }
663    }
664}
665
666#[cfg(any(feature = "sdl", target_arch = "wasm32"))]
667pub fn render_logo<F>(srx: f32, sry: f32, spw: u32, sph: u32, rd: &mut Rand, stage: u32, mut f: F)
668where
669    F: FnMut(&(u8, u8, u8, u8), ARect, ARect, usize, usize),
670{
671    let rx = srx * 1.0;
672    let ry = sry * 1.0;
673    for y in 0usize..PIXEL_LOGO_HEIGHT {
674        for x in 0usize..PIXEL_LOGO_WIDTH {
675            let sci = y * PIXEL_LOGO_WIDTH + x;
676            let symw = PIXEL_SYM_WIDTH.get().expect("lazylock init") / rx;
677            let symh = PIXEL_SYM_HEIGHT.get().expect("lazylock init") / ry;
678
679            let (_s0, s1, mut s2, texidx, symidx) = render_helper(
680                PIXEL_LOGO_WIDTH as u16,
681                PointF32 { x: rx, y: ry },
682                sci,
683                &(
684                    PIXEL_LOGO[sci * 3],
685                    PIXEL_LOGO[sci * 3 + 2],
686                    Color::Indexed(PIXEL_LOGO[sci * 3 + 1]),
687                    Color::Reset,
688                ),
689                PointU16 {
690                    x: spw as u16 / 2 - (PIXEL_LOGO_WIDTH as f32 / 2.0 * symw) as u16,
691                    y: sph as u16 / 2 - (PIXEL_LOGO_HEIGHT as f32 / 2.0 * symh) as u16,
692                },
693                false,
694            );
695            let fc = Color::Indexed(PIXEL_LOGO[sci * 3 + 1]).get_rgba();
696
697            let randadj = 12 - (rd.rand() % 24) as i32;
698            let sg = LOGO_FRAME as u8 / 3;
699            let r: u8;
700            let g: u8;
701            let b: u8;
702            let a: u8;
703            if stage <= sg as u32 {
704                r = (stage as u8).saturating_mul(10);
705                g = (stage as u8).saturating_mul(10);
706                b = (stage as u8).saturating_mul(10);
707                a = 255;
708                s2.x += randadj;
709            } else if stage <= sg as u32 * 2 {
710                r = fc.0;
711                g = fc.1;
712                b = fc.2;
713                a = 255;
714            } else {
715                let cc = (stage as u8 - sg * 2).saturating_mul(10);
716                r = fc.0.saturating_sub(cc);
717                g = fc.1.saturating_sub(cc);
718                b = fc.2.saturating_sub(cc);
719                a = 255;
720            }
721            f(&(r, g, b, a), s1, s2, texidx, symidx);
722        }
723    }
724}
725