mirror of
https://github.com/wez/wezterm.git
synced 2024-11-10 15:04:32 +03:00
introduce a paint context trait
This commit is contained in:
parent
a88f0f1beb
commit
311760df90
@ -1,133 +1,5 @@
|
||||
use palette::{Blend, Srgb, Srgba};
|
||||
|
||||
/// A color stored as big endian bgra32
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Color(u32);
|
||||
|
||||
impl From<Srgb> for Color {
|
||||
#[inline]
|
||||
fn from(s: Srgb) -> Color {
|
||||
let b: Srgb<u8> = s.into_format();
|
||||
let b = b.into_components();
|
||||
Color::rgb(b.0, b.1, b.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Srgba> for Color {
|
||||
#[inline]
|
||||
fn from(s: Srgba) -> Color {
|
||||
let b: Srgba<u8> = s.into_format();
|
||||
let b = b.into_components();
|
||||
Color::rgba(b.0, b.1, b.2, b.3)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Srgb {
|
||||
#[inline]
|
||||
fn from(c: Color) -> Srgb {
|
||||
let c = c.as_rgba();
|
||||
let s = Srgb::<u8>::new(c.0, c.1, c.2);
|
||||
s.into_format()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Srgba {
|
||||
#[inline]
|
||||
fn from(c: Color) -> Srgba {
|
||||
let c = c.as_rgba();
|
||||
let s = Srgba::<u8>::new(c.0, c.1, c.2, c.3);
|
||||
s.into_format()
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
#[inline]
|
||||
pub fn rgb(red: u8, green: u8, blue: u8) -> Color {
|
||||
Color::rgba(red, green, blue, 0xff)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||
let word = (blue as u32) << 24 | (green as u32) << 16 | (red as u32) << 8 | alpha as u32;
|
||||
Color(word.to_be())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_rgba(&self) -> (u8, u8, u8, u8) {
|
||||
let host = u32::from_be(self.0);
|
||||
(
|
||||
(host >> 8) as u8,
|
||||
(host >> 16) as u8,
|
||||
(host >> 24) as u8,
|
||||
(host & 0xff) as u8,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the composite of two colors according to the supplied operator.
|
||||
/// self is the src operand, dest is the dest operand.
|
||||
#[inline]
|
||||
pub fn composite(&self, dest: Color, operator: &Operator) -> Color {
|
||||
match operator {
|
||||
&Operator::Over => {
|
||||
let src: Srgba = (*self).into();
|
||||
let dest: Srgba = dest.into();
|
||||
Srgba::from_linear(src.into_linear().over(dest.into_linear())).into()
|
||||
}
|
||||
&Operator::Source => *self,
|
||||
&Operator::Multiply => {
|
||||
let src: Srgba = (*self).into();
|
||||
let dest: Srgba = dest.into();
|
||||
let result: Color =
|
||||
Srgba::from_linear(src.into_linear().multiply(dest.into_linear())).into();
|
||||
result.into()
|
||||
}
|
||||
&Operator::MultiplyThenOver(ref tint) => {
|
||||
// First multiply by the tint color. This colorizes the glyph.
|
||||
let src: Srgba = (*self).into();
|
||||
let tint: Srgba = (*tint).into();
|
||||
let mut tinted = src.into_linear().multiply(tint.into_linear());
|
||||
// We take the alpha from the source. This is important because
|
||||
// we're using Multiply to tint the glyph and if we don't reset the
|
||||
// alpha we tend to end up with a background square of the tint color.
|
||||
tinted.alpha = src.alpha;
|
||||
// Then blend the tinted glyph over the destination background
|
||||
let dest: Srgba = dest.into();
|
||||
Srgba::from_linear(tinted.over(dest.into_linear())).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compositing operator.
|
||||
/// We implement a small subset of possible compositing operators.
|
||||
/// More information on these and their temrinology can be found
|
||||
/// in the Cairo documentation here:
|
||||
/// https://www.cairographics.org/operators/
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Operator {
|
||||
/// Apply the alpha channel of src and combine src with dest,
|
||||
/// according to the classic OVER composite operator
|
||||
Over,
|
||||
/// Ignore dest; take src as the result of the operation
|
||||
Source,
|
||||
/// Multiply src x dest. The result is at least as dark as
|
||||
/// the darker of the two input colors. This is used to
|
||||
/// apply a color tint.
|
||||
Multiply,
|
||||
/// Multiply src with the provided color, then apply the
|
||||
/// Over operator on the result with the dest as the dest.
|
||||
/// This is used to colorize the src and then blend the
|
||||
/// result into the destination.
|
||||
MultiplyThenOver(Color),
|
||||
}
|
||||
|
||||
/// A bitmap in big endian bgra32 color format, with storage
|
||||
/// in a Vec<u8>.
|
||||
pub struct Image {
|
||||
data: Vec<u8>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
use crate::color::Color;
|
||||
use crate::Operator;
|
||||
|
||||
/// A bitmap in big endian bgra32 color format with abstract
|
||||
/// storage filled in by the trait implementation.
|
||||
@ -289,7 +161,13 @@ pub trait BitmapImage {
|
||||
self.draw_horizontal_line(dest_x, dest_y + height as isize, width, color, operator);
|
||||
}
|
||||
|
||||
fn draw_image(&mut self, dest_x: isize, dest_y: isize, im: &BitmapImage, operator: Operator) {
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
dest_x: isize,
|
||||
dest_y: isize,
|
||||
im: &dyn BitmapImage,
|
||||
operator: Operator,
|
||||
) {
|
||||
let (dest_width, dest_height) = im.image_dimensions();
|
||||
self.draw_image_subset(dest_x, dest_y, 0, 0, dest_width, dest_height, im, operator)
|
||||
}
|
||||
@ -302,7 +180,7 @@ pub trait BitmapImage {
|
||||
src_y: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
im: &BitmapImage,
|
||||
im: &dyn BitmapImage,
|
||||
operator: Operator,
|
||||
) {
|
||||
let (dest_width, dest_height) = im.image_dimensions();
|
||||
@ -332,6 +210,14 @@ pub trait BitmapImage {
|
||||
}
|
||||
}
|
||||
|
||||
/// A bitmap in big endian bgra32 color format, with storage
|
||||
/// in a Vec<u8>.
|
||||
pub struct Image {
|
||||
data: Vec<u8>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
/// Create a new bgra32 image buffer with the specified dimensions.
|
||||
/// The buffer is initialized to all zeroes.
|
||||
|
100
window/src/color.rs
Normal file
100
window/src/color.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use crate::Operator;
|
||||
use palette::{Blend, Srgb, Srgba};
|
||||
|
||||
/// A color stored as big endian bgra32
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Color(pub u32);
|
||||
|
||||
impl From<Srgb> for Color {
|
||||
#[inline]
|
||||
fn from(s: Srgb) -> Color {
|
||||
let b: Srgb<u8> = s.into_format();
|
||||
let b = b.into_components();
|
||||
Color::rgb(b.0, b.1, b.2)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Srgba> for Color {
|
||||
#[inline]
|
||||
fn from(s: Srgba) -> Color {
|
||||
let b: Srgba<u8> = s.into_format();
|
||||
let b = b.into_components();
|
||||
Color::rgba(b.0, b.1, b.2, b.3)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Srgb {
|
||||
#[inline]
|
||||
fn from(c: Color) -> Srgb {
|
||||
let c = c.as_rgba();
|
||||
let s = Srgb::<u8>::new(c.0, c.1, c.2);
|
||||
s.into_format()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Color> for Srgba {
|
||||
#[inline]
|
||||
fn from(c: Color) -> Srgba {
|
||||
let c = c.as_rgba();
|
||||
let s = Srgba::<u8>::new(c.0, c.1, c.2, c.3);
|
||||
s.into_format()
|
||||
}
|
||||
}
|
||||
|
||||
impl Color {
|
||||
#[inline]
|
||||
pub fn rgb(red: u8, green: u8, blue: u8) -> Color {
|
||||
Color::rgba(red, green, blue, 0xff)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn rgba(red: u8, green: u8, blue: u8, alpha: u8) -> Color {
|
||||
let word = (blue as u32) << 24 | (green as u32) << 16 | (red as u32) << 8 | alpha as u32;
|
||||
Color(word.to_be())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_rgba(&self) -> (u8, u8, u8, u8) {
|
||||
let host = u32::from_be(self.0);
|
||||
(
|
||||
(host >> 8) as u8,
|
||||
(host >> 16) as u8,
|
||||
(host >> 24) as u8,
|
||||
(host & 0xff) as u8,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the composite of two colors according to the supplied operator.
|
||||
/// self is the src operand, dest is the dest operand.
|
||||
#[inline]
|
||||
pub fn composite(&self, dest: Color, operator: &Operator) -> Color {
|
||||
match operator {
|
||||
&Operator::Over => {
|
||||
let src: Srgba = (*self).into();
|
||||
let dest: Srgba = dest.into();
|
||||
Srgba::from_linear(src.into_linear().over(dest.into_linear())).into()
|
||||
}
|
||||
&Operator::Source => *self,
|
||||
&Operator::Multiply => {
|
||||
let src: Srgba = (*self).into();
|
||||
let dest: Srgba = dest.into();
|
||||
let result: Color =
|
||||
Srgba::from_linear(src.into_linear().multiply(dest.into_linear())).into();
|
||||
result.into()
|
||||
}
|
||||
&Operator::MultiplyThenOver(ref tint) => {
|
||||
// First multiply by the tint color. This colorizes the glyph.
|
||||
let src: Srgba = (*self).into();
|
||||
let tint: Srgba = (*tint).into();
|
||||
let mut tinted = src.into_linear().multiply(tint.into_linear());
|
||||
// We take the alpha from the source. This is important because
|
||||
// we're using Multiply to tint the glyph and if we don't reset the
|
||||
// alpha we tend to end up with a background square of the tint color.
|
||||
tinted.alpha = src.alpha;
|
||||
// Then blend the tinted glyph over the destination background
|
||||
let dest: Srgba = dest.into();
|
||||
Srgba::from_linear(tinted.over(dest.into_linear())).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,2 +1,79 @@
|
||||
pub mod bitmaps;
|
||||
pub mod color;
|
||||
pub mod os;
|
||||
|
||||
pub use bitmaps::BitmapImage;
|
||||
pub use color::Color;
|
||||
|
||||
/// Compositing operator.
|
||||
/// We implement a small subset of possible compositing operators.
|
||||
/// More information on these and their temrinology can be found
|
||||
/// in the Cairo documentation here:
|
||||
/// https://www.cairographics.org/operators/
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Operator {
|
||||
/// Apply the alpha channel of src and combine src with dest,
|
||||
/// according to the classic OVER composite operator
|
||||
Over,
|
||||
/// Ignore dest; take src as the result of the operation
|
||||
Source,
|
||||
/// Multiply src x dest. The result is at least as dark as
|
||||
/// the darker of the two input colors. This is used to
|
||||
/// apply a color tint.
|
||||
Multiply,
|
||||
/// Multiply src with the provided color, then apply the
|
||||
/// Over operator on the result with the dest as the dest.
|
||||
/// This is used to colorize the src and then blend the
|
||||
/// result into the destination.
|
||||
MultiplyThenOver(Color),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Dimensions {
|
||||
pub pixel_width: usize,
|
||||
pub pixel_height: usize,
|
||||
pub dpi: usize,
|
||||
}
|
||||
|
||||
pub trait PaintContext {
|
||||
fn get_dimensions(&self) -> Dimensions;
|
||||
|
||||
/// Clear the entire context to the specified color
|
||||
fn clear(&mut self, color: Color) {
|
||||
let dims = self.get_dimensions();
|
||||
self.clear_rect(0, 0, dims.pixel_width, dims.pixel_height, color);
|
||||
}
|
||||
|
||||
/// Clear a rectangle to the specified color
|
||||
fn clear_rect(
|
||||
&mut self,
|
||||
dest_x: isize,
|
||||
dest_y: isize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
color: Color,
|
||||
);
|
||||
|
||||
fn draw_image(
|
||||
&mut self,
|
||||
dest_x: isize,
|
||||
dest_y: isize,
|
||||
im: &dyn BitmapImage,
|
||||
operator: Operator,
|
||||
) {
|
||||
let (dest_width, dest_height) = im.image_dimensions();
|
||||
self.draw_image_subset(dest_x, dest_y, 0, 0, dest_width, dest_height, im, operator)
|
||||
}
|
||||
|
||||
fn draw_image_subset(
|
||||
&mut self,
|
||||
dest_x: isize,
|
||||
dest_y: isize,
|
||||
src_x: usize,
|
||||
src_y: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
im: &dyn BitmapImage,
|
||||
operator: Operator,
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
use super::gdi::*;
|
||||
use super::*;
|
||||
use crate::bitmaps::*;
|
||||
use crate::color::Color;
|
||||
use crate::{Dimensions, Operator, PaintContext};
|
||||
use failure::Fallible;
|
||||
use std::io::Error as IoError;
|
||||
use std::ptr::{null, null_mut};
|
||||
@ -21,6 +23,10 @@ pub trait WindowCallbacks {
|
||||
|
||||
/// Called when the window is being destroyed by the gui system
|
||||
fn destroy(&mut self) {}
|
||||
|
||||
fn paint(&mut self, context: &mut dyn PaintContext) {
|
||||
// context.clear(Color::rgb(0, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
struct WindowInner {
|
||||
@ -238,8 +244,55 @@ fn enable_dark_mode(hwnd: HWND) {
|
||||
}
|
||||
}
|
||||
|
||||
struct GdiGraphicsContext {
|
||||
bitmap: GdiBitmap,
|
||||
}
|
||||
|
||||
impl PaintContext for GdiGraphicsContext {
|
||||
fn clear_rect(
|
||||
&mut self,
|
||||
dest_x: isize,
|
||||
dest_y: isize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
color: Color,
|
||||
) {
|
||||
self.bitmap.clear_rect(dest_x, dest_y, width, height, color)
|
||||
}
|
||||
|
||||
fn clear(&mut self, color: Color) {
|
||||
self.bitmap.clear(color);
|
||||
}
|
||||
|
||||
fn get_dimensions(&self) -> Dimensions {
|
||||
let (pixel_width, pixel_height) = self.bitmap.image_dimensions();
|
||||
Dimensions {
|
||||
pixel_width,
|
||||
pixel_height,
|
||||
dpi: 96,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_image_subset(
|
||||
&mut self,
|
||||
dest_x: isize,
|
||||
dest_y: isize,
|
||||
src_x: usize,
|
||||
src_y: usize,
|
||||
width: usize,
|
||||
height: usize,
|
||||
im: &dyn BitmapImage,
|
||||
operator: Operator,
|
||||
) {
|
||||
self.bitmap
|
||||
.draw_image_subset(dest_x, dest_y, src_x, src_y, width, height, im, operator)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn wm_paint(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) -> Option<LRESULT> {
|
||||
if let Some(_inner) = arc_from_hwnd(hwnd) {
|
||||
if let Some(inner) = arc_from_hwnd(hwnd) {
|
||||
let mut inner = inner.lock().unwrap();
|
||||
|
||||
let dc = GetDC(hwnd);
|
||||
|
||||
let mut rect = RECT {
|
||||
@ -252,20 +305,23 @@ unsafe fn wm_paint(hwnd: HWND, _msg: UINT, _wparam: WPARAM, _lparam: LPARAM) ->
|
||||
let width = rect_width(&rect) as usize;
|
||||
let height = rect_height(&rect) as usize;
|
||||
|
||||
let mut bitmap = GdiBitmap::new_compatible(width, height, dc).unwrap();
|
||||
if width > 0 && height > 0 {
|
||||
let bitmap = GdiBitmap::new_compatible(width, height, dc).unwrap();
|
||||
let mut context = GdiGraphicsContext { bitmap };
|
||||
|
||||
bitmap.clear(Color::rgb(0, 0, 0));
|
||||
BitBlt(
|
||||
dc,
|
||||
0,
|
||||
0,
|
||||
width as i32,
|
||||
height as i32,
|
||||
bitmap.hdc(),
|
||||
0,
|
||||
0,
|
||||
SRCCOPY,
|
||||
);
|
||||
inner.callbacks.paint(&mut context);
|
||||
BitBlt(
|
||||
dc,
|
||||
0,
|
||||
0,
|
||||
width as i32,
|
||||
height as i32,
|
||||
context.bitmap.hdc(),
|
||||
0,
|
||||
0,
|
||||
SRCCOPY,
|
||||
);
|
||||
}
|
||||
ValidateRect(hwnd, std::ptr::null());
|
||||
|
||||
Some(0)
|
||||
|
Loading…
Reference in New Issue
Block a user