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}