1use std::cmp::max;
2use std::sync::atomic::{AtomicBool, Ordering};
3use std::sync::LazyLock;
4
5use crate::audio::Audio;
6use crate::channel::{Channel, SharedChannel};
7use crate::graphics::Graphics;
8use crate::image::{Image, Rgb24, SharedImage};
9use crate::input::Input;
10use crate::keys::Key;
11use crate::music::{Music, SharedMusic};
12use crate::resource::Resource;
13use crate::settings::{
14 CURSOR_DATA, CURSOR_HEIGHT, CURSOR_WIDTH, DEFAULT_COLORS, DEFAULT_FPS, DEFAULT_QUIT_KEY,
15 DEFAULT_TITLE, DEFAULT_TONES, DISPLAY_RATIO, FONT_DATA, FONT_HEIGHT, FONT_WIDTH, ICON_COLKEY,
16 ICON_DATA, ICON_SCALE, IMAGE_SIZE, NUM_CHANNELS, NUM_FONT_ROWS, NUM_IMAGES, NUM_MUSICS,
17 NUM_SAMPLES, NUM_SOUNDS, NUM_TILEMAPS, NUM_TONES, SAMPLE_RATE, TILEMAP_SIZE,
18};
19use crate::sound::{SharedSound, Sound};
20use crate::system::System;
21use crate::tilemap::{ImageSource, SharedTilemap, Tilemap};
22use crate::tone::{SharedTone, Tone};
23
24static IS_INITIALIZED: AtomicBool = AtomicBool::new(false);
25
26pub static COLORS: LazyLock<shared_type!(Vec<Rgb24>)> =
27 LazyLock::new(|| new_shared_type!(DEFAULT_COLORS.to_vec()));
28
29pub static IMAGES: LazyLock<shared_type!(Vec<SharedImage>)> = LazyLock::new(|| {
30 new_shared_type!((0..NUM_IMAGES)
31 .map(|_| Image::new(IMAGE_SIZE, IMAGE_SIZE))
32 .collect())
33});
34
35static TILEMAPS: LazyLock<shared_type!(Vec<SharedTilemap>)> = LazyLock::new(|| {
36 new_shared_type!((0..NUM_TILEMAPS)
37 .map(|_| Tilemap::new(TILEMAP_SIZE, TILEMAP_SIZE, ImageSource::Index(0)))
38 .collect())
39});
40
41static CURSOR_IMAGE: LazyLock<SharedImage> = LazyLock::new(|| {
42 let image = Image::new(CURSOR_WIDTH, CURSOR_HEIGHT);
43 image.lock().set(0, 0, &CURSOR_DATA);
44 image
45});
46
47pub static FONT_IMAGE: LazyLock<SharedImage> = LazyLock::new(|| {
48 let width = FONT_WIDTH * NUM_FONT_ROWS;
49 let height = FONT_HEIGHT * (FONT_DATA.len() as u32).div_ceil(NUM_FONT_ROWS);
50 let image = Image::new(width, height);
51 {
52 let mut image = image.lock();
53 for (fi, data) in FONT_DATA.iter().enumerate() {
54 let row = fi as u32 / NUM_FONT_ROWS;
55 let col = fi as u32 % NUM_FONT_ROWS;
56 let mut data = *data;
57 for yi in 0..FONT_HEIGHT {
58 for xi in 0..FONT_WIDTH {
59 let x = FONT_WIDTH * col + xi;
60 let y = FONT_HEIGHT * row + yi;
61 let color = u8::from((data & 0x800000) != 0);
62 image.canvas.write_data(x as usize, y as usize, color);
63 data <<= 1;
64 }
65 }
66 }
67 }
68 image
69});
70
71pub static CHANNELS: LazyLock<shared_type!(Vec<SharedChannel>)> =
72 LazyLock::new(|| new_shared_type!((0..NUM_CHANNELS).map(|_| Channel::new()).collect()));
73
74pub static TONES: LazyLock<shared_type!(Vec<SharedTone>)> = LazyLock::new(|| {
75 new_shared_type!((0..NUM_TONES)
76 .map(|index| {
77 let tone = Tone::new();
78 {
79 let mut tone = tone.lock();
80 tone.gain = DEFAULT_TONES[index as usize].0;
81 tone.noise = DEFAULT_TONES[index as usize].1;
82 tone.waveform = DEFAULT_TONES[index as usize].2;
83 }
84 tone
85 })
86 .collect())
87});
88
89pub static SOUNDS: LazyLock<shared_type!(Vec<SharedSound>)> =
90 LazyLock::new(|| new_shared_type!((0..NUM_SOUNDS).map(|_| Sound::new()).collect()));
91
92static MUSICS: LazyLock<shared_type!(Vec<SharedMusic>)> =
93 LazyLock::new(|| new_shared_type!((0..NUM_MUSICS).map(|_| Music::new()).collect()));
94
95pub struct Pyxel {
96 pub(crate) system: System,
98 pub width: u32,
99 pub height: u32,
100 pub frame_count: u32,
101
102 pub(crate) resource: Resource,
104
105 pub(crate) input: Input,
107 pub mouse_x: i32,
108 pub mouse_y: i32,
109 pub mouse_wheel: i32,
110 pub input_keys: Vec<Key>,
111 pub input_text: String,
112 pub dropped_files: Vec<String>,
113
114 pub(crate) graphics: Graphics,
116 pub colors: shared_type!(Vec<Rgb24>),
117 pub images: shared_type!(Vec<SharedImage>),
118 pub tilemaps: shared_type!(Vec<SharedTilemap>),
119 pub screen: SharedImage,
120 pub cursor: SharedImage,
121 pub font: SharedImage,
122
123 pub channels: shared_type!(Vec<SharedChannel>),
125 pub tones: shared_type!(Vec<SharedTone>),
126 pub sounds: shared_type!(Vec<SharedSound>),
127 pub musics: shared_type!(Vec<SharedMusic>),
128}
129
130pub fn init(
131 width: u32,
132 height: u32,
133 title: Option<&str>,
134 fps: Option<u32>,
135 quit_key: Option<Key>,
136 display_scale: Option<u32>,
137 capture_scale: Option<u32>,
138 capture_sec: Option<u32>,
139) -> Pyxel {
140 assert!(
141 !IS_INITIALIZED.swap(true, Ordering::Relaxed),
142 "Pyxel already initialized"
143 );
144
145 let title = title.unwrap_or(DEFAULT_TITLE);
147 let quit_key = quit_key.unwrap_or(DEFAULT_QUIT_KEY);
148 let fps = fps.unwrap_or(DEFAULT_FPS);
149
150 pyxel_platform::init(|display_width, display_height| {
152 let display_scale = max(
153 display_scale.map_or_else(
154 || {
155 (f64::min(
156 display_width as f64 / width as f64,
157 display_height as f64 / height as f64,
158 ) * DISPLAY_RATIO) as u32
159 },
160 |display_scale| display_scale,
161 ),
162 1,
163 );
164 (title, width * display_scale, height * display_scale)
165 });
166
167 let system = System::new(fps, quit_key);
169 let frame_count = 0;
170
171 let resource = Resource::new(capture_scale, capture_sec, fps);
173
174 let input = Input::new();
176 let mouse_x = 0;
177 let mouse_y = 0;
178 let mouse_wheel = 0;
179 let input_keys = Vec::new();
180 let input_text = String::new();
181 let dropped_files = Vec::new();
182
183 let graphics = Graphics::new();
185 let colors = COLORS.clone();
186 let images = IMAGES.clone();
187 let tilemaps = TILEMAPS.clone();
188 let screen = Image::new(width, height);
189 let cursor = CURSOR_IMAGE.clone();
190 let font = FONT_IMAGE.clone();
191
192 let _ = Audio::new(SAMPLE_RATE, NUM_SAMPLES);
194 let channels = CHANNELS.clone();
195 let tones = TONES.clone();
196 let sounds = SOUNDS.clone();
197 let musics = MUSICS.clone();
198
199 let pyxel = Pyxel {
200 system,
202 width,
203 height,
204 frame_count,
205
206 resource,
208
209 input,
211 mouse_x,
212 mouse_y,
213 mouse_wheel,
214 input_keys,
215 input_text,
216 dropped_files,
217
218 graphics,
220 colors,
221 images,
222 tilemaps,
223 screen,
224 cursor,
225 font,
226
227 channels,
229 tones,
230 sounds,
231 musics,
232 };
233
234 pyxel.icon(&ICON_DATA, ICON_SCALE, ICON_COLKEY);
235 pyxel
236}