Skip to main content

kas_soft/
draw.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4//     https://www.apache.org/licenses/LICENSE-2.0
5
6//! Root drawing implementations
7
8use super::{atlas, basic};
9use kas::cast::Cast;
10use kas::draw::{
11    AllocError, DrawImpl, DrawSharedImpl, PassId, PassType, UploadError, WindowCommon,
12};
13use kas::draw::{ImageFormat, ImageId, color};
14use kas::geom::{Quad, Size, Vec2};
15use kas::prelude::{Offset, Rect};
16use kas::text::{self};
17
18#[derive(Debug)]
19struct ClipRegion {
20    rect: Rect,
21    offset: Offset,
22    order: u64,
23}
24
25impl Default for ClipRegion {
26    fn default() -> Self {
27        ClipRegion {
28            rect: Rect::ZERO,
29            offset: Offset::ZERO,
30            order: 1,
31        }
32    }
33}
34
35kas::impl_scope! {
36    #[impl_default]
37    pub struct Draw {
38        pub(crate) common: WindowCommon,
39        images: atlas::Window,
40        clip_regions: Vec<ClipRegion> = vec![Default::default()],
41        basic: basic::Draw,
42    }
43}
44
45impl DrawImpl for Draw {
46    fn common_mut(&mut self) -> &mut WindowCommon {
47        &mut self.common
48    }
49
50    fn new_pass(
51        &mut self,
52        parent_pass: PassId,
53        rect: Rect,
54        offset: Offset,
55        class: PassType,
56    ) -> PassId {
57        let parent = match class {
58            PassType::Clip => &self.clip_regions[parent_pass.pass()],
59            PassType::Overlay => {
60                // NOTE: parent_pass.pass() is always zero in this case since
61                // this is only invoked from the Window (root).
62                &self.clip_regions[0]
63            }
64        };
65        let order = match class {
66            PassType::Clip => (parent.order << 4) + 1,
67            PassType::Overlay => (parent.order << 16) + 1,
68        };
69        let rect = rect - parent.offset;
70        let offset = offset + parent.offset;
71        let rect = rect.intersection(&parent.rect).unwrap_or(Rect::ZERO);
72        let pass = self.clip_regions.len().cast();
73        self.clip_regions.push(ClipRegion {
74            rect,
75            offset,
76            order,
77        });
78        PassId::new(pass)
79    }
80
81    #[inline]
82    fn get_clip_rect(&self, pass: PassId) -> Rect {
83        let region = &self.clip_regions[pass.pass()];
84        region.rect + region.offset
85    }
86
87    fn rect(&mut self, pass: PassId, rect: Quad, col: color::Rgba) {
88        self.basic.rect(pass, rect, col);
89    }
90
91    #[inline]
92    fn frame(&mut self, pass: PassId, outer: Quad, inner: Quad, col: color::Rgba) {
93        self.basic.frame(pass, outer, inner, col);
94    }
95
96    #[inline]
97    fn line(&mut self, pass: PassId, p1: Vec2, p2: Vec2, width: f32, col: color::Rgba) {
98        self.basic.line(pass, p1, p2, width, col);
99    }
100}
101
102impl Draw {
103    pub fn resize(&mut self, size: Size) {
104        self.clip_regions[0].rect.size = size;
105    }
106
107    pub fn render(&mut self, shared: &mut Shared, buffer: &mut [u32], size: (usize, usize)) {
108        shared.images.prepare(&mut shared.text);
109
110        // Order passes to ensure overlays are drawn after other content
111        let mut passes: Vec<_> = self.clip_regions.iter().enumerate().collect();
112        // Note that sorting is stable (does not re-order equal elements):
113        passes.sort_by_key(|pass| pass.1.order);
114
115        for (pass, clip) in passes.drain(..) {
116            self.basic
117                .render(pass, buffer, size, clip.rect, clip.offset);
118            self.images
119                .render(&shared.images, pass, buffer, size, clip.rect, clip.offset);
120        }
121
122        // Keep only first clip region (which is the entire window)
123        self.clip_regions.truncate(1);
124    }
125}
126
127pub struct Shared {
128    images: atlas::Shared,
129    text: kas::text::raster::State,
130}
131
132impl Default for Shared {
133    fn default() -> Self {
134        Shared {
135            images: atlas::Shared::new(),
136            text: Default::default(),
137        }
138    }
139}
140
141impl DrawSharedImpl for Shared {
142    type Draw = Draw;
143
144    #[inline]
145    fn max_texture_dimension_2d(&self) -> u32 {
146        atlas::MAX_TEX_SIZE.cast()
147    }
148
149    #[inline]
150    fn set_raster_config(&mut self, config: &kas::config::RasterConfig) {
151        self.images.set_raster_config(config);
152        self.text.set_raster_config(config);
153    }
154
155    #[inline]
156    fn image_alloc(&mut self, format: ImageFormat, size: Size) -> Result<ImageId, AllocError> {
157        self.images.alloc(format, size)
158    }
159
160    #[inline]
161    fn image_upload(&mut self, id: ImageId, data: &[u8]) -> Result<(), UploadError> {
162        self.images.upload(id, data)
163    }
164
165    #[inline]
166    fn image_free(&mut self, id: ImageId) {
167        self.images.free(id);
168    }
169
170    #[inline]
171    fn image_size(&self, id: ImageId) -> Option<Size> {
172        self.images.image_size(id)
173    }
174
175    fn draw_image(&self, draw: &mut Draw, pass: PassId, id: ImageId, rect: Quad) {
176        if let Some((atlas, tex)) = self.images.get_im_atlas_coords(id) {
177            draw.images.rect(pass, atlas, tex, rect);
178        }
179    }
180
181    #[inline]
182    fn draw_text(
183        &mut self,
184        draw: &mut Draw,
185        pass: PassId,
186        pos: Vec2,
187        bb: Quad,
188        text: &text::TextDisplay,
189        col: color::Rgba,
190    ) {
191        let time = std::time::Instant::now();
192        self.text
193            .text(&mut self.images, &mut draw.images, pass, pos, bb, text, col);
194        draw.common.report_dur_text(time.elapsed());
195    }
196
197    fn draw_text_effects(
198        &mut self,
199        draw: &mut Draw,
200        pass: PassId,
201        pos: Vec2,
202        bb: Quad,
203        text: &text::TextDisplay,
204        colors: &[color::Rgba],
205        effects: &[text::Effect],
206    ) {
207        let time = std::time::Instant::now();
208        self.text.text_effects(
209            &mut self.images,
210            &mut draw.images,
211            pass,
212            pos,
213            bb,
214            text,
215            colors,
216            effects,
217            |quad, col| {
218                draw.basic.rect(pass, quad, col);
219            },
220        );
221        draw.common.report_dur_text(time.elapsed());
222    }
223}