Got texture to display on quad
+ implemented Texture * tweaked Application API ! some work need on Texture API to mask Rc<RefCell>>
This commit is contained in:
parent
7c01dfab75
commit
79b86afc2a
165
Cargo.lock
generated
165
Cargo.lock
generated
@ -2,6 +2,18 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.7.6"
|
||||
@ -141,6 +153,7 @@ dependencies = [
|
||||
"cgmath",
|
||||
"chrono",
|
||||
"fern",
|
||||
"image",
|
||||
"log",
|
||||
"pollster",
|
||||
"raw-window-handle",
|
||||
@ -236,6 +249,12 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "colored"
|
||||
version = "1.9.3"
|
||||
@ -335,6 +354,15 @@ dependencies = [
|
||||
"objc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cty"
|
||||
version = "0.2.2"
|
||||
@ -387,6 +415,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deflate"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.2.0"
|
||||
@ -521,9 +558,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.1"
|
||||
version = "0.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3"
|
||||
checksum = "607c8a29735385251a339424dd462993c0fed8fa09d378f259377df08c126022"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
@ -546,6 +583,22 @@ version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"jpeg-decoder",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
@ -553,7 +606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.1",
|
||||
"hashbrown 0.12.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -580,6 +633,12 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.58"
|
||||
@ -694,6 +753,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.8.4"
|
||||
@ -812,6 +880,28 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
@ -863,9 +953,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.12.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225"
|
||||
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
@ -875,7 +965,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
"parking_lot_core 0.8.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -892,6 +992,19 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -904,6 +1017,18 @@ version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pollster"
|
||||
version = "0.2.5"
|
||||
@ -994,9 +1119,9 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.137"
|
||||
version = "1.0.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
@ -1009,9 +1134,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.1"
|
||||
version = "1.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc88c725d61fc6c3132893370cac4a0200e3fedf5da8331c570664b1987f5ca2"
|
||||
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
@ -1295,15 +1420,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wgpu"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd28e7c69ffd19c02e609322e4170738ac3340e699d8adfa16f5745625e4aa8c"
|
||||
checksum = "277e967bf8b7820a76852645a6bce8bbd31c32fda2042e82d8e3ea75fda8892d"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"js-sys",
|
||||
"log",
|
||||
"naga",
|
||||
"parking_lot",
|
||||
"parking_lot 0.12.1",
|
||||
"raw-window-handle",
|
||||
"smallvec",
|
||||
"wasm-bindgen",
|
||||
@ -1316,9 +1441,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bb155661d02bf104303589fbf9206fa971c80dbb6d4763e66879253bd0a072c"
|
||||
checksum = "266ca6be6004fd1b2a768023b1cb0afbf7af0cbffaba19af25c5792d44e74784"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-vec",
|
||||
@ -1329,7 +1454,7 @@ dependencies = [
|
||||
"fxhash",
|
||||
"log",
|
||||
"naga",
|
||||
"parking_lot",
|
||||
"parking_lot 0.12.1",
|
||||
"profiling",
|
||||
"raw-window-handle",
|
||||
"smallvec",
|
||||
@ -1341,9 +1466,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "0.13.0"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9f9cb367209e2ad214afa8d823348334994dc1579f4a521d53a3bc4d0848e73"
|
||||
checksum = "bef50e48812c7eb958fa52d28a912f8b77c96453ebab21c72b01cdda61d3e65d"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"arrayvec",
|
||||
@ -1366,7 +1491,7 @@ dependencies = [
|
||||
"metal",
|
||||
"naga",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"parking_lot 0.12.1",
|
||||
"profiling",
|
||||
"range-alloc",
|
||||
"raw-window-handle",
|
||||
@ -1482,7 +1607,7 @@ dependencies = [
|
||||
"ndk-glue",
|
||||
"ndk-sys",
|
||||
"objc",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"percent-encoding",
|
||||
"raw-window-handle",
|
||||
"smithay-client-toolkit",
|
||||
|
||||
@ -22,3 +22,8 @@ raw-window-handle = "^0.4.3"
|
||||
wgpu = "^0.13.0"
|
||||
bytemuck = { version = "1.4", features = [ "derive" ] }
|
||||
|
||||
[dependencies.image]
|
||||
version = "^0.24.2"
|
||||
default-features = false
|
||||
features = ["png", "jpeg"]
|
||||
|
||||
|
||||
BIN
assets/camel.jpg
Normal file
BIN
assets/camel.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@ -17,11 +17,11 @@ use std::{
|
||||
};
|
||||
|
||||
//--Application trait-------------------------------------------------------------------------------
|
||||
pub trait Application {
|
||||
pub trait Application<S> {
|
||||
|
||||
fn init(&mut self, canvas: &mut Canvas) -> Result<(), &'static str>;
|
||||
fn init(canvas: &mut Canvas) -> Result<S, &'static str>;
|
||||
|
||||
fn tick(&mut self, canvas: &mut Canvas) -> Result<(), &'static str>;
|
||||
fn tick(state: &mut S, canvas: &mut Canvas) -> Result<(), &'static str>;
|
||||
}
|
||||
|
||||
//--Canvas struct-----------------------------------------------------------------------------------
|
||||
@ -35,7 +35,8 @@ impl Canvas {
|
||||
pub async fn create<W: HasRawWindowHandle>(window: &W, size: Size)
|
||||
-> Result<Canvas, &'static str>
|
||||
{
|
||||
let renderer = WgpuRenderer::create(window, size).await?;
|
||||
let mut renderer = WgpuRenderer::create(window, size).await?;
|
||||
renderer.clear(Color::WHITE);
|
||||
|
||||
Ok(Self {
|
||||
renderer,
|
||||
@ -63,18 +64,54 @@ impl Canvas {
|
||||
}
|
||||
|
||||
pub fn create_texture_from_file(&mut self,
|
||||
_pos: Position,
|
||||
_file: &'static str,
|
||||
file: &'static str,
|
||||
_offset: Option<Position>,
|
||||
_size: Option<Size>,
|
||||
_background: Option<Pixel>)
|
||||
-> Result<Rc<RefCell<Texture>>, &'static str>
|
||||
{
|
||||
use image::{
|
||||
io::Reader,
|
||||
GenericImageView,
|
||||
};
|
||||
|
||||
let img = Reader::open(file)
|
||||
.map_err(|_| "Failed to open file")?
|
||||
.decode()
|
||||
.map_err(|_| "Failed to decode picture")?;
|
||||
let rgba = img.to_rgba8();
|
||||
let img_size = {
|
||||
let dimensions = img.dimensions();
|
||||
Size {
|
||||
w: dimensions.0,
|
||||
h: dimensions.1,
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Rc::new(RefCell::new(
|
||||
self.renderer.create_texture(&rgba, img_size)
|
||||
)))
|
||||
}
|
||||
|
||||
pub fn create_texture_from_bytes(&mut self,
|
||||
_buf: &[u8],
|
||||
_offset: Option<Position>,
|
||||
_size: Option<Size>,
|
||||
_background: Option<Pixel>)
|
||||
-> Rc<RefCell<Texture>>
|
||||
{
|
||||
unimplemented!();
|
||||
todo!()
|
||||
// Rc::new(RefCell::new(
|
||||
// self.renderer.create_texture_from_bytes(pos, buf, background).unwrap()
|
||||
// ))
|
||||
}
|
||||
|
||||
//--Output functions--
|
||||
pub fn draw<S: Sprite>(&mut self, sprite: &S) {
|
||||
// pub fn draw<S: Sprite>(&mut self, sprite: &S) {
|
||||
pub fn draw(&mut self, texture: &Texture) {
|
||||
|
||||
//update texture
|
||||
self.renderer.render(sprite);
|
||||
self.renderer.render(texture);
|
||||
}
|
||||
|
||||
pub fn set_clear_color(&mut self, color: Pixel) {
|
||||
@ -86,7 +123,7 @@ impl Canvas {
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
todo!();
|
||||
self.renderer.clear(self.clear_color);
|
||||
}
|
||||
|
||||
//--Input functions--
|
||||
|
||||
@ -15,7 +15,7 @@ mod renderer;
|
||||
|
||||
use utils::Size;
|
||||
|
||||
pub fn run_canvas<A: 'static + Application>(title: &'static str, size: Size, mut app: A) -> ! {
|
||||
pub fn run_canvas<S: 'static + Sized, A: 'static + Application<S>>(title: &'static str, size: Size, mut app: A) -> ! {
|
||||
use winit::{
|
||||
event_loop::EventLoop,
|
||||
window::WindowBuilder,
|
||||
@ -35,7 +35,7 @@ pub fn run_canvas<A: 'static + Application>(title: &'static str, size: Size, mut
|
||||
let mut canvas = Canvas::create(&window, size.into()).block_on().unwrap();
|
||||
|
||||
// init application
|
||||
app.init(&mut canvas).unwrap();
|
||||
let mut state = A::init(&mut canvas).unwrap();
|
||||
canvas.update();
|
||||
window.set_visible(true);
|
||||
|
||||
@ -66,9 +66,10 @@ pub fn run_canvas<A: 'static + Application>(title: &'static str, size: Size, mut
|
||||
},
|
||||
|
||||
Event::MainEventsCleared => {
|
||||
let _ = app.tick(&mut canvas);
|
||||
},
|
||||
let _ = A::tick(&mut state, &mut canvas);
|
||||
},
|
||||
Event::RedrawRequested(_) => {
|
||||
canvas.clear();
|
||||
let _ = canvas.update();
|
||||
},
|
||||
|
||||
|
||||
34
src/main.rs
34
src/main.rs
@ -1,4 +1,13 @@
|
||||
use canvas::{Application, Canvas};
|
||||
use canvas::{
|
||||
Application,
|
||||
Canvas,
|
||||
texture::Texture,
|
||||
};
|
||||
|
||||
use std::{
|
||||
rc::Rc,
|
||||
cell::RefCell,
|
||||
};
|
||||
|
||||
fn setup_logger() -> Result<(), fern::InitError> {
|
||||
use fern::colors::{Color, ColoredLevelConfig};
|
||||
@ -26,15 +35,28 @@ fn setup_logger() -> Result<(), fern::InitError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct ExampleState {
|
||||
pub texture: Rc<RefCell<Texture>>,
|
||||
}
|
||||
|
||||
struct ExampleApp {}
|
||||
|
||||
impl Application for ExampleApp {
|
||||
impl Application<ExampleState> for ExampleApp {
|
||||
|
||||
fn init(&mut self, _canvas: &mut Canvas) -> Result<(), &'static str> {
|
||||
Ok(())
|
||||
fn init(canvas: &mut Canvas) -> Result<ExampleState, &'static str> {
|
||||
|
||||
let texture = canvas.create_texture_from_file("assets/camel.jpg", None, None, None)
|
||||
.unwrap();
|
||||
|
||||
canvas.clear();
|
||||
canvas.update();
|
||||
|
||||
Ok(ExampleState {
|
||||
texture,
|
||||
})
|
||||
}
|
||||
|
||||
fn tick(&mut self, canvas: &mut Canvas) -> Result<(), &'static str> {
|
||||
fn tick(state: &mut ExampleState, canvas: &mut Canvas) -> Result<(), &'static str> {
|
||||
use canvas::{
|
||||
io::Key,
|
||||
sprite::Sprite,
|
||||
@ -103,7 +125,7 @@ impl Application for ExampleApp {
|
||||
//txt_sprite.set_alpha(255);
|
||||
//txt_sprite.set_scale(0.5);
|
||||
//canvas.draw(&txt_sprite);
|
||||
|
||||
canvas.draw(&state.texture.borrow());
|
||||
canvas.update();
|
||||
|
||||
Ok(())
|
||||
|
||||
317
src/renderer.rs
317
src/renderer.rs
@ -6,9 +6,12 @@ use wgpu;
|
||||
|
||||
use crate::{
|
||||
sprite::Sprite,
|
||||
utils::Size,
|
||||
texture::Texture,
|
||||
utils::{Pixel, Position, Size},
|
||||
};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
//--Renderer struct---------------------------------------------------------------------------------
|
||||
pub struct WgpuRenderer {
|
||||
surface: wgpu::Surface,
|
||||
@ -16,8 +19,12 @@ pub struct WgpuRenderer {
|
||||
queue: wgpu::Queue,
|
||||
config: wgpu::SurfaceConfiguration,
|
||||
size: Size,
|
||||
render_pipeline: wgpu::RenderPipeline,
|
||||
quad_mesh: GpuMesh,
|
||||
texture_bind_group_layout: wgpu::BindGroupLayout,
|
||||
texture_render_pipeline: wgpu::RenderPipeline,
|
||||
shape_render_pipeline: wgpu::RenderPipeline,
|
||||
texture_quad: GpuMesh<TextureVertex>,
|
||||
quad_mesh: GpuMesh<ColorVertex>, //TODO temporary, to be moved to shapes.rs
|
||||
output: Option<wgpu::SurfaceTexture>,
|
||||
}
|
||||
|
||||
impl WgpuRenderer {
|
||||
@ -61,27 +68,52 @@ impl WgpuRenderer {
|
||||
};
|
||||
surface.configure(&device, &config);
|
||||
|
||||
let render_pipeline = {
|
||||
let texture_bind_group_layout =
|
||||
device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 0,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
ty: wgpu::BindingType::Texture {
|
||||
multisampled: false,
|
||||
view_dimension: wgpu::TextureViewDimension::D2,
|
||||
sample_type: wgpu::TextureSampleType::Float { filterable: true },
|
||||
},
|
||||
count: None,
|
||||
},
|
||||
wgpu::BindGroupLayoutEntry {
|
||||
binding: 1,
|
||||
visibility: wgpu::ShaderStages::FRAGMENT,
|
||||
// This should match the filterable field of the
|
||||
// corresponding Texture entry above.
|
||||
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
|
||||
count: None,
|
||||
},
|
||||
],
|
||||
label: Some("texture_bind_group_layout"),
|
||||
});
|
||||
|
||||
let texture_render_pipeline = {
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("shape shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/shape.wgsl").into()),
|
||||
label: Some("texture shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/texture.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("shape render pipeline layout"),
|
||||
bind_group_layouts: &[],
|
||||
label: Some("texture render pipeline layout"),
|
||||
bind_group_layouts: &[&texture_bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("shape render pipeline"),
|
||||
label: Some("texture render pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[
|
||||
Vertex::desc(),
|
||||
TextureVertex::desc(),
|
||||
],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
@ -117,43 +149,99 @@ impl WgpuRenderer {
|
||||
})
|
||||
};
|
||||
|
||||
let shape_render_pipeline = {
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("shape shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shaders/shape.wgsl").into()),
|
||||
});
|
||||
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("shape render pipeline layout"),
|
||||
bind_group_layouts: &[],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("shape render pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[
|
||||
ColorVertex::desc(),
|
||||
],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: Some(wgpu::Face::Back),
|
||||
// Setting this to anything other than Fill requires
|
||||
// Features::NON_FILL_POLYGON_MODE
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
// Requires Features::DEPTH_CLIP_CONTROL
|
||||
unclipped_depth: false,
|
||||
// Requires Features::CONSERVATIVE_RASTERIZATION
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
// multisampling, we don't need it
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
multiview: None,
|
||||
})
|
||||
};
|
||||
|
||||
let texture_quad = GpuMesh::create(&device, &TEXTURE_QUAD);
|
||||
let quad_mesh = GpuMesh::create(&device, &QUAD);
|
||||
|
||||
let output = Some(surface.get_current_texture()
|
||||
.map_err(|_| "Failed to create SurfaceTexture")?);
|
||||
|
||||
Ok(Self {
|
||||
surface,
|
||||
device,
|
||||
queue,
|
||||
config,
|
||||
size,
|
||||
render_pipeline,
|
||||
texture_bind_group_layout,
|
||||
texture_render_pipeline,
|
||||
shape_render_pipeline,
|
||||
texture_quad,
|
||||
quad_mesh,
|
||||
output,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn render<S: Sprite>(&mut self, _sprite: &S) {
|
||||
unimplemented!();
|
||||
pub fn create_texture(&mut self, buf: &[u8], size: Size)
|
||||
-> Texture
|
||||
{
|
||||
Texture::create(&self.device, &self.queue, &self.texture_bind_group_layout, buf, size)
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: Size) {
|
||||
if size.w == 0 || size.h == 0 {
|
||||
panic!("window has zero as at least one of its dimensions");
|
||||
}
|
||||
self.size = size;
|
||||
self.config.width = size.w;
|
||||
self.config.height = size.h;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
}
|
||||
// pub fn render<S: Sprite>(&mut self, _sprite: &S) {
|
||||
pub fn render(&mut self, texture: &Texture) {
|
||||
|
||||
pub fn present(&mut self) {
|
||||
let output = self.surface.get_current_texture()
|
||||
.map_err(|err| match err {
|
||||
wgpu::SurfaceError::Lost => {
|
||||
warn!("Lost surface, trying resizing");
|
||||
self.resize(self.size);
|
||||
self.surface.get_current_texture()
|
||||
},
|
||||
_ => Err(err)
|
||||
}).unwrap();
|
||||
let output = match &self.output {
|
||||
Some(out) => out,
|
||||
None => {
|
||||
self.create_output();
|
||||
&self.output.as_ref().unwrap()
|
||||
},
|
||||
};
|
||||
|
||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
@ -167,39 +255,102 @@ impl WgpuRenderer {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.2,
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
}),
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.render_pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.quad_mesh.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(self.quad_mesh.index_buffer.slice(..),
|
||||
render_pass.set_pipeline(&self.texture_render_pipeline);
|
||||
render_pass.set_vertex_buffer(0, self.texture_quad.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(self.texture_quad.index_buffer.slice(..),
|
||||
wgpu::IndexFormat::Uint16);
|
||||
render_pass.draw_indexed(0..self.quad_mesh.index_number, 0, 0..1);
|
||||
render_pass.set_bind_group(0, texture.bind_group(), &[]);
|
||||
render_pass.draw_indexed(0..self.texture_quad.index_number, 0, 0..1);
|
||||
}
|
||||
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
output.present();
|
||||
}
|
||||
|
||||
pub fn clear(&mut self, color: Pixel) {
|
||||
|
||||
let output = match &self.output {
|
||||
Some(out) => out,
|
||||
None => {
|
||||
self.create_output();
|
||||
&self.output.as_ref().unwrap()
|
||||
},
|
||||
};
|
||||
|
||||
let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
|
||||
label: Some("Clear Encoder"),
|
||||
});
|
||||
|
||||
{
|
||||
let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
label: Some("Clear Render Pass"),
|
||||
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
|
||||
view: &view,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(color.into()),
|
||||
store: true,
|
||||
},
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
});
|
||||
}
|
||||
|
||||
self.queue.submit(std::iter::once(encoder.finish()));
|
||||
}
|
||||
|
||||
pub fn resize(&mut self, size: Size) {
|
||||
if size.w == 0 || size.h == 0 {
|
||||
panic!("window has zero as at least one of its dimensions");
|
||||
}
|
||||
self.size = size;
|
||||
self.config.width = size.w;
|
||||
self.config.height = size.h;
|
||||
self.surface.configure(&self.device, &self.config);
|
||||
}
|
||||
|
||||
pub fn present(&mut self) {
|
||||
|
||||
match self.output.take() {
|
||||
Some(out) => out.present(),
|
||||
None => {
|
||||
}//nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
fn create_output(&mut self) {
|
||||
self.output = Some(
|
||||
self.surface.get_current_texture()
|
||||
.map_err(|err| match err {
|
||||
wgpu::SurfaceError::Lost => {
|
||||
warn!("Lost surface, trying resizing");
|
||||
self.resize(self.size);
|
||||
self.surface.get_current_texture()
|
||||
},
|
||||
_ => Err(err)
|
||||
}).unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct GpuMesh {
|
||||
struct GpuMesh<V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> {
|
||||
vertex_buffer: wgpu::Buffer,
|
||||
index_buffer: wgpu::Buffer,
|
||||
index_number: u32,
|
||||
|
||||
phantom: PhantomData<V>, //keep track of wich type of vertex is used
|
||||
}
|
||||
|
||||
impl GpuMesh {
|
||||
impl<V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> GpuMesh<V> {
|
||||
|
||||
pub fn create(device: &wgpu::Device, mesh: &Mesh) -> Self {
|
||||
fn create(device: &wgpu::Device, mesh: &Mesh<V>) -> Self {
|
||||
use wgpu::util::DeviceExt;
|
||||
|
||||
let vertex_buffer = device.create_buffer_init(
|
||||
@ -222,6 +373,7 @@ impl GpuMesh {
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
index_number: mesh.indices.len() as u32,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,40 +381,87 @@ impl GpuMesh {
|
||||
//--Renderer struct utils---------------------------------------------------------------------------
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct Vertex {
|
||||
struct TextureVertex {
|
||||
pub position: [f32; 2],
|
||||
pub color: [f32; 3],
|
||||
pub tex_coords: [f32; 2],
|
||||
}
|
||||
|
||||
// lib.rs
|
||||
impl Vertex {
|
||||
impl TextureVertex {
|
||||
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 2] =
|
||||
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3];
|
||||
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2];
|
||||
|
||||
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
|
||||
array_stride: std::mem::size_of::<TextureVertex>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &Self::ATTRIBS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Mesh<'a> {
|
||||
pub vertices: &'a[Vertex],
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
|
||||
struct ColorVertex {
|
||||
pub position: [f32; 2],
|
||||
pub color: [f32; 3],
|
||||
}
|
||||
|
||||
impl ColorVertex {
|
||||
|
||||
const ATTRIBS: [wgpu::VertexAttribute; 2] =
|
||||
wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3];
|
||||
|
||||
fn desc<'a>() -> wgpu::VertexBufferLayout<'a> {
|
||||
wgpu::VertexBufferLayout {
|
||||
array_stride: std::mem::size_of::<ColorVertex>() as wgpu::BufferAddress,
|
||||
step_mode: wgpu::VertexStepMode::Vertex,
|
||||
attributes: &Self::ATTRIBS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Mesh<'a, V: Copy + Clone + bytemuck::Pod + bytemuck::Zeroable> {
|
||||
pub vertices: &'a[V],
|
||||
pub indices: &'a[u16],
|
||||
}
|
||||
|
||||
const QUAD: Mesh<'static> = Mesh {
|
||||
const TEXTURE_QUAD: Mesh<'static, TextureVertex> = Mesh {
|
||||
vertices: &[
|
||||
Vertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] },
|
||||
Vertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] },
|
||||
Vertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] },
|
||||
Vertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] },
|
||||
TextureVertex { position: [0.0, 0.0], tex_coords: [0.0, 1.0] },
|
||||
TextureVertex { position: [1.0, 0.0], tex_coords: [1.0, 1.0] },
|
||||
TextureVertex { position: [1.0, 1.0], tex_coords: [1.0, 0.0] },
|
||||
TextureVertex { position: [0.0, 1.0], tex_coords: [0.0, 0.0] },
|
||||
],
|
||||
indices: &[
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
],
|
||||
};
|
||||
|
||||
const QUAD: Mesh<'static, ColorVertex> = Mesh {
|
||||
vertices: &[
|
||||
ColorVertex { position: [0.0, 0.0], color: [1.0, 0.0, 0.0] },
|
||||
ColorVertex { position: [1.0, 0.0], color: [0.0, 1.0, 0.0] },
|
||||
ColorVertex { position: [1.0, 1.0], color: [0.0, 0.0, 1.0] },
|
||||
ColorVertex { position: [0.0, 1.0], color: [0.5, 0.5, 0.5] },
|
||||
],
|
||||
indices: &[
|
||||
0, 1, 2,
|
||||
0, 2, 3,
|
||||
],
|
||||
};
|
||||
|
||||
impl From<Pixel> for wgpu::Color {
|
||||
|
||||
fn from(pix: Pixel) -> Self {
|
||||
let rgba = pix.to_rgba();
|
||||
Self {
|
||||
r: (rgba.r as f64) / 255.0,
|
||||
g: (rgba.g as f64) / 255.0,
|
||||
b: (rgba.b as f64) / 255.0,
|
||||
a: (rgba.a as f64) / 255.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
src/shaders/texture.wgsl
Normal file
33
src/shaders/texture.wgsl
Normal file
@ -0,0 +1,33 @@
|
||||
// Vertex shader
|
||||
|
||||
struct VertexInput {
|
||||
@location(0) position: vec2<f32>,
|
||||
@location(1) tex_coords: vec2<f32>,
|
||||
}
|
||||
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) tex_coords: vec2<f32>,
|
||||
}
|
||||
|
||||
@vertex
|
||||
fn vs_main(model: VertexInput) -> VertexOutput {
|
||||
|
||||
var out: VertexOutput;
|
||||
out.tex_coords = model.tex_coords;
|
||||
out.clip_position = vec4<f32>(model.position, 0.0, 1.0);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Fragment shader
|
||||
|
||||
@group(0) @binding(0)
|
||||
var t_diffuse: texture_2d<f32>;
|
||||
@group(0)@binding(1)
|
||||
var s_diffuse: sampler;
|
||||
|
||||
@fragment
|
||||
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
|
||||
return textureSample(t_diffuse, s_diffuse, in.tex_coords);
|
||||
}
|
||||
|
||||
@ -1,15 +1,91 @@
|
||||
#[allow(unused_imports)]
|
||||
use log::{debug, error, info, trace, warn};
|
||||
|
||||
use crate::utils::{Pixel, Position};
|
||||
use crate::utils::{Pixel, Position, Size};
|
||||
|
||||
use std::slice::{Iter, IterMut};
|
||||
|
||||
//--Texture struct----------------------------------------------------------------------------------
|
||||
pub struct Texture {}
|
||||
pub struct Texture {
|
||||
texture: wgpu::Texture,
|
||||
bind_group: wgpu::BindGroup,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
|
||||
pub fn create(device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
layout: &wgpu::BindGroupLayout,
|
||||
buf: &[u8],
|
||||
buf_size: Size)
|
||||
-> Self
|
||||
{
|
||||
let size = wgpu::Extent3d {
|
||||
width: buf_size.w,
|
||||
height: buf_size.h,
|
||||
depth_or_array_layers: 1,
|
||||
};
|
||||
let texture = device.create_texture(
|
||||
&wgpu::TextureDescriptor {
|
||||
label: None,
|
||||
size,
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
dimension: wgpu::TextureDimension::D2,
|
||||
format: wgpu::TextureFormat::Rgba8UnormSrgb,
|
||||
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
|
||||
}
|
||||
);
|
||||
|
||||
queue.write_texture(
|
||||
wgpu::ImageCopyTexture {
|
||||
aspect: wgpu::TextureAspect::All,
|
||||
texture: &texture,
|
||||
mip_level: 0,
|
||||
origin: wgpu::Origin3d::ZERO,
|
||||
},
|
||||
&buf,
|
||||
wgpu::ImageDataLayout {
|
||||
offset: 0,
|
||||
bytes_per_row: std::num::NonZeroU32::new(4 * buf_size.w),
|
||||
rows_per_image: std::num::NonZeroU32::new(buf_size.h),
|
||||
},
|
||||
size,
|
||||
);
|
||||
|
||||
let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
|
||||
let sampler = device.create_sampler(
|
||||
&wgpu::SamplerDescriptor {
|
||||
address_mode_u: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_v: wgpu::AddressMode::ClampToEdge,
|
||||
address_mode_w: wgpu::AddressMode::ClampToEdge,
|
||||
mag_filter: wgpu::FilterMode::Linear,
|
||||
min_filter: wgpu::FilterMode::Nearest,
|
||||
mipmap_filter: wgpu::FilterMode::Nearest,
|
||||
..Default::default()
|
||||
}
|
||||
);
|
||||
|
||||
let bind_group = device.create_bind_group(
|
||||
&wgpu::BindGroupDescriptor {
|
||||
layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&sampler),
|
||||
}
|
||||
],
|
||||
label: Some("diffuse_bind_group"),
|
||||
}
|
||||
);
|
||||
|
||||
Self { texture, bind_group }
|
||||
}
|
||||
|
||||
pub fn set_pixel(&mut self, _pos: Position, _pix: Pixel) {
|
||||
unimplemented!();
|
||||
}
|
||||
@ -21,5 +97,9 @@ impl Texture {
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, Pixel> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn bind_group(&self) -> &wgpu::BindGroup {
|
||||
&self.bind_group
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
23
src/utils.rs
23
src/utils.rs
@ -39,13 +39,14 @@ impl From<winit::dpi::PhysicalSize<u32>> for Size {
|
||||
|
||||
//--Pixel struct------------------------------------------------------------------------------------
|
||||
#[derive(Copy, Clone)]
|
||||
struct Rgba {
|
||||
r: u8,
|
||||
g: u8,
|
||||
b: u8,
|
||||
a: u8,
|
||||
pub struct Rgba {
|
||||
pub r: u8,
|
||||
pub g: u8,
|
||||
pub b: u8,
|
||||
pub a: u8,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub union Pixel {
|
||||
flat: u32,
|
||||
rgba: Rgba,
|
||||
@ -58,6 +59,10 @@ impl Pixel {
|
||||
rgba: Rgba{r, g, b, a: 255},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_rgba(&self) -> Rgba {
|
||||
unsafe { self.rgba }
|
||||
}
|
||||
}
|
||||
|
||||
//--Color values------------------------------------------------------------------------------------
|
||||
@ -65,8 +70,12 @@ impl Pixel {
|
||||
pub struct Color;
|
||||
|
||||
impl Color {
|
||||
pub const WHITE: Pixel = Pixel {flat: 0x000000ff};
|
||||
pub const BLACK: Pixel = Pixel {flat: 0xffffffff};
|
||||
pub const NONE: Pixel = Pixel {flat: 0x00000000};
|
||||
pub const WHITE: Pixel = Pixel {flat: 0xffffffff};
|
||||
pub const BLACK: Pixel = Pixel {flat: 0x000000ff};
|
||||
pub const RED: Pixel = Pixel {flat: 0xff0000ff};
|
||||
pub const GREEN: Pixel = Pixel {flat: 0x00ff00ff};
|
||||
pub const BLUE: Pixel = Pixel {flat: 0x0000ffff};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user