1
1
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:
Wez Furlong 2019-08-08 14:00:27 -07:00
parent a88f0f1beb
commit 311760df90
4 changed files with 265 additions and 146 deletions

View File

@ -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
View 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()
}
}
}
}

View File

@ -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,
);
}

View File

@ -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)