speedy2d/
renderer2d.rs

1/*
2 *  Copyright 2021 QuantumBadger
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16
17use std::rc::Rc;
18
19#[cfg(any(feature = "image-loading", doc, doctest))]
20use {
21    crate::image::ImageFileFormat,
22    image::GenericImageView,
23    std::fs::File,
24    std::io::{BufRead, BufReader, Seek},
25    std::path::Path
26};
27
28use crate::color::Color;
29use crate::dimen::{UVec2, Vec2};
30use crate::error::{BacktraceError, Context, ErrorMessage};
31use crate::font::{FormattedGlyph, FormattedTextBlock};
32use crate::font_cache::GlyphCache;
33use crate::glwrapper::*;
34use crate::image::{ImageDataType, ImageHandle, ImageSmoothingMode};
35use crate::{Polygon, RawBitmapData, Rect, Rectangle};
36
37struct AttributeBuffers
38{
39    position: Vec<f32>,
40    color: Vec<f32>,
41    texture_coord: Vec<f32>,
42    texture_mix: Vec<f32>,
43    circle_mix: Vec<f32>,
44
45    glbuf_position: GLBuffer,
46    glbuf_color: GLBuffer,
47    glbuf_texture_coord: GLBuffer,
48    glbuf_texture_mix: GLBuffer,
49    glbuf_circle_mix: GLBuffer
50}
51
52impl AttributeBuffers
53{
54    pub fn new(
55        context: &GLContextManager,
56        program: &GLProgram
57    ) -> Result<Self, BacktraceError<ErrorMessage>>
58    {
59        Ok(AttributeBuffers {
60            position: Vec::new(),
61            color: Vec::new(),
62            texture_coord: Vec::new(),
63            texture_mix: Vec::new(),
64            circle_mix: Vec::new(),
65
66            glbuf_position: context
67                .new_buffer(
68                    GLBufferTarget::Array,
69                    2,
70                    program
71                        .get_attribute_handle(Renderer2D::ATTR_NAME_POSITION)
72                        .context("Failed to get attribute POSITION")?
73                )
74                .context("Failed to create buffer for attribute POSITION")?,
75
76            glbuf_color: context
77                .new_buffer(
78                    GLBufferTarget::Array,
79                    4,
80                    program
81                        .get_attribute_handle(Renderer2D::ATTR_NAME_COLOR)
82                        .context("Failed to get attribute COLOR")?
83                )
84                .context("Failed to create buffer for attribute COLOR")?,
85
86            glbuf_texture_coord: context
87                .new_buffer(
88                    GLBufferTarget::Array,
89                    2,
90                    program
91                        .get_attribute_handle(Renderer2D::ATTR_NAME_TEXTURE_COORD)
92                        .context("Failed to get attribute TEXTURE_COORD")?
93                )
94                .context("Failed to create buffer for attribute TEXTURE_COORD")?,
95
96            glbuf_texture_mix: context
97                .new_buffer(
98                    GLBufferTarget::Array,
99                    1,
100                    program
101                        .get_attribute_handle(Renderer2D::ATTR_NAME_TEXTURE_MIX)
102                        .context("Failed to get attribute TEXTURE_MIX")?
103                )
104                .context("Failed to create buffer for attribute TEXTURE_MIX")?,
105
106            glbuf_circle_mix: context
107                .new_buffer(
108                    GLBufferTarget::Array,
109                    1,
110                    program
111                        .get_attribute_handle(Renderer2D::ATTR_NAME_CIRCLE_MIX)
112                        .context("Failed to get attribute CIRCLE_MIX")?
113                )
114                .context("Failed to create buffer for attribute CIRCLE_MIX")?
115        })
116    }
117
118    #[inline]
119    pub fn get_vertex_count(&self) -> usize
120    {
121        self.texture_mix.len()
122    }
123
124    pub fn upload_and_clear(&mut self, context: &GLContextManager)
125    {
126        self.glbuf_position.set_data(context, &self.position);
127        self.glbuf_color.set_data(context, &self.color);
128        self.glbuf_texture_coord
129            .set_data(context, &self.texture_coord);
130        self.glbuf_texture_mix.set_data(context, &self.texture_mix);
131        self.glbuf_circle_mix.set_data(context, &self.circle_mix);
132        self.clear();
133    }
134
135    pub fn clear(&mut self)
136    {
137        self.position.clear();
138        self.color.clear();
139        self.texture_coord.clear();
140        self.texture_mix.clear();
141        self.circle_mix.clear();
142    }
143
144    #[inline]
145    pub fn append(
146        &mut self,
147        position: &Vec2,
148        color: &Color,
149        texture_coord: &Vec2,
150        texture_mix: f32,
151        circle_mix: f32
152    )
153    {
154        AttributeBuffers::push_vec2(&mut self.position, position);
155        AttributeBuffers::push_color(&mut self.color, color);
156        AttributeBuffers::push_vec2(&mut self.texture_coord, texture_coord);
157        self.texture_mix.push(texture_mix);
158        self.circle_mix.push(circle_mix);
159    }
160
161    #[inline]
162    fn push_vec2(dest: &mut Vec<f32>, vertices: &Vec2)
163    {
164        dest.push(vertices.x);
165        dest.push(vertices.y);
166    }
167
168    #[inline]
169    fn push_color(dest: &mut Vec<f32>, color: &Color)
170    {
171        dest.push(color.r());
172        dest.push(color.g());
173        dest.push(color.b());
174        dest.push(color.a());
175    }
176}
177
178struct Uniforms
179{
180    scale_x: GLUniformHandle,
181    scale_y: GLUniformHandle,
182    texture: GLUniformHandle
183}
184
185impl Uniforms
186{
187    fn new(
188        context: &GLContextManager,
189        program: &Rc<GLProgram>
190    ) -> Result<Uniforms, BacktraceError<ErrorMessage>>
191    {
192        Ok(Uniforms {
193            scale_x: program
194                .get_uniform_handle(context, Renderer2D::UNIFORM_NAME_SCALE_X)
195                .context("Failed to find SCALE_X uniform")?,
196            scale_y: program
197                .get_uniform_handle(context, Renderer2D::UNIFORM_NAME_SCALE_Y)
198                .context("Failed to find SCALE_Y uniform")?,
199            texture: program
200                .get_uniform_handle(context, Renderer2D::UNIFORM_NAME_TEXTURE)
201                .context("Failed to find TEXTURE uniform")?
202        })
203    }
204
205    fn set_viewport_size_pixels(
206        &self,
207        context: &GLContextManager,
208        viewport_size_pixels: UVec2
209    )
210    {
211        self.scale_x
212            .set_value_float(context, 2.0 / viewport_size_pixels.x as f32);
213        self.scale_y
214            .set_value_float(context, -2.0 / viewport_size_pixels.y as f32);
215    }
216
217    fn set_texture_unit(&self, context: &GLContextManager, texture_unit: i32)
218    {
219        self.texture.set_value_int(context, texture_unit);
220    }
221}
222
223pub(crate) struct Renderer2DVertex
224{
225    pub position: Vec2,
226    pub texture_coord: Vec2,
227    pub color: Color,
228    pub texture_mix: f32,
229    pub circle_mix: f32
230}
231
232impl Renderer2DVertex
233{
234    #[inline]
235    fn append_to_attribute_buffers(&self, attribute_buffers: &mut AttributeBuffers)
236    {
237        attribute_buffers.append(
238            &self.position,
239            &self.color,
240            &self.texture_coord,
241            self.texture_mix,
242            self.circle_mix
243        );
244    }
245}
246
247pub(crate) struct Renderer2DAction
248{
249    pub texture: Option<GLTexture>,
250    pub vertices_clockwise: [Renderer2DVertex; 3]
251}
252
253impl Renderer2DAction
254{
255    #[inline]
256    fn update_current_texture_if_empty(
257        &self,
258        current_texture: &mut Option<GLTexture>
259    ) -> bool
260    {
261        match &self.texture {
262            None => true,
263
264            Some(own_texture) => match current_texture {
265                None => {
266                    *current_texture = Some(own_texture.clone());
267                    true
268                }
269                Some(current_texture) => *current_texture == *own_texture
270            }
271        }
272    }
273
274    #[inline]
275    fn append_to_attribute_buffers(&self, attribute_buffers: &mut AttributeBuffers)
276    {
277        for vertex in self.vertices_clockwise.iter() {
278            vertex.append_to_attribute_buffers(attribute_buffers);
279        }
280    }
281}
282
283enum RenderQueueItem
284{
285    FormattedTextBlock
286    {
287        position: Vec2,
288        color: Color,
289        block: FormattedTextBlock
290    },
291
292    FormattedTextGlyph
293    {
294        position: Vec2,
295        color: Color,
296        glyph: FormattedGlyph,
297        crop_window: Rect
298    },
299
300    CircleSectionColored
301    {
302        vertex_positions_clockwise: [Vec2; 3],
303        vertex_colors_clockwise: [Color; 3],
304        vertex_normalized_circle_coords_clockwise: [Vec2; 3]
305    },
306
307    TriangleColored
308    {
309        vertex_positions_clockwise: [Vec2; 3],
310        vertex_colors_clockwise: [Color; 3]
311    },
312
313    TriangleTextured
314    {
315        vertex_positions_clockwise: [Vec2; 3],
316        vertex_colors_clockwise: [Color; 3],
317        vertex_texture_coords_clockwise: [Vec2; 3],
318        texture: GLTexture
319    }
320}
321
322impl RenderQueueItem
323{
324    #[inline]
325    fn generate_actions(
326        &self,
327        glyph_cache: &GlyphCache,
328        runner: &mut impl FnMut(Renderer2DAction)
329    )
330    {
331        match self {
332            RenderQueueItem::FormattedTextBlock {
333                position,
334                color,
335                block
336            } => {
337                for line in block.iter_lines() {
338                    for glyph in line.iter_glyphs() {
339                        glyph_cache.get_renderer2d_actions(
340                            glyph, *position, *color, None, runner
341                        );
342                    }
343                }
344            }
345
346            RenderQueueItem::FormattedTextGlyph {
347                glyph,
348                position,
349                color,
350                crop_window
351            } => {
352                glyph_cache.get_renderer2d_actions(
353                    glyph,
354                    *position,
355                    *color,
356                    Some(crop_window),
357                    runner
358                );
359            }
360
361            RenderQueueItem::CircleSectionColored {
362                vertex_positions_clockwise,
363                vertex_colors_clockwise,
364                vertex_normalized_circle_coords_clockwise
365            } => runner(Renderer2DAction {
366                texture: None,
367                vertices_clockwise: [
368                    Renderer2DVertex {
369                        position: vertex_positions_clockwise[0],
370                        texture_coord: vertex_normalized_circle_coords_clockwise[0],
371                        color: vertex_colors_clockwise[0],
372                        texture_mix: 0.0,
373                        circle_mix: 1.0
374                    },
375                    Renderer2DVertex {
376                        position: vertex_positions_clockwise[1],
377                        texture_coord: vertex_normalized_circle_coords_clockwise[1],
378                        color: vertex_colors_clockwise[1],
379                        texture_mix: 0.0,
380                        circle_mix: 1.0
381                    },
382                    Renderer2DVertex {
383                        position: vertex_positions_clockwise[2],
384                        texture_coord: vertex_normalized_circle_coords_clockwise[2],
385                        color: vertex_colors_clockwise[2],
386                        texture_mix: 0.0,
387                        circle_mix: 1.0
388                    }
389                ]
390            }),
391
392            RenderQueueItem::TriangleColored {
393                vertex_positions_clockwise,
394                vertex_colors_clockwise
395            } => runner(Renderer2DAction {
396                texture: None,
397                vertices_clockwise: [
398                    Renderer2DVertex {
399                        position: vertex_positions_clockwise[0],
400                        texture_coord: Vec2::ZERO,
401                        color: vertex_colors_clockwise[0],
402                        texture_mix: 0.0,
403                        circle_mix: 0.0
404                    },
405                    Renderer2DVertex {
406                        position: vertex_positions_clockwise[1],
407                        texture_coord: Vec2::ZERO,
408                        color: vertex_colors_clockwise[1],
409                        texture_mix: 0.0,
410                        circle_mix: 0.0
411                    },
412                    Renderer2DVertex {
413                        position: vertex_positions_clockwise[2],
414                        texture_coord: Vec2::ZERO,
415                        color: vertex_colors_clockwise[2],
416                        texture_mix: 0.0,
417                        circle_mix: 0.0
418                    }
419                ]
420            }),
421
422            RenderQueueItem::TriangleTextured {
423                vertex_positions_clockwise,
424                vertex_colors_clockwise,
425                vertex_texture_coords_clockwise,
426                texture
427            } => runner(Renderer2DAction {
428                texture: Some(texture.clone()),
429                vertices_clockwise: [
430                    Renderer2DVertex {
431                        position: vertex_positions_clockwise[0],
432                        texture_coord: vertex_texture_coords_clockwise[0],
433                        color: vertex_colors_clockwise[0],
434                        texture_mix: 1.0,
435                        circle_mix: 0.0
436                    },
437                    Renderer2DVertex {
438                        position: vertex_positions_clockwise[1],
439                        texture_coord: vertex_texture_coords_clockwise[1],
440                        color: vertex_colors_clockwise[1],
441                        texture_mix: 1.0,
442                        circle_mix: 0.0
443                    },
444                    Renderer2DVertex {
445                        position: vertex_positions_clockwise[2],
446                        texture_coord: vertex_texture_coords_clockwise[2],
447                        color: vertex_colors_clockwise[2],
448                        texture_mix: 1.0,
449                        circle_mix: 0.0
450                    }
451                ]
452            })
453        }
454    }
455}
456
457pub struct Renderer2D
458{
459    context: GLContextManager,
460
461    program: Rc<GLProgram>,
462
463    render_queue: Vec<RenderQueueItem>,
464
465    glyph_cache: GlyphCache,
466    attribute_buffers: AttributeBuffers,
467    current_texture: Option<GLTexture>,
468
469    #[allow(dead_code)]
470    uniforms: Uniforms
471}
472
473impl Renderer2D
474{
475    const ATTR_NAME_POSITION: &'static str = "in_Position";
476    const ATTR_NAME_COLOR: &'static str = "in_Color";
477    const ATTR_NAME_TEXTURE_COORD: &'static str = "in_TextureCoord";
478    const ATTR_NAME_TEXTURE_MIX: &'static str = "in_TextureMix";
479    const ATTR_NAME_CIRCLE_MIX: &'static str = "in_CircleMix";
480
481    const UNIFORM_NAME_SCALE_X: &'static str = "in_ScaleX";
482    const UNIFORM_NAME_SCALE_Y: &'static str = "in_ScaleY";
483    const UNIFORM_NAME_TEXTURE: &'static str = "in_Texture";
484
485    const ALL_ATTRIBUTES: [&'static str; 5] = [
486        Renderer2D::ATTR_NAME_POSITION,
487        Renderer2D::ATTR_NAME_COLOR,
488        Renderer2D::ATTR_NAME_TEXTURE_COORD,
489        Renderer2D::ATTR_NAME_TEXTURE_MIX,
490        Renderer2D::ATTR_NAME_CIRCLE_MIX
491    ];
492
493    pub fn new(
494        context: &GLContextManager,
495        viewport_size_pixels: UVec2
496    ) -> Result<Self, BacktraceError<ErrorMessage>>
497    {
498        log::info!("Creating vertex shader");
499
500        let (vertex_shader_src, fragment_shader_src) = match context.version() {
501            GLVersion::OpenGL2_0 => {
502                log::info!("Using OpenGL 2.0 shaders");
503                (
504                    include_str!("shaders/r2d_vertex_v110.glsl"),
505                    include_str!("shaders/r2d_fragment_v110.glsl")
506                )
507            }
508            GLVersion::WebGL2_0 => {
509                log::info!("Using WebGL 2.0 shaders");
510                (
511                    include_str!("shaders/r2d_vertex_v300es.glsl"),
512                    include_str!("shaders/r2d_fragment_v300es.glsl")
513                )
514            }
515        };
516
517        let vertex_shader = context
518            .new_shader(GLShaderType::Vertex, vertex_shader_src)
519            .context("Failed to create Renderer2D vertex shader")?;
520
521        log::info!("Creating fragment shader");
522
523        let fragment_shader = context
524            .new_shader(GLShaderType::Fragment, fragment_shader_src)
525            .context("Failed to create Renderer2D fragment shader")?;
526
527        log::info!("Compiling program");
528
529        let program = context
530            .new_program(
531                &vertex_shader,
532                &fragment_shader,
533                &Renderer2D::ALL_ATTRIBUTES
534            )
535            .context("Failed to create Renderer2D program")?;
536
537        let attribute_buffers = AttributeBuffers::new(context, &program)?;
538        let uniforms = Uniforms::new(context, &program)?;
539
540        context.use_program(&program);
541
542        uniforms.set_texture_unit(context, 0);
543
544        uniforms.set_viewport_size_pixels(context, viewport_size_pixels);
545
546        context.set_viewport_size(viewport_size_pixels);
547
548        Ok(Renderer2D {
549            context: context.clone(),
550            program,
551            render_queue: Vec::new(),
552            glyph_cache: GlyphCache::new(),
553            attribute_buffers,
554            current_texture: None,
555            uniforms
556        })
557    }
558
559    pub fn set_viewport_size_pixels(&self, viewport_size_pixels: UVec2)
560    {
561        self.uniforms
562            .set_viewport_size_pixels(&self.context, viewport_size_pixels);
563
564        self.context.set_viewport_size(viewport_size_pixels);
565    }
566
567    pub fn finish_frame(&mut self)
568    {
569        self.flush_render_queue();
570        self.glyph_cache.on_new_frame_start();
571    }
572
573    fn flush_render_queue(&mut self)
574    {
575        if self.render_queue.is_empty() {
576            return;
577        }
578
579        self.attribute_buffers.clear();
580
581        let mut has_text = false;
582
583        for item in &self.render_queue {
584            match item {
585                RenderQueueItem::FormattedTextBlock {
586                    block, position, ..
587                } => {
588                    for line in block.iter_lines() {
589                        for glyph in line.iter_glyphs() {
590                            self.glyph_cache.add_to_cache(
591                                &self.context,
592                                glyph,
593                                *position
594                            );
595                        }
596                    }
597
598                    has_text = true;
599                }
600                RenderQueueItem::FormattedTextGlyph {
601                    glyph, position, ..
602                } => {
603                    self.glyph_cache
604                        .add_to_cache(&self.context, glyph, *position);
605                    has_text = true;
606                }
607                RenderQueueItem::CircleSectionColored { .. }
608                | RenderQueueItem::TriangleColored { .. }
609                | RenderQueueItem::TriangleTextured { .. } => {}
610            }
611        }
612
613        if has_text {
614            if let Err(err) = self.glyph_cache.prepare_for_draw(&self.context) {
615                log::error!("Error updating font texture, continuing anyway: {:?}", err);
616            }
617        }
618
619        {
620            let current_texture = &mut self.current_texture;
621            let context = &self.context;
622            let program = &self.program;
623            let attribute_buffers = &mut self.attribute_buffers;
624
625            for item in &self.render_queue {
626                item.generate_actions(&self.glyph_cache, &mut |action| {
627                    if !action.update_current_texture_if_empty(current_texture) {
628                        Renderer2D::draw_buffers(
629                            context,
630                            program,
631                            attribute_buffers,
632                            current_texture
633                        );
634
635                        current_texture.clone_from(&action.texture);
636                    }
637
638                    action.append_to_attribute_buffers(attribute_buffers);
639                });
640            }
641        }
642
643        self.render_queue.clear();
644
645        Renderer2D::draw_buffers(
646            &self.context,
647            &self.program,
648            &mut self.attribute_buffers,
649            &mut self.current_texture
650        );
651    }
652
653    fn draw_buffers(
654        context: &GLContextManager,
655        program: &Rc<GLProgram>,
656        attribute_buffers: &mut AttributeBuffers,
657        current_texture: &mut Option<GLTexture>
658    )
659    {
660        let vertex_count = attribute_buffers.get_vertex_count();
661
662        if vertex_count == 0 {
663            return;
664        }
665
666        context.use_program(program);
667
668        attribute_buffers.upload_and_clear(context);
669
670        let current_texture = current_texture.take();
671
672        match &current_texture {
673            None => context.unbind_texture(),
674            Some(texture) => context.bind_texture(texture)
675        }
676
677        context.draw_triangles(
678            GLBlendEnabled::Enabled(GLBlendMode::OneMinusSrcAlpha),
679            vertex_count
680        );
681    }
682
683    pub(crate) fn create_image_from_raw_pixels<S: Into<UVec2>>(
684        &self,
685        data_type: ImageDataType,
686        smoothing_mode: ImageSmoothingMode,
687        size: S,
688        data: &[u8]
689    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
690    {
691        let size = size.into();
692
693        let pixel_bytes = match data_type {
694            ImageDataType::RGB => 3,
695            ImageDataType::RGBA => 4
696        };
697
698        {
699            let expected_bytes = pixel_bytes * size.x as usize * size.y as usize;
700
701            if expected_bytes != data.len() {
702                return Err(ErrorMessage::msg(format!(
703                    "Expecting {} bytes ({}x{}x{}), got {}",
704                    expected_bytes,
705                    size.x,
706                    size.y,
707                    pixel_bytes,
708                    data.len()
709                )));
710            }
711        }
712
713        let gl_format = data_type.into();
714
715        let gl_smoothing = match smoothing_mode {
716            ImageSmoothingMode::NearestNeighbor => GLTextureSmoothing::NearestNeighbour,
717            ImageSmoothingMode::Linear => GLTextureSmoothing::Linear
718        };
719
720        let texture = self
721            .context
722            .new_texture()
723            .context("Failed to create GPU texture")?;
724
725        texture
726            .set_image_data(&self.context, gl_format, gl_smoothing, &size, data)
727            .context("Failed to upload image data")?;
728
729        Ok(ImageHandle { size, texture })
730    }
731
732    #[cfg(any(feature = "image-loading", doc, doctest))]
733    pub fn create_image_from_file_path<P: AsRef<Path>>(
734        &mut self,
735        data_type: Option<ImageFileFormat>,
736        smoothing_mode: ImageSmoothingMode,
737        path: P
738    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
739    {
740        let file = File::open(path.as_ref()).context(format!(
741            "Failed to open file '{:?}' for reading",
742            path.as_ref()
743        ))?;
744
745        self.create_image_from_file_bytes(data_type, smoothing_mode, BufReader::new(file))
746    }
747
748    #[cfg(any(feature = "image-loading", doc, doctest))]
749    pub fn create_image_from_file_bytes<R: Seek + BufRead>(
750        &mut self,
751        data_type: Option<ImageFileFormat>,
752        smoothing_mode: ImageSmoothingMode,
753        file_bytes: R
754    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
755    {
756        let mut reader = image::io::Reader::new(file_bytes);
757
758        match data_type {
759            None => {
760                reader = reader
761                    .with_guessed_format()
762                    .context("Could not guess file format")?
763            }
764            Some(format) => reader.set_format(match format {
765                ImageFileFormat::PNG => image::ImageFormat::Png,
766                ImageFileFormat::JPEG => image::ImageFormat::Jpeg,
767                ImageFileFormat::GIF => image::ImageFormat::Gif,
768                ImageFileFormat::BMP => image::ImageFormat::Bmp,
769                ImageFileFormat::ICO => image::ImageFormat::Ico,
770                ImageFileFormat::TIFF => image::ImageFormat::Tiff,
771                ImageFileFormat::WebP => image::ImageFormat::WebP,
772                ImageFileFormat::AVIF => image::ImageFormat::Avif,
773                ImageFileFormat::PNM => image::ImageFormat::Pnm,
774                ImageFileFormat::DDS => image::ImageFormat::Dds,
775                ImageFileFormat::TGA => image::ImageFormat::Tga,
776                ImageFileFormat::Farbfeld => image::ImageFormat::Farbfeld
777            })
778        }
779
780        let image = reader.decode().context("Failed to parse image data")?;
781
782        let dimensions = image.dimensions();
783
784        let bytes_rgba8 = image.into_rgba8().into_raw();
785
786        self.create_image_from_raw_pixels(
787            ImageDataType::RGBA,
788            smoothing_mode,
789            dimensions,
790            bytes_rgba8.as_slice()
791        )
792    }
793
794    #[inline]
795    pub(crate) fn clear_screen(&mut self, color: Color)
796    {
797        if color.a() < 1.0 {
798            self.flush_render_queue();
799        } else {
800            self.render_queue.clear();
801        }
802
803        self.context.clear_screen(color);
804    }
805
806    #[inline]
807    fn add_to_render_queue(&mut self, item: RenderQueueItem)
808    {
809        self.render_queue.push(item);
810
811        if self.render_queue.len() > 100000 {
812            self.flush_render_queue();
813        }
814    }
815
816    #[inline]
817    pub(crate) fn draw_polygon<V: Into<Vec2>>(
818        &mut self,
819        polygon: &Polygon,
820        offset: V,
821        color: Color
822    )
823    {
824        let color = [color; 3];
825        let offset = offset.into();
826
827        for triangle in polygon.triangles.iter() {
828            let triangle = triangle.map(|vertex| vertex + offset);
829
830            self.draw_triangle_three_color(triangle, color);
831        }
832    }
833
834    #[inline]
835    pub(crate) fn draw_triangle_three_color(
836        &mut self,
837        vertex_positions_clockwise: [Vec2; 3],
838        vertex_colors_clockwise: [Color; 3]
839    )
840    {
841        self.add_to_render_queue(RenderQueueItem::TriangleColored {
842            vertex_positions_clockwise,
843            vertex_colors_clockwise
844        })
845    }
846
847    #[inline]
848    pub(crate) fn draw_triangle_image_tinted(
849        &mut self,
850        vertex_positions_clockwise: [Vec2; 3],
851        vertex_colors_clockwise: [Color; 3],
852        vertex_texture_coords_clockwise: [Vec2; 3],
853        image: &ImageHandle
854    )
855    {
856        self.add_to_render_queue(RenderQueueItem::TriangleTextured {
857            vertex_positions_clockwise,
858            vertex_colors_clockwise,
859            vertex_texture_coords_clockwise,
860            texture: image.texture.clone()
861        })
862    }
863
864    #[inline]
865    pub(crate) fn draw_text<V: Into<Vec2>>(
866        &mut self,
867        position: V,
868        color: Color,
869        text: &FormattedTextBlock
870    )
871    {
872        self.add_to_render_queue(RenderQueueItem::FormattedTextBlock {
873            position: position.into(),
874            color,
875            block: text.clone()
876        })
877    }
878
879    #[inline]
880    pub(crate) fn draw_text_cropped<V: Into<Vec2>>(
881        &mut self,
882        position: V,
883        crop_window: Rect,
884        color: Color,
885        text: &FormattedTextBlock
886    )
887    {
888        let position = position.into();
889
890        for line in text.iter_lines() {
891            for glyph in line.iter_glyphs() {
892                if let Some(glyph_outline) = glyph.pixel_bounding_box() {
893                    let glyph_outline = glyph_outline.with_offset(position);
894                    if glyph_outline.intersect(&crop_window).is_some() {
895                        self.add_to_render_queue(RenderQueueItem::FormattedTextGlyph {
896                            position,
897                            color,
898                            glyph: glyph.clone(),
899                            crop_window: crop_window.clone()
900                        })
901                    }
902                }
903            }
904        }
905    }
906
907    #[inline]
908    pub(crate) fn draw_circle_section(
909        &mut self,
910        vertex_positions_clockwise: [Vec2; 3],
911        vertex_colors_clockwise: [Color; 3],
912        vertex_normalized_circle_coords_clockwise: [Vec2; 3]
913    )
914    {
915        self.add_to_render_queue(RenderQueueItem::CircleSectionColored {
916            vertex_positions_clockwise,
917            vertex_colors_clockwise,
918            vertex_normalized_circle_coords_clockwise
919        })
920    }
921
922    #[inline]
923    pub(crate) fn set_clip(&mut self, rect: Option<Rectangle<i32>>)
924    {
925        // If we change the clip area, we need to draw everything in a queue
926        // through the current clip before setting new one.
927        self.flush_render_queue();
928        match rect {
929            None => self.context.set_enable_scissor(false),
930            Some(rect) => {
931                self.context.set_enable_scissor(true);
932                self.context.set_clip(
933                    rect.top_left().x,
934                    rect.top_left().y,
935                    rect.width(),
936                    rect.height()
937                )
938            }
939        }
940    }
941
942    pub(crate) fn capture(&mut self, format: ImageDataType) -> RawBitmapData
943    {
944        self.flush_render_queue();
945        self.context.capture(format)
946    }
947}