1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
68
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
//! Adds integrations with [bevy_egui] and other [egui] types. This modules
//! requires the `egui` feature.

use bevy::{math::Vec2, prelude::*};
use bevy_egui::{
    egui::{self, Color32},
    EguiContext,
};

use crate::{
    pixel::Pixel,
    pixel_buffer::{Fill, FillKind, PixelBuffer, PixelBufferSize},
};

/// Component inserted by the [PixelBufferEguiPlugin]. Holds all the necesary
/// information to draw a egui image with the pixel buffer.
#[derive(Component, Clone, Copy, Debug)]
pub struct EguiTexture {
    /// Egui texture ID
    pub id: egui::TextureId,
    /// Natural Size of the texture
    pub size: egui::Vec2,
}

/// Plugin that adds a [EguiTexture] component to all pixel buffers and keeps them up to date
pub struct PixelBufferEguiPlugin;

impl Plugin for PixelBufferEguiPlugin {
    fn build(&self, app: &mut App) {
        app.add_stage_after(
            CoreStage::PreUpdate, // So EguiTexture updates after pixel buffer is done, but before user updates the frame
            "pixel_buffer_egui",
            SystemStage::single_threaded(),
        )
        .add_system_to_stage("pixel_buffer_egui", register_egui)
        .add_system_to_stage(
            "pixel_buffer_egui",
            update_egui_texture_size.after(register_egui),
        );
    }
}

#[allow(clippy::type_complexity)]
fn register_egui(
    mut commands: Commands,
    mut egui_ctx: ResMut<EguiContext>,
    pixel_buffer: Query<(Entity, &PixelBuffer, &Handle<Image>), Added<Handle<Image>>>,
) {
    for (entity, pb, image_handle) in pixel_buffer.iter() {
        let texture = EguiTexture {
            id: egui_ctx.add_image(image_handle.clone_weak()),
            size: pb.size.egui_texture_size(),
        };
        commands.entity(entity).insert(texture);
    }
}

fn update_egui_texture_size(
    mut pixel_buffer: Query<(&PixelBuffer, &mut EguiTexture), Changed<PixelBuffer>>,
) {
    for (pb, mut texture) in pixel_buffer.iter_mut() {
        texture.size = pb.size.egui_texture_size();
    }
}

/*

EGUI EXTENSIONS FOR OTHER TYPES IN THE CRATE

*/

impl PixelBufferSize {
    /// Gets the size of the texture for the pixel buffer
    pub fn egui_texture_size(&self) -> egui::Vec2 {
        let sz = self.screen_size();
        egui::Vec2::new(sz.x as f32, sz.y as f32)
    }
}

impl Fill {
    /// Updates the [Fill] component to a [egui::Vec2] size.
    pub fn update_egui(&mut self, evec2: egui::Vec2) {
        self.kind = FillKind::Custom(Vec2::new(evec2.x, evec2.y));
    }
}

impl From<bevy_egui::egui::Color32> for Pixel {
    fn from(c: bevy_egui::egui::Color32) -> Self {
        c.to_array().into()
    }
}

impl Pixel {
    /// Gets the color as the egui Color32 sRGB color type
    pub fn as_egui_color32(self) -> egui::Color32 {
        Color32::from_rgba_unmultiplied(self.r, self.g, self.b, self.a)
    }

    /// Get the color as the egui RGBA linear color type
    pub fn as_egui_rgba(self) -> egui::Rgba {
        egui::Rgba::from_rgba_unmultiplied(
            self.r as f32 / 255.0,
            self.g as f32 / 255.0,
            self.b as f32 / 255.0,
            self.a as f32 / 255.0,
        )
    }
}

impl crate::query::PixelBuffersItem<'_> {
    /// Update the [PixelBuffer::fill] to an egui area
    pub fn update_fill_egui(&mut self, available_size: egui::Vec2) {
        self.pixel_buffer.fill.update_egui(available_size);
    }

    /// Gets the [EguiTexture] component of the query item.
    ///
    /// This is a shorthand for `item.egui_texture.unwrap()`. The `unwrap` will
    /// only panic if the [PixelBufferEguiPlugin] is not enabled.
    ///
    /// # Panics
    /// If the entity did not have an [EguiTexture] component because the
    /// [PixelBufferEguiPlugin] was not added to the app.
    pub fn egui_texture(&self) -> &EguiTexture {
        self.egui_texture.unwrap()
    }
}

impl crate::query::PixelBuffersReadOnlyItem<'_> {
    /// Gets the [EguiTexture] component of the query item.
    ///
    /// This is a shorthand for `item.egui_texture.unwrap()`. The `unwrap` will
    /// only panic if the [PixelBufferEguiPlugin] is not enabled.
    ///
    /// # Panics
    /// If the entity did not have an [EguiTexture] component because the
    /// [PixelBufferEguiPlugin] was not added to the app.
    pub fn egui_texture(&self) -> &EguiTexture {
        self.egui_texture.unwrap()
    }
}

impl<'w, 's> crate::query::QueryPixelBuffer<'w, 's> {
    /// Update the [PixelBuffer::fill] to an egui area
    ///
    /// # Panics
    /// If there are none or more than one pixel buffers. This method is
    /// intented to be used when there's only one pixel buffer.
    pub fn update_fill_egui(&mut self, available_size: egui::Vec2) {
        self.query
            .single_mut()
            .pixel_buffer
            .fill
            .update_egui(available_size);
    }

    /// Get the [EguiTexture] component.
    ///
    /// # Panics
    /// - If there are none or more than one pixel buffers. This method is
    /// intented to be used when there's only one pixel buffer.
    ///
    /// - If the entity did not have an [EguiTexture] component because the
    /// [PixelBufferEguiPlugin] was not added to the app.
    pub fn egui_texture(&self) -> &EguiTexture {
        self.query.single().egui_texture.unwrap()
    }
}