speedy2d/
lib.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
17//! Hardware-accelerated drawing of shapes, images, and text, with an easy to
18//! use API.
19//!
20//! Speedy2D aims to be:
21//!
22//!  - The simplest Rust API for creating a window, rendering graphics/text, and
23//!    handling input
24//!  - Compatible with any device supporting OpenGL 2.0+ or WebGL 2.0. Support
25//!    for OpenGL ES 2.0+ is planned.
26//!  - Very fast
27//!
28//! Supports Windows, Mac, Linux, and WebGL. Support for Android and iOS is in
29//! development.
30//!
31//! By default, Speedy2D contains support for setting up a window with an OpenGL
32//! context. If you'd like to handle this yourself, and use Speedy2D only for
33//! rendering, you can disable the `windowing` feature.
34//!
35//! # Useful Links
36//!
37//! * [Source repository](https://github.com/QuantumBadger/Speedy2D)
38//! * [Crate](https://crates.io/crates/speedy2d)
39//!
40//! # Getting Started (Windows/Mac/Linux)
41//!
42//! ## Create a window
43//!
44//! After adding Speedy2D to your Cargo.toml dependencies, create a window as
45//! follows:
46//!
47//! ```rust,no_run
48//! use speedy2d::Window;
49//!
50//! let window = Window::new_centered("Title", (640, 480)).unwrap();
51//! ```
52//!
53//! You may also use [Window::new_fullscreen_borderless()],
54//! [Window::new_with_options()], or [Window::new_with_user_events()].
55//!
56//! ## Implement the callbacks
57//!
58//! Create a struct implementing the `WindowHandler` trait. Override
59//! whichever callbacks you're interested in, for example `on_draw()`,
60//! `on_mouse_move()`, or `on_key_down()`.
61//!
62//! ```
63//! use speedy2d::window::{WindowHandler, WindowHelper};
64//! use speedy2d::Graphics2D;
65//!
66//! struct MyWindowHandler {}
67//!
68//! impl WindowHandler for MyWindowHandler
69//! {
70//!     fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D)
71//!     {
72//!         // Draw things here using `graphics`
73//!     }
74//! }
75//! ```
76//!
77//! The full list of possible callbacks is currently as follows. See
78//! [WindowHandler] for full documentation.
79//!
80//! It's only necessary to implement the callbacks you actually want to use. The
81//! default implementation will do nothing and continue the event loop.
82//!
83//! ```text
84//! fn on_start()
85//! fn on_user_event()
86//! fn on_resize()
87//! fn on_scale_factor_changed()
88//! fn on_draw()
89//! fn on_mouse_move()
90//! fn on_mouse_button_down()
91//! fn on_mouse_button_up()
92//! fn on_key_down()
93//! fn on_key_up()
94//! fn on_keyboard_char()
95//! fn on_keyboard_modifiers_changed()
96//! ```
97//!
98//! Each callback gives you a [window::WindowHelper] instance, which
99//! lets you perform window-related actions, like requesting that a new frame is
100//! drawn using [window::WindowHelper::request_redraw()].
101//!
102//! Note: Unless you call [window::WindowHelper::request_redraw()], frames will
103//! only be drawn when necessary, for example when resizing the window.
104//!
105//! ## Render some graphics
106//!
107//! The [WindowHandler::on_draw()] callback gives you a [Graphics2D]
108//! instance, which lets you draw shapes, text, and images.
109//!
110//! ```
111//! # use speedy2d::window::{WindowHandler, WindowHelper};
112//! # use speedy2d::Graphics2D;
113//! # use speedy2d::color::Color;
114//! #
115//! # struct MyWindowHandler {}
116//! #
117//! # impl WindowHandler for MyWindowHandler
118//! # {
119//!     fn on_draw(&mut self, helper: &mut WindowHelper, graphics: &mut Graphics2D)
120//!     {
121//!         graphics.clear_screen(Color::from_rgb(0.8, 0.9, 1.0));
122//!         graphics.draw_circle((100.0, 100.0), 75.0, Color::BLUE);
123//!
124//!         // Request that we draw another frame once this one has finished
125//!         helper.request_redraw();
126//!     }
127//! # }
128//! ```
129//!
130//! ## Start it running!
131//!
132//! Once you've implemented the callbacks you're interested in, start the event
133//! loop running with [Window::run_loop()]:
134//!
135//! ```rust,no_run
136//! # use speedy2d::Window;
137//! # struct MyWindowHandler {}
138//! # impl speedy2d::window::WindowHandler for MyWindowHandler {}
139//! let window = Window::<()>::new_centered("Title", (640, 480)).unwrap();
140//!
141//! window.run_loop(MyWindowHandler{});
142//! ```
143//!
144//! ## Alternative: Managing the GL context yourself
145//!
146//! If you'd rather handle the window creation and OpenGL context management
147//! yourself, simply disable Speedy2D's `windowing` feature in your `Cargo.toml`
148//! file, and create a context as follows. You will need to specify a loader
149//! function to allow Speedy2D to obtain the OpenGL function pointers.
150//!
151//! ```rust,no_run
152//! use speedy2d::GLRenderer;
153//! # struct WindowContext {}
154//! # impl WindowContext {
155//! #     fn get_proc_address(&self, fn_name: &str) -> *const std::ffi::c_void
156//! #     {
157//! #         std::ptr::null()
158//! #     }
159//! # }
160//! # let window_context = WindowContext {};
161//!
162//! let mut renderer = unsafe {
163//!     GLRenderer::new_for_gl_context((640, 480), |fn_name| {
164//!         window_context.get_proc_address(fn_name) as *const _
165//!     })
166//! }.unwrap();
167//! ```
168//!
169//! Then, draw a frame using [GLRenderer::draw_frame()]:
170//!
171//! ```rust,no_run
172//! # use speedy2d::GLRenderer;
173//! # use speedy2d::color::Color;
174//! # let mut renderer = unsafe {
175//! #     GLRenderer::new_for_gl_context((640, 480), |fn_name| {
176//! #         std::ptr::null() as *const _
177//! #     })
178//! # }.unwrap();
179//! renderer.draw_frame(|graphics| {
180//!     graphics.clear_screen(Color::WHITE);
181//!     graphics.draw_circle((100.0, 100.0), 75.0, Color::BLUE);
182//! });
183//! ```
184//!
185//! # Laying out text
186//!
187//! To render text, a font must be created. Call [font::Font::new()] with the
188//! bytes from the TTF or OTF font file.
189//!
190//! (note: OTF support may be limited)
191//!
192//! ```rust,no_run
193//! use speedy2d::font::Font;
194//!
195//! let bytes = include_bytes!("../assets/fonts/NotoSans-Regular.ttf");
196//! let font = Font::new(bytes).unwrap();
197//! ```
198//!
199//! Then, invoke `font.layout_text()` (part of the [font::TextLayout] trait) to
200//! calculate the necessary line breaks and spacing. This will give you
201//! a [font::FormattedTextBlock].
202//!
203//! ```rust,no_run
204//! # use speedy2d::font::{Font, TextOptions};
205//! # let font = Font::new(&[]).unwrap();
206//! use speedy2d::font::TextLayout;
207//!
208//! let block = font.layout_text("Hello World", 32.0, TextOptions::new());
209//! ```
210//!
211//! Finally, call [Graphics2D::draw_text()] to draw the text block!
212//!
213//! ```rust,no_run
214//! # use speedy2d::GLRenderer;
215//! # use speedy2d::color::Color;
216//! # use speedy2d::font::{Font, TextOptions, TextLayout};
217//! # let font = Font::new(&[]).unwrap();
218//! # let block = font.layout_text("Hello World", 32.0, TextOptions::new());
219//! # let mut renderer = unsafe {
220//! #     GLRenderer::new_for_gl_context((640, 480), |fn_name| {
221//! #         std::ptr::null() as *const _
222//! #     })
223//! # }.unwrap();
224//! # renderer.draw_frame(|graphics| {
225//! graphics.draw_text((100.0, 100.0), Color::BLUE, &block);
226//! # });
227//! ```
228//!
229//! ## Word wrap
230//!
231//! To wrap lines of text to a certain width, use
232//! [font::TextOptions::with_wrap_to_width()]:
233//!
234//! ```rust,no_run
235//! # use speedy2d::font::{Font, TextOptions};
236//! # let font = Font::new(&[]).unwrap();
237//! use speedy2d::font::{TextLayout, TextAlignment};
238//!
239//! let block = font.layout_text(
240//!     "The quick brown fox jumps over the lazy dog.",
241//!     32.0,
242//!     TextOptions::new().with_wrap_to_width(300.0, TextAlignment::Left));
243//! ```
244//!
245//! # Loading images
246//!
247//! Image files (in formats such as PNG, JPG, and BMP) can be loaded using the
248//! following APIs, available in both `Graphics2D` and `GLRenderer`.
249//!
250//! * [Graphics2D::create_image_from_file_path()]
251//! * [Graphics2D::create_image_from_file_bytes()]
252//! * [GLRenderer::create_image_from_file_path()]
253//! * [GLRenderer::create_image_from_file_bytes()]
254//!
255//! Alternatively, you can create an image from raw pixel data, using:
256//!
257//! * [Graphics2D::create_image_from_raw_pixels()]
258//! * [GLRenderer::create_image_from_raw_pixels()]
259//!
260//! # Getting Started (WebGL)
261//!
262//! To use Speedy2D with WebGL, your app must be compiled for WebAssembly.
263//! Speedy2D can attach itself to a `canvas` on the page using an ID you
264//! specify.
265//!
266//! As with Windows/Mac/Linux targets, it's possible to use Speedy2D either in a
267//! full rendering and event handling configuation, or for rendering only.
268//!
269//! For rendering only, use the following API:
270//!
271//! * [GLRenderer::new_for_web_canvas_by_id()]
272//!
273//! For full keyboard/mouse/etc event handling in addition to rendering, use:
274//!
275//! * [WebCanvas::new_for_id()]
276//! * [WebCanvas::new_for_id_with_user_events()]
277//!
278//! After initialization, the usual [WindowHandler] callbacks and
279//! [window::WindowHelper]/[Graphics2D] APIs should operate as on other
280//! platforms.
281//!
282//! For an example, see the `examples/webgl` directory. To build this, first
283//! install the prerequisites:
284//!
285//! ```shell
286//! cargo install wasm-bindgen-cli just
287//! ```
288//!
289//! Then use the following command to run the build:
290//!
291//! ```shell
292//! just build-example-webgl
293//! ```
294
295#![deny(warnings)]
296#![deny(missing_docs)]
297
298use std::fmt::{Display, Formatter};
299#[cfg(any(doc, doctest, all(target_arch = "wasm32", feature = "windowing")))]
300use std::marker::PhantomData;
301use std::rc::Rc;
302
303#[cfg(any(feature = "image-loading", doc, doctest))]
304use {
305    crate::image::ImageFileFormat,
306    std::io::{BufRead, Seek},
307    std::path::Path
308};
309
310use crate::color::Color;
311use crate::dimen::{UVec2, Vec2};
312use crate::error::{BacktraceError, ErrorMessage};
313use crate::font::FormattedTextBlock;
314use crate::glbackend::GLBackend;
315#[cfg(not(target_arch = "wasm32"))]
316use crate::glbackend::GLBackendGlow;
317use crate::glwrapper::{GLContextManager, GLVersion};
318use crate::image::{ImageDataType, ImageHandle, ImageSmoothingMode, RawBitmapData};
319use crate::renderer2d::Renderer2D;
320use crate::shape::{Polygon, Rect, Rectangle, RoundedRectangle};
321#[cfg(target_arch = "wasm32")]
322use crate::web::WebCanvasElement;
323#[cfg(any(doc, doctest, feature = "windowing"))]
324use crate::window::WindowHandler;
325#[cfg(any(doc, doctest, all(feature = "windowing", not(target_arch = "wasm32"))))]
326use crate::window::{
327    UserEventSender,
328    WindowCreationError,
329    WindowCreationOptions,
330    WindowPosition,
331    WindowSize
332};
333#[cfg(any(doc, doctest))]
334use crate::window_internal_doctest::{WebCanvasImpl, WindowGlutin};
335#[cfg(all(
336    feature = "windowing",
337    not(target_arch = "wasm32"),
338    not(any(doc, doctest))
339))]
340use crate::window_internal_glutin::WindowGlutin;
341#[cfg(all(feature = "windowing", target_arch = "wasm32", not(any(doc, doctest))))]
342use crate::window_internal_web::WebCanvasImpl;
343
344/// Types representing colors.
345pub mod color;
346
347/// Types representing shapes.
348pub mod shape;
349
350/// Components for loading fonts and laying out text.
351pub mod font;
352
353/// Types representing sizes and positions.
354pub mod dimen;
355
356/// Utilities and traits for numeric values.
357pub mod numeric;
358
359/// Error types.
360pub mod error;
361
362/// Types relating to images.
363pub mod image;
364
365/// Utilities for accessing the system clock on all platforms.
366pub mod time;
367
368/// Allows for the creation and management of windows.
369#[cfg(any(doc, doctest, feature = "windowing"))]
370pub mod window;
371
372#[cfg(all(
373    feature = "windowing",
374    not(target_arch = "wasm32"),
375    not(any(doc, doctest))
376))]
377mod window_internal_glutin;
378
379#[cfg(all(feature = "windowing", target_arch = "wasm32", not(any(doc, doctest))))]
380mod window_internal_web;
381
382#[cfg(any(doc, doctest))]
383mod window_internal_doctest;
384
385#[cfg(target_arch = "wasm32")]
386mod web;
387
388mod font_cache;
389mod glbackend;
390#[cfg(all(feature = "windowing", not(target_arch = "wasm32")))]
391mod glutin_winit;
392mod glwrapper;
393mod renderer2d;
394mod texture_packer;
395mod utils;
396
397/// An error encountered during the creation of a [GLRenderer].
398#[derive(Clone, Debug)]
399pub struct GLRendererCreationError
400{
401    description: String
402}
403
404impl GLRendererCreationError
405{
406    fn msg_with_cause<S, Cause>(description: S, cause: Cause) -> BacktraceError<Self>
407    where
408        S: AsRef<str>,
409        Cause: std::error::Error + 'static
410    {
411        BacktraceError::new_with_cause(
412            Self {
413                description: description.as_ref().to_string()
414            },
415            cause
416        )
417    }
418
419    #[allow(dead_code)]
420    fn msg<S>(description: S) -> BacktraceError<Self>
421    where
422        S: AsRef<str>
423    {
424        BacktraceError::new(Self {
425            description: description.as_ref().to_string()
426        })
427    }
428}
429
430impl Display for GLRendererCreationError
431{
432    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
433    {
434        Display::fmt("GL renderer creation error: ", f)?;
435        Display::fmt(&self.description, f)
436    }
437}
438
439/// A graphics renderer using an OpenGL backend.
440///
441/// Note: There is no need to use this struct if you are letting Speedy2D create
442/// a window for you.
443pub struct GLRenderer
444{
445    context: GLContextManager,
446    renderer: Graphics2D
447}
448
449impl GLRenderer
450{
451    /// Creates a `GLRenderer` with the specified OpenGL loader function. The
452    /// loader function takes the name of an OpenGL function, and returns the
453    /// associated function pointer. `viewport_size_pixels` should be set to
454    /// the initial viewport size, however this can be changed later using
455    /// [GLRenderer:: set_viewport_size_pixels()].
456    ///
457    /// Note: This function must not be called if you are letting Speedy2D
458    /// create a window for you.
459    ///
460    /// # Safety
461    ///
462    /// While a `GLRenderer` object is active, you must not make any changes to
463    /// the active GL context. Doing so may lead to undefined behavior,
464    /// which is why this function is marked `unsafe`. It is strongly
465    /// advised not to use any other OpenGL libraries in the same thread
466    /// as `GLRenderer`.
467    #[cfg(not(target_arch = "wasm32"))]
468    pub unsafe fn new_for_gl_context<V, F>(
469        viewport_size_pixels: V,
470        loader_function: F
471    ) -> Result<Self, BacktraceError<GLRendererCreationError>>
472    where
473        V: Into<UVec2>,
474        F: FnMut(&str) -> *const std::os::raw::c_void
475    {
476        let backend =
477            GLBackendGlow::new(glow::Context::from_loader_function(loader_function));
478
479        Self::new_with_gl_backend(
480            viewport_size_pixels,
481            Rc::new(backend),
482            GLVersion::OpenGL2_0
483        )
484    }
485
486    /// Creates a `GLRenderer` for the specified HTML canvas. The canvas
487    /// will be found based on the specified ID.
488    ///
489    /// The parameter `viewport_size_pixels` should be set to
490    /// the initial canvas size, however this can be changed later using
491    /// [GLRenderer:: set_viewport_size_pixels()].
492    #[cfg(any(doc, doctest, target_arch = "wasm32"))]
493    pub fn new_for_web_canvas_by_id<V, S>(
494        viewport_size_pixels: V,
495        element_id: S
496    ) -> Result<Self, BacktraceError<GLRendererCreationError>>
497    where
498        V: Into<UVec2>,
499        S: AsRef<str>
500    {
501        WebCanvasElement::new_by_id(element_id.as_ref())
502            .map_err(|err| {
503                GLRendererCreationError::msg_with_cause("Failed to get canvas", err)
504            })?
505            .get_webgl2_context(viewport_size_pixels)
506    }
507
508    fn new_with_gl_backend<V: Into<UVec2>>(
509        viewport_size_pixels: V,
510        gl_backend: Rc<dyn GLBackend>,
511        gl_version: GLVersion
512    ) -> Result<Self, BacktraceError<GLRendererCreationError>>
513    {
514        let viewport_size_pixels = viewport_size_pixels.into();
515
516        let context =
517            GLContextManager::create(gl_backend, gl_version).map_err(|err| {
518                GLRendererCreationError::msg_with_cause(
519                    "GL context manager creation failed",
520                    err
521                )
522            })?;
523
524        let renderer = Graphics2D {
525            renderer: Renderer2D::new(&context, viewport_size_pixels).map_err(|err| {
526                GLRendererCreationError::msg_with_cause("Renderer2D creation failed", err)
527            })?
528        };
529
530        Ok(GLRenderer { context, renderer })
531    }
532
533    /// Sets the renderer viewport to the specified pixel size, in response to a
534    /// change in the window size.
535    pub fn set_viewport_size_pixels(&mut self, viewport_size_pixels: UVec2)
536    {
537        self.renderer
538            .renderer
539            .set_viewport_size_pixels(viewport_size_pixels)
540    }
541
542    /// Creates a new [ImageHandle] from the specified raw pixel data.
543    ///
544    /// The data provided in the `data` parameter must be in the format
545    /// specified by `data_type`.
546    ///
547    /// The returned [ImageHandle] is valid only for the current graphics
548    /// context.
549    pub fn create_image_from_raw_pixels(
550        &mut self,
551        data_type: ImageDataType,
552        smoothing_mode: ImageSmoothingMode,
553        size: UVec2,
554        data: &[u8]
555    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
556    {
557        self.renderer
558            .create_image_from_raw_pixels(data_type, smoothing_mode, size, data)
559    }
560
561    /// Loads an image from the specified file path.
562    ///
563    /// If no `data_type` is provided, an attempt will be made to guess the file
564    /// format.
565    ///
566    /// For a list of supported image types, see [image::ImageFileFormat].
567    ///
568    /// The returned [ImageHandle] is valid only for the current graphics
569    /// context.
570    #[cfg(any(feature = "image-loading", doc, doctest))]
571    pub fn create_image_from_file_path<S: AsRef<Path>>(
572        &mut self,
573        data_type: Option<ImageFileFormat>,
574        smoothing_mode: ImageSmoothingMode,
575        path: S
576    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
577    {
578        self.renderer
579            .create_image_from_file_path(data_type, smoothing_mode, path)
580    }
581
582    /// Loads an image from the provided encoded image file data.
583    ///
584    /// If no `data_type` is provided, an attempt will be made to guess the file
585    /// format.
586    ///
587    /// The data source must implement `std::io::BufRead` and `std::io::Seek`.
588    /// For example, if you have a `&[u8]`, you may wrap it in a
589    /// `std::io::Cursor` as follows:
590    ///
591    /// ```rust,no_run
592    /// # use speedy2d::GLRenderer;
593    /// # use speedy2d::color::Color;
594    /// # use speedy2d::image::ImageSmoothingMode;
595    /// use std::io::Cursor;
596    /// # let mut renderer = unsafe {
597    /// #     GLRenderer::new_for_gl_context((640, 480), |fn_name| {
598    /// #         std::ptr::null() as *const _
599    /// #     })
600    /// # }.unwrap();
601    ///
602    /// let image_bytes : &[u8] = include_bytes!("../assets/screenshots/hello_world.png");
603    ///
604    /// let image_result = renderer.create_image_from_file_bytes(
605    ///     None,
606    ///     ImageSmoothingMode::Linear,
607    ///     Cursor::new(image_bytes));
608    /// ```
609    ///
610    /// For a list of supported image types, see [image::ImageFileFormat].
611    ///
612    /// The returned [ImageHandle] is valid only for the current graphics
613    /// context.
614    #[cfg(any(feature = "image-loading", doc, doctest))]
615    pub fn create_image_from_file_bytes<R: Seek + BufRead>(
616        &mut self,
617        data_type: Option<ImageFileFormat>,
618        smoothing_mode: ImageSmoothingMode,
619        file_bytes: R
620    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
621    {
622        self.renderer
623            .create_image_from_file_bytes(data_type, smoothing_mode, file_bytes)
624    }
625
626    /// Starts the process of drawing a frame. A `Graphics2D` object will be
627    /// provided to the callback. When the callback returns, the internal
628    /// render queue will be flushed.
629    ///
630    /// Note: if calling this method, you are responsible for swapping the
631    /// window context buffers if necessary.
632    #[inline]
633    pub fn draw_frame<F: FnOnce(&mut Graphics2D) -> R, R>(&mut self, callback: F) -> R
634    {
635        self.renderer.set_clip(None);
636        let result = callback(&mut self.renderer);
637        self.renderer.renderer.finish_frame();
638        result
639    }
640}
641
642impl Drop for GLRenderer
643{
644    fn drop(&mut self)
645    {
646        self.context.mark_invalid();
647    }
648}
649
650/// A `Graphics2D` object allows you to draw shapes, images, and text to the
651/// screen.
652///
653/// An instance is provided in the [window::WindowHandler::on_draw] callback.
654///
655/// If you are managing the GL context yourself, you must invoke
656/// [GLRenderer::draw_frame] to obtain an instance.
657pub struct Graphics2D
658{
659    renderer: Renderer2D
660}
661
662impl Graphics2D
663{
664    /// Creates a new [ImageHandle] from the specified raw pixel data.
665    ///
666    /// The data provided in the `data` parameter must be in the format
667    /// specified by `data_type`.
668    ///
669    /// The returned [ImageHandle] is valid only for the current graphics
670    /// context.
671    pub fn create_image_from_raw_pixels<S: Into<UVec2>>(
672        &mut self,
673        data_type: ImageDataType,
674        smoothing_mode: ImageSmoothingMode,
675        size: S,
676        data: &[u8]
677    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
678    {
679        self.renderer.create_image_from_raw_pixels(
680            data_type,
681            smoothing_mode,
682            size.into(),
683            data
684        )
685    }
686
687    /// Loads an image from the specified file path.
688    ///
689    /// If no `data_type` is provided, an attempt will be made to guess the file
690    /// format.
691    ///
692    /// For a list of supported image types, see [image::ImageFileFormat].
693    ///
694    /// The returned [ImageHandle] is valid only for the current graphics
695    /// context.
696    #[cfg(any(feature = "image-loading", doc, doctest))]
697    pub fn create_image_from_file_path<S: AsRef<Path>>(
698        &mut self,
699        data_type: Option<ImageFileFormat>,
700        smoothing_mode: ImageSmoothingMode,
701        path: S
702    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
703    {
704        self.renderer
705            .create_image_from_file_path(data_type, smoothing_mode, path)
706    }
707
708    /// Loads an image from the provided encoded image file data.
709    ///
710    /// If no `data_type` is provided, an attempt will be made to guess the file
711    /// format.
712    ///
713    /// The data source must implement `std::io::BufRead` and `std::io::Seek`.
714    /// For example, if you have a `&[u8]`, you may wrap it in a
715    /// `std::io::Cursor` as follows:
716    ///
717    /// ```rust,no_run
718    /// # use speedy2d::GLRenderer;
719    /// # use speedy2d::color::Color;
720    /// # use speedy2d::image::ImageSmoothingMode;
721    /// use std::io::Cursor;
722    /// # let mut renderer = unsafe {
723    /// #     GLRenderer::new_for_gl_context((640, 480), |fn_name| {
724    /// #         std::ptr::null() as *const _
725    /// #     })
726    /// # }.unwrap();
727    /// # renderer.draw_frame(|graphics| {
728    ///
729    /// let image_bytes : &[u8] = include_bytes!("../assets/screenshots/hello_world.png");
730    ///
731    /// let image_result = graphics.create_image_from_file_bytes(
732    ///     None,
733    ///     ImageSmoothingMode::Linear,
734    ///     Cursor::new(image_bytes));
735    /// # });
736    /// ```
737    ///
738    /// For a list of supported image types, see [image::ImageFileFormat].
739    ///
740    /// The returned [ImageHandle] is valid only for the current graphics
741    /// context.
742    #[cfg(any(feature = "image-loading", doc, doctest))]
743    pub fn create_image_from_file_bytes<R: Seek + BufRead>(
744        &mut self,
745        data_type: Option<ImageFileFormat>,
746        smoothing_mode: ImageSmoothingMode,
747        file_bytes: R
748    ) -> Result<ImageHandle, BacktraceError<ErrorMessage>>
749    {
750        self.renderer
751            .create_image_from_file_bytes(data_type, smoothing_mode, file_bytes)
752    }
753
754    /// Fills the screen with the specified color.
755    pub fn clear_screen(&mut self, color: Color)
756    {
757        self.renderer.clear_screen(color);
758    }
759
760    /// Draws the provided block of text at the specified position.
761    ///
762    /// Lines of text can be prepared by loading a font (using
763    /// [crate::font::Font::new]), and calling `layout_text_line()` on that
764    /// font with your desired text.
765    ///
766    /// To fall back to another font if a glyph isn't found, see
767    /// [crate::font::FontFamily].
768    ///
769    /// To achieve good performance, it's possible to layout a line of text
770    /// once, and then re-use the same [crate::font::FormattedTextLine]
771    /// object whenever you need to draw that text to the screen.
772    ///
773    /// Note: Text will be rendered with subpixel precision. If the subpixel
774    /// position changes between frames, performance may be degraded, as the
775    /// text will need to be re-rendered and re-uploaded. To avoid this,
776    /// call `round()` on the position coordinates, to ensure that
777    /// the text is always located at an integer pixel position.
778    pub fn draw_text<V: Into<Vec2>>(
779        &mut self,
780        position: V,
781        color: Color,
782        text: &FormattedTextBlock
783    )
784    {
785        self.renderer.draw_text(position, color, text);
786    }
787
788    /// Draws the provided block of text at the specified position, cropped to
789    /// the specified window. Characters outside this window will not be
790    /// rendered. Characters partially inside the window will be cropped.
791    ///
792    /// Both `position` and `crop_window` are relative to the overall render
793    /// window.
794    ///
795    /// See the documentation for [Graphics2D::draw_text] for more details.
796    pub fn draw_text_cropped<V: Into<Vec2>>(
797        &mut self,
798        position: V,
799        crop_window: Rect,
800        color: Color,
801        text: &FormattedTextBlock
802    )
803    {
804        self.renderer
805            .draw_text_cropped(position, crop_window, color, text);
806    }
807
808    /// Draws a polygon with a single color, with the specified offset in
809    /// pixels.
810    pub fn draw_polygon<V: Into<Vec2>>(
811        &mut self,
812        polygon: &Polygon,
813        offset: V,
814        color: Color
815    )
816    {
817        self.renderer.draw_polygon(polygon, offset, color)
818    }
819
820    /// Draws a triangle with the specified colors (one color for each corner).
821    ///
822    /// The vertex positions (and associated colors) must be provided in
823    /// clockwise order.
824    pub fn draw_triangle_three_color(
825        &mut self,
826        vertex_positions_clockwise: [Vec2; 3],
827        vertex_colors_clockwise: [Color; 3]
828    )
829    {
830        self.renderer.draw_triangle_three_color(
831            vertex_positions_clockwise,
832            vertex_colors_clockwise
833        );
834    }
835
836    /// Draws part of an image, tinted with the provided colors, at the
837    /// specified location. The sub-image will be scaled to fill the
838    /// triangle described by the vertices in `vertex_positions_clockwise`.
839    ///
840    /// The coordinates in `image_coords_normalized` should be in the range
841    /// `0.0` to `1.0`, and define the portion of the source image which
842    /// should be drawn.
843    ///
844    /// The tinting is performed by for each pixel by multiplying each color
845    /// component in the image pixel by the corresponding color component in
846    /// the `color` parameter.
847    ///
848    /// The vertex positions (and associated colors and image coordinates) must
849    /// be provided in clockwise order.
850    pub fn draw_triangle_image_tinted_three_color(
851        &mut self,
852        vertex_positions_clockwise: [Vec2; 3],
853        vertex_colors: [Color; 3],
854        image_coords_normalized: [Vec2; 3],
855        image: &ImageHandle
856    )
857    {
858        self.renderer.draw_triangle_image_tinted(
859            vertex_positions_clockwise,
860            vertex_colors,
861            image_coords_normalized,
862            image
863        );
864    }
865
866    /// Draws a triangle with the specified color.
867    ///
868    /// The vertex positions must be provided in clockwise order.
869    #[inline]
870    pub fn draw_triangle(&mut self, vertex_positions_clockwise: [Vec2; 3], color: Color)
871    {
872        self.draw_triangle_three_color(vertex_positions_clockwise, [color, color, color]);
873    }
874
875    /// Draws a quadrilateral with the specified colors (one color for each
876    /// corner).
877    ///
878    /// The vertex positions (and associated colors) must be provided in
879    /// clockwise order.
880    #[inline]
881    pub fn draw_quad_four_color(
882        &mut self,
883        vertex_positions_clockwise: [Vec2; 4],
884        vertex_colors: [Color; 4]
885    )
886    {
887        let vp = vertex_positions_clockwise;
888        let vc = vertex_colors;
889
890        self.draw_triangle_three_color([vp[0], vp[1], vp[2]], [vc[0], vc[1], vc[2]]);
891
892        self.draw_triangle_three_color([vp[2], vp[3], vp[0]], [vc[2], vc[3], vc[0]]);
893    }
894
895    /// Draws a quadrilateral with the specified color.
896    ///
897    /// The vertex positions must be provided in clockwise order.
898    #[inline]
899    pub fn draw_quad(&mut self, vertex_positions_clockwise: [Vec2; 4], color: Color)
900    {
901        self.draw_quad_four_color(
902            vertex_positions_clockwise,
903            [color, color, color, color]
904        );
905    }
906
907    /// Draws part of an image, tinted with the provided colors, at the
908    /// specified location. The sub-image will be scaled to fill the
909    /// quadrilateral described by the vertices in
910    /// `vertex_positions_clockwise`.
911    ///
912    /// The coordinates in `image_coords_normalized` should be in the range
913    /// `0.0` to `1.0`, and define the portion of the source image which
914    /// should be drawn.
915    ///
916    /// The tinting is performed by for each pixel by multiplying each color
917    /// component in the image pixel by the corresponding color component in
918    /// the `color` parameter.
919    ///
920    /// The vertex positions (and associated colors and image coordinates) must
921    /// be provided in clockwise order.
922    #[inline]
923    pub fn draw_quad_image_tinted_four_color(
924        &mut self,
925        vertex_positions_clockwise: [Vec2; 4],
926        vertex_colors: [Color; 4],
927        image_coords_normalized: [Vec2; 4],
928        image: &ImageHandle
929    )
930    {
931        let vp = vertex_positions_clockwise;
932        let vc = vertex_colors;
933        let ic = image_coords_normalized;
934
935        self.draw_triangle_image_tinted_three_color(
936            [vp[0], vp[1], vp[2]],
937            [vc[0], vc[1], vc[2]],
938            [ic[0], ic[1], ic[2]],
939            image
940        );
941
942        self.draw_triangle_image_tinted_three_color(
943            [vp[2], vp[3], vp[0]],
944            [vc[2], vc[3], vc[0]],
945            [ic[2], ic[3], ic[0]],
946            image
947        );
948    }
949
950    /// Draws part of an image, tinted with the provided color, at the specified
951    /// location. The sub-image will be scaled to fill the pixel coordinates
952    /// in the provided rectangle.
953    ///
954    /// The coordinates in `image_coords_normalized` should be in the range
955    /// `0.0` to `1.0`, and define the portion of the source image which
956    /// should be drawn.
957    ///
958    /// The tinting is performed by for each pixel by multiplying each color
959    /// component in the image pixel by the corresponding color component in
960    /// the `color` parameter.
961    #[inline]
962    pub fn draw_rectangle_image_subset_tinted(
963        &mut self,
964        rect: impl AsRef<Rectangle>,
965        color: Color,
966        image_coords_normalized: impl AsRef<Rectangle>,
967        image: &ImageHandle
968    )
969    {
970        let rect = rect.as_ref();
971        let image_coords_normalized = image_coords_normalized.as_ref();
972
973        self.draw_quad_image_tinted_four_color(
974            [
975                *rect.top_left(),
976                rect.top_right(),
977                *rect.bottom_right(),
978                rect.bottom_left()
979            ],
980            [color, color, color, color],
981            [
982                *image_coords_normalized.top_left(),
983                image_coords_normalized.top_right(),
984                *image_coords_normalized.bottom_right(),
985                image_coords_normalized.bottom_left()
986            ],
987            image
988        );
989    }
990
991    /// Draws an image, tinted with the provided color, at the specified
992    /// location. The image will be scaled to fill the pixel coordinates in
993    /// the provided rectangle.
994    ///
995    /// The tinting is performed by for each pixel by multiplying each color
996    /// component in the image pixel by the corresponding color component in
997    /// the `color` parameter.
998    #[inline]
999    pub fn draw_rectangle_image_tinted(
1000        &mut self,
1001        rect: impl AsRef<Rectangle>,
1002        color: Color,
1003        image: &ImageHandle
1004    )
1005    {
1006        self.draw_rectangle_image_subset_tinted(
1007            rect,
1008            color,
1009            Rectangle::new(Vec2::ZERO, Vec2::new(1.0, 1.0)),
1010            image
1011        );
1012    }
1013
1014    /// Draws an image at the specified location. The image will be
1015    /// scaled to fill the pixel coordinates in the provided rectangle.
1016    #[inline]
1017    pub fn draw_rectangle_image(
1018        &mut self,
1019        rect: impl AsRef<Rectangle>,
1020        image: &ImageHandle
1021    )
1022    {
1023        self.draw_rectangle_image_tinted(rect, Color::WHITE, image);
1024    }
1025
1026    /// Draws an image at the specified pixel location. The image will be
1027    /// drawn at its original size with no scaling.
1028    #[inline]
1029    pub fn draw_image<P: Into<Vec2>>(&mut self, position: P, image: &ImageHandle)
1030    {
1031        let position = position.into();
1032
1033        self.draw_rectangle_image(
1034            Rectangle::new(position, position + image.size().into_f32()),
1035            image
1036        );
1037    }
1038
1039    /// Draws a single-color rectangle at the specified location. The
1040    /// coordinates of the rectangle are specified in pixels.
1041    #[inline]
1042    pub fn draw_rectangle(&mut self, rect: impl AsRef<Rectangle>, color: Color)
1043    {
1044        let rect = rect.as_ref();
1045
1046        self.draw_quad(
1047            [
1048                *rect.top_left(),
1049                rect.top_right(),
1050                *rect.bottom_right(),
1051                rect.bottom_left()
1052            ],
1053            color
1054        );
1055    }
1056
1057    /// Draws a single-color rounded rectangle at the specified location. The
1058    /// coordinates of the rounded rectangle are specified in pixels.
1059    #[inline]
1060    pub fn draw_rounded_rectangle(
1061        &mut self,
1062        round_rect: impl AsRef<RoundedRectangle>,
1063        color: Color
1064    )
1065    {
1066        let round_rect = round_rect.as_ref();
1067
1068        //create 3 rectangles (the middle one is taller)
1069        //draw middle quad (the taller one)
1070        self.draw_quad(
1071            [
1072                round_rect.top_left() + Vec2::new(round_rect.radius(), 0.0),
1073                round_rect.top_right() + Vec2::new(-round_rect.radius(), 0.0),
1074                round_rect.bottom_right() + Vec2::new(-round_rect.radius(), 0.0),
1075                round_rect.bottom_left() + Vec2::new(round_rect.radius(), 0.0)
1076            ],
1077            color
1078        );
1079
1080        //draw left quad
1081        self.draw_quad(
1082            [
1083                round_rect.top_left() + Vec2::new(0.0, round_rect.radius()),
1084                round_rect.top_left()
1085                    + Vec2::new(round_rect.radius(), round_rect.radius()),
1086                round_rect.bottom_left()
1087                    + Vec2::new(round_rect.radius(), -round_rect.radius()),
1088                round_rect.bottom_left() + Vec2::new(0.0, -round_rect.radius())
1089            ],
1090            color
1091        );
1092
1093        //draw right quad
1094        self.draw_quad(
1095            [
1096                round_rect.top_right() + Vec2::new(0.0, round_rect.radius()),
1097                round_rect.top_right()
1098                    + Vec2::new(-round_rect.radius(), round_rect.radius()),
1099                round_rect.bottom_right()
1100                    + Vec2::new(-round_rect.radius(), -round_rect.radius()),
1101                round_rect.bottom_right() + Vec2::new(0.0, -round_rect.radius())
1102            ],
1103            color
1104        );
1105
1106        //draw triangles
1107        self.draw_triangle(
1108            [
1109                round_rect.top_left() + Vec2::new(round_rect.radius(), 0.0),
1110                round_rect.top_left()
1111                    + Vec2::new(round_rect.radius(), round_rect.radius()),
1112                round_rect.top_left() + Vec2::new(0.0, round_rect.radius())
1113            ],
1114            color
1115        );
1116        self.draw_triangle(
1117            [
1118                round_rect.top_right() + Vec2::new(-round_rect.radius(), 0.0),
1119                round_rect.top_right()
1120                    + Vec2::new(-round_rect.radius(), round_rect.radius()),
1121                round_rect.top_right() + Vec2::new(0.0, round_rect.radius())
1122            ],
1123            color
1124        );
1125        self.draw_triangle(
1126            [
1127                round_rect.bottom_left() + Vec2::new(round_rect.radius(), 0.0),
1128                round_rect.bottom_left() + Vec2::new(0.0, -round_rect.radius()),
1129                round_rect.bottom_left()
1130                    + Vec2::new(round_rect.radius(), -round_rect.radius())
1131            ],
1132            color
1133        );
1134        self.draw_triangle(
1135            [
1136                round_rect.bottom_right() + Vec2::new(-round_rect.radius(), 0.0),
1137                round_rect.bottom_right()
1138                    + Vec2::new(-round_rect.radius(), -round_rect.radius()),
1139                round_rect.bottom_right() + Vec2::new(0.0, -round_rect.radius())
1140            ],
1141            color
1142        );
1143
1144        //draw top right circle
1145        self.draw_circle_section_triangular_three_color(
1146            [
1147                round_rect.top_right() + Vec2::new(-round_rect.radius(), 0.0),
1148                round_rect.top_right(),
1149                round_rect.top_right() + Vec2::new(0.0, round_rect.radius())
1150            ],
1151            [color; 3],
1152            [
1153                Vec2::new(0.0, 1.0),
1154                Vec2::new(1.0, 1.0),
1155                Vec2::new(1.0, 0.0)
1156            ]
1157        );
1158
1159        //draw top left circle
1160        self.draw_circle_section_triangular_three_color(
1161            [
1162                round_rect.top_left() + Vec2::new(0.0, round_rect.radius()),
1163                *round_rect.top_left(),
1164                round_rect.top_left() + Vec2::new(round_rect.radius(), 0.0)
1165            ],
1166            [color; 3],
1167            [
1168                Vec2::new(-1.0, 0.0),
1169                Vec2::new(-1.0, 1.0),
1170                Vec2::new(0.0, 1.0)
1171            ]
1172        );
1173
1174        //draw bottom left circle
1175        self.draw_circle_section_triangular_three_color(
1176            [
1177                round_rect.bottom_left() + Vec2::new(round_rect.radius(), 0.0),
1178                round_rect.bottom_left(),
1179                round_rect.bottom_left() + Vec2::new(0.0, -round_rect.radius())
1180            ],
1181            [color; 3],
1182            [
1183                Vec2::new(0.0, -1.0),
1184                Vec2::new(-1.0, -1.0),
1185                Vec2::new(-1.0, 0.0)
1186            ]
1187        );
1188
1189        // draw bottom right circle
1190        self.draw_circle_section_triangular_three_color(
1191            [
1192                round_rect.bottom_right() + Vec2::new(0.0, -round_rect.radius()),
1193                *round_rect.bottom_right(),
1194                round_rect.bottom_right() + Vec2::new(-round_rect.radius(), 0.0)
1195            ],
1196            [color; 3],
1197            [
1198                Vec2::new(1.0, 0.0),
1199                Vec2::new(1.0, -1.0),
1200                Vec2::new(0.0, -1.0)
1201            ]
1202        );
1203    }
1204
1205    /// Draws a single-color line between the given points, specified in pixels.
1206    ///
1207    /// # Pixel alignment
1208    ///
1209    /// On a display with square pixels, an integer-valued coordinate is located
1210    /// at the boundary between two pixels, rather than the center of the
1211    /// pixel. For example:
1212    ///
1213    ///  * `(0.0, 0.0)` = Top left of pixel
1214    ///  * `(0.5, 0.5)` = Center of pixel
1215    ///  * `(1.0, 1.0)` = Bottom right of pixel
1216    ///
1217    /// If drawing a line of odd-numbered thickness, it is advisable to locate
1218    /// the start and end of the line at the centers of pixels, rather than
1219    /// the edges.
1220    ///
1221    /// For example, a one-pixel-thick line between `(0.0, 10.0)` and `(100.0,
1222    /// 10.0)` will be drawn as a rectangle with corners `(0.0, 9.5)` and
1223    /// `(100.0, 10.5)`, meaning that the line's thickness will actually
1224    /// span two half-pixels. Drawing the same line between `(0.0, 10.5)`
1225    /// and `(100.0, 10.5)` will result in a pixel-aligned rectangle between
1226    /// `(0.0, 10.0)` and `(100.0, 11.0)`.
1227    pub fn draw_line<VStart: Into<Vec2>, VEnd: Into<Vec2>>(
1228        &mut self,
1229        start_position: VStart,
1230        end_position: VEnd,
1231        thickness: f32,
1232        color: Color
1233    )
1234    {
1235        let start_position = start_position.into();
1236        let end_position = end_position.into();
1237
1238        let gradient_normalized = match (end_position - start_position).normalize() {
1239            None => return,
1240            Some(gradient) => gradient
1241        };
1242
1243        let gradient_thickness = gradient_normalized * (thickness / 2.0);
1244
1245        let offset_anticlockwise = gradient_thickness.rotate_90_degrees_anticlockwise();
1246        let offset_clockwise = gradient_thickness.rotate_90_degrees_clockwise();
1247
1248        let start_anticlockwise = start_position + offset_anticlockwise;
1249        let start_clockwise = start_position + offset_clockwise;
1250
1251        let end_anticlockwise = end_position + offset_anticlockwise;
1252        let end_clockwise = end_position + offset_clockwise;
1253
1254        self.draw_quad(
1255            [
1256                start_anticlockwise,
1257                end_anticlockwise,
1258                end_clockwise,
1259                start_clockwise
1260            ],
1261            color
1262        );
1263    }
1264
1265    /// Draws a circle, filled with a single color, at the specified pixel
1266    /// location.
1267    pub fn draw_circle<V: Into<Vec2>>(
1268        &mut self,
1269        center_position: V,
1270        radius: f32,
1271        color: Color
1272    )
1273    {
1274        let center_position = center_position.into();
1275
1276        let top_left = center_position + Vec2::new(-radius, -radius);
1277        let top_right = center_position + Vec2::new(radius, -radius);
1278        let bottom_right = center_position + Vec2::new(radius, radius);
1279        let bottom_left = center_position + Vec2::new(-radius, radius);
1280
1281        self.renderer.draw_circle_section(
1282            [top_left, top_right, bottom_right],
1283            [color, color, color],
1284            [
1285                Vec2::new(-1.0, -1.0),
1286                Vec2::new(1.0, -1.0),
1287                Vec2::new(1.0, 1.0)
1288            ]
1289        );
1290
1291        self.renderer.draw_circle_section(
1292            [bottom_right, bottom_left, top_left],
1293            [color, color, color],
1294            [
1295                Vec2::new(1.0, 1.0),
1296                Vec2::new(-1.0, 1.0),
1297                Vec2::new(-1.0, -1.0)
1298            ]
1299        );
1300    }
1301
1302    /// Draws a triangular subset of a circle.
1303    ///
1304    /// Put simply, this function will draw a triangle on the screen, textured
1305    /// with a region of a circle.
1306    ///
1307    /// The circle region is specified using `vertex_circle_coords_normalized`,
1308    /// which denotes UV coordinates relative to an infinitely-detailed
1309    /// circle of radius `1.0`, and center `(0.0, 0.0)`.
1310    ///
1311    /// For example, to draw the top-right half of a circle with radius 100px:
1312    ///
1313    /// ```rust,no_run
1314    /// # use speedy2d::GLRenderer;
1315    /// # use speedy2d::dimen::Vec2;
1316    /// # use speedy2d::color::Color;
1317    /// # let mut renderer = unsafe {
1318    /// #     GLRenderer::new_for_gl_context((640, 480), |fn_name| {
1319    /// #         std::ptr::null() as *const _
1320    /// #     })
1321    /// # }.unwrap();
1322    /// # renderer.draw_frame(|graphics| {
1323    /// graphics.draw_circle_section_triangular_three_color(
1324    ///         [
1325    ///                 Vec2::new(200.0, 200.0),
1326    ///                 Vec2::new(300.0, 200.0),
1327    ///                 Vec2::new(300.0, 300.0)],
1328    ///         [Color::MAGENTA; 3],
1329    ///         [
1330    ///                 Vec2::new(-1.0, -1.0),
1331    ///                 Vec2::new(1.0, -1.0),
1332    ///                 Vec2::new(1.0, 1.0)]);
1333    /// # });
1334    /// ```
1335    #[inline]
1336    pub fn draw_circle_section_triangular_three_color(
1337        &mut self,
1338        vertex_positions_clockwise: [Vec2; 3],
1339        vertex_colors: [Color; 3],
1340        vertex_circle_coords_normalized: [Vec2; 3]
1341    )
1342    {
1343        self.renderer.draw_circle_section(
1344            vertex_positions_clockwise,
1345            vertex_colors,
1346            vertex_circle_coords_normalized
1347        );
1348    }
1349
1350    /// Sets the current clip to the rectangle specified by the given
1351    /// coordinates. Rendering operations have no effect outside of the
1352    /// clipping area.
1353    pub fn set_clip(&mut self, rect: Option<Rectangle<i32>>)
1354    {
1355        self.renderer.set_clip(rect);
1356    }
1357
1358    /// Captures a screenshot of the render window. The returned data contains
1359    /// the color of each pixel. Pixels are represented using a `u8` for each
1360    /// component (red, green, blue, and alpha). Use the `format` parameter to
1361    /// specify the byte layout (and size) of each pixel.
1362    pub fn capture(&mut self, format: ImageDataType) -> RawBitmapData
1363    {
1364        self.renderer.capture(format)
1365    }
1366}
1367
1368/// Struct representing a window.
1369#[cfg(any(doc, doctest, all(feature = "windowing", not(target_arch = "wasm32"))))]
1370pub struct Window<UserEventType = ()>
1371where
1372    UserEventType: 'static
1373{
1374    window_impl: WindowGlutin<UserEventType>,
1375    renderer: GLRenderer
1376}
1377
1378#[cfg(any(doc, doctest, all(feature = "windowing", not(target_arch = "wasm32"))))]
1379impl Window<()>
1380{
1381    /// Create a new window, centered in the middle of the primary monitor.
1382    pub fn new_centered<Str, Size>(
1383        title: Str,
1384        size: Size
1385    ) -> Result<Window<()>, BacktraceError<WindowCreationError>>
1386    where
1387        Str: AsRef<str>,
1388        Size: Into<UVec2>
1389    {
1390        let size = size.into();
1391
1392        Self::new_with_options(
1393            title.as_ref(),
1394            WindowCreationOptions::new_windowed(
1395                WindowSize::PhysicalPixels(size),
1396                Some(WindowPosition::Center)
1397            )
1398        )
1399    }
1400
1401    /// Create a new window, in fullscreen borderless mode on the primary
1402    /// monitor.
1403    pub fn new_fullscreen_borderless<Str>(
1404        title: Str
1405    ) -> Result<Window<()>, BacktraceError<WindowCreationError>>
1406    where
1407        Str: AsRef<str>
1408    {
1409        Self::new_with_options(
1410            title.as_ref(),
1411            WindowCreationOptions::new_fullscreen_borderless()
1412        )
1413    }
1414
1415    /// Create a new window with the specified options.
1416    pub fn new_with_options(
1417        title: &str,
1418        options: WindowCreationOptions
1419    ) -> Result<Window<()>, BacktraceError<WindowCreationError>>
1420    {
1421        Self::new_with_user_events(title, options)
1422    }
1423}
1424
1425#[cfg(any(doc, doctest, all(feature = "windowing", not(target_arch = "wasm32"))))]
1426impl<UserEventType: 'static> Window<UserEventType>
1427{
1428    /// Create a new window with the specified options, with support for user
1429    /// events. See [window::UserEventSender].
1430    pub fn new_with_user_events(
1431        title: &str,
1432        options: WindowCreationOptions
1433    ) -> Result<Self, BacktraceError<WindowCreationError>>
1434    {
1435        let window_impl = WindowGlutin::new(title, options)?;
1436
1437        let renderer = GLRenderer::new_with_gl_backend(
1438            window_impl.get_inner_size_pixels(),
1439            window_impl.gl_backend().clone(),
1440            GLVersion::OpenGL2_0
1441        )
1442        .map_err(|err| {
1443            BacktraceError::new_with_cause(
1444                WindowCreationError::RendererCreationFailed,
1445                err
1446            )
1447        })?;
1448
1449        Ok(Window {
1450            window_impl,
1451            renderer
1452        })
1453    }
1454
1455    /// Creates a [window::UserEventSender], which can be used to post custom
1456    /// events to this event loop from another thread.
1457    ///
1458    /// If calling this, specify the type of the event data using
1459    /// `Window::<YourTypeHere>::new_with_user_events()`.
1460    ///
1461    /// See [UserEventSender::send_event], [WindowHandler::on_user_event].
1462    pub fn create_user_event_sender(&self) -> UserEventSender<UserEventType>
1463    {
1464        self.window_impl.create_user_event_sender()
1465    }
1466
1467    /// Run the window event loop, with the specified callback handler.
1468    ///
1469    /// Once the event loop finishes running, the entire app will terminate,
1470    /// even if other threads are still running. See
1471    /// [window::WindowHelper::terminate_loop()].
1472    pub fn run_loop<H>(self, handler: H) -> !
1473    where
1474        H: WindowHandler<UserEventType> + 'static
1475    {
1476        self.window_impl.run_loop(handler, self.renderer);
1477    }
1478}
1479
1480/// Struct representing an HTML canvas.
1481#[cfg(any(doc, doctest, all(target_arch = "wasm32", feature = "windowing")))]
1482pub struct WebCanvas<UserEventType = ()>
1483where
1484    UserEventType: 'static
1485{
1486    inner: Option<WebCanvasImpl>,
1487    should_cleanup: bool,
1488    user_event_type: PhantomData<UserEventType>
1489}
1490
1491#[cfg(any(doc, doctest, all(target_arch = "wasm32", feature = "windowing")))]
1492impl WebCanvas<()>
1493{
1494    /// Creates (and starts running) a new WebCanvas instance, attached to the
1495    /// HTML canvas with the specified ID. Event handlers will be registered for
1496    /// keyboard, mouse, and other events.
1497    ///
1498    /// The event loop/handlers will continue to exist after the WebCanvas is
1499    /// dropped. This behaviour can be avoided using
1500    /// [WebCanvas::unregister_when_dropped].
1501    ///
1502    /// The provided [WindowHandler] will start to receive callbacks as soon as
1503    /// this function returns. Note that the main thread must not be blocked.
1504    pub fn new_for_id<S, H>(
1505        element_id: S,
1506        handler: H
1507    ) -> Result<WebCanvas<()>, BacktraceError<ErrorMessage>>
1508    where
1509        S: AsRef<str>,
1510        H: WindowHandler<()> + 'static
1511    {
1512        WebCanvas::<()>::new_for_id_with_user_events(element_id, handler)
1513    }
1514}
1515
1516#[cfg(any(doc, doctest, all(target_arch = "wasm32", feature = "windowing")))]
1517impl<UserEventType: 'static> WebCanvas<UserEventType>
1518{
1519    /// Creates (and starts running) a new WebCanvas instance, attached to the
1520    /// HTML canvas with the specified ID. Event handlers will be registered for
1521    /// keyboard, mouse, and other events.
1522    ///
1523    /// This variant has support for user-generated events. See
1524    /// [window::UserEventSender] for more details.
1525    ///
1526    /// The event loop/handlers will continue to exist after the WebCanvas is
1527    /// dropped. This behaviour can be avoided using
1528    /// [WebCanvas::unregister_when_dropped].
1529    ///
1530    /// The provided [WindowHandler] will start to receive callbacks as soon as
1531    /// this function returns. Note that the main thread must not be blocked.
1532    pub fn new_for_id_with_user_events<S, H>(
1533        element_id: S,
1534        handler: H
1535    ) -> Result<Self, BacktraceError<ErrorMessage>>
1536    where
1537        S: AsRef<str>,
1538        H: WindowHandler<UserEventType> + 'static
1539    {
1540        Ok(WebCanvas {
1541            inner: Some(WebCanvasImpl::new(element_id, handler)?),
1542            should_cleanup: false,
1543            user_event_type: PhantomData
1544        })
1545    }
1546
1547    /// Causes the WebCanvas event loop to terminate when the WebCanvas is
1548    /// dropped. If this function is not called, then the event loop (and
1549    /// associated event handlers) will continue to run after the WebCanvas
1550    /// struct is dropped.
1551    pub fn unregister_when_dropped(&mut self)
1552    {
1553        self.should_cleanup = true;
1554    }
1555}
1556
1557#[cfg(any(doc, doctest, all(target_arch = "wasm32", feature = "windowing")))]
1558impl<UserEventType: 'static> Drop for WebCanvas<UserEventType>
1559{
1560    fn drop(&mut self)
1561    {
1562        if !self.should_cleanup {
1563            std::mem::forget(self.inner.take());
1564            log::info!(
1565                "Deliberately leaking speedy2d::WebCanvas object. This is normally \
1566                 fine, but if you want to clean up before the page closes, call \
1567                 WebCanvas::unregister_when_dropped(), and retain ownership of the \
1568                 WebCanvas until you want to delete it."
1569            )
1570        }
1571    }
1572}