Struct bevy_doryen::doryen::Image

source ·
pub struct Image { /* private fields */ }
Expand description

An easy way to load PNG images and blit them on the console

Implementations§

source§

impl Image

source

pub fn new(file_path: &str) -> Image

Create an image and load a PNG file. On the web platform, image loading is asynchronous. Using blit methods before the image is loaded has no impact on the console.

Examples found in repository?
examples/subcell.rs (line 14)
12
13
14
15
16
    fn default() -> Self {
        Self {
            skull: Image::new("skull.png"),
        }
    }
More examples
Hide additional examples
examples/image.rs (line 16)
14
15
16
17
18
19
20
    fn default() -> Self {
        Self {
            skull: Image::new("skull.png"),
            angle: 0.0,
            scale_time: 0.0,
        }
    }
examples/demo/level.rs (line 79)
78
79
80
81
fn init(world: &mut World) {
    world.insert_non_send_resource(LevelImage(Image::new("demo/level.png")));
    world.insert_non_send_resource(GroundImage(Image::new("demo/level_color.png")));
}
source

pub fn width(&self) -> u32

Returns the image’s width in pixels or 0 if the image has not yet been loaded

Examples found in repository?
examples/demo/light.rs (line 125)
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
fn compute_lightmap(
    mut light_map: NonSendMut<LightMap>,
    size: Res<LevelSize>,
    query: Query<(&Light, &Position)>,
    level_map: Res<LevelMap>,
) {
    let light_map = &mut light_map.0;
    *light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let mut fov = FovRestrictive::new();
    for (light, light_position) in &query {
        let (px, py, intensity, radius) = if light.flicker {
            // alter light position, radius and intensity over time
            (
                light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
                light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
                light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
                light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
            )
        } else {
            (
                light_position.x,
                light_position.y,
                light.intensity,
                light.radius,
            )
        };

        let minx = ((px - radius).floor() as i32).max(0) as u32;
        let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
        let miny = ((py - radius).floor() as i32).max(0) as u32;
        let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
        let width = maxx - minx + 1;
        let height = maxy - miny + 1;
        let mut map = MapData::new(width as usize, height as usize);
        for y in miny..=maxy {
            for x in minx..=maxx {
                map.set_transparent(
                    (x - minx) as usize,
                    (y - miny) as usize,
                    level_map.0.is_transparent(x as usize, y as usize),
                );
            }
        }
        fov.compute_fov(
            &mut map,
            px as usize - minx as usize,
            py as usize - miny as usize,
            radius as usize,
            true,
        );
        let light_color = color_scale(light.color, intensity);
        let radius_squared = radius * radius;
        let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
        for y in miny..=maxy {
            for x in minx..=maxx {
                if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
                    let dx = x as f32 - px;
                    let dy = y as f32 - py;
                    // good looking lights.
                    let squared_dist = dx * dx + dy * dy;
                    let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
                    let intensity_coef = intensity_coef - radius_coef;
                    let intensity_coef = intensity_coef / (1.0 - radius_coef);
                    if intensity_coef > 0.0 {
                        let light = color_blend(BLACK, light_color, intensity_coef);
                        let cur_light = light_map.pixel(x, y).unwrap();
                        light_map.put_pixel(x, y, color_add(light, cur_light));
                    }
                }
            }
        }
    }
}
source

pub fn height(&self) -> u32

Returns the image’s height in pixels or 0 if the image has not yet been loaded

Examples found in repository?
examples/demo/light.rs (line 127)
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
fn compute_lightmap(
    mut light_map: NonSendMut<LightMap>,
    size: Res<LevelSize>,
    query: Query<(&Light, &Position)>,
    level_map: Res<LevelMap>,
) {
    let light_map = &mut light_map.0;
    *light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let mut fov = FovRestrictive::new();
    for (light, light_position) in &query {
        let (px, py, intensity, radius) = if light.flicker {
            // alter light position, radius and intensity over time
            (
                light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
                light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
                light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
                light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
            )
        } else {
            (
                light_position.x,
                light_position.y,
                light.intensity,
                light.radius,
            )
        };

        let minx = ((px - radius).floor() as i32).max(0) as u32;
        let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
        let miny = ((py - radius).floor() as i32).max(0) as u32;
        let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
        let width = maxx - minx + 1;
        let height = maxy - miny + 1;
        let mut map = MapData::new(width as usize, height as usize);
        for y in miny..=maxy {
            for x in minx..=maxx {
                map.set_transparent(
                    (x - minx) as usize,
                    (y - miny) as usize,
                    level_map.0.is_transparent(x as usize, y as usize),
                );
            }
        }
        fov.compute_fov(
            &mut map,
            px as usize - minx as usize,
            py as usize - miny as usize,
            radius as usize,
            true,
        );
        let light_color = color_scale(light.color, intensity);
        let radius_squared = radius * radius;
        let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
        for y in miny..=maxy {
            for x in minx..=maxx {
                if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
                    let dx = x as f32 - px;
                    let dy = y as f32 - py;
                    // good looking lights.
                    let squared_dist = dx * dx + dy * dy;
                    let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
                    let intensity_coef = intensity_coef - radius_coef;
                    let intensity_coef = intensity_coef / (1.0 - radius_coef);
                    if intensity_coef > 0.0 {
                        let light = color_blend(BLACK, light_color, intensity_coef);
                        let cur_light = light_map.pixel(x, y).unwrap();
                        light_map.put_pixel(x, y, color_add(light, cur_light));
                    }
                }
            }
        }
    }
}
source

pub fn new_empty(width: u32, height: u32) -> Image

Create an empty image.

Examples found in repository?
examples/demo/light.rs (line 70)
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
    fn default() -> Self {
        Self(Image::new_empty(0, 0))
    }
}

impl Light {
    pub fn new(radius: f32, color: Color, flicker: bool) -> Self {
        Self {
            radius,
            color,
            intensity: LIGHT_INTENSITY,
            // random t initial value so that all lights don't flicker in sync
            t: thread_rng().gen::<f32>() * 100. * 150.,
            flicker,
        }
    }
}

fn init(world: &mut World) {
    world.init_non_send_resource::<LightMap>();
}

fn update_time(mut query: Query<&mut Light>) {
    for mut light in &mut query {
        light.t += TIME_SCALE;
    }
}

fn compute_lightmap(
    mut light_map: NonSendMut<LightMap>,
    size: Res<LevelSize>,
    query: Query<(&Light, &Position)>,
    level_map: Res<LevelMap>,
) {
    let light_map = &mut light_map.0;
    *light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let mut fov = FovRestrictive::new();
    for (light, light_position) in &query {
        let (px, py, intensity, radius) = if light.flicker {
            // alter light position, radius and intensity over time
            (
                light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
                light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
                light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
                light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
            )
        } else {
            (
                light_position.x,
                light_position.y,
                light.intensity,
                light.radius,
            )
        };

        let minx = ((px - radius).floor() as i32).max(0) as u32;
        let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
        let miny = ((py - radius).floor() as i32).max(0) as u32;
        let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
        let width = maxx - minx + 1;
        let height = maxy - miny + 1;
        let mut map = MapData::new(width as usize, height as usize);
        for y in miny..=maxy {
            for x in minx..=maxx {
                map.set_transparent(
                    (x - minx) as usize,
                    (y - miny) as usize,
                    level_map.0.is_transparent(x as usize, y as usize),
                );
            }
        }
        fov.compute_fov(
            &mut map,
            px as usize - minx as usize,
            py as usize - miny as usize,
            radius as usize,
            true,
        );
        let light_color = color_scale(light.color, intensity);
        let radius_squared = radius * radius;
        let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
        for y in miny..=maxy {
            for x in minx..=maxx {
                if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
                    let dx = x as f32 - px;
                    let dy = y as f32 - py;
                    // good looking lights.
                    let squared_dist = dx * dx + dy * dy;
                    let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
                    let intensity_coef = intensity_coef - radius_coef;
                    let intensity_coef = intensity_coef / (1.0 - radius_coef);
                    if intensity_coef > 0.0 {
                        let light = color_blend(BLACK, light_color, intensity_coef);
                        let cur_light = light_map.pixel(x, y).unwrap();
                        light_map.put_pixel(x, y, color_add(light, cur_light));
                    }
                }
            }
        }
    }
}
More examples
Hide additional examples
examples/demo/level.rs (line 220)
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
fn render_level(
    mut root_console: ResMut<RootConsole>,
    ground: NonSend<GroundImage>,
    size: Res<LevelSize>,
    map: Res<LevelMap>,
    mut visited_2x: ResMut<Visited2x>,
    light_map: NonSend<LightMap>,
) {
    let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let light_map = &light_map.0;
    for y in 0..size.height * 2 {
        for x in 0..size.width * 2 {
            let off = x + y * size.width * 2;
            if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
                visited_2x.0[off] = true;
                let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
                let light_col = light_map.pixel(x as u32, y as u32).unwrap();
                let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
                let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
                let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
                r = r.min(255.0);
                g = g.min(255.0);
                b = b.min(255.0);
                render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
            } else if visited_2x.0[off] {
                let col = ground.0.pixel(x as u32, y as u32).unwrap();
                let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
                render_output.put_pixel(x as u32, y as u32, dark_col);
            } else {
                render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
            }
        }
    }
    render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
source

pub fn pixel(&self, x: u32, y: u32) -> Option<(u8, u8, u8, u8)>

get the color of a specific pixel inside the image

Examples found in repository?
examples/demo/player.rs (line 98)
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
fn render_player(
    mut root_console: ResMut<RootConsole>,
    light_map: NonSend<LightMap>,
    query: Query<&Position, With<Player>>,
) {
    let player_position = query.single();
    let light = light_map
        .0
        .pixel(player_position.x as u32, player_position.y as u32)
        .unwrap();
    root_console.ascii(
        player_position.character_x(),
        player_position.character_y(),
        '@' as u16,
    );
    root_console.fore(
        player_position.character_x(),
        player_position.character_y(),
        light,
    );
}
More examples
Hide additional examples
examples/demo/shared/character.rs (line 50)
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
fn render_character(
    mut root_console: ResMut<RootConsole>,
    query: Query<(&Character, &Position)>,
    level_map: Res<LevelMap>,
    light_map: NonSend<LightMap>,
) {
    for (character, position) in &query {
        if !level_map
            .0
            .is_in_fov(position.x as usize, position.y as usize)
        {
            continue;
        }
        let (color, penumbra) = if character.light {
            (character.color, false)
        } else {
            let light = light_map
                .0
                .pixel(position.x as u32, position.y as u32)
                .unwrap();

            let penumbra = is_penumbra(light, 100);
            let mut color = color_mul(character.color, light);
            if penumbra {
                color = color_scale(color, LIGHT_COEF);
            }
            (color, penumbra)
        };
        root_console.ascii(
            position.character_x(),
            position.character_y(),
            if penumbra { '?' as u16 } else { character.ch },
        );
        root_console.fore(position.character_x(), position.character_y(), color);
    }
}
examples/demo/light.rs (line 162)
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
fn compute_lightmap(
    mut light_map: NonSendMut<LightMap>,
    size: Res<LevelSize>,
    query: Query<(&Light, &Position)>,
    level_map: Res<LevelMap>,
) {
    let light_map = &mut light_map.0;
    *light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let mut fov = FovRestrictive::new();
    for (light, light_position) in &query {
        let (px, py, intensity, radius) = if light.flicker {
            // alter light position, radius and intensity over time
            (
                light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
                light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
                light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
                light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
            )
        } else {
            (
                light_position.x,
                light_position.y,
                light.intensity,
                light.radius,
            )
        };

        let minx = ((px - radius).floor() as i32).max(0) as u32;
        let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
        let miny = ((py - radius).floor() as i32).max(0) as u32;
        let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
        let width = maxx - minx + 1;
        let height = maxy - miny + 1;
        let mut map = MapData::new(width as usize, height as usize);
        for y in miny..=maxy {
            for x in minx..=maxx {
                map.set_transparent(
                    (x - minx) as usize,
                    (y - miny) as usize,
                    level_map.0.is_transparent(x as usize, y as usize),
                );
            }
        }
        fov.compute_fov(
            &mut map,
            px as usize - minx as usize,
            py as usize - miny as usize,
            radius as usize,
            true,
        );
        let light_color = color_scale(light.color, intensity);
        let radius_squared = radius * radius;
        let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
        for y in miny..=maxy {
            for x in minx..=maxx {
                if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
                    let dx = x as f32 - px;
                    let dy = y as f32 - py;
                    // good looking lights.
                    let squared_dist = dx * dx + dy * dy;
                    let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
                    let intensity_coef = intensity_coef - radius_coef;
                    let intensity_coef = intensity_coef / (1.0 - radius_coef);
                    if intensity_coef > 0.0 {
                        let light = color_blend(BLACK, light_color, intensity_coef);
                        let cur_light = light_map.pixel(x, y).unwrap();
                        light_map.put_pixel(x, y, color_add(light, cur_light));
                    }
                }
            }
        }
    }
}
examples/demo/level.rs (line 104)
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
fn load_level(
    mut level_image: NonSendMut<LevelImage>,
    mut ground_image: NonSendMut<GroundImage>,
    mut next_game_state: ResMut<NextState<GameState>>,
    mut visited_2x: ResMut<Visited2x>,
    mut walls: ResMut<Walls>,
    mut commands: Commands,
    mut size: ResMut<LevelSize>,
) {
    let level_image = &mut level_image.0;
    let level_image_try_load = level_image.try_load();
    if level_image_try_load {
        let image_size = level_image.try_get_size().unwrap();
        size.width = image_size.0 as usize / 2;
        size.height = image_size.1 as usize / 2;
        walls.data = vec![false; size.width * size.height];
        walls.level_width = size.width;
        let mut map = MapData::new(image_size.0 as usize, image_size.1 as usize);
        visited_2x.0.reserve((image_size.0 * image_size.1) as usize);
        for y in 0..image_size.1 {
            for x in 0..image_size.0 {
                let p = level_image.pixel(x, y).unwrap();
                map.set_transparent(x as usize, y as usize, p != WALL_COLOR);
                visited_2x.0.push(false);
                let pos_1x = (x as f32 / 2., y as f32 / 2.);
                match p {
                    START_COLOR => commands.insert_resource(Start((x as f32, y as f32).into())),
                    LIGHT_COLOR => {
                        commands.spawn(LightBundle {
                            position: Position {
                                x: x as f32,
                                y: y as f32,
                            },
                            ..LightBundle::new(LIGHT_RADIUS, LIGHT_COLOR, true)
                        });
                    }
                    GOBLIN_COLOR => {
                        let offset =
                            (pos_1x.0 as i32 + pos_1x.1 as i32 * size.width as i32) as usize;
                        walls.data[offset] = true;
                        commands.spawn(GoblinBundle {
                            position: Position {
                                x: x as f32,
                                y: y as f32,
                            },
                            ..Default::default()
                        });
                    }
                    _ => (),
                }
            }
        }

        for y in 0..size.height {
            for x in 0..size.width {
                let mut count = 0;
                let x2 = x * 2;
                let y2 = y * 2;
                if map.is_transparent(x2, y2) {
                    count += 1;
                }
                if map.is_transparent(x2 + 1, y2) {
                    count += 1;
                }
                if map.is_transparent(x2, y2 + 1) {
                    count += 1;
                }
                if map.is_transparent(x2 + 1, y2 + 1) {
                    count += 1;
                }
                if count < 2 {
                    let offset = x + y * size.width;
                    walls.data[offset] = true;
                }
            }
        }

        commands.insert_resource(LevelMap(map));
    }

    if level_image_try_load && ground_image.0.try_load() {
        next_game_state.set(GameState::Running);
    }
}

fn unload_level_image(world: &mut World) {
    world.remove_non_send_resource::<LevelImage>();
}

fn initial_compute_fov(
    player_query: Query<&Position, With<Player>>,
    mut map: ResMut<LevelMap>,
    mut fov: ResMut<PlayerFov>,
) {
    let player_position = player_query.single();
    map.0.clear_fov();
    fov.0.compute_fov(
        &mut map.0,
        player_position.x as usize,
        player_position.y as usize,
        PLAYER_FOV_RADIUS,
        true,
    );
}

fn compute_fov(
    player_query: Query<&Position, (With<Player>, Changed<Position>)>,
    mut map: ResMut<LevelMap>,
    mut fov: ResMut<PlayerFov>,
) {
    if let Ok(player_position) = player_query.get_single() {
        map.0.clear_fov();
        fov.0.compute_fov(
            &mut map.0,
            player_position.x as usize,
            player_position.y as usize,
            PLAYER_FOV_RADIUS,
            true,
        );
    }
}

fn render_loading_level(mut root_console: ResMut<RootConsole>) {
    let x = (root_console.get_width() / 2) as i32;
    let y = (root_console.get_height() / 2) as i32;

    root_console.print_color(x, y, "#[white]Loading#[red]...", TextAlign::Center, None);
}

fn render_level(
    mut root_console: ResMut<RootConsole>,
    ground: NonSend<GroundImage>,
    size: Res<LevelSize>,
    map: Res<LevelMap>,
    mut visited_2x: ResMut<Visited2x>,
    light_map: NonSend<LightMap>,
) {
    let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let light_map = &light_map.0;
    for y in 0..size.height * 2 {
        for x in 0..size.width * 2 {
            let off = x + y * size.width * 2;
            if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
                visited_2x.0[off] = true;
                let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
                let light_col = light_map.pixel(x as u32, y as u32).unwrap();
                let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
                let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
                let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
                r = r.min(255.0);
                g = g.min(255.0);
                b = b.min(255.0);
                render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
            } else if visited_2x.0[off] {
                let col = ground.0.pixel(x as u32, y as u32).unwrap();
                let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
                render_output.put_pixel(x as u32, y as u32, dark_col);
            } else {
                render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
            }
        }
    }
    render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
source

pub fn put_pixel(&mut self, x: u32, y: u32, color: (u8, u8, u8, u8))

sets the color of a specific pixel inside the image

Examples found in repository?
examples/demo/level.rs (line 235)
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
fn render_level(
    mut root_console: ResMut<RootConsole>,
    ground: NonSend<GroundImage>,
    size: Res<LevelSize>,
    map: Res<LevelMap>,
    mut visited_2x: ResMut<Visited2x>,
    light_map: NonSend<LightMap>,
) {
    let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let light_map = &light_map.0;
    for y in 0..size.height * 2 {
        for x in 0..size.width * 2 {
            let off = x + y * size.width * 2;
            if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
                visited_2x.0[off] = true;
                let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
                let light_col = light_map.pixel(x as u32, y as u32).unwrap();
                let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
                let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
                let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
                r = r.min(255.0);
                g = g.min(255.0);
                b = b.min(255.0);
                render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
            } else if visited_2x.0[off] {
                let col = ground.0.pixel(x as u32, y as u32).unwrap();
                let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
                render_output.put_pixel(x as u32, y as u32, dark_col);
            } else {
                render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
            }
        }
    }
    render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
More examples
Hide additional examples
examples/demo/light.rs (line 163)
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
fn compute_lightmap(
    mut light_map: NonSendMut<LightMap>,
    size: Res<LevelSize>,
    query: Query<(&Light, &Position)>,
    level_map: Res<LevelMap>,
) {
    let light_map = &mut light_map.0;
    *light_map = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let mut fov = FovRestrictive::new();
    for (light, light_position) in &query {
        let (px, py, intensity, radius) = if light.flicker {
            // alter light position, radius and intensity over time
            (
                light_position.x + (LIGHT_FLICKER_MOVE * (simplex(light.t) - 0.5)),
                light_position.y + (LIGHT_FLICKER_MOVE * (simplex(light.t + 2.0) - 0.5)),
                light.intensity + LIGHT_FLICKER_INTENSITY * (simplex(light.t + 4.0) - 0.5),
                light.radius * (1.0 + LIGHT_FLICKER_RADIUS * (simplex(light.t + 6.0) - 0.5)),
            )
        } else {
            (
                light_position.x,
                light_position.y,
                light.intensity,
                light.radius,
            )
        };

        let minx = ((px - radius).floor() as i32).max(0) as u32;
        let maxx = ((px + radius).ceil() as i32).min(light_map.width() as i32 - 1) as u32;
        let miny = ((py - radius).floor() as i32).max(0) as u32;
        let maxy = ((py + radius).ceil() as i32).min(light_map.height() as i32 - 1) as u32;
        let width = maxx - minx + 1;
        let height = maxy - miny + 1;
        let mut map = MapData::new(width as usize, height as usize);
        for y in miny..=maxy {
            for x in minx..=maxx {
                map.set_transparent(
                    (x - minx) as usize,
                    (y - miny) as usize,
                    level_map.0.is_transparent(x as usize, y as usize),
                );
            }
        }
        fov.compute_fov(
            &mut map,
            px as usize - minx as usize,
            py as usize - miny as usize,
            radius as usize,
            true,
        );
        let light_color = color_scale(light.color, intensity);
        let radius_squared = radius * radius;
        let radius_coef = 1.0 / (1.0 + radius_squared / 20.0);
        for y in miny..=maxy {
            for x in minx..=maxx {
                if map.is_in_fov((x - minx) as usize, (y - miny) as usize) {
                    let dx = x as f32 - px;
                    let dy = y as f32 - py;
                    // good looking lights.
                    let squared_dist = dx * dx + dy * dy;
                    let intensity_coef = 1.0 / (1.0 + squared_dist / 20.0);
                    let intensity_coef = intensity_coef - radius_coef;
                    let intensity_coef = intensity_coef / (1.0 - radius_coef);
                    if intensity_coef > 0.0 {
                        let light = color_blend(BLACK, light_color, intensity_coef);
                        let cur_light = light_map.pixel(x, y).unwrap();
                        light_map.put_pixel(x, y, color_add(light, cur_light));
                    }
                }
            }
        }
    }
}
source

pub fn try_load(&mut self) -> bool

Check if the image has been loaded. Since there’s no background thread doing the work for you, you have to call some method on image for it to actually load. Use either Image::try_load, [Image::get_size], Image::blit or Image::blit_ex to run the loading code.

Examples found in repository?
examples/demo/level.rs (line 93)
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
fn load_level(
    mut level_image: NonSendMut<LevelImage>,
    mut ground_image: NonSendMut<GroundImage>,
    mut next_game_state: ResMut<NextState<GameState>>,
    mut visited_2x: ResMut<Visited2x>,
    mut walls: ResMut<Walls>,
    mut commands: Commands,
    mut size: ResMut<LevelSize>,
) {
    let level_image = &mut level_image.0;
    let level_image_try_load = level_image.try_load();
    if level_image_try_load {
        let image_size = level_image.try_get_size().unwrap();
        size.width = image_size.0 as usize / 2;
        size.height = image_size.1 as usize / 2;
        walls.data = vec![false; size.width * size.height];
        walls.level_width = size.width;
        let mut map = MapData::new(image_size.0 as usize, image_size.1 as usize);
        visited_2x.0.reserve((image_size.0 * image_size.1) as usize);
        for y in 0..image_size.1 {
            for x in 0..image_size.0 {
                let p = level_image.pixel(x, y).unwrap();
                map.set_transparent(x as usize, y as usize, p != WALL_COLOR);
                visited_2x.0.push(false);
                let pos_1x = (x as f32 / 2., y as f32 / 2.);
                match p {
                    START_COLOR => commands.insert_resource(Start((x as f32, y as f32).into())),
                    LIGHT_COLOR => {
                        commands.spawn(LightBundle {
                            position: Position {
                                x: x as f32,
                                y: y as f32,
                            },
                            ..LightBundle::new(LIGHT_RADIUS, LIGHT_COLOR, true)
                        });
                    }
                    GOBLIN_COLOR => {
                        let offset =
                            (pos_1x.0 as i32 + pos_1x.1 as i32 * size.width as i32) as usize;
                        walls.data[offset] = true;
                        commands.spawn(GoblinBundle {
                            position: Position {
                                x: x as f32,
                                y: y as f32,
                            },
                            ..Default::default()
                        });
                    }
                    _ => (),
                }
            }
        }

        for y in 0..size.height {
            for x in 0..size.width {
                let mut count = 0;
                let x2 = x * 2;
                let y2 = y * 2;
                if map.is_transparent(x2, y2) {
                    count += 1;
                }
                if map.is_transparent(x2 + 1, y2) {
                    count += 1;
                }
                if map.is_transparent(x2, y2 + 1) {
                    count += 1;
                }
                if map.is_transparent(x2 + 1, y2 + 1) {
                    count += 1;
                }
                if count < 2 {
                    let offset = x + y * size.width;
                    walls.data[offset] = true;
                }
            }
        }

        commands.insert_resource(LevelMap(map));
    }

    if level_image_try_load && ground_image.0.try_load() {
        next_game_state.set(GameState::Running);
    }
}
source

pub fn try_get_size(&mut self) -> Option<(u32, u32)>

If the image has already been loaded, return its size, else return None

Examples found in repository?
examples/demo/level.rs (line 95)
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
fn load_level(
    mut level_image: NonSendMut<LevelImage>,
    mut ground_image: NonSendMut<GroundImage>,
    mut next_game_state: ResMut<NextState<GameState>>,
    mut visited_2x: ResMut<Visited2x>,
    mut walls: ResMut<Walls>,
    mut commands: Commands,
    mut size: ResMut<LevelSize>,
) {
    let level_image = &mut level_image.0;
    let level_image_try_load = level_image.try_load();
    if level_image_try_load {
        let image_size = level_image.try_get_size().unwrap();
        size.width = image_size.0 as usize / 2;
        size.height = image_size.1 as usize / 2;
        walls.data = vec![false; size.width * size.height];
        walls.level_width = size.width;
        let mut map = MapData::new(image_size.0 as usize, image_size.1 as usize);
        visited_2x.0.reserve((image_size.0 * image_size.1) as usize);
        for y in 0..image_size.1 {
            for x in 0..image_size.0 {
                let p = level_image.pixel(x, y).unwrap();
                map.set_transparent(x as usize, y as usize, p != WALL_COLOR);
                visited_2x.0.push(false);
                let pos_1x = (x as f32 / 2., y as f32 / 2.);
                match p {
                    START_COLOR => commands.insert_resource(Start((x as f32, y as f32).into())),
                    LIGHT_COLOR => {
                        commands.spawn(LightBundle {
                            position: Position {
                                x: x as f32,
                                y: y as f32,
                            },
                            ..LightBundle::new(LIGHT_RADIUS, LIGHT_COLOR, true)
                        });
                    }
                    GOBLIN_COLOR => {
                        let offset =
                            (pos_1x.0 as i32 + pos_1x.1 as i32 * size.width as i32) as usize;
                        walls.data[offset] = true;
                        commands.spawn(GoblinBundle {
                            position: Position {
                                x: x as f32,
                                y: y as f32,
                            },
                            ..Default::default()
                        });
                    }
                    _ => (),
                }
            }
        }

        for y in 0..size.height {
            for x in 0..size.width {
                let mut count = 0;
                let x2 = x * 2;
                let y2 = y * 2;
                if map.is_transparent(x2, y2) {
                    count += 1;
                }
                if map.is_transparent(x2 + 1, y2) {
                    count += 1;
                }
                if map.is_transparent(x2, y2 + 1) {
                    count += 1;
                }
                if map.is_transparent(x2 + 1, y2 + 1) {
                    count += 1;
                }
                if count < 2 {
                    let offset = x + y * size.width;
                    walls.data[offset] = true;
                }
            }
        }

        commands.insert_resource(LevelMap(map));
    }

    if level_image_try_load && ground_image.0.try_load() {
        next_game_state.set(GameState::Running);
    }
}
source

pub fn blit( &mut self, con: &mut Console, x: i32, y: i32, transparent: Option<(u8, u8, u8, u8)> )

blit an image on a console

x,y are the coordinate of the top left image pixel in the console

image pixels using the transparent color will be ignored

source

pub fn blit_ex( &mut self, con: &mut Console, x: f32, y: f32, scalex: f32, scaley: f32, angle: f32, transparent: Option<(u8, u8, u8, u8)> )

blit an image on a console

x,y are the coordinate of the image center in the console image can be scaled and rotated (angle is in radians) image pixels using the transparent color will be ignored

Examples found in repository?
examples/image.rs (lines 56-64)
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
fn render(mut root_console: ResMut<RootConsole>, mut skull: NonSendMut<SkullImage>) {
    let root_console = &mut **root_console;
    let skull = &mut *skull;
    let scale = skull.scale_time.cos();
    root_console.clear(None, Some((0, 0, 0, 255)), None);
    skull.skull.blit_ex(
        root_console,
        (root_console.get_width() / 2) as f32,
        (root_console.get_height() / 2) as f32,
        scale,
        scale,
        skull.angle,
        None,
    );
}
source

pub fn blit_2x( &mut self, con: &mut Console, dx: i32, dy: i32, sx: i32, sy: i32, w: Option<i32>, h: Option<i32>, transparent: Option<(u8, u8, u8, u8)> )

blit an image on the console, using the subcell characters to achieve twice the normal resolution. This uses the CHAR_SUBCELL_* ascii codes (from 226 to 232):

subcell_chars

Comparison before/after subcell in the chronicles of Doryen :

subcell_comp

Pyromancer! screenshot, making full usage of subcell resolution:

subcell_pyro

Examples found in repository?
examples/subcell.rs (line 45)
41
42
43
44
45
46
47
48
49
50
51
52
53
54
fn render(mut root_console: ResMut<RootConsole>, mut skull: NonSendMut<SkullImage>) {
    root_console.clear(None, Some((0, 0, 0, 255)), None);
    skull
        .skull
        .blit_2x(&mut root_console, 23, 0, 0, 0, None, None, None);
    root_console.print(
        40,
        4,
        "Those pixels\nare twice smaller\nthan a console cell.\nMagic!",
        TextAlign::Center,
        Some((0, 0, 0, 255)),
        None,
    );
}
More examples
Hide additional examples
examples/demo/level.rs (line 245)
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
fn render_level(
    mut root_console: ResMut<RootConsole>,
    ground: NonSend<GroundImage>,
    size: Res<LevelSize>,
    map: Res<LevelMap>,
    mut visited_2x: ResMut<Visited2x>,
    light_map: NonSend<LightMap>,
) {
    let mut render_output = Image::new_empty(size.width as u32 * 2, size.height as u32 * 2);
    let light_map = &light_map.0;
    for y in 0..size.height * 2 {
        for x in 0..size.width * 2 {
            let off = x + y * size.width * 2;
            if map.0.is_in_fov(x, y) && (map.0.is_transparent(x, y) || !visited_2x.0[off]) {
                visited_2x.0[off] = true;
                let ground_col = ground.0.pixel(x as u32, y as u32).unwrap();
                let light_col = light_map.pixel(x as u32, y as u32).unwrap();
                let mut r = f32::from(ground_col.0) * f32::from(light_col.0) * LIGHT_COEF / 255.0;
                let mut g = f32::from(ground_col.1) * f32::from(light_col.1) * LIGHT_COEF / 255.0;
                let mut b = f32::from(ground_col.2) * f32::from(light_col.2) * LIGHT_COEF / 255.0;
                r = r.min(255.0);
                g = g.min(255.0);
                b = b.min(255.0);
                render_output.put_pixel(x as u32, y as u32, (r as u8, g as u8, b as u8, 255));
            } else if visited_2x.0[off] {
                let col = ground.0.pixel(x as u32, y as u32).unwrap();
                let dark_col = color_blend(col, VISITED_BLEND_COLOR, VISITED_BLEND_COEF);
                render_output.put_pixel(x as u32, y as u32, dark_col);
            } else {
                render_output.put_pixel(x as u32, y as u32, (0, 0, 0, 255));
            }
        }
    }
    render_output.blit_2x(&mut root_console, 0, 0, 0, 0, None, None, None);
}
source

pub fn blit_2x_image( img: &ImageBuffer<Rgba<u8>, Vec<u8>>, con: &mut Console, dx: i32, dy: i32, sx: i32, sy: i32, w: Option<i32>, h: Option<i32>, transparent: Option<(u8, u8, u8, u8)> )

blit an image on a console. See Image::blit_2x

Auto Trait Implementations§

§

impl RefUnwindSafe for Image

§

impl Send for Image

§

impl Sync for Image

§

impl Unpin for Image

§

impl UnwindSafe for Image

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> Downcast for T
where T: Any,

§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Sync + Send>

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more